/** * TemplateHashService - Hash-based change detection for CDK templates * * Replaces slow CDK diff with fast local hash comparison. * Uses SHA-256 hashing of synthesised templates. */ import { type Result } from "@fjall/generator"; import { BaseServiceError } from "../../types/errors/ServiceError.js"; /** * TemplateHash-specific error types */ export declare class TemplateHashError extends BaseServiceError { readonly errorType: "read_failed" | "hash_failed" | "state_write_failed" | "cdk_out_not_found"; constructor(message: string, errorType: "read_failed" | "hash_failed" | "state_write_failed" | "cdk_out_not_found", details?: unknown, recoverable?: boolean); } /** * Result of comparing template hashes with state */ export interface TemplateComparisonResult { /** Map of stack name to whether it has changes (true = changed or new) */ stackChanges: Map; /** Current hashes for all stacks */ currentHashes: Map; /** Number of stacks with changes */ changedCount: number; /** Number of stacks unchanged */ unchangedCount: number; } /** * Result of comparing a single cascade account's synthesised stack against its * per-account state file. */ export interface CascadeStackComparison { /** True when the stack is new, changed, or its hash could not be read. */ changed: boolean; /** Hash of the current synthesised template; undefined if hashing failed. */ currentHash: string | undefined; } /** * Template Hash Service - provides hash-based change detection */ export declare class TemplateHashService { /** * Compute SHA-256 hash of a template file */ computeTemplateHash(templatePath: string): Promise>; /** * Get hashes for all templates in cdk.out directory. * Returns map of stack name to hash. */ getTemplateHashes(cdkOutPath: string): Promise, TemplateHashError>>; /** * Compare current template hashes with stored state. * Returns which stacks have changed. */ compareWithState(currentHashes: Map, appPath: string): Promise>; /** * Update state file with new hashes after successful deployment */ updateStateAfterDeploy(appPath: string, deployedStacks: Map, stackStatuses?: Map): Promise>; /** * Remove state entries for stacks the caller has verified are BOTH no * longer synthesised and absent from CloudFormation. Self-heals the * phantom-drift class: a state-only entry for a stack that no longer * exists anywhere reports "changed" on every comparison forever, so the * plan gate can never converge to "no changes" after a stack removal. * Entries for removed stacks that still exist in CFN must NOT be pruned — * their "changed" flag is the genuine removed-stack signal the * destruction gate consumes. * * Returns true only when a prune was actually written; false means there * was nothing to remove (no state file, or no matching entries) so * callers never report a heal that did not land. */ pruneStateEntries(appPath: string, stackNames: readonly string[]): Promise>; /** * Compare a single cascade account's synthesised stack against its * per-account state file. Returns `changed: true` on ANY read/hash failure so * an uncertain cascade always redeploys (never silently skips a needed deploy). */ compareCascadeStack(assemblyDir: string, stackName: string, stateFilePath: string): Promise; /** * Persist a single cascade account's stack hash after a successful deploy. * Writes to the per-account state file so the next same-source deploy can skip. */ persistCascadeStack(stateFilePath: string, stackName: string, hash: string, stackStatus?: string): Promise>; /** * Get state file path for an application (exposed for testing/logging) */ getStateFilePath(appPath: string): string; /** * Check if a specific stack has changes */ stackHasChanges(comparison: TemplateComparisonResult, stackName: string): boolean; /** * Get list of stacks that have changes */ getChangedStacks(comparison: TemplateComparisonResult): string[]; /** * Get list of stacks that are unchanged */ getUnchangedStacks(comparison: TemplateComparisonResult): string[]; }