import type { EnvironmentConfiguration } from "../../config/types.js"; import { type ItemWorkflowState } from "../../workflow/api/client.js"; import { type AuthoringRequestOptions } from "./graphql.js"; export type { ItemWorkflowState } from "../../workflow/api/client.js"; /** * Authoring GraphQL operations used by `scai hygiene audit` and `scai hygiene cleanup`. * * Schema verified against XM Cloud Authoring API by introspection * (2026-05-13). The shape of `SearchQueryInput`, `Item.versions`, * `ItemWorkflow`, `archivedItems`, and `deleteItemVersion` are pinned * here; if a tenant exposes a divergent schema, the call surfaces as * a `NETWORK` `ScaiError` with the upstream message preserved. * * Index name: `sitecore_master_index` is the conventional master-DB * index name on XM Cloud. Indexes are not user-renameable on XM Cloud, * but the search index is overridable per-call so callers can target * `sitecore_web_index` for published-state queries if needed. */ declare const DEFAULT_MASTER_INDEX = "sitecore_master_index"; declare const MEDIA_LIBRARY_ROOT = "/sitecore/media library"; export type SearchCriteriaType = "EXACT" | "STARTSWITH" | "CONTAINS" | "ENDSWITH" | "WILDCARD" | "SEARCH" | "RANGE" | "FUZZY" | "PROXIMITY" | "REGEXP"; export type SearchOperator = "MUST" | "SHOULD" | "NOT"; export interface SearchCriterion { field: string; value: string; criteriaType?: SearchCriteriaType; operator?: SearchOperator; } export interface SearchStatement { operator?: SearchOperator; criteria?: SearchCriterion; subStatements?: SearchStatement[]; } export interface SearchPaging { pageIndex?: number; pageSize?: number; } export interface SearchQuery { index?: string; language?: string; latestVersionOnly?: boolean; paging?: SearchPaging; searchStatement?: SearchStatement; filterStatement?: SearchStatement; sort?: { field: string; direction?: "ASCENDING" | "DESCENDING"; }; } export interface SearchResultItem { itemId: string; path: string; name: string; displayName?: string | null; templateId?: string | null; templateName?: string | null; language?: { name: string; } | null; version?: number | null; updatedDate?: string | null; createdDate?: string | null; database?: string | null; parentId?: string | null; } export interface SearchPage { totalCount: number; results: SearchResultItem[]; } export interface ItemVersion { itemId: string; version: number; versionName: string | null; language: { name: string; } | null; } export interface ItemField { fieldId: string; name: string; value: string; } export interface ArchivedItem { archivalId: string; itemId: string; name: string; originalLocation: string; archivedBy: string | null; archivedDate: string | null; parentId: string | null; } export interface DeleteItemVersionInput { itemId?: string; path?: string; language: string; version: number; database?: string; } export interface DeleteItemInput { itemId?: string; path?: string; database?: string; /** When false, item moves to archive instead of full delete. Default true. */ permanently?: boolean; } export interface ArchiveVersionInput { itemId?: string; itemPath?: string; language: string; version: number; archiveName?: string; } export interface ItemTemplateSummary { templateId: string; name: string; fullName: string | null; /** itemId of the standard-values item, if one exists. Used to exclude * the SV item from the "item-count" check for dead-template detection. */ standardValuesItemId: string | null; } export interface ChildSummary { itemId: string; name: string; path: string; templateId: string | null; templateName: string | null; } export interface HygieneApiClient { search(query: SearchQuery): Promise; /** * Paged iteration over a search query. * * `parallel` controls how many page-windows are fetched * concurrently after the first page reveals `totalCount`. Defaults * to 1 (serial, the original behavior). Set to 4+ for big tenants * where the search-crawl phase dominates wall-clock. Result order * is NOT stable across parallel runs — callers that need ordered * results should sort the final accumulated set. */ searchAll(query: SearchQuery, perPage?: number, parallel?: number): AsyncIterable; getItemFields(selector: { itemId?: string; path?: string; }): Promise; getItemFieldsBatch(itemIds: readonly string[]): Promise>; itemExists(itemId: string): Promise; itemsExistBatch(itemIds: readonly string[]): Promise>; getItemVersions(selector: { itemId?: string; path?: string; language?: string; }): Promise; getItemWorkflow(itemId: string, path: string): Promise; listArchivedItems(options?: { archiveName?: string; pageIndex?: number; pageSize?: number; }): Promise; deleteItemVersion(input: DeleteItemVersionInput): Promise; /** Permanently delete an item (or move to archive when `permanently: false`). */ deleteItem(input: DeleteItemInput): Promise; /** Delete an item template (master DB). Throws if items still derive from it. */ deleteItemTemplate(templateId: string, database?: string): Promise; /** Purge a single record from the archive. */ deleteArchivedItem(archivalId: string, archiveName?: string): Promise; /** Archive a version (soft alternative to deleteItemVersion). */ archiveVersion(input: ArchiveVersionInput): Promise; /** List item templates under a content-tree root. */ listItemTemplates(options?: { rootPath?: string; database?: string; pageSize?: number; }): Promise; /** Direct children of an item (one level), keyed for folder-empty checks. */ getChildren(selector: { itemId?: string; path?: string; }): Promise; /** * Apply field updates to a single item. Fields are passed by name + * new value. Used by `cleanup find-replace`. Authoring API resolves * field names against the item's template. */ updateItemFields(input: { itemId: string; fields: Array<{ name: string; value: string; }>; }): Promise; /** * Rename an item — sets the item's `name` (which becomes the path * slug). Backed by the same `updateItem` mutation as updateItemFields; * called separately so the cleanup task can validate the new name * shape (no slashes, non-empty) before the wire call. */ renameItem(input: { itemId: string; name: string; }): Promise; /** * Create a new versioned form of an item in the given language. * Used by `cleanup language-version-add` to seed empty translation * stubs so translators can pick them up without per-item clicking. */ addItemVersion(input: { itemId: string; language: string; /** Optional source version to copy fields from. */ baseVersion?: number; }): Promise<{ versionNumber: number | null; }>; /** Page through every user. */ listUsers(options?: { pageSize?: number; }): Promise; /** Page through every role. Returns name + memberCount. */ listRoles(options?: { pageSize?: number; }): Promise; /** * Fetch a single user's roles + profile.lastActivity. Used by the * stale-user audit; one round trip per user, kept here so audits * can wrap with bounded concurrency. */ getUserDetail(userName: string): Promise; /** Delete a user account. */ deleteUser(userName: string): Promise; /** Delete a role. */ deleteRole(roleName: string): Promise; /** * Execute a workflow command on an item. Used by * `cleanup workflow advance` to push stale in-flight items * to their next state. */ executeWorkflowCommand(input: { commandId: string; itemId?: string; path?: string; comments?: string; }): Promise<{ successful: boolean; nextStateId: string | null; message: string | null; }>; /** * Resolve a workflow's available commands at a given state. Used by * the cleanup task to map a human-friendly command name (e.g. * "Submit") to its commandId for the current state. */ /** * Resolve the workflow commands available for a specific item. * The Authoring API's `Workflow.commands` requires a * state-or-item context (`WorkflowStateOrItemQueryInput`) — same * workflow can expose different commands depending on the item's * current state. */ getWorkflowCommandsForItem(input: { workflowId: string; itemId: string; }): Promise>; } export interface UserSummary { name: string; isAdministrator: boolean; isAuthenticated: boolean; domain: string | null; } export interface RoleSummary { name: string; domain: string | null; memberCount: number; } export interface UserDetail { name: string; isAdministrator: boolean; roles: string[]; /** From UserProfile.lastLoginDate. */ lastLogin: string | null; /** From UserProfile.lastActivityDate (broader signal — any session activity). */ lastActivity: string | null; } export interface HygieneClientOptions { environment: EnvironmentConfiguration; request?: AuthoringRequestOptions; /** Override the search index. Defaults to `sitecore_master_index`. */ defaultIndex?: string; } export declare const createHygieneApiClient: (options: HygieneClientOptions) => HygieneApiClient; export { MEDIA_LIBRARY_ROOT, DEFAULT_MASTER_INDEX };