import { Logger } from "../../shared/logger.js"; import type { EnvironmentConfiguration, RootConfiguration } from "../../config/types.js"; import { type HygieneApiClient } from "../api/client.js"; import { type FieldCache } from "../cache.js"; import { type BaselineHandle } from "../baseline.js"; import { type OutputFormat } from "../output-adapters.js"; /** * Shared option shape, tenant resolution, and link-extraction helpers for * `scai hygiene audit` + `scai hygiene cleanup` tasks. Both groups read through the same * Authoring API client and produce JSON-shaped results. */ export interface HygieneCommonOptions { config?: string; environmentName?: string; verbose?: boolean; trace?: boolean; quiet?: boolean; json?: boolean; logFile?: string; nonInteractive?: boolean; } export declare const toLogger: (options: HygieneCommonOptions) => Logger; export interface ResolvedHygieneTenant { envName: string; environment: EnvironmentConfiguration; root: RootConfiguration; client: HygieneApiClient; } export declare const resolveTenant: (options: HygieneCommonOptions) => ResolvedHygieneTenant; /** Print either a JSON envelope (when `--json`) or a heading + items table. */ export interface PrintReportOptions { logger: Logger; command: string; envName: string; results: T[]; /** Returns one human-readable line per result for non-JSON output. */ formatLine: (item: T) => string; /** Optional headline shown above the list. Defaults to `${results.length} item(s) found`. */ summary?: string; extra?: Record; /** * The audit's task options. When provided, enables baseline * filtering (`options.baseline`), output redirect (`options.output`), * and format selection (`options.format`). Each audit passes its * own `options` object through. */ options?: { baseline?: boolean; output?: string; format?: OutputFormat; config?: string; environmentName?: string; }; } export declare const printReport: (params: PrintReportOptions) => void; /** * Extended print helper used by every audit task runner. Adds: * * - **Baseline filtering** (`options.baseline`): splits findings * into "new" vs. "ignored" using the per-env baseline file. * - **Output redirection** (`options.output` / `options.format`): * writes a serialized report to a file instead of (or in * addition to) printing the JSON envelope to stdout. * * `auditName` is the bare audit slug used as the baseline key * (e.g. `"broken-links"`). Without it, the `--baseline` flag is a * no-op with a warning. */ export interface FinishAuditOptions extends PrintReportOptions { /** Bare audit name (e.g. "broken-links"). Required for baseline filtering. */ auditName?: string; /** * Pass `options.baseline`, `options.output`, `options.format`, plus * `options.config` through here so this helper can locate the * baseline file and the output target. */ options?: { baseline?: boolean; output?: string; format?: OutputFormat; config?: string; environmentName?: string; }; } export declare const finishAudit: (params: FinishAuditOptions) => void; export type { BaselineHandle }; /** Normalize a Sitecore itemId to lowercase, no dashes, no braces (search-index form). */ export declare const normalizeItemId: (raw: string) => string; /** Dashify a flat 32-char itemId back to canonical 8-4-4-4-12. */ export declare const dashifyItemId: (flat: string) => string; /** * Extract internal Sitecore itemId references from a field value. * * Recognises three field-value shapes: * * 1. Bare GUID — `{11111111-...}` or unwrapped uuid. Used by single-item-ref * fields (Droplink, General Link, Droptree, etc.). * 2. Pipe-delimited GUIDs — `{guid1}|{guid2}|...`. Used by Multilist * (Treelist, Multilist, TreelistEx, MultilistEx). * 3. RichText `` tags — `` * (Sitecore RichText editor inserts internal links this way). * External (`linktype="external"`) and media (`linktype="media"`) tags * are ignored at this layer — media refs are surfaced separately by * `extractMediaReferences`. * * Returns normalized lowercase no-dash itemIds (search-index form) so a * Set comparison with `_path` / `parentId` fields from search results * compares clean. */ export declare const extractInternalRefs: (value: string) => string[]; /** * Extract media item references — refs that point at items under the * `/sitecore/media library` tree. * * Recognises: * 1. RichText `` tags. * 2. Image-field XML — `` (Sitecore's Image * field stores this XML in field values). * 3. Bare GUIDs in Multilist-style fields targeting media items (caller * passes those through `extractInternalRefs` and resolves against the * media library at a higher layer). * * Returns normalized itemIds. Callers de-dup with a Set; ordering is not * preserved. */ export declare const extractMediaRefs: (value: string) => string[]; /** * Build a SearchStatement that limits results to descendants of the given * itemId (i.e. items whose `_path` indexed field contains the ancestor's * itemId). Used to scope audits to a sub-tree. */ export declare const buildPathFilterStatement: (ancestorItemId: string) => { criteria: { field: string; value: string; criteriaType: "CONTAINS"; }; }; /** * Sitecore-controlled item paths that audits should skip by default. * * `/sitecore/system` and `/sitecore/templates/System` carry hundreds of * thousands of platform-supplied items that aren't user-authored and * aren't actionable hygiene targets. Without this filter, `audit * broken-links list` and `audit stale-workflow list` would surface * platform noise that the operator can't fix. * * Operators can opt in to scanning system items with `--include-system`. */ export declare const SYSTEM_PATH_PREFIXES: string[]; export declare const isSystemPath: (path: string) => boolean; /** * Resolved performance knobs for hygiene audits. Reads precedence: * * 1. Explicit per-call option (when `--concurrency` / `--batch-size` * is passed on the command line). * 2. Environment variable (`SITECOREAI_HYGIENE_CONCURRENCY`, * `SITECOREAI_HYGIENE_BATCH_SIZE`, `SITECOREAI_HYGIENE_PAGE_PARALLELISM`). * 3. Built-in defaults — biased toward parallelism since XM Cloud * tolerates moderate fan-out and the transport already absorbs * 429/503 backoff. * * Defaults vs. the original 4/25: * - concurrency 8 (was 4) — doubles field-read throughput. * - batchSize 50 (was 25) — halves the number of aliased queries * for the same item set; payload stays under the typical 1-2MB * GraphQL request cap. * - pageParallelism 4 (was 1, i.e. serial) — once the first page * reveals totalCount, fetch up to 4 page-windows concurrently. * * Operators can dial these down for restricted tenants or up for * faster cold runs. */ export interface HygienePerfKnobs { concurrency: number; batchSize: number; pageParallelism: number; } export declare const resolveHygieneKnobs: (options: { concurrency?: number; batchSize?: number; pageParallelism?: number; }) => HygienePerfKnobs; /** * Sitecore field names that hold layout rendering XML. These are the * fields parsed by `extractRenderingDatasources` and * `extractPersonalizationVariantRefs`. * * - `__Renderings` — shared (multi-language) presentation layer. * - `__Final Renderings` — per-language presentation overlay. * - `__Renderings (Page)` — page-design-tier layout (rare; only on Page * Design items). */ export declare const RENDERING_FIELDS: string[]; export declare const isRenderingField: (fieldName: string) => boolean; /** * Extract datasource references from a rendering-layout XML value. * * Sitecore stores rendering presentation as XML like: * * * * The `ds` (or sometimes `s:ds`) attribute on each `` element holds * the datasource — either a content-tree path, a bare itemId, or a * Sitecore query (`query:./ancestor-or-self::*[@@templatename='Site']/Data`). * Queries are dynamic; they're returned with a `query:` prefix so the * caller can decide whether to skip them. Empty `ds` values are * omitted from the result. * * Returns values verbatim (path / itemId / `query:…`) for the caller to * resolve. */ export declare const extractRenderingDatasources: (renderingsXml: string) => Array<{ datasource: string; renderingId: string | null; }>; /** * Extract personalization variant + rule references from a rendering-layout * XML value. * * Personalization is stored in `` elements nested under ``: * * * * * ... * * * * * * * * Returns refs found in `datasource=` attributes of `` elements * AND in rule `s:set=` attributes (the rule-set itemId). */ export declare const extractPersonalizationRefs: (renderingsXml: string) => string[]; /** * Compute a content hash over an item's authored fields, suitable for * exact-duplicate detection. * * Includes only authored fields (not `__`-prefixed system fields, which * carry per-item metadata like `__Created`, `__Updated`, `__Lock`). * Field name + trimmed value joined by `\0`, fields sorted by name for * determinism. SHA-256, returned as 16-char hex (sufficient to bucket * tenant-scale item counts without realistic collision). */ export declare const computeContentHash: (fields: ReadonlyArray<{ name: string; value: string; }>, options?: { includeSystem?: boolean; }) => Promise; /** * Sitecore's `__Final Page Design` (or fallback `__Page Design`) field * holds a page design itemId GUID. Empty string means "inherit from * ancestor / no override." */ export declare const PAGE_DESIGN_FIELDS: string[]; export declare const isPageDesignField: (fieldName: string) => boolean; /** * Common scan-then-fetch pipeline used by every field-reading audit. * * Combines: * 1. Root-path → itemId resolution (one search call). * 2. Paged enumeration via `searchAll(parallel: knobs.pageParallelism)`, * with system-path filter applied at gather time. * 3. Batched field reads with bounded concurrency (`knobs.concurrency`) * and `knobs.batchSize` per aliased GraphQL query. * 4. Optional cross-audit field cache (opt-in via `--cache` / env). * * Returns the scanned item set and the fields-by-itemId map. The cache * is also returned so callers can `await cache.flush()` at audit end. * * Callers that DON'T need fields (e.g. `audit dead-templates` enumerates * templates, not field-bearing items) should not use this helper. */ export interface ScanItem { itemId: string; path: string; name: string; templateName: string | null; templateId: string | null; language: string | null; version: number | null; createdDate: string | null; updatedDate: string | null; } export interface ScanFieldsResult { scanned: ScanItem[]; fieldsByItemId: Map; cache: FieldCache | null; knobs: HygienePerfKnobs; } export interface ItemFieldRecord { fieldId: string; name: string; value: string; } export interface ScanItemsAndFieldsParams { client: import("../api/client").HygieneApiClient; envName: string; root: string; logger: Logger; options: HygieneCommonOptions & { limit?: number; batchSize?: number; concurrency?: number; pageParallelism?: number; includeSystem?: boolean; language?: string; index?: string; cache?: boolean; /** Repeatable: skip items whose path starts with any of these. */ exclude?: string[]; /** ISO date (YYYY-MM-DD or full ISO): only items updated on/after this date. */ since?: string; /** Filter by createdBy or updatedBy. */ owner?: string; }; /** Override `latestVersionOnly` on the search. Default true. */ latestVersionOnly?: boolean; /** Skip the field-fetch phase entirely (returns empty fields map). */ skipFields?: boolean; } /** * Cross-cutting result-filter helpers — applied during the * enumeration phase before items are accumulated. Exposed here so * audits that don't go through `scanItemsAndFields` (orphans, * dead-templates) can re-use the same predicates. */ export interface ScanFilters { excludePaths?: string[]; sinceMs?: number; owner?: string; } export declare const resolveScanFilters: (options: { exclude?: string[]; since?: string; owner?: string; }) => ScanFilters; /** Returns true if the search result should be excluded by the filters. */ export declare const matchesScanFilters: (r: { path?: string; updatedDate?: string | null; createdDate?: string | null; }, filters: ScanFilters) => boolean; export declare const scanItemsAndFields: ({ client, envName, root, logger, options, latestVersionOnly, skipFields, }: ScanItemsAndFieldsParams) => Promise; export { ensureAllowWrite } from "../../policy/allow-write.js";