@truefoundry/tfy-infra-cli 0.1.4-canary.c42a870 → 0.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -16,5 +16,5 @@ ${t.length} file(s) changed:
16
16
  `);if(m!==l){let p=V(l,m,i);n.push({file:i,diff:p})}}catch{n.push({file:i})}}let o=Date.now()-r;if(n.length===0)s(`[test] \u2713 default: passed (${o}ms)`);else{g("[test] \u2717 default: failed");for(let i of n)if(s(` ${i.file}: content mismatch`),i.diff){let a=i.diff.split(`
17
17
  `).slice(4,14);for(let c of a)c&&s(` ${c}`);i.diff.split(`
18
18
  `).length>14&&s(" ... (diff truncated)")}}}var Ot=require("commander"),ye=require("path"),bt=require("@truefoundry/tfy-infra-engine");var Er=1;function Tt(){return new Ot.Command("verify").description("Check integrity of files on disk against a manifest").option("-d, --directory <dir>","Path to cluster directory containing manifest.json",".").option("--json","Output drift report as structured JSON").action(async e=>{try{let r=await xr(e);process.exit(r)}catch(r){S(r)}})}async function xr(t){let e=(0,ye.resolve)(t.directory),r=(0,ye.join)(e,"manifest.json");y(`Loading manifest from ${r}`);let n=await ee(r);y(`Reading files from ${e}`);let o=await pe(e,n.platformPrefix),a=await(0,bt.createEngine)().verify(o,n);return t.json?s(br(a.driftReport)):s(Or(a.driftReport)),a.driftReport.valid?d.SUCCESS:Er}function Or(t){let e=[],r=t.summary.driftedFiles+t.summary.missingFiles+t.summary.unexpectedFiles;if(t.valid)e.push(`\u2713 Verification passed: ${t.summary.totalFiles} files checked, ${r} issues`),e.push(""),e.push(`Aggregate Hash: ${t.aggregateHash.expected}`);else{e.push(`\u2717 Drift detected: ${r} issues found`),e.push("");for(let n of t.entries){let o=ue(n.type);e.push(` ${o} ${n.path}`),e.push(` ${n.details}`),e.push("")}e.push(`Summary: ${t.summary.totalFiles} files checked, ${t.summary.driftedFiles} drifted, ${t.summary.missingFiles} missing`+(t.summary.unexpectedFiles>0?`, ${t.summary.unexpectedFiles} unexpected`:""))}return e.join(`
19
- `)}function br(t){return JSON.stringify(t,null,2)}function It(){let t=new St.Command("tpl").description("Template operations").enablePositionalOptions();return t.addCommand(Me()),t.addCommand(Rt()),t.addCommand(it()),t.addCommand(lt()),t.addCommand(gt()),t.addCommand(Tt()),t}process.env.INIT_CWD&&process.chdir(process.env.INIT_CWD);var Tr="0.1.4-canary.c42a870";async function Sr(){let t=new Ft.Command;t.name("tfy-init").description("TrueFoundry infrastructure templating CLI").enablePositionalOptions().version(Tr,"-v, --version","Show version").option("--verbose","Enable verbose output").option("-q, --quiet","Suppress non-error output").hook("preAction",e=>{let r=e.opts();r.quiet?Re("quiet"):r.verbose&&Re("verbose")}),t.addCommand(It()),await t.parseAsync(process.argv)}Sr().catch(t=>{console.error("Fatal error:",t),process.exit(1)});
19
+ `)}function br(t){return JSON.stringify(t,null,2)}function It(){let t=new St.Command("tpl").description("Template operations").enablePositionalOptions();return t.addCommand(Me()),t.addCommand(Rt()),t.addCommand(it()),t.addCommand(lt()),t.addCommand(gt()),t.addCommand(Tt()),t}process.env.INIT_CWD&&process.chdir(process.env.INIT_CWD);var Tr="0.1.4";async function Sr(){let t=new Ft.Command;t.name("tfy-init").description("TrueFoundry infrastructure templating CLI").enablePositionalOptions().version(Tr,"-v, --version","Show version").option("--verbose","Enable verbose output").option("-q, --quiet","Suppress non-error output").hook("preAction",e=>{let r=e.opts();r.quiet?Re("quiet"):r.verbose&&Re("verbose")}),t.addCommand(It()),await t.parseAsync(process.argv)}Sr().catch(t=>{console.error("Fatal error:",t),process.exit(1)});
20
20
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/commands/tpl/index.ts","../src/commands/tpl/bundle.ts","../src/utils/walk-src.ts","../src/utils/error-handler.ts","../src/utils/output.ts","../src/commands/tpl/render.ts","../src/resolvers/index.ts","../src/resolvers/file.ts","../src/resolvers/https.ts","../src/resolvers/base.ts","../src/cache/template-cache.ts","../src/utils/config-loader.ts","../src/utils/fs-helpers.ts","../src/utils/manifest-loader.ts","../src/utils/file-reader.ts","../src/utils/file-writer.ts","../src/utils/diff.ts","../src/commands/tpl/validate.ts","../src/commands/tpl/test.ts","../src/commands/tpl/dev.ts","../src/utils/file-watcher.ts","../src/commands/tpl/verify.ts"],"sourcesContent":["/**\n * @truefoundry/tfy-infra-cli\n *\n * CLI for TrueFoundry infrastructure templating engine.\n * Binary: tfy-init\n */\n\nimport { Command } from 'commander';\nimport { createTplCommand } from './commands/tpl/index.js';\nimport { setVerbosity } from './utils/output.js';\n\n// When invoked via `npm run`, npm sets CWD to the package root.\n// INIT_CWD preserves the directory the user actually ran the command from.\nif (process.env['INIT_CWD']) {\n process.chdir(process.env['INIT_CWD']);\n}\n\ndeclare const __CLI_VERSION__: string;\nconst VERSION = __CLI_VERSION__;\n\n/**\n * Main CLI entry point.\n */\nasync function main(): Promise<void> {\n const program = new Command();\n\n program\n .name('tfy-init')\n .description('TrueFoundry infrastructure templating CLI')\n .enablePositionalOptions()\n .version(VERSION, '-v, --version', 'Show version')\n .option('--verbose', 'Enable verbose output')\n .option('-q, --quiet', 'Suppress non-error output')\n .hook('preAction', (thisCommand) => {\n const opts = thisCommand.opts();\n if (opts['quiet']) {\n setVerbosity('quiet');\n } else if (opts['verbose']) {\n setVerbosity('verbose');\n }\n });\n\n // Add command groups\n program.addCommand(createTplCommand());\n\n // Parse and execute\n await program.parseAsync(process.argv);\n}\n\n// Run CLI\nmain().catch((error) => {\n console.error('Fatal error:', error);\n process.exit(1);\n});\n","/**\n * tfy-init tpl command group\n *\n * CHANGE (006): Removed push command (S3 support removed).\n * CHANGE (008): Removed init command (templates scaffolded via CUE).\n */\n\nimport { Command } from 'commander';\nimport { createBundleCommand } from './bundle.js';\nimport { createRenderCommand } from './render.js';\nimport { createValidateCommand } from './validate.js';\nimport { createTestCommand } from './test.js';\n\nimport { createDevCommand } from './dev.js';\nimport { createVerifyCommand } from './verify.js';\n/**\n * Create the tpl command group with all subcommands.\n */\nexport function createTplCommand(): Command {\n const tpl = new Command('tpl').description('Template operations').enablePositionalOptions();\n\n tpl.addCommand(createBundleCommand());\n tpl.addCommand(createDevCommand());\n tpl.addCommand(createRenderCommand());\n tpl.addCommand(createValidateCommand());\n tpl.addCommand(createTestCommand());\n\n tpl.addCommand(createVerifyCommand());\n\n return tpl;\n}\n","/**\n * tfy-init tpl bundle command\n *\n * Accepts a pre-computed schema package (from tfy-schema-tool --schema-package)\n * plus a src/ directory and metadata flags, and produces a bundle.json.\n * No CUE or Go dependencies — pure TypeScript.\n */\n\nimport { Command } from 'commander';\nimport { readFile, writeFile, mkdir } from 'node:fs/promises';\nimport { resolve, dirname } from 'node:path';\nimport { walkSrcDir } from '../../utils/walk-src.js';\nimport { handleEngineError } from '../../utils/error-handler.js';\nimport * as output from '../../utils/output.js';\n\ninterface SchemaPackage {\n jsonSchema: Record<string, unknown>;\n uiSchema: unknown[];\n}\n\ninterface BundleJson {\n metadata: {\n name: string;\n version: string;\n description?: string;\n };\n jsonSchema: Record<string, unknown>;\n uiSchema: unknown[];\n files: Record<string, string>;\n staticFiles: Record<string, string>;\n version: {\n semver: string;\n };\n}\n\ninterface BundleOptions {\n schema: string;\n src: string;\n name: string;\n version?: string;\n description?: string;\n output?: string;\n}\n\nexport function createBundleCommand(): Command {\n const cmd = new Command('bundle')\n .description('Build a bundle.json from a schema package and src/ directory')\n .requiredOption('--schema <path-or-dash>', 'Schema package JSON file, or \"-\" for stdin')\n .requiredOption('--src <dir>', 'Template source directory containing .hbs and static files')\n .requiredOption('--name <string>', 'Template name')\n .option('--description <string>', 'Template description')\n .option('-o, --output <path>', 'Output file path (default: stdout)')\n .action(async (options: BundleOptions) => {\n try {\n await runBundle(options);\n } catch (error) {\n handleEngineError(error);\n }\n });\n\n return cmd;\n}\n\nasync function readSchemaPackage(schemaPath: string): Promise<SchemaPackage> {\n let raw: string;\n\n if (schemaPath === '-') {\n raw = await readStdin();\n } else {\n raw = await readFile(resolve(schemaPath), 'utf-8');\n }\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch (err) {\n throw new Error(`Failed to parse schema package JSON: ${(err as Error).message}`, {\n cause: err,\n });\n }\n\n const obj = parsed as Record<string, unknown>;\n if (!obj['jsonSchema'] || typeof obj['jsonSchema'] !== 'object') {\n throw new Error('Schema package must contain a \"jsonSchema\" object');\n }\n if (!Array.isArray(obj['uiSchema'])) {\n throw new Error('Schema package must contain a \"uiSchema\" array');\n }\n\n return {\n jsonSchema: obj['jsonSchema'] as Record<string, unknown>,\n uiSchema: obj['uiSchema'] as unknown[],\n };\n}\n\nfunction readStdin(): Promise<string> {\n return new Promise((resolve, reject) => {\n const chunks: Buffer[] = [];\n process.stdin.on('data', (chunk: Buffer) => chunks.push(chunk));\n process.stdin.on('end', () => resolve(Buffer.concat(chunks).toString('utf-8')));\n process.stdin.on('error', reject);\n });\n}\n\nfunction extractVersionFromSchema(jsonSchema: Record<string, unknown>): string | undefined {\n const props = jsonSchema['properties'] as Record<string, unknown> | undefined;\n if (!props) return undefined;\n const versionProp = props['version'] as Record<string, unknown> | undefined;\n if (!versionProp) return undefined;\n const def = versionProp['default'];\n return typeof def === 'string' ? def : undefined;\n}\n\nasync function runBundle(options: BundleOptions): Promise<void> {\n const schemaPkg = await readSchemaPackage(options.schema);\n\n const version = options.version ?? extractVersionFromSchema(schemaPkg.jsonSchema);\n if (!version) {\n throw new Error(\n 'Could not determine template version. Provide --version or ensure the schema has a \"version\" property with a default value.'\n );\n }\n\n const srcDir = resolve(options.src);\n const { files, staticFiles } = await walkSrcDir(srcDir);\n\n if (files.size === 0 && staticFiles.size === 0) {\n throw new Error(`No template files found in ${srcDir}`);\n }\n\n const metadata: BundleJson['metadata'] = {\n name: options.name,\n version,\n };\n if (options.description) {\n metadata.description = options.description;\n }\n\n const bundle: BundleJson = {\n metadata,\n jsonSchema: schemaPkg.jsonSchema,\n uiSchema: schemaPkg.uiSchema,\n files: Object.fromEntries(files),\n staticFiles: Object.fromEntries(staticFiles),\n version: { semver: version },\n };\n\n const json = JSON.stringify(bundle, null, 2) + '\\n';\n\n if (options.output) {\n const outPath = resolve(options.output);\n await mkdir(dirname(outPath), { recursive: true });\n await writeFile(outPath, json, 'utf-8');\n output.success(`Bundle written to ${outPath}`);\n } else {\n process.stdout.write(json);\n }\n}\n","/**\n * Recursive directory walker for template src/ directories.\n *\n * Walks a directory tree and classifies files into Handlebars templates\n * (.hbs → rendered) and static files (copied verbatim). Skips entries\n * whose names start with '_'. Map keys are relative paths from the\n * root directory, preserving subdirectory structure.\n */\n\nimport { readdir, readFile } from 'node:fs/promises';\nimport { join } from 'node:path';\n\nexport interface WalkResult {\n files: Map<string, string>;\n staticFiles: Map<string, string>;\n}\n\n/**\n * Recursively walk a directory, reading all files into maps.\n *\n * @param rootDir - The src/ directory to walk\n * @returns Maps of relative-path → content, split by .hbs (rendered) vs static\n */\nexport async function walkSrcDir(rootDir: string): Promise<WalkResult> {\n const files = new Map<string, string>();\n const staticFiles = new Map<string, string>();\n\n await walk(rootDir, '', files, staticFiles);\n\n return { files, staticFiles };\n}\n\nasync function walk(\n rootDir: string,\n relativePath: string,\n files: Map<string, string>,\n staticFiles: Map<string, string>\n): Promise<void> {\n const currentDir = relativePath ? join(rootDir, relativePath) : rootDir;\n const entries = await readdir(currentDir, { withFileTypes: true });\n\n for (const entry of entries) {\n if (entry.name.startsWith('_')) continue;\n\n const entryRelative = relativePath ? `${relativePath}/${entry.name}` : entry.name;\n\n if (entry.isDirectory()) {\n await walk(rootDir, entryRelative, files, staticFiles);\n } else {\n const content = await readFile(join(currentDir, entry.name), 'utf-8');\n if (entry.name.endsWith('.hbs')) {\n const outputPath = entryRelative.replace(/\\.hbs$/, '');\n files.set(outputPath, content);\n } else {\n staticFiles.set(entryRelative, content);\n }\n }\n }\n}\n","/**\n * Shared error handling for CLI commands.\n *\n * Provides consistent error output and exit code mapping across all\n * tfy-init tpl commands.\n */\n\nimport { EngineError, EngineErrorCode } from '@truefoundry/tfy-infra-engine';\nimport * as output from './output.js';\n\n/**\n * Standard exit codes shared across all commands.\n */\nexport const EXIT_CODES = {\n SUCCESS: 0,\n VALIDATION_ERROR: 1,\n FETCH_ERROR: 2,\n RENDER_ERROR: 3,\n FORMAT_ERROR: 4,\n DRIFT_BLOCKED: 5,\n} as const;\n\n/**\n * Handle an error from a CLI command and exit with the appropriate code.\n *\n * Provides consistent behavior:\n * - Displays the error message\n * - Shows validation error details when available\n * - Shows engine error details in verbose mode\n * - Maps EngineErrorCode to standardized exit codes\n * - Shows format hint for tofu-related errors\n *\n * @param error - The caught error\n * @param fallbackExitCode - Exit code for unrecognized errors (default: RENDER_ERROR)\n */\nexport function handleEngineError(\n error: unknown,\n fallbackExitCode: number = EXIT_CODES.RENDER_ERROR\n): never {\n if (error instanceof EngineError) {\n output.error(error.message);\n\n // Show engine error details in verbose mode\n if (output.getVerbosity() === 'verbose' && error.details) {\n output.info('');\n output.info('Details:');\n for (const [key, value] of Object.entries(error.details)) {\n if (key !== 'errors') {\n output.info(` ${key}: ${JSON.stringify(value)}`);\n }\n }\n }\n\n // Show validation error list when available\n if (error.details?.['errors']) {\n output.info('');\n output.info('Errors:');\n output.info(\n output.formatValidationErrors(\n error.details['errors'] as Array<{ path: string; message: string }>\n )\n );\n }\n\n // Map error codes to exit codes\n switch (error.code) {\n case EngineErrorCode.INPUT_VALIDATION_FAILED:\n case EngineErrorCode.SCHEMA_INVALID:\n case EngineErrorCode.ENVELOPE_VALIDATION_FAILED:\n process.exit(EXIT_CODES.VALIDATION_ERROR);\n break;\n\n case EngineErrorCode.TEMPLATE_NOT_FOUND:\n case EngineErrorCode.FILE_NOT_FOUND:\n case EngineErrorCode.NETWORK_ERROR:\n process.exit(EXIT_CODES.FETCH_ERROR);\n break;\n\n case EngineErrorCode.TOFU_NOT_FOUND:\n case EngineErrorCode.TOFU_FMT_FAILED:\n output.info('');\n output.info('Run with --skip-format to skip formatting.');\n process.exit(EXIT_CODES.FORMAT_ERROR);\n break;\n\n case EngineErrorCode.MANIFEST_PARSE_ERROR:\n process.exit(EXIT_CODES.VALIDATION_ERROR);\n break;\n\n default:\n process.exit(fallbackExitCode);\n }\n }\n\n // Generic (non-engine) error\n output.error((error as Error).message);\n if (output.getVerbosity() === 'verbose') {\n console.error((error as Error).stack);\n }\n process.exit(fallbackExitCode);\n}\n","/**\n * Output formatting utilities for CLI.\n *\n * Supports a JSON mode where human-readable output is redirected to stderr\n * so that stdout remains clean for structured machine output via `data()`.\n */\n\nimport type { DriftType } from '@truefoundry/tfy-infra-engine';\n\n/* eslint-disable no-console */\n\nexport type Verbosity = 'quiet' | 'normal' | 'verbose';\n\nlet currentVerbosity: Verbosity = 'normal';\nlet jsonMode = false;\n\nexport function setVerbosity(level: Verbosity): void {\n currentVerbosity = level;\n}\n\nexport function getVerbosity(): Verbosity {\n return currentVerbosity;\n}\n\n/**\n * Enable JSON mode: human-readable output routes to stderr,\n * structured output goes to stdout via `data()`.\n */\nexport function setJsonMode(enabled: boolean): void {\n jsonMode = enabled;\n}\n\nexport function getJsonMode(): boolean {\n return jsonMode;\n}\n\n/**\n * Write structured data to stdout. Use for JSON payloads\n * that machine consumers will parse.\n */\nexport function data(message: string): void {\n process.stdout.write(message + '\\n');\n}\n\nexport function success(message: string): void {\n if (currentVerbosity !== 'quiet') {\n const write = jsonMode ? console.error : console.log;\n write(`✓ ${message}`);\n }\n}\n\nexport function error(message: string): void {\n console.error(`✗ ${message}`);\n}\n\nexport function info(message: string): void {\n if (currentVerbosity !== 'quiet') {\n const write = jsonMode ? console.error : console.log;\n write(message);\n }\n}\n\nexport function verbose(message: string): void {\n if (currentVerbosity === 'verbose') {\n const write = jsonMode ? console.error : console.log;\n write(` ${message}`);\n }\n}\n\nexport function warn(message: string): void {\n if (currentVerbosity !== 'quiet') {\n console.warn(`⚠ ${message}`);\n }\n}\n\nexport function formatSize(bytes: number): string {\n if (bytes < 1024) return `${bytes} B`;\n if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;\n return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;\n}\n\nexport function formatValidationErrors(errors: Array<{ path: string; message: string }>): string {\n return errors.map((e) => ` • ${e.path}: ${e.message}`).join('\\n');\n}\n\nexport function driftTypeTag(type: DriftType): string {\n switch (type) {\n case 'content_drift':\n return 'DRIFTED';\n case 'missing_file':\n return 'MISSING';\n case 'unexpected_file':\n return 'UNEXPECTED';\n case 'metadata_inconsistency':\n return 'INCONSISTENT';\n case 'source_mismatch':\n return 'SOURCE MISMATCH';\n }\n}\n\nexport function printFileList(files: Array<{ path: string; size: number }>): void {\n if (currentVerbosity === 'quiet') return;\n\n const write = jsonMode ? console.error : console.log;\n write('Files:');\n for (const file of files) {\n write(` - ${file.path} (${formatSize(file.size)})`);\n }\n}\n","/**\n * tfy-init tpl render command\n *\n * Unified render command that handles fresh install, upgrade, and\n * manifest re-generation via auto-detection.\n *\n * Config mode auto-detects upgrade when manifest.json exists in the\n * output directory. Manifest mode always performs a fresh install\n * (disaster recovery). Dev mode renders from a local template directory.\n */\n\nimport { Command } from 'commander';\nimport { createInterface } from 'node:readline';\nimport { readFile, readdir, access } from 'node:fs/promises';\nimport { join, resolve } from 'node:path';\nimport * as yaml from 'js-yaml';\nimport { createEngine } from '@truefoundry/tfy-infra-engine';\nimport type {\n Envelope,\n InstallResult,\n UpgradeResult,\n DriftReport,\n FileMap,\n} from '@truefoundry/tfy-infra-engine';\nimport { createResolverRegistry } from '../../resolvers/index.js';\nimport { loadConfig } from '../../utils/config-loader.js';\nimport { loadManifest, envelopeFromManifest } from '../../utils/manifest-loader.js';\nimport { readDirectoryToFileMap } from '../../utils/file-reader.js';\nimport { writeEngineOutput } from '../../utils/file-writer.js';\nimport { handleEngineError, EXIT_CODES } from '../../utils/error-handler.js';\nimport * as output from '../../utils/output.js';\nimport { driftTypeTag } from '../../utils/output.js';\nimport { findDefaultFixture } from '../../utils/fs-helpers.js';\nimport { generateUnifiedDiff, type DiffEntry } from '../../utils/diff.js';\n\nexport { EXIT_CODES };\n\nexport interface RenderArgs {\n config?: string;\n directory?: string;\n fromManifest?: string;\n}\n\nexport function validateRenderArgs(args: RenderArgs): void {\n const modes = [args.config, args.fromManifest].filter(Boolean);\n if (modes.length > 1) {\n throw new Error('--config and --from-manifest are mutually exclusive. Use one or the other.');\n }\n if (args.config && args.directory !== '.') {\n throw new Error('--config and --directory are mutually exclusive. Use one or the other.');\n }\n}\n\nexport function createRenderCommand(): Command {\n const cmd = new Command('render')\n .description('Render a template to HCL files')\n .option('-c, --config <path>', 'Path to envelope config file (YAML or JSON)')\n .option('-d, --directory <dir>', 'Path to template version directory', '.')\n .option('-f, --fixture <path>', 'Input file to use (when using --directory)')\n .option('-i, --input <key=value>', 'Inline input override (can be repeated)', collect, [])\n .requiredOption('-o, --output <dir>', 'Output directory')\n .option('--from-manifest <path>', 'Path to manifest.json to use as input source')\n .option('--force', 'Proceed even if managed files have drifted (upgrade mode)')\n .option('--allow-source-change', 'Allow upgrade when template source has changed')\n .option('--dry-run', 'Show what would change without writing files')\n .option('--json', 'Output result as structured JSON')\n .option('--skip-format', 'Skip tofu fmt formatting')\n .option('--no-cache', 'Force re-fetch template')\n .option('--timeout <ms>', 'Network timeout in milliseconds', '30000')\n .option('--intent-id <id>', 'Cluster identity for integrity tracking')\n .action(async (options: RenderOptions) => {\n try {\n if (options.config) options.config = resolve(options.config);\n if (options.fromManifest) options.fromManifest = resolve(options.fromManifest);\n\n validateRenderArgs({\n config: options.config,\n directory: options.directory,\n fromManifest: options.fromManifest,\n });\n const exitCode = await runRender(options);\n process.exit(exitCode);\n } catch (error) {\n handleEngineError(error);\n }\n });\n\n return cmd;\n}\n\nfunction collect(value: string, previous: string[]): string[] {\n return [...previous, value];\n}\n\ninterface RenderOptions {\n config?: string;\n directory: string;\n fixture?: string;\n input?: string[];\n output: string;\n fromManifest?: string;\n force?: boolean;\n allowSourceChange?: boolean;\n dryRun?: boolean;\n json?: boolean;\n skipFormat?: boolean;\n cache?: boolean;\n timeout: string;\n intentId?: string;\n}\n\nexport { findDefaultFixture } from '../../utils/fs-helpers.js';\n\nexport function parseInputOverrides(inputs?: string[]): Record<string, string> {\n const result: Record<string, string> = {};\n if (!inputs) return result;\n\n for (const input of inputs) {\n const eqIndex = input.indexOf('=');\n if (eqIndex > 0) {\n const key = input.substring(0, eqIndex);\n const value = input.substring(eqIndex + 1);\n result[key] = value;\n }\n }\n\n return result;\n}\n\nasync function runRender(options: RenderOptions): Promise<number> {\n if (options.json) {\n output.setJsonMode(true);\n }\n\n const outputDir = resolve(options.output);\n const timeout = parseInt(options.timeout, 10);\n\n if (options.fromManifest) {\n return runRenderFromManifest(options.fromManifest, options, outputDir, timeout);\n }\n\n if (options.config) {\n const manifestPath = join(outputDir, 'manifest.json');\n if (await fileExists(manifestPath)) {\n return runRenderFromConfigUpgrade(options.config, options, outputDir, timeout, manifestPath);\n }\n return runRenderFromConfig(options.config, options, outputDir, timeout);\n }\n\n const templateDir = resolve(options.directory);\n return runRenderFromDirectory(templateDir, options, outputDir, timeout);\n}\n\nasync function fileExists(path: string): Promise<boolean> {\n try {\n await access(path);\n return true;\n } catch {\n return false;\n }\n}\n\nasync function confirmPrompt(message: string): Promise<boolean> {\n if (!process.stdin.isTTY) return false;\n const rl = createInterface({ input: process.stdin, output: process.stderr });\n return new Promise((resolve) => {\n rl.question(`${message} [y/N] `, (answer) => {\n rl.close();\n resolve(answer.trim().toLowerCase() === 'y');\n });\n });\n}\n\nasync function countExistingFiles(dir: string): Promise<number> {\n try {\n const entries = await readdir(dir, { withFileTypes: true });\n return entries.filter((e) => e.isFile() && e.name !== 'config.yaml' && e.name !== 'config.json')\n .length;\n } catch {\n return 0;\n }\n}\n\nasync function guardOverwrite(outputDir: string, options: RenderOptions): Promise<number | null> {\n if (options.force) return null;\n const count = await countExistingFiles(outputDir);\n if (count === 0) return null;\n\n output.warn(`Output directory contains ${count} existing files that will be overwritten.`);\n const confirmed = await confirmPrompt('Proceed?');\n if (confirmed) return null;\n\n if (options.json) {\n output.data(\n JSON.stringify(\n { aborted: true, reason: 'overwrite_confirmation', existingFiles: count },\n null,\n 2\n )\n );\n } else {\n output.info('Aborted. Use --force to skip confirmation.');\n }\n return EXIT_CODES.VALIDATION_ERROR;\n}\n\n/**\n * Render from a manifest file (re-render / disaster recovery).\n */\nasync function runRenderFromManifest(\n manifestPath: string,\n options: RenderOptions,\n outputDir: string,\n timeout: number\n): Promise<number> {\n output.verbose(`Loading manifest from ${manifestPath}`);\n const manifest = await loadManifest(manifestPath);\n const rawConfig = envelopeFromManifest(manifest);\n\n if (options.intentId) {\n rawConfig.intentId = options.intentId;\n }\n\n output.success(`Template: ${rawConfig.template}`);\n output.verbose(`Intent ID: ${rawConfig.intentId}`);\n output.verbose('Resolving template...');\n\n const resolverRegistry = createResolverRegistry();\n const template = await resolverRegistry.resolve(rawConfig.template, {\n noCache: options.cache === false,\n timeout,\n });\n\n const envelope: Envelope = {\n template,\n inputs: rawConfig.inputs,\n intentId: rawConfig.intentId,\n platformPrefix: rawConfig.platformPrefix,\n options: {\n skipFormat: options.skipFormat,\n },\n };\n\n output.verbose('Re-rendering from manifest...');\n\n const engine = createEngine();\n const result = await engine.install(envelope);\n\n if (options.dryRun) {\n return await handleInstallDryRun(result, outputDir, options.json);\n }\n\n const aborted = await guardOverwrite(outputDir, options);\n if (aborted !== null) return aborted;\n\n output.success('Inputs validated');\n output.success(`Generated ${result.files.size - 1} files + manifest.json`);\n\n if (!options.skipFormat) {\n output.success('Formatted with tofu fmt');\n }\n\n await writeEngineOutput(outputDir, result, 'install');\n\n printInstallResult(result, outputDir, options.json);\n\n return EXIT_CODES.SUCCESS;\n}\n\n/**\n * Render from a template directory with fixture inputs.\n */\nasync function runRenderFromDirectory(\n templateDir: string,\n options: RenderOptions,\n outputDir: string,\n timeout: number\n): Promise<number> {\n const templateUri = `file://${templateDir}`;\n\n let fixturePath: string | null = options.fixture ? resolve(templateDir, options.fixture) : null;\n\n if (fixturePath) {\n if (!(await fileExists(fixturePath))) {\n output.error(`Fixture not found: ${options.fixture}`);\n return EXIT_CODES.VALIDATION_ERROR;\n }\n } else {\n fixturePath = await findDefaultFixture(templateDir);\n if (!fixturePath) {\n output.error('No default fixture found at tests/default/inputs.yaml');\n output.info('Either create a default fixture or use --fixture to specify one.');\n return EXIT_CODES.VALIDATION_ERROR;\n }\n }\n\n const fixtureContent = await readFile(fixturePath, 'utf-8');\n let inputs = yaml.load(fixtureContent) as Record<string, unknown>;\n\n const overrides = parseInputOverrides(options.input);\n inputs = { ...inputs, ...overrides };\n\n const intentId =\n options.intentId ?? (inputs['intentId'] as string | undefined) ?? `local-${Date.now()}`;\n\n output.success(`Template: ${templateUri}`);\n output.verbose(`Fixture: ${fixturePath}`);\n output.verbose(`Intent ID: ${intentId}`);\n\n output.verbose('Resolving template...');\n const resolverRegistry = createResolverRegistry();\n const template = await resolverRegistry.resolve(templateUri, {\n noCache: options.cache === false,\n timeout,\n });\n\n const envelope: Envelope = {\n template,\n inputs,\n intentId,\n options: {\n skipFormat: options.skipFormat,\n },\n };\n\n const engine = createEngine();\n\n output.verbose('Validating inputs...');\n const result = await engine.install(envelope);\n\n if (options.dryRun) {\n return await handleInstallDryRun(result, outputDir, options.json);\n }\n\n const aborted = await guardOverwrite(outputDir, options);\n if (aborted !== null) return aborted;\n\n output.success('Inputs validated');\n output.success(`Generated ${result.files.size - 1} files + manifest.json`);\n\n if (!options.skipFormat) {\n output.success('Formatted with tofu fmt');\n }\n\n await writeEngineOutput(outputDir, result, 'install');\n\n printInstallResult(result, outputDir, options.json);\n\n return EXIT_CODES.SUCCESS;\n}\n\n/**\n * Render from a config file (fresh install).\n */\nasync function runRenderFromConfig(\n configFile: string,\n options: RenderOptions,\n outputDir: string,\n timeout: number\n): Promise<number> {\n output.verbose(`Loading config from ${configFile}`);\n const rawConfig = await loadConfig(configFile);\n\n if (options.intentId) {\n rawConfig.intentId = options.intentId;\n }\n\n if (options.input && options.input.length > 0) {\n const overrides = parseInputOverrides(options.input);\n rawConfig.inputs = { ...(rawConfig.inputs || {}), ...overrides };\n }\n\n output.success(`Template: ${rawConfig.template}`);\n output.verbose(`Intent ID: ${rawConfig.intentId}`);\n\n output.verbose('Resolving template...');\n const resolverRegistry = createResolverRegistry();\n const template = await resolverRegistry.resolve(rawConfig.template, {\n noCache: options.cache === false,\n timeout,\n });\n\n const envelope: Envelope = {\n template,\n inputs: rawConfig.inputs,\n intentId: rawConfig.intentId,\n platformPrefix: rawConfig.platformPrefix,\n options: {\n skipFormat: options.skipFormat ?? rawConfig.options?.skipFormat,\n },\n };\n\n const engine = createEngine();\n\n output.verbose('Validating inputs...');\n const result = await engine.install(envelope);\n\n if (options.dryRun) {\n return await handleInstallDryRun(result, outputDir, options.json);\n }\n\n const aborted = await guardOverwrite(outputDir, options);\n if (aborted !== null) return aborted;\n\n output.success('Inputs validated');\n output.success(`Generated ${result.files.size - 1} files + manifest.json`);\n\n if (!envelope.options?.skipFormat) {\n output.success('Formatted with tofu fmt');\n }\n\n await writeEngineOutput(outputDir, result, 'install');\n\n printInstallResult(result, outputDir, options.json);\n\n return EXIT_CODES.SUCCESS;\n}\n\n/**\n * Upgrade from a config file when manifest.json exists in the output directory.\n * Auto-detected when config mode finds an existing manifest.\n */\nasync function runRenderFromConfigUpgrade(\n configFile: string,\n options: RenderOptions,\n outputDir: string,\n timeout: number,\n manifestPath: string\n): Promise<number> {\n output.verbose(`Existing manifest detected at ${manifestPath} — running upgrade`);\n\n const previousManifest = await loadManifest(manifestPath);\n\n output.verbose(`Reading files from ${outputDir}`);\n const currentFiles = await readDirectoryToFileMap(outputDir, previousManifest.platformPrefix);\n\n output.verbose(`Loading config from ${configFile}`);\n const rawConfig = await loadConfig(configFile);\n\n if (options.intentId) {\n rawConfig.intentId = options.intentId;\n }\n\n if (options.input && options.input.length > 0) {\n const overrides = parseInputOverrides(options.input);\n rawConfig.inputs = { ...(rawConfig.inputs || {}), ...overrides };\n }\n\n output.success(`Template: ${rawConfig.template}`);\n output.verbose(`Intent ID: ${rawConfig.intentId}`);\n\n output.verbose('Resolving template...');\n const resolverRegistry = createResolverRegistry();\n const template = await resolverRegistry.resolve(rawConfig.template, {\n noCache: options.cache === false,\n timeout,\n });\n\n const envelope: Envelope = {\n template,\n inputs: rawConfig.inputs,\n intentId: rawConfig.intentId,\n platformPrefix: rawConfig.platformPrefix,\n options: {\n skipFormat: options.skipFormat ?? rawConfig.options?.skipFormat,\n },\n };\n\n const engine = createEngine();\n const result = await engine.upgrade(envelope, currentFiles, previousManifest);\n\n if (result.sourceBlocked && !options.allowSourceChange) {\n output.warn('Source mismatch: current files were generated from a different template source.');\n printDriftReport(result.driftReport, options.json);\n if (options.json) {\n output.data(\n JSON.stringify(\n {\n blocked: true,\n reason: 'source_mismatch',\n sourceBlocked: true,\n driftReport: { drift: !result.driftReport.valid, summary: result.driftReport.summary },\n },\n null,\n 2\n )\n );\n } else {\n output.info('Use --allow-source-change to proceed with source migration.');\n }\n return EXIT_CODES.DRIFT_BLOCKED;\n }\n if (result.sourceBlocked) {\n output.warn('Proceeding with source migration (--allow-source-change).');\n }\n\n printDriftReport(result.driftReport, options.json);\n\n if (!result.driftReport.valid && !options.force) {\n if (options.json) {\n output.data(\n JSON.stringify(\n {\n blocked: true,\n reason: 'content_drift',\n sourceBlocked: result.sourceBlocked,\n driftReport: { drift: true, summary: result.driftReport.summary },\n },\n null,\n 2\n )\n );\n } else {\n output.info('Use --force to proceed despite drift.');\n }\n return EXIT_CODES.DRIFT_BLOCKED;\n }\n if (!result.driftReport.valid) {\n output.warn('Proceeding despite drift (--force).');\n }\n\n if (options.dryRun) {\n return handleUpgradeDryRun(result, currentFiles, options.json);\n }\n\n await writeEngineOutput(outputDir, result, 'upgrade');\n\n if (options.json) {\n output.data(formatUpgradeResultJson(result));\n } else {\n output.info(formatUpgradeResult(result));\n }\n\n return EXIT_CODES.SUCCESS;\n}\n\n// --- Diff computation helpers ---\n\nasync function computeInstallDiffs(newFiles: FileMap, outputDir: string): Promise<DiffEntry[]> {\n const diffs: DiffEntry[] = [];\n for (const [filename, entry] of newFiles) {\n if (filename === 'manifest.json') continue;\n let existing = '';\n try {\n existing = await readFile(join(outputDir, filename), 'utf-8');\n } catch {\n /* new file */\n }\n if (existing === entry.content) continue;\n diffs.push({ filename, diff: generateUnifiedDiff(existing, entry.content, filename) });\n }\n return diffs;\n}\n\nfunction computeUpgradeDiffs(newFiles: FileMap, currentFiles: FileMap): DiffEntry[] {\n const diffs: DiffEntry[] = [];\n for (const [filename, entry] of newFiles) {\n if (filename === 'manifest.json') continue;\n const current = currentFiles.get(filename);\n const currentContent = current?.content ?? '';\n if (currentContent === entry.content) continue;\n diffs.push({ filename, diff: generateUnifiedDiff(currentContent, entry.content, filename) });\n }\n return diffs;\n}\n\nfunction printDiffs(diffs: DiffEntry[]): void {\n if (diffs.length === 0) {\n output.info('\\nNo file changes detected.');\n return;\n }\n output.info(`\\n${diffs.length} file(s) changed:\\n`);\n for (const d of diffs) {\n output.info(d.diff);\n }\n}\n\n// --- Drift report helper ---\n\nfunction printDriftReport(report: DriftReport, json?: boolean): void {\n if (json) {\n output.data(\n JSON.stringify(\n {\n drift: !report.valid,\n summary: report.summary,\n entries: report.entries.map((e) => ({\n path: e.path,\n type: e.type,\n details: e.details,\n })),\n },\n null,\n 2\n )\n );\n return;\n }\n if (report.valid) {\n output.success('No drift detected in managed files.');\n return;\n }\n const total =\n report.summary.driftedFiles +\n report.summary.missingFiles +\n report.summary.unexpectedFiles +\n report.summary.inconsistentFiles;\n output.warn(`Drift detected in ${total} managed files:`);\n for (const entry of report.entries) {\n output.info(` ${driftTypeTag(entry.type)} ${entry.path}`);\n output.info(` ${entry.details}`);\n }\n output.info('');\n output.info('Use --force to override and proceed with upgrade.');\n}\n\n// --- Install mode output helpers ---\n\nasync function handleInstallDryRun(\n result: InstallResult,\n outputDir: string,\n json?: boolean\n): Promise<number> {\n const diffs = await computeInstallDiffs(result.files, outputDir);\n if (json) {\n output.data(\n JSON.stringify(\n {\n dryRun: true,\n aggregateHash: result.manifest.aggregateHash,\n templateSource: result.manifest.templateSource,\n templateVersion: result.manifest.templateVersion,\n fileCount: result.files.size,\n files: Array.from(result.files.entries()).map(([path, entry]) => ({\n path,\n size: Buffer.byteLength(entry.content, 'utf-8'),\n })),\n changes: diffs.map((d) => ({ filename: d.filename, diff: d.diff })),\n },\n null,\n 2\n )\n );\n } else {\n output.info('Dry run — no files written.');\n output.info('');\n output.info(`Aggregate Hash: ${result.manifest.aggregateHash}`);\n output.info(`Template: ${result.manifest.templateSource}`);\n output.info(`Version: ${result.manifest.templateVersion}`);\n output.info(`Files: ${result.files.size}`);\n printDiffs(diffs);\n }\n return EXIT_CODES.SUCCESS;\n}\n\nfunction printInstallResult(result: InstallResult, outputDir: string, json?: boolean): void {\n if (json) {\n output.data(\n JSON.stringify(\n {\n success: true,\n aggregateHash: result.manifest.aggregateHash,\n fileCount: result.files.size,\n outputDir,\n files: Array.from(result.files.entries()).map(([path, entry]) => ({\n path,\n size: Buffer.byteLength(entry.content, 'utf-8'),\n })),\n },\n null,\n 2\n )\n );\n } else {\n const fileList: Array<{ path: string; size: number }> = [];\n for (const [filePath, entry] of result.files) {\n fileList.push({\n path: filePath,\n size: Buffer.byteLength(entry.content, 'utf-8'),\n });\n }\n output.info('');\n output.info(`Output written to: ${outputDir}`);\n output.info(`Aggregate Hash: ${result.manifest.aggregateHash}`);\n output.printFileList(fileList);\n }\n}\n\n// --- Upgrade mode output helpers ---\n\nfunction handleUpgradeDryRun(result: UpgradeResult, currentFiles: FileMap, json?: boolean): number {\n const diffs = computeUpgradeDiffs(result.files, currentFiles);\n if (json) {\n output.data(\n JSON.stringify(\n {\n dryRun: true,\n templateSource: result.manifest.templateSource,\n templateVersion: result.manifest.templateVersion,\n aggregateHash: result.manifest.aggregateHash,\n driftReport: result.driftReport,\n files: Array.from(result.files.entries()).map(([path, entry]) => ({\n path,\n zone: entry.zone,\n })),\n changes: diffs.map((d) => ({ filename: d.filename, diff: d.diff })),\n },\n null,\n 2\n )\n );\n } else {\n output.info('Dry run — no files written.');\n output.info('');\n output.info(formatUpgradeResult(result));\n printDiffs(diffs);\n }\n return EXIT_CODES.SUCCESS;\n}\n\nfunction formatUpgradeResult(result: UpgradeResult): string {\n const lines: string[] = [];\n lines.push(\n `✓ Upgrade complete: ${result.manifest.templateSource} v${result.manifest.templateVersion}`\n );\n lines.push('');\n let platformCount = 0;\n let userCount = 0;\n for (const [, entry] of result.files) {\n if (entry.zone === 'platform') platformCount++;\n else userCount++;\n }\n lines.push(` Updated: ${platformCount} files (Platform Zone)`);\n lines.push(` Unchanged: ${userCount} files (User Zone)`);\n lines.push('');\n lines.push(`Aggregate Hash: ${result.manifest.aggregateHash}`);\n return lines.join('\\n');\n}\n\nfunction formatUpgradeResultJson(result: UpgradeResult): string {\n return JSON.stringify(\n {\n success: true,\n templateSource: result.manifest.templateSource,\n templateVersion: result.manifest.templateVersion,\n aggregateHash: result.manifest.aggregateHash,\n driftReport: result.driftReport,\n files: Array.from(result.files.entries()).map(([path, entry]) => ({\n path,\n zone: entry.zone,\n })),\n },\n null,\n 2\n );\n}\n","/**\n * Resolver registry for managing template source resolvers.\n *\n * Supports file:// and https://*.json bundle URIs.\n */\n\nimport type { Resolver } from './base.js';\nimport type { Template, ResolverOptions } from '@truefoundry/tfy-infra-engine';\nimport { EngineError, EngineErrorCode } from '@truefoundry/tfy-infra-engine';\nimport { FileResolver } from './file.js';\nimport { HttpsBundleResolver } from './https.js';\nimport { TemplateCache } from '../cache/template-cache.js';\n\nexport type { Resolver } from './base.js';\nexport { FileResolver } from './file.js';\nexport { HttpsBundleResolver } from './https.js';\nexport { TemplateCache, createTemplateCache, getDefaultCacheDir } from '../cache/template-cache.js';\n\n/**\n * Registry for template resolvers.\n * Routes template URIs to the appropriate resolver based on scheme.\n */\nexport class ResolverRegistry {\n private resolvers: Resolver[] = [];\n private cache: TemplateCache;\n\n constructor(resolvers?: Resolver[], cacheDir?: string) {\n this.cache = new TemplateCache(cacheDir);\n\n if (resolvers) {\n this.resolvers = resolvers;\n } else {\n this.register(new FileResolver());\n this.register(new HttpsBundleResolver());\n }\n }\n\n register(resolver: Resolver): void {\n this.resolvers.push(resolver);\n }\n\n getResolver(uri: string): Resolver | undefined {\n return this.resolvers.find((r) => r.canResolve(uri));\n }\n\n async resolve(uri: string, options?: ResolverOptions): Promise<Template> {\n const resolver = this.getResolver(uri);\n\n if (!resolver) {\n const scheme = uri.split('://')[0] || 'unknown';\n throw new EngineError(\n `No resolver found for URI scheme: ${scheme}://`,\n EngineErrorCode.TEMPLATE_NOT_FOUND,\n undefined,\n {\n uri,\n scheme,\n supportedSchemes: ['file://', 'https://*.json'],\n }\n );\n }\n\n const template = await resolver.resolve(uri, options);\n\n // Cache the result (unless it's a file:// URI)\n if (!uri.startsWith('file://')) {\n await this.cache.set(template);\n }\n\n return template;\n }\n\n canResolve(uri: string): boolean {\n return this.resolvers.some((r) => r.canResolve(uri));\n }\n\n getSupportedSchemes(): string[] {\n return ['file://', 'https://*.json'];\n }\n\n async clearCache(uri?: string): Promise<void> {\n if (uri) {\n await this.cache.delete(uri);\n } else {\n await this.cache.clear();\n }\n }\n\n async getCacheStats(): Promise<{ sources: number; templates: number; size: number }> {\n return this.cache.stats();\n }\n}\n\n/**\n * Create a default resolver registry with standard resolvers (file + https bundle).\n */\nexport function createResolverRegistry(cacheDir?: string): ResolverRegistry {\n return new ResolverRegistry(undefined, cacheDir);\n}\n","/**\n * File resolver for local template sources (file://).\n *\n * Supports bundle.json — single-file distribution format.\n *\n * Detection order: if the URI points directly at a .json file, treat it as a\n * bundle; if it's a directory containing bundle.json, load the bundle.\n */\n\nimport { readFile, stat } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport type { Resolver } from './base.js';\nimport type { Template, ResolverOptions, VersionInfo } from '@truefoundry/tfy-infra-engine';\nimport { EngineError, EngineErrorCode } from '@truefoundry/tfy-infra-engine';\n\n/**\n * Resolver for file:// URIs.\n * Loads templates from the local filesystem.\n */\nexport class FileResolver implements Resolver {\n canResolve(uri: string): boolean {\n return uri.startsWith('file://');\n }\n\n async resolve(uri: string, _options?: ResolverOptions): Promise<Template> {\n const fsPath = this.uriToPath(uri);\n\n try {\n const info = await stat(fsPath);\n\n if (info.isFile() && fsPath.endsWith('.json')) {\n return this.resolveBundle(fsPath, uri);\n }\n\n if (info.isDirectory()) {\n const bundlePath = join(fsPath, 'bundle.json');\n if (await this.fileExists(bundlePath)) {\n return this.resolveBundle(bundlePath, uri);\n }\n throw new EngineError(\n `bundle.json not found in ${fsPath}`,\n EngineErrorCode.BUNDLE_NOT_FOUND,\n undefined,\n { path: fsPath }\n );\n }\n\n throw new EngineError(\n `Not a file or directory: ${fsPath}`,\n EngineErrorCode.TEMPLATE_NOT_FOUND,\n undefined,\n { path: fsPath }\n );\n } catch (error) {\n if (error instanceof EngineError) {\n throw error;\n }\n\n const nodeError = error as NodeJS.ErrnoException;\n if (nodeError.code === 'ENOENT') {\n throw new EngineError(\n `Template not found: ${fsPath}`,\n EngineErrorCode.FILE_NOT_FOUND,\n error as Error,\n { path: fsPath, originalCode: nodeError.code }\n );\n }\n\n if (nodeError.code === 'EACCES') {\n throw new EngineError(\n `Permission denied: ${fsPath}`,\n EngineErrorCode.FILE_NOT_FOUND,\n error as Error,\n { path: fsPath, originalCode: nodeError.code }\n );\n }\n\n throw new EngineError(\n `Failed to load template from ${fsPath}: ${(error as Error).message}`,\n EngineErrorCode.FILE_NOT_FOUND,\n error as Error,\n { path: fsPath }\n );\n }\n }\n\n private async resolveBundle(bundlePath: string, uri: string): Promise<Template> {\n const content = await readFile(bundlePath, 'utf-8');\n\n let raw: Record<string, unknown>;\n try {\n raw = JSON.parse(content) as Record<string, unknown>;\n } catch (error) {\n throw new EngineError(\n `Failed to parse bundle.json: ${(error as Error).message}`,\n EngineErrorCode.BUNDLE_INVALID,\n error as Error,\n { path: bundlePath }\n );\n }\n\n const rawMetadata = raw['metadata'] as Record<string, unknown> | undefined;\n if (!rawMetadata || typeof rawMetadata !== 'object') {\n throw new EngineError(\n 'bundle.json must contain a \"metadata\" object',\n EngineErrorCode.BUNDLE_INVALID,\n undefined,\n { path: bundlePath }\n );\n }\n if (!rawMetadata['name'] || !rawMetadata['version']) {\n throw new EngineError(\n 'bundle.json metadata must contain \"name\" and \"version\"',\n EngineErrorCode.BUNDLE_INVALID,\n undefined,\n { path: bundlePath }\n );\n }\n\n const metadata = {\n name: rawMetadata['name'] as string,\n version: rawMetadata['version'] as string,\n ...(rawMetadata['description'] ? { description: rawMetadata['description'] as string } : {}),\n };\n\n const jsonSchema = raw['jsonSchema'] as Record<string, unknown>;\n if (!jsonSchema || typeof jsonSchema !== 'object') {\n throw new EngineError(\n 'bundle.json must contain a \"jsonSchema\" object',\n EngineErrorCode.BUNDLE_INVALID,\n undefined,\n { path: bundlePath }\n );\n }\n\n const files = new Map(Object.entries((raw['files'] as Record<string, string>) ?? {}));\n const staticFiles = new Map(\n Object.entries((raw['staticFiles'] as Record<string, string>) ?? {})\n );\n\n if (files.size === 0 && staticFiles.size === 0) {\n throw new EngineError(\n `Bundle contains no template files: ${bundlePath}`,\n EngineErrorCode.TEMPLATE_NOT_FOUND,\n undefined,\n { path: bundlePath }\n );\n }\n\n const version: VersionInfo = { semver: metadata.version };\n\n return { metadata, jsonSchema, files, staticFiles, source: uri, version };\n }\n\n private uriToPath(uri: string): string {\n if (uri.startsWith('file:///')) {\n return uri.substring(7);\n }\n if (uri.startsWith('file://')) {\n return uri.substring(7);\n }\n return uri;\n }\n\n private async fileExists(path: string): Promise<boolean> {\n try {\n await stat(path);\n return true;\n } catch {\n return false;\n }\n }\n}\n","/**\n * HTTPS bundle resolver for pre-built template bundles (https://*.json).\n *\n * Fetches a bundle JSON file over HTTPS and unpacks it into a Template.\n * Automatically attaches a Bearer token for Artifactory hosts when\n * JFROG_ACCESS_TOKEN is present in the environment.\n */\n\nimport type { Resolver } from './base.js';\nimport type { Template, ResolverOptions, VersionInfo } from '@truefoundry/tfy-infra-engine';\nimport { validateBundle, EngineError, EngineErrorCode } from '@truefoundry/tfy-infra-engine';\nimport { mergeResolverOptions, withRetry, withTimeout } from './base.js';\n\nconst JFROG_TOKEN_ENV = 'JFROG_ACCESS_TOKEN';\n\nfunction isArtifactoryHost(uri: string): boolean {\n try {\n const { hostname } = new URL(uri);\n return hostname.includes('jfrog');\n } catch {\n return false;\n }\n}\n\nfunction buildHeaders(uri: string): Record<string, string> {\n const headers: Record<string, string> = {\n 'User-Agent': 'tfy-infra-cli',\n Accept: 'application/json',\n };\n\n if (isArtifactoryHost(uri)) {\n const token = process.env[JFROG_TOKEN_ENV];\n if (token) {\n headers['Authorization'] = `Bearer ${token}`;\n }\n }\n\n return headers;\n}\n\nasync function fetchBundle(uri: string, headers: Record<string, string>): Promise<Response> {\n const response = await fetch(uri, { headers });\n\n if (response.status === 401 || response.status === 403) {\n const hint = isArtifactoryHost(uri)\n ? ` Set ${JFROG_TOKEN_ENV} in your environment to authenticate.`\n : '';\n throw new EngineError(\n `Authentication failed (HTTP ${response.status}) for ${uri}.${hint}`,\n EngineErrorCode.HTTPS_AUTH_FAILED,\n undefined,\n { uri, status: response.status }\n );\n }\n\n if (!response.ok) {\n throw new EngineError(\n `Bundle download failed: HTTP ${response.status} for ${uri}`,\n EngineErrorCode.NETWORK_ERROR,\n undefined,\n { uri, status: response.status }\n );\n }\n\n return response;\n}\n\nasync function unpackBundle(response: Response, uri: string): Promise<Template> {\n const body: unknown = await response.json();\n const raw = body as Record<string, unknown>;\n\n const { metadata, jsonSchema } = validateBundle(raw);\n\n const files = new Map(Object.entries((raw['files'] as Record<string, string>) ?? {}));\n const staticFiles = new Map(Object.entries((raw['staticFiles'] as Record<string, string>) ?? {}));\n\n if (files.size === 0 && staticFiles.size === 0) {\n throw new EngineError(\n `Bundle contains no template files: ${uri}`,\n EngineErrorCode.TEMPLATE_NOT_FOUND,\n undefined,\n { uri }\n );\n }\n\n const version: VersionInfo = { semver: metadata.version };\n\n return { metadata, jsonSchema, files, staticFiles, source: uri, version };\n}\n\n/**\n * Resolver for HTTPS bundle URLs (https://.../*.json).\n */\nexport class HttpsBundleResolver implements Resolver {\n canResolve(uri: string): boolean {\n return uri.startsWith('https://') && uri.toLowerCase().endsWith('.json');\n }\n\n async resolve(uri: string, options?: ResolverOptions): Promise<Template> {\n const opts = mergeResolverOptions(options);\n const headers = buildHeaders(uri);\n\n const response = await withTimeout(\n withRetry(() => fetchBundle(uri, headers), opts.retries),\n opts.timeout,\n `HTTPS bundle download timed out after ${opts.timeout}ms`\n );\n\n return unpackBundle(response, uri);\n }\n}\n","/**\n * Base resolver interface and common utilities.\n *\n * Moved from @truefoundry/tfy-infra-engine (006).\n * Imports reference the engine package for shared types.\n */\n\nimport type { Template, ResolverOptions } from '@truefoundry/tfy-infra-engine';\n\n/**\n * Interface for template resolvers.\n * Each resolver handles a specific URI scheme (file://, https://).\n */\nexport interface Resolver {\n canResolve(uri: string): boolean;\n resolve(uri: string, options?: ResolverOptions): Promise<Template>;\n}\n\n/**\n * Default resolver options.\n */\nexport const DEFAULT_RESOLVER_OPTIONS: Required<ResolverOptions> = {\n timeout: 30000,\n retries: 3,\n noCache: false,\n};\n\n/**\n * Merge resolver options with defaults.\n */\nexport function mergeResolverOptions(options?: ResolverOptions): Required<ResolverOptions> {\n return {\n ...DEFAULT_RESOLVER_OPTIONS,\n ...options,\n };\n}\n\n/**\n * Retry an async operation with exponential backoff.\n */\nexport async function withRetry<T>(\n fn: () => Promise<T>,\n retries: number,\n baseDelay = 1000\n): Promise<T> {\n let lastError: unknown;\n\n for (let attempt = 0; attempt <= retries; attempt++) {\n try {\n return await fn();\n } catch (error) {\n lastError = error;\n\n if (attempt < retries) {\n const delay = baseDelay * Math.pow(2, attempt) * (0.5 + Math.random() * 0.5);\n await new Promise((resolve) => setTimeout(resolve, delay));\n }\n }\n }\n\n throw lastError instanceof Error ? lastError : new Error(String(lastError));\n}\n\n/**\n * Create a timeout wrapper for promises.\n */\nexport function withTimeout<T>(promise: Promise<T>, ms: number, message?: string): Promise<T> {\n let timer: ReturnType<typeof setTimeout>;\n\n const timeout = new Promise<never>((_, reject) => {\n timer = setTimeout(() => reject(new Error(message ?? `Operation timed out after ${ms}ms`)), ms);\n });\n\n return Promise.race([promise, timeout]).finally(() => clearTimeout(timer));\n}\n","/**\n * Template cache for storing fetched templates locally.\n * Uses filesystem-based caching with version-keyed storage.\n *\n * Stores templates as bundle.json files containing all template data\n * in a single self-contained JSON file.\n */\n\nimport { mkdir, readFile, writeFile, rm, readdir, stat } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport { createHash } from 'node:crypto';\nimport type { Template, VersionInfo } from '@truefoundry/tfy-infra-engine';\nimport { validateBundle, EngineError, EngineErrorCode } from '@truefoundry/tfy-infra-engine';\n\n/**\n * Default cache directory.\n */\nexport function getDefaultCacheDir(): string {\n const home = process.env['HOME'] || process.env['USERPROFILE'] || '~';\n return join(home, '.cache', 'tfy-infra', 'templates');\n}\n\n/**\n * Template cache manager.\n */\nexport class TemplateCache {\n private readonly cacheDir: string;\n\n constructor(cacheDir?: string) {\n this.cacheDir = cacheDir ?? getDefaultCacheDir();\n }\n\n private getCacheKey(source: string): string {\n return createHash('sha256').update(source).digest('hex').substring(0, 16);\n }\n\n private getCachePath(source: string, version: string): string {\n const key = this.getCacheKey(source);\n const safeVersion = version.replace(/\\//g, '-');\n return join(this.cacheDir, key, safeVersion);\n }\n\n async has(source: string, version: string): Promise<boolean> {\n const cachePath = this.getCachePath(source, version);\n try {\n const bundlePath = join(cachePath, 'bundle.json');\n await stat(bundlePath);\n return true;\n } catch {\n return false;\n }\n }\n\n async get(source: string, version: string): Promise<Template | null> {\n const cachePath = this.getCachePath(source, version);\n\n try {\n const bundlePath = join(cachePath, 'bundle.json');\n const bundleContent = await readFile(bundlePath, 'utf-8');\n const parsed: unknown = JSON.parse(bundleContent);\n const { metadata, jsonSchema } = validateBundle(parsed);\n\n const record = parsed as Record<string, unknown>;\n const files = new Map(Object.entries((record['files'] as Record<string, string>) ?? {}));\n const staticFiles = new Map(\n Object.entries((record['staticFiles'] as Record<string, string>) ?? {})\n );\n\n const versionInfo: VersionInfo = {\n semver: metadata.version,\n };\n\n return {\n metadata,\n jsonSchema,\n files,\n staticFiles,\n source,\n version: versionInfo,\n };\n } catch {\n return null;\n }\n }\n\n async set(template: Template): Promise<void> {\n const cachePath = this.getCachePath(template.source, template.version.semver);\n\n try {\n await mkdir(cachePath, { recursive: true });\n\n const bundle = {\n metadata: {\n name: template.metadata.name,\n version: template.metadata.version,\n ...(template.metadata.description ? { description: template.metadata.description } : {}),\n },\n jsonSchema: template.jsonSchema,\n files: Object.fromEntries(template.files),\n staticFiles: Object.fromEntries(template.staticFiles),\n };\n await writeFile(join(cachePath, 'bundle.json'), JSON.stringify(bundle, null, 2), 'utf-8');\n } catch (error) {\n throw new EngineError(\n `Failed to cache template: ${(error as Error).message}`,\n EngineErrorCode.CACHE_ERROR,\n error as Error,\n { source: template.source, version: template.version.semver }\n );\n }\n }\n\n async delete(source: string, version?: string): Promise<void> {\n if (version) {\n const cachePath = this.getCachePath(source, version);\n try {\n await rm(cachePath, { recursive: true, force: true });\n } catch {\n // Ignore if doesn't exist\n }\n } else {\n const key = this.getCacheKey(source);\n const keyPath = join(this.cacheDir, key);\n try {\n await rm(keyPath, { recursive: true, force: true });\n } catch {\n // Ignore if doesn't exist\n }\n }\n }\n\n async clear(): Promise<void> {\n try {\n await rm(this.cacheDir, { recursive: true, force: true });\n } catch {\n // Ignore if doesn't exist\n }\n }\n\n async stats(): Promise<{ sources: number; templates: number; size: number }> {\n let sources = 0;\n let templates = 0;\n let size = 0;\n\n try {\n const sourceKeys = await readdir(this.cacheDir);\n sources = sourceKeys.length;\n\n for (const key of sourceKeys) {\n const keyPath = join(this.cacheDir, key);\n const versions = await readdir(keyPath);\n templates += versions.length;\n\n for (const version of versions) {\n const versionPath = join(keyPath, version);\n size += await this.dirSize(versionPath);\n }\n }\n } catch {\n // Cache doesn't exist or is empty\n }\n\n return { sources, templates, size };\n }\n\n private async dirSize(dir: string): Promise<number> {\n let total = 0;\n const entries = await readdir(dir, { withFileTypes: true });\n for (const entry of entries) {\n const fullPath = join(dir, entry.name);\n if (entry.isDirectory()) {\n total += await this.dirSize(fullPath);\n } else {\n const fileStat = await stat(fullPath);\n total += fileStat.size;\n }\n }\n return total;\n }\n}\n\n/**\n * Create a template cache instance.\n */\nexport function createTemplateCache(cacheDir?: string): TemplateCache {\n return new TemplateCache(cacheDir);\n}\n","/**\n * Configuration file loader for YAML and JSON formats.\n *\n * The CLI command is responsible for resolving the template URI before calling the engine.\n */\n\nimport { readFile } from 'node:fs/promises';\nimport * as yaml from 'js-yaml';\n\n/**\n * Raw configuration from a config file.\n * `template` is a URI string that must be resolved before passing to the engine.\n */\nexport interface RawConfig {\n template: string;\n inputs: Record<string, unknown>;\n intentId: string;\n platformPrefix?: string;\n options?: {\n skipFormat?: boolean;\n };\n}\n\n/**\n * Load a configuration file (YAML or JSON).\n *\n * @param filePath - Path to the config file\n * @returns Raw configuration with template URI string\n */\nexport async function loadConfig(filePath: string): Promise<RawConfig> {\n const content = await readFile(filePath, 'utf-8');\n\n const isJson = filePath.endsWith('.json');\n\n let parsed: Record<string, unknown>;\n try {\n if (isJson) {\n parsed = JSON.parse(content) as Record<string, unknown>;\n } else {\n parsed = yaml.load(content) as Record<string, unknown>;\n }\n } catch (error) {\n const format = isJson ? 'JSON' : 'YAML';\n throw new Error(`Failed to parse ${format} config file: ${(error as Error).message}`, {\n cause: error,\n });\n }\n\n if (\n !parsed['intentId'] ||\n typeof parsed['intentId'] !== 'string' ||\n parsed['intentId'].trim() === ''\n ) {\n throw new Error(\n `Configuration file is missing required field 'intentId'. ` +\n `Add 'intentId: \"<your-cluster-identity>\"' to ${filePath}. ` +\n `The intentId uniquely identifies the cluster for integrity tracking.`\n );\n }\n\n return {\n template: parsed['template'] as string,\n inputs: (parsed['inputs'] as Record<string, unknown>) ?? {},\n intentId: parsed['intentId'],\n platformPrefix: parsed['platformPrefix'] as string | undefined,\n options: parsed['options'] as RawConfig['options'],\n };\n}\n\nexport { fileExists } from './fs-helpers.js';\n","/**\n * Shared filesystem helper utilities.\n */\n\nimport { access } from 'node:fs/promises';\nimport { join } from 'node:path';\n\n/**\n * Check if a file exists on disk.\n */\nexport async function fileExists(filePath: string): Promise<boolean> {\n try {\n await access(filePath);\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Find the default fixture path (tests/default/inputs.yaml) for a template version.\n * Returns null if the file does not exist.\n */\nexport async function findDefaultFixture(versionDir: string): Promise<string | null> {\n const defaultPath = join(versionDir, 'tests', 'default', 'inputs.yaml');\n try {\n await access(defaultPath);\n return defaultPath;\n } catch {\n return null;\n }\n}\n","/**\n * Manifest loading and raw config reconstruction utility.\n *\n * CHANGE (006): envelopeFromManifest now returns RawConfig (with template as\n * URI string) instead of Envelope. The CLI command resolves the URI.\n *\n * Reference: data-model.md §11, research.md R-008\n */\n\nimport { readFile } from 'node:fs/promises';\nimport type { Manifest } from '@truefoundry/tfy-infra-engine';\nimport type { RawConfig } from './config-loader.js';\n\n/**\n * Load and validate a manifest from disk.\n */\nexport async function loadManifest(manifestPath: string): Promise<Manifest> {\n let content: string;\n try {\n content = await readFile(manifestPath, 'utf-8');\n } catch (error) {\n const nodeError = error as NodeJS.ErrnoException;\n if (nodeError.code === 'ENOENT') {\n throw new Error(`Manifest not found: ${manifestPath}`, { cause: error });\n }\n throw new Error(`Failed to read manifest: ${(error as Error).message}`, { cause: error });\n }\n\n let parsed: Record<string, unknown>;\n try {\n parsed = JSON.parse(content) as Record<string, unknown>;\n } catch {\n throw new Error(`Invalid JSON in manifest: ${manifestPath}`);\n }\n\n const requiredFields = [\n 'manifestVersion',\n 'files',\n 'aggregateHash',\n 'inputs',\n 'platformPrefix',\n 'intentId',\n 'templateSource',\n 'templateVersion',\n ] as const;\n\n for (const field of requiredFields) {\n if (parsed[field] === undefined) {\n throw new Error(`Manifest is missing required field: ${field}`);\n }\n }\n\n return parsed as unknown as Manifest;\n}\n\n/**\n * Reconstruct a RawConfig from a manifest for re-render.\n *\n * CHANGE (006): Returns RawConfig with template URI string.\n */\nexport function envelopeFromManifest(manifest: Manifest): RawConfig {\n if (!manifest.inputs) {\n throw new Error('Cannot reconstruct envelope: manifest is missing inputs field');\n }\n if (!manifest.platformPrefix) {\n throw new Error('Cannot reconstruct envelope: manifest is missing platformPrefix field');\n }\n\n return {\n template: manifest.templateSource,\n inputs: manifest.inputs,\n intentId: manifest.intentId,\n platformPrefix: manifest.platformPrefix,\n };\n}\n","/**\n * Disk-to-FileMap reader utility.\n *\n * Reads a cluster directory into an in-memory FileMap for verify and\n * upgrade operations. Classifies files by zone and parses @tfy-status\n * headers for platform files.\n *\n * Reference: data-model.md §9, research.md R-012\n */\n\nimport { readFile, readdir } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport {\n classifyFile,\n parseHeader,\n DEFAULT_PREFIX,\n type FileMap,\n type FileEntry,\n} from '@truefoundry/tfy-infra-engine';\n\n/**\n * Read all files from a cluster directory into a FileMap.\n *\n * Scans the directory, classifies each file by zone, parses @tfy-status\n * headers for platform files, and returns a typed FileMap.\n *\n * Does NOT include manifest.json in the output (callers load it separately).\n *\n * @param dir - Path to cluster directory\n * @param prefix - Platform Zone filename prefix (default: \"tfy_\")\n * @returns FileMap with zone-tagged entries and parsed headers\n */\nexport async function readDirectoryToFileMap(\n dir: string,\n prefix: string = DEFAULT_PREFIX\n): Promise<FileMap> {\n const fileMap: FileMap = new Map();\n\n let entries: string[];\n try {\n const dirEntries = await readdir(dir, { withFileTypes: true });\n entries = dirEntries.filter((e) => e.isFile()).map((e) => e.name);\n } catch (error) {\n const nodeError = error as NodeJS.ErrnoException;\n if (nodeError.code === 'ENOENT') {\n throw new Error(`Directory not found: ${dir}`, { cause: error });\n }\n throw new Error(`Failed to read directory: ${(error as Error).message}`, { cause: error });\n }\n\n for (const filename of entries) {\n // Skip manifest.json — callers load it separately\n if (filename === 'manifest.json') continue;\n\n const content = await readFile(join(dir, filename), 'utf-8');\n const zone = classifyFile(filename, prefix);\n\n const entry: FileEntry = { content, zone };\n\n if (zone === 'platform') {\n const header = parseHeader(content);\n if (header) {\n entry.header = header;\n }\n }\n\n fileMap.set(filename, entry);\n }\n\n return fileMap;\n}\n","/**\n * Engine output-to-disk writer utility.\n *\n * Writes engine results to disk with zone-aware policies.\n *\n * CHANGE (008): Removed sideOutputs handling — no more expose_variable pipeline.\n *\n * Reference: data-model.md §10, spec.md FR-012a\n */\n\nimport { writeFile, mkdir } from 'node:fs/promises';\nimport { join, dirname } from 'node:path';\nimport type { InstallResult, UpgradeResult } from '@truefoundry/tfy-infra-engine';\nimport { fileExists } from './fs-helpers.js';\n\n/**\n * Write engine output to disk with zone-aware policies.\n *\n * Modes:\n * - 'install': Overwrite all files\n * - 'upgrade': Overwrite platform zone files, skip existing user zone files,\n * create new user zone files\n *\n * @param dir - Target directory\n * @param result - InstallResult or UpgradeResult from engine\n * @param mode - 'install' (overwrite all) or 'upgrade' (zone-aware)\n */\nexport async function writeEngineOutput(\n dir: string,\n result: InstallResult | UpgradeResult,\n mode: 'install' | 'upgrade'\n): Promise<void> {\n await mkdir(dir, { recursive: true });\n\n for (const [filePath, entry] of result.files) {\n const fullPath = join(dir, filePath);\n\n if (mode === 'upgrade' && entry.zone === 'user') {\n if (await fileExists(fullPath)) {\n continue;\n }\n }\n\n await mkdir(dirname(fullPath), { recursive: true });\n await writeFile(fullPath, entry.content, 'utf-8');\n }\n}\n","/**\n * Unified diff generation utility\n * Generates unified diff output for test failures\n * @file packages/infra-cli/src/utils/diff.ts\n */\n\nimport { createTwoFilesPatch } from 'diff';\n\n/**\n * Generate a unified diff between expected and actual content.\n *\n * @param expected - Expected file content\n * @param actual - Actual file content\n * @param filename - Name of the file being compared\n * @returns Unified diff string, or empty string if contents match\n */\nexport function generateUnifiedDiff(expected: string, actual: string, filename: string): string {\n const normalizedExpected = expected.replace(/\\r\\n/g, '\\n');\n const normalizedActual = actual.replace(/\\r\\n/g, '\\n');\n\n const patch = createTwoFilesPatch(\n `expected/${filename}`,\n `actual/${filename}`,\n normalizedExpected,\n normalizedActual,\n '', // oldHeader\n '', // newHeader\n { context: 3 }\n );\n\n return patch;\n}\n\n/**\n * Diff entry for a single file\n */\nexport interface DiffEntry {\n filename: string;\n diff: string;\n}\n\n/**\n * Format multiple diffs for display output.\n *\n * @param diffs - Array of diff entries with filenames\n * @returns Formatted string with all diffs\n */\nexport function formatDiffOutput(diffs: DiffEntry[]): string {\n if (diffs.length === 0) {\n return '';\n }\n\n return diffs\n .map((entry) => {\n return ` ${entry.filename}: content mismatch\\n${entry.diff}`;\n })\n .join('\\n');\n}\n","/**\n * tfy-init tpl validate command\n *\n * Validates a bundle.json template package.\n * Detects bundle.json in the specified directory.\n */\n\nimport { Command } from 'commander';\nimport { readFile, stat } from 'node:fs/promises';\nimport { join, resolve } from 'node:path';\nimport Handlebars from 'handlebars';\nimport { validateJsonSchemaStructure, isTofuAvailable } from '@truefoundry/tfy-infra-engine';\nimport { handleEngineError, EXIT_CODES } from '../../utils/error-handler.js';\nimport * as output from '../../utils/output.js';\n\nexport { EXIT_CODES } from '../../utils/error-handler.js';\n\nexport function createValidateCommand(): Command {\n const cmd = new Command('validate')\n .description('Validate a template (for template authors)')\n .option('-d, --directory <dir>', 'Path to local template directory', '.')\n .option('--strict', 'Fail on warnings')\n .option('--skip-format', 'Skip tofu fmt validation')\n .action(async (options: ValidateOptions) => {\n try {\n const exitCode = await runValidate(options);\n process.exit(exitCode);\n } catch (error) {\n handleEngineError(error);\n }\n });\n\n return cmd;\n}\n\ninterface ValidateOptions {\n directory: string;\n strict?: boolean;\n skipFormat?: boolean;\n}\n\ninterface ValidationIssue {\n type: 'error' | 'warning';\n message: string;\n file?: string;\n}\n\nasync function fileExists(path: string): Promise<boolean> {\n try {\n await stat(path);\n return true;\n } catch {\n return false;\n }\n}\n\nasync function runValidate(options: ValidateOptions): Promise<number> {\n const templateDir = resolve(options.directory);\n const issues: ValidationIssue[] = [];\n let hasErrors = false;\n\n output.info(`Validating template at: ${templateDir}`);\n output.info('');\n\n const bundlePath = join(templateDir, 'bundle.json');\n const usesBundle = await fileExists(bundlePath);\n\n if (!usesBundle) {\n output.error('bundle.json not found in template directory');\n return EXIT_CODES.VALIDATION_ERROR;\n }\n\n output.verbose('Checking bundle.json...');\n try {\n const content = await readFile(bundlePath, 'utf-8');\n\n let parsed: Record<string, unknown>;\n try {\n parsed = JSON.parse(content) as Record<string, unknown>;\n } catch (error) {\n throw new Error(`Invalid JSON: ${(error as Error).message}`, { cause: error });\n }\n\n const rawMeta = parsed['metadata'] as Record<string, unknown> | undefined;\n if (!rawMeta || typeof rawMeta !== 'object') {\n throw new Error('bundle.json must contain a \"metadata\" object');\n }\n if (!rawMeta['name'] || !rawMeta['version']) {\n throw new Error('bundle.json metadata must contain \"name\" and \"version\"');\n }\n\n const jsonSchema = parsed['jsonSchema'] as Record<string, unknown> | undefined;\n if (!jsonSchema || typeof jsonSchema !== 'object') {\n throw new Error('bundle.json must contain a \"jsonSchema\" object');\n }\n\n validateJsonSchemaStructure(jsonSchema);\n output.success('bundle.json schema is valid');\n\n const files = parsed['files'] as Record<string, string> | undefined;\n const staticFiles = parsed['staticFiles'] as Record<string, string> | undefined;\n const fileCount = Object.keys(files ?? {}).length;\n const staticCount = Object.keys(staticFiles ?? {}).length;\n\n if (fileCount === 0 && staticCount === 0) {\n issues.push({ type: 'error', message: 'Bundle contains no template files' });\n hasErrors = true;\n } else {\n for (const [relPath, content] of Object.entries(files ?? {})) {\n try {\n Handlebars.precompile(content);\n output.verbose(` ✓ ${relPath}`);\n } catch (error) {\n issues.push({\n type: 'error',\n message: `Invalid Handlebars syntax: ${(error as Error).message}`,\n file: relPath,\n });\n hasErrors = true;\n }\n }\n output.success(\n `Found ${fileCount} HBS template(s)` +\n (staticCount > 0 ? ` and ${staticCount} static file(s)` : '') +\n ' with valid syntax'\n );\n }\n } catch (error) {\n issues.push({\n type: 'error',\n message: (error as Error).message,\n file: 'bundle.json',\n });\n hasErrors = true;\n }\n\n if (!options.skipFormat) {\n output.verbose('Checking tofu fmt availability...');\n const tofuAvailable = await isTofuAvailable();\n if (tofuAvailable) {\n output.success('tofu fmt available');\n } else {\n issues.push({ type: 'warning', message: 'tofu not found - format validation skipped' });\n }\n }\n\n output.info('');\n\n const warnings = issues.filter((i) => i.type === 'warning');\n const errors = issues.filter((i) => i.type === 'error');\n\n if (warnings.length > 0) {\n output.warn(`${warnings.length} warning(s):`);\n for (const issue of warnings) {\n const prefix = issue.file ? `${issue.file}: ` : '';\n output.info(` • ${prefix}${issue.message}`);\n }\n output.info('');\n }\n\n if (errors.length > 0) {\n output.error(`${errors.length} error(s):`);\n for (const issue of errors) {\n const prefix = issue.file ? `${issue.file}: ` : '';\n output.info(` • ${prefix}${issue.message}`);\n }\n output.info('');\n }\n\n if (hasErrors) {\n output.error('Template validation failed');\n return EXIT_CODES.VALIDATION_ERROR;\n }\n\n if (options.strict && warnings.length > 0) {\n output.error('Template validation failed (strict mode)');\n return EXIT_CODES.VALIDATION_ERROR;\n }\n\n output.success('Template is valid');\n return EXIT_CODES.SUCCESS;\n}\n","/**\n * tfy-init tpl test command\n *\n * CHANGE (006): Fetch-first — resolves template via ResolverRegistry\n * before calling engine.install(). Envelope uses Template object.\n */\n\nimport { Command } from 'commander';\nimport { readFile, readdir, writeFile, mkdir } from 'node:fs/promises';\nimport { join, resolve } from 'node:path';\nimport * as yaml from 'js-yaml';\nimport { createEngine, type Envelope, type InstallResult } from '@truefoundry/tfy-infra-engine';\nimport { createResolverRegistry } from '../../resolvers/index.js';\nimport { handleEngineError, EXIT_CODES } from '../../utils/error-handler.js';\nimport * as output from '../../utils/output.js';\nimport { generateUnifiedDiff } from '../../utils/diff.js';\n\nexport { EXIT_CODES } from '../../utils/error-handler.js';\n\nexport function createTestCommand(): Command {\n const cmd = new Command('test')\n .description('Run test fixtures against a template')\n .option('-d, --directory <dir>', 'Path to local template directory', '.')\n .option('--fixture <name>', 'Run specific fixture only')\n .option('--update', 'Update expected outputs with actual results')\n .option('--diff', 'Show unified diff on failures')\n .option('--skip-format', 'Skip tofu fmt in tests')\n .action(async (options: TestOptions) => {\n try {\n const exitCode = await runTest(options);\n process.exit(exitCode);\n } catch (error) {\n handleEngineError(error);\n }\n });\n\n return cmd;\n}\n\ninterface TestOptions {\n directory: string;\n fixture?: string;\n update?: boolean;\n diff?: boolean;\n skipFormat?: boolean;\n}\n\ninterface TestResult {\n name: string;\n passed: boolean;\n errors?: string[];\n diffs?: Array<{ filename: string; diff: string }>;\n}\n\nasync function runTest(options: TestOptions): Promise<number> {\n const templateDir = resolve(options.directory);\n const testsDir = join(templateDir, 'tests');\n const templateUri = `file://${templateDir}`;\n\n output.info(`Testing template at: ${templateDir}`);\n output.info('');\n\n let fixtures: string[];\n try {\n const entries = await readdir(testsDir);\n fixtures = [];\n for (const entry of entries) {\n try {\n const inputsPath = join(testsDir, entry, 'inputs.yaml');\n await readFile(inputsPath);\n fixtures.push(entry);\n } catch {\n // Not a valid fixture directory\n }\n }\n } catch {\n output.error(`No tests directory found at ${testsDir}`);\n return EXIT_CODES.VALIDATION_ERROR;\n }\n\n if (fixtures.length === 0) {\n output.error('No test fixtures found');\n output.info('');\n output.info('Expected structure:');\n output.info(' tests/');\n output.info(' <fixture-name>/');\n output.info(' inputs.yaml');\n output.info(' expected/');\n return EXIT_CODES.VALIDATION_ERROR;\n }\n\n if (options.fixture) {\n if (!fixtures.includes(options.fixture)) {\n output.error(`Fixture '${options.fixture}' not found`);\n output.info(`Available fixtures: ${fixtures.join(', ')}`);\n return EXIT_CODES.VALIDATION_ERROR;\n }\n fixtures = [options.fixture];\n }\n\n output.info(`Running ${fixtures.length} fixture(s)...`);\n output.info('');\n\n // Fetch-first: resolve template once for all fixtures\n const resolverRegistry = createResolverRegistry();\n const template = await resolverRegistry.resolve(templateUri, { noCache: true });\n\n const engine = createEngine();\n const results: TestResult[] = [];\n\n for (const fixture of fixtures) {\n const fixtureDir = join(testsDir, fixture);\n const inputsPath = join(fixtureDir, 'inputs.yaml');\n const expectedDir = join(fixtureDir, 'expected');\n\n output.verbose(`Running fixture: ${fixture}`);\n\n try {\n const inputsContent = await readFile(inputsPath, 'utf-8');\n const inputs = yaml.load(inputsContent) as Record<string, unknown>;\n\n const envelope: Envelope = {\n template,\n inputs,\n intentId: `test-${fixture}`,\n options: {\n skipFormat: options.skipFormat,\n },\n };\n\n const result = await engine.install(envelope);\n\n if (options.update) {\n await updateExpected(expectedDir, result);\n output.success(`${fixture}: updated`);\n results.push({ name: fixture, passed: true });\n } else {\n const { errors, diffs } = await compareOutputs(expectedDir, result, options.diff);\n if (errors.length === 0) {\n output.success(`${fixture}: passed`);\n results.push({ name: fixture, passed: true });\n } else {\n output.error(`${fixture}: failed`);\n for (const err of errors) {\n output.info(` ${err}`);\n }\n if (options.diff && diffs && diffs.length > 0) {\n for (const d of diffs) {\n output.info(` --- expected/${d.filename}`);\n output.info(` +++ actual/${d.filename}`);\n const diffLines = d.diff.split('\\n').slice(4);\n for (const line of diffLines) {\n if (line) {\n output.info(` ${line}`);\n }\n }\n }\n }\n results.push({ name: fixture, passed: false, errors, diffs });\n }\n }\n } catch (error) {\n output.error(`${fixture}: error`);\n output.info(` ${(error as Error).message}`);\n results.push({ name: fixture, passed: false, errors: [(error as Error).message] });\n }\n }\n\n output.info('');\n const passed = results.filter((r) => r.passed).length;\n const failed = results.filter((r) => !r.passed).length;\n\n if (failed === 0) {\n output.success(`All ${passed} fixture(s) passed`);\n return EXIT_CODES.SUCCESS;\n } else {\n output.error(`${failed} fixture(s) failed, ${passed} passed`);\n return EXIT_CODES.VALIDATION_ERROR;\n }\n}\n\n/**\n * Replaces machine-specific `file://` paths in @tfy-status source fields\n * with a stable placeholder so snapshots are portable across environments.\n */\nfunction normalizeStatusSource(content: string): string {\n return content.replace(/(\"source\"\\s*:\\s*)\"file:\\/\\/[^\"]*\"/g, '$1\"<local>\"');\n}\n\nasync function updateExpected(expectedDir: string, result: InstallResult): Promise<void> {\n await mkdir(expectedDir, { recursive: true });\n for (const [filePath, entry] of result.files) {\n if (filePath === 'manifest.json') continue;\n const outputPath = join(expectedDir, filePath);\n await writeFile(outputPath, normalizeStatusSource(entry.content), 'utf-8');\n }\n}\n\ninterface CompareResult {\n errors: string[];\n diffs?: Array<{ filename: string; diff: string }>;\n}\n\nasync function compareOutputs(\n expectedDir: string,\n result: InstallResult,\n includeDiffs?: boolean\n): Promise<CompareResult> {\n const errors: string[] = [];\n const diffs: Array<{ filename: string; diff: string }> = [];\n\n for (const [filePath, entry] of result.files) {\n if (filePath === 'manifest.json') continue;\n const actual = entry.content;\n\n const expectedPath = join(expectedDir, filePath);\n try {\n const expected = await readFile(expectedPath, 'utf-8');\n const normalizedActual = normalizeStatusSource(actual.trim().replace(/\\r\\n/g, '\\n'));\n const normalizedExpected = normalizeStatusSource(expected.trim().replace(/\\r\\n/g, '\\n'));\n\n if (normalizedActual !== normalizedExpected) {\n errors.push(`${filePath}: content mismatch`);\n if (includeDiffs) {\n const diff = generateUnifiedDiff(normalizedExpected, normalizedActual, filePath);\n diffs.push({ filename: filePath, diff });\n }\n }\n } catch {\n errors.push(`${filePath}: expected file not found`);\n }\n }\n\n try {\n const expectedFiles = await readdir(expectedDir);\n for (const file of expectedFiles) {\n if (!result.files.has(file) && file !== 'manifest.json') {\n errors.push(`${file}: unexpected file in expected/`);\n }\n }\n } catch {\n // Expected directory might not exist\n }\n\n return { errors, diffs: includeDiffs ? diffs : undefined };\n}\n","/**\n * tfy-init tpl dev command\n * Watch mode for rapid template iteration.\n *\n * Supports two modes:\n * 1. Schema mode (--schema + --src): reads a pre-computed schema package\n * and a src/ directory. Watches both for changes.\n * 2. Directory mode (--directory): reads bundle.json from a template directory.\n */\n\nimport { Command } from 'commander';\nimport { readFile, mkdir, rm, writeFile, access } from 'node:fs/promises';\nimport { dirname, join, relative, resolve } from 'node:path';\nimport * as yaml from 'js-yaml';\nimport {\n createEngine,\n EngineError,\n EngineErrorCode,\n type Envelope,\n type InstallResult,\n type JSONSchema7,\n type Template,\n type TemplateMetadata,\n} from '@truefoundry/tfy-infra-engine';\nimport { createResolverRegistry } from '../../resolvers/index.js';\nimport { handleEngineError, EXIT_CODES } from '../../utils/error-handler.js';\nimport * as output from '../../utils/output.js';\nimport { findDefaultFixture } from '../../utils/fs-helpers.js';\nimport { walkSrcDir } from '../../utils/walk-src.js';\nimport { createWatcher, type WatchEvent } from '../../utils/file-watcher.js';\nimport { generateUnifiedDiff } from '../../utils/diff.js';\n\nexport { EXIT_CODES } from '../../utils/error-handler.js';\n\nexport interface DevOptions {\n directory: string;\n schema?: string;\n src?: string;\n name?: string;\n version?: string;\n fixture?: string;\n output: string;\n test?: boolean;\n}\n\nexport function createDevCommand(): Command {\n const cmd = new Command('dev')\n .description('Watch mode for rapid template iteration')\n .option('-d, --directory <dir>', 'Path to template version directory')\n .option(\n '--schema <path>',\n 'Path to schema package JSON (from tfy-schema-tool --schema-package)'\n )\n .option('--src <dir>', 'Path to template src/ directory')\n .option('--name <string>', 'Template name (used with --schema mode)')\n .option('--version <semver>', 'Template version (used with --schema mode)')\n .option('-f, --fixture <path>', 'Input file or fixture to use for testing')\n .requiredOption('-o, --output <dir>', 'Output directory for rendered files')\n .option('--no-test', 'Only validate, do not run tests')\n .action(async (options: DevOptions) => {\n try {\n const exitCode = await runDev(options);\n process.exit(exitCode);\n } catch (error) {\n handleEngineError(error);\n }\n });\n\n return cmd;\n}\n\nexport { findDefaultFixture } from '../../utils/fs-helpers.js';\n\nexport async function validateTemplateDir(\n versionDir: string\n): Promise<{ valid: boolean; error?: string }> {\n try {\n await access(versionDir);\n } catch {\n return { valid: false, error: `Directory not found: ${versionDir}` };\n }\n\n const bundleJsonPath = join(versionDir, 'bundle.json');\n try {\n await access(bundleJsonPath);\n return { valid: true };\n } catch {\n return { valid: false, error: `bundle.json not found in ${versionDir}` };\n }\n}\n\nexport async function ensureOutputDir(_versionDir: string, customOutput: string): Promise<string> {\n const outputDir = resolve(customOutput);\n\n try {\n await rm(outputDir, { recursive: true, force: true });\n } catch {\n // Directory might not exist\n }\n\n await mkdir(outputDir, { recursive: true });\n return outputDir;\n}\n\nfunction isSchemaMode(options: DevOptions): boolean {\n return !!(options.schema && options.src);\n}\n\nasync function runDev(options: DevOptions): Promise<number> {\n if (isSchemaMode(options)) {\n return runSchemaModeDev(options);\n }\n return runDirectoryModeDev(options);\n}\n\n/**\n * Schema mode: --schema + --src.\n * Reads schema package from file, walks src/, constructs Template in-memory.\n */\nasync function runSchemaModeDev(options: DevOptions): Promise<number> {\n const schemaPath = resolve(options.schema!);\n const srcDir = resolve(options.src!);\n const templateName = options.name ?? 'dev-template';\n const templateVersion = options.version;\n\n let fixturePath: string;\n if (options.fixture) {\n fixturePath = resolve(options.fixture);\n try {\n await access(fixturePath);\n } catch {\n output.error(`Fixture not found: ${options.fixture}`);\n return EXIT_CODES.VALIDATION_ERROR;\n }\n } else {\n output.error('--fixture is required in schema mode');\n output.info('Use -f to specify an inputs.yaml file.');\n return EXIT_CODES.VALIDATION_ERROR;\n }\n\n const outputDir = await ensureOutputDir('.', options.output);\n const relativeOutputDir = relative(process.cwd(), outputDir);\n\n output.info(`[schema] ${relative(process.cwd(), schemaPath)}`);\n output.info(`[src] ${relative(process.cwd(), srcDir)}`);\n output.info(`[output] ${relativeOutputDir}/`);\n output.info(`[fixture] ${relative(process.cwd(), fixturePath)}`);\n output.info('');\n\n const engine = createEngine();\n\n const renderFromSchema = async () => {\n try {\n const template = await buildTemplateFromSchema(\n schemaPath,\n srcDir,\n templateName,\n templateVersion\n );\n await renderWithTemplate(engine, template, fixturePath, outputDir, options.test);\n } catch (error) {\n output.error(`[render] ✗ Render failed`);\n output.info(` ${(error as Error).message}`);\n }\n };\n\n await renderFromSchema();\n\n const watchPaths = [schemaPath, srcDir, fixturePath];\n\n const watcher = createWatcher(watchPaths, {\n schemaFile: schemaPath,\n onChange: (event: WatchEvent) => {\n void (async () => {\n output.info(`[changed] ${relative(process.cwd(), event.path)}`);\n await renderFromSchema();\n })();\n },\n onReady: () => {\n output.info('[ready] Watching for changes... (Ctrl+C to exit)');\n output.info('');\n },\n onError: (error) => {\n output.error(`Watch error: ${error.message}`);\n },\n });\n\n return new Promise<number>((resolvePromise) => {\n const cleanup = () => {\n output.info('');\n output.info('[stopped] Watch mode ended');\n void watcher.close();\n resolvePromise(EXIT_CODES.SUCCESS);\n };\n\n process.on('SIGINT', cleanup);\n process.on('SIGTERM', cleanup);\n });\n}\n\nfunction extractVersionFromSchema(jsonSchema: JSONSchema7): string | undefined {\n const props = jsonSchema.properties;\n if (!props) return undefined;\n const versionProp = props['version'];\n if (!versionProp || typeof versionProp === 'boolean') return undefined;\n const def = versionProp.default;\n return typeof def === 'string' ? def : undefined;\n}\n\nasync function buildTemplateFromSchema(\n schemaPath: string,\n srcDir: string,\n name: string,\n version?: string\n): Promise<Template> {\n const schemaContent = await readFile(schemaPath, 'utf-8');\n const schemaPkg = JSON.parse(schemaContent) as Record<string, unknown>;\n\n const jsonSchema = schemaPkg['jsonSchema'] as JSONSchema7;\n if (!jsonSchema || typeof jsonSchema !== 'object') {\n throw new Error('Schema package must contain a \"jsonSchema\" object');\n }\n\n const resolvedVersion = version ?? extractVersionFromSchema(jsonSchema) ?? '0.0.0-dev';\n\n const { files, staticFiles } = await walkSrcDir(srcDir);\n\n const metadata: TemplateMetadata = { name, version: resolvedVersion };\n\n return {\n metadata,\n jsonSchema,\n files,\n staticFiles,\n source: `file://${srcDir}`,\n version: { semver: resolvedVersion },\n };\n}\n\nasync function renderWithTemplate(\n engine: ReturnType<typeof createEngine>,\n template: Template,\n fixturePath: string,\n outputDir: string,\n runTests?: boolean\n): Promise<void> {\n const inputsContent = await readFile(fixturePath, 'utf-8');\n const inputs = yaml.load(inputsContent) as Record<string, unknown>;\n\n const envelope: Envelope = {\n template,\n inputs,\n intentId: `dev-${Date.now()}`,\n options: { skipFormat: false },\n };\n\n const result = await engine.install(envelope);\n\n const templateFileCount = Array.from(result.files.keys()).filter(\n (f) => f !== 'manifest.json'\n ).length;\n output.info(`[validate] ✓ ${templateFileCount} template files valid`);\n\n await rm(outputDir, { recursive: true, force: true });\n await mkdir(outputDir, { recursive: true });\n\n let filesWritten = 0;\n for (const [filePath, entry] of result.files) {\n if (filePath === 'manifest.json') continue;\n const outputPath = join(outputDir, filePath);\n await mkdir(dirname(outputPath), { recursive: true });\n await writeFile(outputPath, entry.content, 'utf-8');\n filesWritten++;\n }\n output.info(\n `[render] ✓ ${filesWritten} files written to ${relative(process.cwd(), outputDir)}/`\n );\n\n if (runTests !== false) {\n await runTestComparisonFromResult(result, outputDir);\n }\n}\n\n/**\n * Directory mode: --directory (legacy, uses FileResolver).\n */\nasync function runDirectoryModeDev(options: DevOptions): Promise<number> {\n const versionDir = resolve(options.directory || '.');\n\n const validation = await validateTemplateDir(versionDir);\n if (!validation.valid) {\n output.error(validation.error!);\n return EXIT_CODES.VALIDATION_ERROR;\n }\n\n let fixturePath: string;\n if (options.fixture) {\n fixturePath = resolve(versionDir, options.fixture);\n try {\n await access(fixturePath);\n } catch {\n output.error(`Fixture not found: ${options.fixture}`);\n return EXIT_CODES.VALIDATION_ERROR;\n }\n } else {\n const defaultFixture = await findDefaultFixture(versionDir);\n if (!defaultFixture) {\n output.error('No default fixture found at tests/default/inputs.yaml');\n output.info('Either create a default fixture or use --fixture to specify one.');\n return EXIT_CODES.VALIDATION_ERROR;\n }\n fixturePath = defaultFixture;\n }\n\n const outputDir = await ensureOutputDir(versionDir, options.output);\n const relativeOutputDir = relative(process.cwd(), outputDir);\n\n output.info(`[watching] ${relative(process.cwd(), versionDir)}`);\n output.info(`[output] ${relativeOutputDir}/`);\n output.info(`[fixture] ${relative(process.cwd(), fixturePath)}`);\n output.info('');\n\n const engine = createEngine();\n const templateUri = `file://${versionDir}`;\n\n await runRenderCycle(engine, templateUri, versionDir, fixturePath, outputDir, options.test);\n\n const watcher = createWatcher(versionDir, {\n onChange: (event: WatchEvent) => {\n void (async () => {\n const relPath = relative(versionDir, event.path);\n output.info(`[changed] ${relPath}`);\n\n if (event.fileType === 'schema') {\n await runValidation(templateUri);\n } else if (event.fileType === 'template' || event.fileType === 'inputs') {\n await runRenderCycle(\n engine,\n templateUri,\n versionDir,\n fixturePath,\n outputDir,\n options.test\n );\n }\n })();\n },\n onReady: () => {\n output.info('[ready] Watching for changes... (Ctrl+C to exit)');\n output.info('');\n },\n onError: (error) => {\n output.error(`Watch error: ${error.message}`);\n },\n });\n\n return new Promise<number>((resolvePromise) => {\n const cleanup = () => {\n output.info('');\n output.info('[stopped] Watch mode ended');\n void watcher.close();\n resolvePromise(EXIT_CODES.SUCCESS);\n };\n\n process.on('SIGINT', cleanup);\n process.on('SIGTERM', cleanup);\n });\n}\n\nasync function runValidation(templateUri: string): Promise<boolean> {\n try {\n const resolverRegistry = createResolverRegistry();\n const template = await resolverRegistry.resolve(templateUri, { noCache: true });\n if (template.jsonSchema) {\n output.info('[validate] ✓ schema valid');\n return true;\n }\n output.error('[validate] ✗ schema invalid');\n return false;\n } catch (error) {\n output.error(`[validate] ✗ schema invalid`);\n output.info(` ${(error as Error).message}`);\n return false;\n }\n}\n\nasync function runRenderCycle(\n engine: ReturnType<typeof createEngine>,\n templateUri: string,\n versionDir: string,\n fixturePath: string,\n outputDir: string,\n runTests?: boolean\n): Promise<void> {\n const valid = await runValidation(templateUri);\n if (!valid) return;\n\n try {\n const inputsContent = await readFile(fixturePath, 'utf-8');\n const inputs = yaml.load(inputsContent) as Record<string, unknown>;\n\n const resolverRegistry = createResolverRegistry();\n const template = await resolverRegistry.resolve(templateUri, { noCache: true });\n\n const envelope: Envelope = {\n template,\n inputs,\n intentId: `dev-${Date.now()}`,\n options: { skipFormat: false },\n };\n\n const result = await engine.install(envelope);\n\n const templateFileCount = Array.from(result.files.keys()).filter(\n (f) => f !== 'manifest.json'\n ).length;\n output.info(`[validate] ✓ ${templateFileCount} template files valid`);\n\n await rm(outputDir, { recursive: true, force: true });\n await mkdir(outputDir, { recursive: true });\n\n let filesWritten = 0;\n for (const [filePath, entry] of result.files) {\n if (filePath === 'manifest.json') continue;\n const outputPath = join(outputDir, filePath);\n await mkdir(dirname(outputPath), { recursive: true });\n await writeFile(outputPath, entry.content, 'utf-8');\n filesWritten++;\n }\n output.info(\n `[render] ✓ ${filesWritten} files written to ${relative(process.cwd(), outputDir)}/`\n );\n\n if (runTests !== false) {\n await runTestComparison(versionDir, result, outputDir);\n }\n } catch (error) {\n output.error(`[render] ✗ Render failed`);\n output.info(` ${(error as Error).message}`);\n if (\n error instanceof EngineError &&\n error.code === EngineErrorCode.INPUT_VALIDATION_FAILED &&\n error.details?.['errors']\n ) {\n output.info(\n output.formatValidationErrors(\n error.details['errors'] as Array<{ path: string; message: string }>\n )\n );\n }\n }\n}\n\nasync function runTestComparison(\n versionDir: string,\n result: InstallResult,\n _outputDir: string\n): Promise<void> {\n const expectedDir = join(versionDir, 'tests', 'default', 'expected');\n await runTestComparisonFromDir(result, expectedDir);\n}\n\nasync function runTestComparisonFromResult(\n _result: InstallResult,\n _outputDir: string\n): Promise<void> {\n // In schema mode, we don't have a versionDir to find expected/ in.\n // Test comparison is skipped unless running from directory mode.\n}\n\nasync function runTestComparisonFromDir(result: InstallResult, expectedDir: string): Promise<void> {\n const startTime = Date.now();\n\n try {\n await access(expectedDir);\n } catch {\n output.info('[test] ⚠ No expected/ directory, skipping comparison');\n return;\n }\n\n const errors: Array<{ file: string; diff?: string }> = [];\n\n for (const [filePath, entry] of result.files) {\n if (filePath === 'manifest.json') continue;\n const actual = entry.content;\n\n const expectedPath = join(expectedDir, filePath);\n try {\n const expected = await readFile(expectedPath, 'utf-8');\n const normalizedActual = actual.trim().replace(/\\r\\n/g, '\\n');\n const normalizedExpected = expected.trim().replace(/\\r\\n/g, '\\n');\n\n if (normalizedActual !== normalizedExpected) {\n const diff = generateUnifiedDiff(normalizedExpected, normalizedActual, filePath);\n errors.push({ file: filePath, diff });\n }\n } catch {\n errors.push({ file: filePath });\n }\n }\n\n const duration = Date.now() - startTime;\n\n if (errors.length === 0) {\n output.info(`[test] ✓ default: passed (${duration}ms)`);\n } else {\n output.error(`[test] ✗ default: failed`);\n for (const err of errors) {\n output.info(` ${err.file}: content mismatch`);\n if (err.diff) {\n const diffLines = err.diff.split('\\n').slice(4, 14);\n for (const line of diffLines) {\n if (line) {\n output.info(` ${line}`);\n }\n }\n if (err.diff.split('\\n').length > 14) {\n output.info(` ... (diff truncated)`);\n }\n }\n }\n }\n}\n","/**\n * File watcher utility\n * Watches template directories for changes and emits events\n * @file packages/infra-cli/src/utils/file-watcher.ts\n */\n\nimport chokidar, { type FSWatcher } from 'chokidar';\nimport { basename } from 'node:path';\n\n/**\n * Type of file change that occurred\n */\nexport type FileType = 'schema' | 'template' | 'inputs' | 'other';\n\n/**\n * Event emitted when a file changes\n */\nexport interface WatchEvent {\n type: 'change' | 'add' | 'unlink';\n path: string;\n fileType: FileType;\n}\n\n/**\n * Options for creating a file watcher\n */\nexport interface WatchOptions {\n /**\n * Callback when a file changes\n */\n onChange?: (event: WatchEvent) => void;\n\n /**\n * Callback when watcher is ready\n */\n onReady?: () => void;\n\n /**\n * Callback when an error occurs\n */\n onError?: (error: Error) => void;\n\n /**\n * Patterns to ignore\n */\n ignored?: string | RegExp | ((path: string) => boolean);\n\n /**\n * Debounce delay in milliseconds\n */\n debounceMs?: number;\n\n /**\n * Path to the schema file for classifying changes.\n */\n schemaFile?: string;\n}\n\n/**\n * Classify a file path by its type.\n *\n * @param path - File path to classify\n * @param schemaFile - Optional path to the schema file to match against\n * @returns File type classification\n */\nexport function classifyFileChange(path: string, schemaFile?: string): FileType {\n const normalizedPath = path.replace(/\\\\/g, '/');\n const filename = basename(normalizedPath);\n\n if (schemaFile && normalizedPath.endsWith(schemaFile.replace(/\\\\/g, '/'))) {\n return 'schema';\n }\n\n if (filename === 'bundle.json' || filename === 'schema-pkg.json') {\n return 'schema';\n }\n\n const inSrcDir = /\\/src\\//.test(normalizedPath);\n if (inSrcDir && !filename.startsWith('_')) {\n return 'template';\n }\n\n if (filename === 'inputs.yaml') {\n return 'inputs';\n }\n\n return 'other';\n}\n\n/**\n * Create a file watcher for one or more paths.\n *\n * @param paths - Path(s) to watch (directory or file)\n * @param options - Watcher options\n * @returns FSWatcher instance\n */\nexport function createWatcher(paths: string | string[], options: WatchOptions = {}): FSWatcher {\n const {\n onChange,\n onReady,\n onError,\n ignored = /(^|[/\\\\])\\..|(^|[/\\\\])node_modules($|[/\\\\])|(^|[/\\\\])dev($|[/\\\\])/,\n debounceMs = 100,\n schemaFile,\n } = options;\n\n const watcher = chokidar.watch(paths, {\n ignored,\n persistent: true,\n ignoreInitial: true,\n awaitWriteFinish: {\n stabilityThreshold: debounceMs,\n pollInterval: 50,\n },\n });\n\n const handleEvent = (type: WatchEvent['type']) => (path: string) => {\n const fileType = classifyFileChange(path, schemaFile);\n const event: WatchEvent = { type, path, fileType };\n\n if (onChange) {\n onChange(event);\n }\n };\n\n watcher.on('change', handleEvent('change'));\n watcher.on('add', handleEvent('add'));\n watcher.on('unlink', handleEvent('unlink'));\n\n if (onReady) {\n watcher.on('ready', onReady);\n }\n\n if (onError) {\n watcher.on('error', (err: unknown) =>\n onError(err instanceof Error ? err : new Error(String(err)))\n );\n }\n\n return watcher;\n}\n","/**\n * tfy-init tpl verify command\n *\n * Checks integrity of files on disk against a manifest.json.\n * Wraps engine.verify() to produce human-readable or JSON drift reports.\n *\n */\n\nimport { Command } from 'commander';\nimport { join, resolve } from 'node:path';\nimport { createEngine } from '@truefoundry/tfy-infra-engine';\nimport type { DriftReport } from '@truefoundry/tfy-infra-engine';\nimport { loadManifest } from '../../utils/manifest-loader.js';\nimport { readDirectoryToFileMap } from '../../utils/file-reader.js';\nimport { handleEngineError, EXIT_CODES } from '../../utils/error-handler.js';\nimport * as output from '../../utils/output.js';\nimport { driftTypeTag } from '../../utils/output.js';\n\n// Re-export for backward compatibility\nexport { EXIT_CODES } from '../../utils/error-handler.js';\n\n/** Verify-specific exit code for drift detected (distinct from error). */\nconst DRIFT_DETECTED = 1;\n\ninterface VerifyOptions {\n directory: string;\n json?: boolean;\n}\n\n/**\n * Create the verify command.\n */\nexport function createVerifyCommand(): Command {\n const cmd = new Command('verify')\n .description('Check integrity of files on disk against a manifest')\n .option('-d, --directory <dir>', 'Path to cluster directory containing manifest.json', '.')\n .option('--json', 'Output drift report as structured JSON')\n .action(async (options: VerifyOptions) => {\n try {\n const exitCode = await runVerify(options);\n process.exit(exitCode);\n } catch (error) {\n handleEngineError(error);\n }\n });\n\n return cmd;\n}\n\n/**\n * Execute the verify command.\n * Exported for testing.\n */\nexport async function runVerify(options: VerifyOptions): Promise<number> {\n const dir = resolve(options.directory);\n const manifestPath = join(dir, 'manifest.json');\n\n // Load manifest\n output.verbose(`Loading manifest from ${manifestPath}`);\n const manifest = await loadManifest(manifestPath);\n\n // Read current files on disk\n output.verbose(`Reading files from ${dir}`);\n const currentFiles = await readDirectoryToFileMap(dir, manifest.platformPrefix);\n\n // Create engine and verify\n const engine = createEngine();\n const result = await engine.verify(currentFiles, manifest);\n\n // Output the report\n if (options.json) {\n output.info(formatDriftReportJson(result.driftReport));\n } else {\n output.info(formatDriftReport(result.driftReport));\n }\n\n return result.driftReport.valid ? EXIT_CODES.SUCCESS : DRIFT_DETECTED;\n}\n\n/**\n * Format a drift report as human-readable text.\n * Exported for testing.\n */\nexport function formatDriftReport(report: DriftReport): string {\n const lines: string[] = [];\n const issueCount =\n report.summary.driftedFiles + report.summary.missingFiles + report.summary.unexpectedFiles;\n\n if (report.valid) {\n lines.push(\n `✓ Verification passed: ${report.summary.totalFiles} files checked, ${issueCount} issues`\n );\n lines.push('');\n lines.push(`Aggregate Hash: ${report.aggregateHash.expected}`);\n } else {\n lines.push(`✗ Drift detected: ${issueCount} issues found`);\n lines.push('');\n\n for (const entry of report.entries) {\n const tag = driftTypeTag(entry.type);\n lines.push(` ${tag} ${entry.path}`);\n lines.push(` ${entry.details}`);\n lines.push('');\n }\n\n lines.push(\n `Summary: ${report.summary.totalFiles} files checked, ` +\n `${report.summary.driftedFiles} drifted, ` +\n `${report.summary.missingFiles} missing` +\n (report.summary.unexpectedFiles > 0 ? `, ${report.summary.unexpectedFiles} unexpected` : '')\n );\n }\n\n return lines.join('\\n');\n}\n\n/**\n * Format a drift report as structured JSON.\n * Exported for testing.\n */\nexport function formatDriftReportJson(report: DriftReport): string {\n return JSON.stringify(report, null, 2);\n}\n"],"mappings":";ueAOA,IAAAA,GAAwB,qBCAxB,IAAAC,GAAwB,qBCCxB,IAAAC,GAAwB,qBACxBC,EAA2C,uBAC3CC,EAAiC,gBCDjC,IAAAC,GAAkC,uBAClCC,GAAqB,gBAarB,eAAsBC,GAAWC,EAAsC,CACrE,IAAMC,EAAQ,IAAI,IACZC,EAAc,IAAI,IAExB,aAAMC,GAAKH,EAAS,GAAIC,EAAOC,CAAW,EAEnC,CAAE,MAAAD,EAAO,YAAAC,CAAY,CAC9B,CAEA,eAAeC,GACbH,EACAI,EACAH,EACAC,EACe,CACf,IAAMG,EAAaD,KAAe,SAAKJ,EAASI,CAAY,EAAIJ,EAC1DM,EAAU,QAAM,YAAQD,EAAY,CAAE,cAAe,EAAK,CAAC,EAEjE,QAAWE,KAASD,EAAS,CAC3B,GAAIC,EAAM,KAAK,WAAW,GAAG,EAAG,SAEhC,IAAMC,EAAgBJ,EAAe,GAAGA,CAAY,IAAIG,EAAM,IAAI,GAAKA,EAAM,KAE7E,GAAIA,EAAM,YAAY,EACpB,MAAMJ,GAAKH,EAASQ,EAAeP,EAAOC,CAAW,MAChD,CACL,IAAMO,EAAU,QAAM,gBAAS,SAAKJ,EAAYE,EAAM,IAAI,EAAG,OAAO,EACpE,GAAIA,EAAM,KAAK,SAAS,MAAM,EAAG,CAC/B,IAAMG,EAAaF,EAAc,QAAQ,SAAU,EAAE,EACrDP,EAAM,IAAIS,EAAYD,CAAO,CAC/B,MACEP,EAAY,IAAIM,EAAeC,CAAO,CAE1C,CACF,CACF,CCnDA,IAAAE,EAA6C,yCCM7C,IAAIC,EAA8B,SAC9BC,EAAW,GAER,SAASC,GAAaC,EAAwB,CACnDH,EAAmBG,CACrB,CAEO,SAASC,IAA0B,CACxC,OAAOJ,CACT,CAMO,SAASK,GAAYC,EAAwB,CAClDL,EAAWK,CACb,CAUO,SAASC,EAAKC,EAAuB,CAC1C,QAAQ,OAAO,MAAMA,EAAU;AAAA,CAAI,CACrC,CAEO,SAASC,EAAQD,EAAuB,CACzCE,IAAqB,UACTC,EAAW,QAAQ,MAAQ,QAAQ,KAC3C,UAAKH,CAAO,EAAE,CAExB,CAEO,SAASI,EAAMJ,EAAuB,CAC3C,QAAQ,MAAM,UAAKA,CAAO,EAAE,CAC9B,CAEO,SAASK,EAAKL,EAAuB,CACtCE,IAAqB,UACTC,EAAW,QAAQ,MAAQ,QAAQ,KAC3CH,CAAO,CAEjB,CAEO,SAASM,EAAQN,EAAuB,CACzCE,IAAqB,YACTC,EAAW,QAAQ,MAAQ,QAAQ,KAC3C,KAAKH,CAAO,EAAE,CAExB,CAEO,SAASO,EAAKP,EAAuB,CACtCE,IAAqB,SACvB,QAAQ,KAAK,UAAKF,CAAO,EAAE,CAE/B,CAEO,SAASQ,GAAWC,EAAuB,CAChD,OAAIA,EAAQ,KAAa,GAAGA,CAAK,KAC7BA,EAAQ,KAAO,KAAa,IAAIA,EAAQ,MAAM,QAAQ,CAAC,CAAC,MACrD,IAAIA,GAAS,KAAO,OAAO,QAAQ,CAAC,CAAC,KAC9C,CAEO,SAASC,GAAuBC,EAA0D,CAC/F,OAAOA,EAAO,IAAK,GAAM,YAAO,EAAE,IAAI,KAAK,EAAE,OAAO,EAAE,EAAE,KAAK;AAAA,CAAI,CACnE,CAEO,SAASC,GAAaC,EAAyB,CACpD,OAAQA,EAAM,CACZ,IAAK,gBACH,MAAO,UACT,IAAK,eACH,MAAO,UACT,IAAK,kBACH,MAAO,aACT,IAAK,yBACH,MAAO,eACT,IAAK,kBACH,MAAO,iBACX,CACF,CAEO,SAASC,GAAcC,EAAoD,CAChF,GAAIb,IAAqB,QAAS,OAElC,IAAMc,EAAQb,EAAW,QAAQ,MAAQ,QAAQ,IACjDa,EAAM,QAAQ,EACd,QAAWC,KAAQF,EACjBC,EAAM,OAAOC,EAAK,IAAI,KAAKT,GAAWS,EAAK,IAAI,CAAC,GAAG,CAEvD,CD/FO,IAAMC,EAAa,CACxB,QAAS,EACT,iBAAkB,EAClB,YAAa,EACb,aAAc,EACd,aAAc,EACd,cAAe,CACjB,EAeO,SAASC,EACdC,EACAC,EAA2BH,EAAW,aAC/B,CACP,GAAIE,aAAiB,cAAa,CAIhC,GAHOA,EAAMA,EAAM,OAAO,EAGfE,GAAa,IAAM,WAAaF,EAAM,QAAS,CACjDG,EAAK,EAAE,EACPA,EAAK,UAAU,EACtB,OAAW,CAACC,EAAKC,CAAK,IAAK,OAAO,QAAQL,EAAM,OAAO,EACjDI,IAAQ,UACHD,EAAK,KAAKC,CAAG,KAAK,KAAK,UAAUC,CAAK,CAAC,EAAE,CAGtD,CAcA,OAXIL,EAAM,SAAU,SACXG,EAAK,EAAE,EACPA,EAAK,SAAS,EACdA,EACEG,GACLN,EAAM,QAAQ,MAChB,CACF,GAIMA,EAAM,KAAM,CAClB,KAAK,kBAAgB,wBACrB,KAAK,kBAAgB,eACrB,KAAK,kBAAgB,2BACnB,QAAQ,KAAKF,EAAW,gBAAgB,EACxC,MAEF,KAAK,kBAAgB,mBACrB,KAAK,kBAAgB,eACrB,KAAK,kBAAgB,cACnB,QAAQ,KAAKA,EAAW,WAAW,EACnC,MAEF,KAAK,kBAAgB,eACrB,KAAK,kBAAgB,gBACZK,EAAK,EAAE,EACPA,EAAK,4CAA4C,EACxD,QAAQ,KAAKL,EAAW,YAAY,EACpC,MAEF,KAAK,kBAAgB,qBACnB,QAAQ,KAAKA,EAAW,gBAAgB,EACxC,MAEF,QACE,QAAQ,KAAKG,CAAgB,CACjC,CACF,CAGOD,EAAOA,EAAgB,OAAO,EAC1BE,GAAa,IAAM,WAC5B,QAAQ,MAAOF,EAAgB,KAAK,EAEtC,QAAQ,KAAKC,CAAgB,CAC/B,CFxDO,SAASM,IAA+B,CAgB7C,OAfY,IAAI,WAAQ,QAAQ,EAC7B,YAAY,8DAA8D,EAC1E,eAAe,0BAA2B,4CAA4C,EACtF,eAAe,cAAe,4DAA4D,EAC1F,eAAe,kBAAmB,eAAe,EACjD,OAAO,yBAA0B,sBAAsB,EACvD,OAAO,sBAAuB,oCAAoC,EAClE,OAAO,MAAOC,GAA2B,CACxC,GAAI,CACF,MAAMC,GAAUD,CAAO,CACzB,OAASE,EAAO,CACdC,EAAkBD,CAAK,CACzB,CACF,CAAC,CAGL,CAEA,eAAeE,GAAkBC,EAA4C,CAC3E,IAAIC,EAEAD,IAAe,IACjBC,EAAM,MAAMC,GAAU,EAEtBD,EAAM,QAAM,eAAS,WAAQD,CAAU,EAAG,OAAO,EAGnD,IAAIG,EACJ,GAAI,CACFA,EAAS,KAAK,MAAMF,CAAG,CACzB,OAASG,EAAK,CACZ,MAAM,IAAI,MAAM,wCAAyCA,EAAc,OAAO,GAAI,CAChF,MAAOA,CACT,CAAC,CACH,CAEA,IAAMC,EAAMF,EACZ,GAAI,CAACE,EAAI,YAAiB,OAAOA,EAAI,YAAkB,SACrD,MAAM,IAAI,MAAM,mDAAmD,EAErE,GAAI,CAAC,MAAM,QAAQA,EAAI,QAAW,EAChC,MAAM,IAAI,MAAM,gDAAgD,EAGlE,MAAO,CACL,WAAYA,EAAI,WAChB,SAAUA,EAAI,QAChB,CACF,CAEA,SAASH,IAA6B,CACpC,OAAO,IAAI,QAAQ,CAACI,EAASC,IAAW,CACtC,IAAMC,EAAmB,CAAC,EAC1B,QAAQ,MAAM,GAAG,OAASC,GAAkBD,EAAO,KAAKC,CAAK,CAAC,EAC9D,QAAQ,MAAM,GAAG,MAAO,IAAMH,EAAQ,OAAO,OAAOE,CAAM,EAAE,SAAS,OAAO,CAAC,CAAC,EAC9E,QAAQ,MAAM,GAAG,QAASD,CAAM,CAClC,CAAC,CACH,CAEA,SAASG,GAAyBC,EAAyD,CACzF,IAAMC,EAAQD,EAAW,WACzB,GAAI,CAACC,EAAO,OACZ,IAAMC,EAAcD,EAAM,QAC1B,GAAI,CAACC,EAAa,OAClB,IAAMC,EAAMD,EAAY,QACxB,OAAO,OAAOC,GAAQ,SAAWA,EAAM,MACzC,CAEA,eAAelB,GAAUD,EAAuC,CAC9D,IAAMoB,EAAY,MAAMhB,GAAkBJ,EAAQ,MAAM,EAElDqB,EAAUrB,EAAQ,SAAWe,GAAyBK,EAAU,UAAU,EAChF,GAAI,CAACC,EACH,MAAM,IAAI,MACR,6HACF,EAGF,IAAMC,KAAS,WAAQtB,EAAQ,GAAG,EAC5B,CAAE,MAAAuB,EAAO,YAAAC,CAAY,EAAI,MAAMC,GAAWH,CAAM,EAEtD,GAAIC,EAAM,OAAS,GAAKC,EAAY,OAAS,EAC3C,MAAM,IAAI,MAAM,8BAA8BF,CAAM,EAAE,EAGxD,IAAMI,EAAmC,CACvC,KAAM1B,EAAQ,KACd,QAAAqB,CACF,EACIrB,EAAQ,cACV0B,EAAS,YAAc1B,EAAQ,aAGjC,IAAM2B,EAAqB,CACzB,SAAAD,EACA,WAAYN,EAAU,WACtB,SAAUA,EAAU,SACpB,MAAO,OAAO,YAAYG,CAAK,EAC/B,YAAa,OAAO,YAAYC,CAAW,EAC3C,QAAS,CAAE,OAAQH,CAAQ,CAC7B,EAEMO,EAAO,KAAK,UAAUD,EAAQ,KAAM,CAAC,EAAI;AAAA,EAE/C,GAAI3B,EAAQ,OAAQ,CAClB,IAAM6B,KAAU,WAAQ7B,EAAQ,MAAM,EACtC,QAAM,YAAM,WAAQ6B,CAAO,EAAG,CAAE,UAAW,EAAK,CAAC,EACjD,QAAM,aAAUA,EAASD,EAAM,OAAO,EAC/BE,EAAQ,qBAAqBD,CAAO,EAAE,CAC/C,MACE,QAAQ,OAAO,MAAMD,CAAI,CAE7B,CIlJA,IAAAG,GAAwB,qBACxBC,GAAgC,oBAChCC,EAA0C,uBAC1CC,EAA8B,gBAC9BC,GAAsB,sBACtBC,GAA6B,yCCR7B,IAAAC,GAA6C,yCCC7C,IAAAC,EAA+B,uBAC/BC,GAAqB,gBAGrBC,EAA6C,yCAMhCC,EAAN,KAAuC,CAC5C,WAAWC,EAAsB,CAC/B,OAAOA,EAAI,WAAW,SAAS,CACjC,CAEA,MAAM,QAAQA,EAAaC,EAA+C,CACxE,IAAMC,EAAS,KAAK,UAAUF,CAAG,EAEjC,GAAI,CACF,IAAMG,EAAO,QAAM,QAAKD,CAAM,EAE9B,GAAIC,EAAK,OAAO,GAAKD,EAAO,SAAS,OAAO,EAC1C,OAAO,KAAK,cAAcA,EAAQF,CAAG,EAGvC,GAAIG,EAAK,YAAY,EAAG,CACtB,IAAMC,KAAa,SAAKF,EAAQ,aAAa,EAC7C,GAAI,MAAM,KAAK,WAAWE,CAAU,EAClC,OAAO,KAAK,cAAcA,EAAYJ,CAAG,EAE3C,MAAM,IAAI,cACR,4BAA4BE,CAAM,GAClC,kBAAgB,iBAChB,OACA,CAAE,KAAMA,CAAO,CACjB,CACF,CAEA,MAAM,IAAI,cACR,4BAA4BA,CAAM,GAClC,kBAAgB,mBAChB,OACA,CAAE,KAAMA,CAAO,CACjB,CACF,OAASG,EAAO,CACd,GAAIA,aAAiB,cACnB,MAAMA,EAGR,IAAMC,EAAYD,EAClB,MAAIC,EAAU,OAAS,SACf,IAAI,cACR,uBAAuBJ,CAAM,GAC7B,kBAAgB,eAChBG,EACA,CAAE,KAAMH,EAAQ,aAAcI,EAAU,IAAK,CAC/C,EAGEA,EAAU,OAAS,SACf,IAAI,cACR,sBAAsBJ,CAAM,GAC5B,kBAAgB,eAChBG,EACA,CAAE,KAAMH,EAAQ,aAAcI,EAAU,IAAK,CAC/C,EAGI,IAAI,cACR,gCAAgCJ,CAAM,KAAMG,EAAgB,OAAO,GACnE,kBAAgB,eAChBA,EACA,CAAE,KAAMH,CAAO,CACjB,CACF,CACF,CAEA,MAAc,cAAcE,EAAoBJ,EAAgC,CAC9E,IAAMO,EAAU,QAAM,YAASH,EAAY,OAAO,EAE9CI,EACJ,GAAI,CACFA,EAAM,KAAK,MAAMD,CAAO,CAC1B,OAASF,EAAO,CACd,MAAM,IAAI,cACR,gCAAiCA,EAAgB,OAAO,GACxD,kBAAgB,eAChBA,EACA,CAAE,KAAMD,CAAW,CACrB,CACF,CAEA,IAAMK,EAAcD,EAAI,SACxB,GAAI,CAACC,GAAe,OAAOA,GAAgB,SACzC,MAAM,IAAI,cACR,+CACA,kBAAgB,eAChB,OACA,CAAE,KAAML,CAAW,CACrB,EAEF,GAAI,CAACK,EAAY,MAAW,CAACA,EAAY,QACvC,MAAM,IAAI,cACR,yDACA,kBAAgB,eAChB,OACA,CAAE,KAAML,CAAW,CACrB,EAGF,IAAMM,EAAW,CACf,KAAMD,EAAY,KAClB,QAASA,EAAY,QACrB,GAAIA,EAAY,YAAiB,CAAE,YAAaA,EAAY,WAAyB,EAAI,CAAC,CAC5F,EAEME,EAAaH,EAAI,WACvB,GAAI,CAACG,GAAc,OAAOA,GAAe,SACvC,MAAM,IAAI,cACR,iDACA,kBAAgB,eAChB,OACA,CAAE,KAAMP,CAAW,CACrB,EAGF,IAAMQ,EAAQ,IAAI,IAAI,OAAO,QAASJ,EAAI,OAAuC,CAAC,CAAC,CAAC,EAC9EK,EAAc,IAAI,IACtB,OAAO,QAASL,EAAI,aAA6C,CAAC,CAAC,CACrE,EAEA,GAAII,EAAM,OAAS,GAAKC,EAAY,OAAS,EAC3C,MAAM,IAAI,cACR,sCAAsCT,CAAU,GAChD,kBAAgB,mBAChB,OACA,CAAE,KAAMA,CAAW,CACrB,EAGF,IAAMU,EAAuB,CAAE,OAAQJ,EAAS,OAAQ,EAExD,MAAO,CAAE,SAAAA,EAAU,WAAAC,EAAY,MAAAC,EAAO,YAAAC,EAAa,OAAQb,EAAK,QAAAc,CAAQ,CAC1E,CAEQ,UAAUd,EAAqB,CAIrC,OAHIA,EAAI,WAAW,UAAU,GAGzBA,EAAI,WAAW,SAAS,EACnBA,EAAI,UAAU,CAAC,EAEjBA,CACT,CAEA,MAAc,WAAWe,EAAgC,CACvD,GAAI,CACF,eAAM,QAAKA,CAAI,EACR,EACT,MAAQ,CACN,MAAO,EACT,CACF,CACF,EClKA,IAAAC,EAA6D,yCCWtD,IAAMC,GAAsD,CACjE,QAAS,IACT,QAAS,EACT,QAAS,EACX,EAKO,SAASC,GAAqBC,EAAsD,CACzF,MAAO,CACL,GAAGF,GACH,GAAGE,CACL,CACF,CAKA,eAAsBC,GACpBC,EACAC,EACAC,EAAY,IACA,CACZ,IAAIC,EAEJ,QAASC,EAAU,EAAGA,GAAWH,EAASG,IACxC,GAAI,CACF,OAAO,MAAMJ,EAAG,CAClB,OAASK,EAAO,CAGd,GAFAF,EAAYE,EAERD,EAAUH,EAAS,CACrB,IAAMK,EAAQJ,EAAY,KAAK,IAAI,EAAGE,CAAO,GAAK,GAAM,KAAK,OAAO,EAAI,IACxE,MAAM,IAAI,QAASG,GAAY,WAAWA,EAASD,CAAK,CAAC,CAC3D,CACF,CAGF,MAAMH,aAAqB,MAAQA,EAAY,IAAI,MAAM,OAAOA,CAAS,CAAC,CAC5E,CAKO,SAASK,GAAeC,EAAqBC,EAAYC,EAA8B,CAC5F,IAAIC,EAEEC,EAAU,IAAI,QAAe,CAACC,EAAGC,IAAW,CAChDH,EAAQ,WAAW,IAAMG,EAAO,IAAI,MAAMJ,GAAW,6BAA6BD,CAAE,IAAI,CAAC,EAAGA,CAAE,CAChG,CAAC,EAED,OAAO,QAAQ,KAAK,CAACD,EAASI,CAAO,CAAC,EAAE,QAAQ,IAAM,aAAaD,CAAK,CAAC,CAC3E,CD7DA,IAAMI,GAAkB,qBAExB,SAASC,GAAkBC,EAAsB,CAC/C,GAAI,CACF,GAAM,CAAE,SAAAC,CAAS,EAAI,IAAI,IAAID,CAAG,EAChC,OAAOC,EAAS,SAAS,OAAO,CAClC,MAAQ,CACN,MAAO,EACT,CACF,CAEA,SAASC,GAAaF,EAAqC,CACzD,IAAMG,EAAkC,CACtC,aAAc,gBACd,OAAQ,kBACV,EAEA,GAAIJ,GAAkBC,CAAG,EAAG,CAC1B,IAAMI,EAAQ,QAAQ,IAAIN,EAAe,EACrCM,IACFD,EAAQ,cAAmB,UAAUC,CAAK,GAE9C,CAEA,OAAOD,CACT,CAEA,eAAeE,GAAYL,EAAaG,EAAoD,CAC1F,IAAMG,EAAW,MAAM,MAAMN,EAAK,CAAE,QAAAG,CAAQ,CAAC,EAE7C,GAAIG,EAAS,SAAW,KAAOA,EAAS,SAAW,IAAK,CACtD,IAAMC,EAAOR,GAAkBC,CAAG,EAC9B,QAAQF,EAAe,wCACvB,GACJ,MAAM,IAAI,cACR,+BAA+BQ,EAAS,MAAM,SAASN,CAAG,IAAIO,CAAI,GAClE,kBAAgB,kBAChB,OACA,CAAE,IAAAP,EAAK,OAAQM,EAAS,MAAO,CACjC,CACF,CAEA,GAAI,CAACA,EAAS,GACZ,MAAM,IAAI,cACR,gCAAgCA,EAAS,MAAM,QAAQN,CAAG,GAC1D,kBAAgB,cAChB,OACA,CAAE,IAAAA,EAAK,OAAQM,EAAS,MAAO,CACjC,EAGF,OAAOA,CACT,CAEA,eAAeE,GAAaF,EAAoBN,EAAgC,CAE9E,IAAMS,EADgB,MAAMH,EAAS,KAAK,EAGpC,CAAE,SAAAI,EAAU,WAAAC,CAAW,KAAI,kBAAeF,CAAG,EAE7CG,EAAQ,IAAI,IAAI,OAAO,QAASH,EAAI,OAAuC,CAAC,CAAC,CAAC,EAC9EI,EAAc,IAAI,IAAI,OAAO,QAASJ,EAAI,aAA6C,CAAC,CAAC,CAAC,EAEhG,GAAIG,EAAM,OAAS,GAAKC,EAAY,OAAS,EAC3C,MAAM,IAAI,cACR,sCAAsCb,CAAG,GACzC,kBAAgB,mBAChB,OACA,CAAE,IAAAA,CAAI,CACR,EAGF,IAAMc,EAAuB,CAAE,OAAQJ,EAAS,OAAQ,EAExD,MAAO,CAAE,SAAAA,EAAU,WAAAC,EAAY,MAAAC,EAAO,YAAAC,EAAa,OAAQb,EAAK,QAAAc,CAAQ,CAC1E,CAKO,IAAMC,EAAN,KAA8C,CACnD,WAAWf,EAAsB,CAC/B,OAAOA,EAAI,WAAW,UAAU,GAAKA,EAAI,YAAY,EAAE,SAAS,OAAO,CACzE,CAEA,MAAM,QAAQA,EAAagB,EAA8C,CACvE,IAAMC,EAAOC,GAAqBF,CAAO,EACnCb,EAAUD,GAAaF,CAAG,EAE1BM,EAAW,MAAMa,GACrBC,GAAU,IAAMf,GAAYL,EAAKG,CAAO,EAAGc,EAAK,OAAO,EACvDA,EAAK,QACL,yCAAyCA,EAAK,OAAO,IACvD,EAEA,OAAOT,GAAaF,EAAUN,CAAG,CACnC,CACF,EEtGA,IAAAqB,EAA8D,uBAC9DC,EAAqB,gBACrBC,GAA2B,kBAE3BC,EAA6D,yCAKtD,SAASC,IAA6B,CAC3C,IAAMC,EAAO,QAAQ,IAAI,MAAW,QAAQ,IAAI,aAAkB,IAClE,SAAO,QAAKA,EAAM,SAAU,YAAa,WAAW,CACtD,CAKO,IAAMC,EAAN,KAAoB,CACR,SAEjB,YAAYC,EAAmB,CAC7B,KAAK,SAAWA,GAAYH,GAAmB,CACjD,CAEQ,YAAYI,EAAwB,CAC1C,SAAO,eAAW,QAAQ,EAAE,OAAOA,CAAM,EAAE,OAAO,KAAK,EAAE,UAAU,EAAG,EAAE,CAC1E,CAEQ,aAAaA,EAAgBC,EAAyB,CAC5D,IAAMC,EAAM,KAAK,YAAYF,CAAM,EAC7BG,EAAcF,EAAQ,QAAQ,MAAO,GAAG,EAC9C,SAAO,QAAK,KAAK,SAAUC,EAAKC,CAAW,CAC7C,CAEA,MAAM,IAAIH,EAAgBC,EAAmC,CAC3D,IAAMG,EAAY,KAAK,aAAaJ,EAAQC,CAAO,EACnD,GAAI,CACF,IAAMI,KAAa,QAAKD,EAAW,aAAa,EAChD,eAAM,QAAKC,CAAU,EACd,EACT,MAAQ,CACN,MAAO,EACT,CACF,CAEA,MAAM,IAAIL,EAAgBC,EAA2C,CACnE,IAAMG,EAAY,KAAK,aAAaJ,EAAQC,CAAO,EAEnD,GAAI,CACF,IAAMI,KAAa,QAAKD,EAAW,aAAa,EAC1CE,EAAgB,QAAM,YAASD,EAAY,OAAO,EAClDE,EAAkB,KAAK,MAAMD,CAAa,EAC1C,CAAE,SAAAE,EAAU,WAAAC,CAAW,KAAI,kBAAeF,CAAM,EAEhDG,EAASH,EACTI,EAAQ,IAAI,IAAI,OAAO,QAASD,EAAO,OAAuC,CAAC,CAAC,CAAC,EACjFE,EAAc,IAAI,IACtB,OAAO,QAASF,EAAO,aAA6C,CAAC,CAAC,CACxE,EAEMG,EAA2B,CAC/B,OAAQL,EAAS,OACnB,EAEA,MAAO,CACL,SAAAA,EACA,WAAAC,EACA,MAAAE,EACA,YAAAC,EACA,OAAAZ,EACA,QAASa,CACX,CACF,MAAQ,CACN,OAAO,IACT,CACF,CAEA,MAAM,IAAIC,EAAmC,CAC3C,IAAMV,EAAY,KAAK,aAAaU,EAAS,OAAQA,EAAS,QAAQ,MAAM,EAE5E,GAAI,CACF,QAAM,SAAMV,EAAW,CAAE,UAAW,EAAK,CAAC,EAE1C,IAAMW,EAAS,CACb,SAAU,CACR,KAAMD,EAAS,SAAS,KACxB,QAASA,EAAS,SAAS,QAC3B,GAAIA,EAAS,SAAS,YAAc,CAAE,YAAaA,EAAS,SAAS,WAAY,EAAI,CAAC,CACxF,EACA,WAAYA,EAAS,WACrB,MAAO,OAAO,YAAYA,EAAS,KAAK,EACxC,YAAa,OAAO,YAAYA,EAAS,WAAW,CACtD,EACA,QAAM,gBAAU,QAAKV,EAAW,aAAa,EAAG,KAAK,UAAUW,EAAQ,KAAM,CAAC,EAAG,OAAO,CAC1F,OAASC,EAAO,CACd,MAAM,IAAI,cACR,6BAA8BA,EAAgB,OAAO,GACrD,kBAAgB,YAChBA,EACA,CAAE,OAAQF,EAAS,OAAQ,QAASA,EAAS,QAAQ,MAAO,CAC9D,CACF,CACF,CAEA,MAAM,OAAOd,EAAgBC,EAAiC,CAC5D,GAAIA,EAAS,CACX,IAAMG,EAAY,KAAK,aAAaJ,EAAQC,CAAO,EACnD,GAAI,CACF,QAAM,MAAGG,EAAW,CAAE,UAAW,GAAM,MAAO,EAAK,CAAC,CACtD,MAAQ,CAER,CACF,KAAO,CACL,IAAMF,EAAM,KAAK,YAAYF,CAAM,EAC7BiB,KAAU,QAAK,KAAK,SAAUf,CAAG,EACvC,GAAI,CACF,QAAM,MAAGe,EAAS,CAAE,UAAW,GAAM,MAAO,EAAK,CAAC,CACpD,MAAQ,CAER,CACF,CACF,CAEA,MAAM,OAAuB,CAC3B,GAAI,CACF,QAAM,MAAG,KAAK,SAAU,CAAE,UAAW,GAAM,MAAO,EAAK,CAAC,CAC1D,MAAQ,CAER,CACF,CAEA,MAAM,OAAuE,CAC3E,IAAIC,EAAU,EACVC,EAAY,EACZC,EAAO,EAEX,GAAI,CACF,IAAMC,EAAa,QAAM,WAAQ,KAAK,QAAQ,EAC9CH,EAAUG,EAAW,OAErB,QAAWnB,KAAOmB,EAAY,CAC5B,IAAMJ,KAAU,QAAK,KAAK,SAAUf,CAAG,EACjCoB,EAAW,QAAM,WAAQL,CAAO,EACtCE,GAAaG,EAAS,OAEtB,QAAWrB,KAAWqB,EAAU,CAC9B,IAAMC,KAAc,QAAKN,EAAShB,CAAO,EACzCmB,GAAQ,MAAM,KAAK,QAAQG,CAAW,CACxC,CACF,CACF,MAAQ,CAER,CAEA,MAAO,CAAE,QAAAL,EAAS,UAAAC,EAAW,KAAAC,CAAK,CACpC,CAEA,MAAc,QAAQI,EAA8B,CAClD,IAAIC,EAAQ,EACNC,EAAU,QAAM,WAAQF,EAAK,CAAE,cAAe,EAAK,CAAC,EAC1D,QAAWG,KAASD,EAAS,CAC3B,IAAME,KAAW,QAAKJ,EAAKG,EAAM,IAAI,EACrC,GAAIA,EAAM,YAAY,EACpBF,GAAS,MAAM,KAAK,QAAQG,CAAQ,MAC/B,CACL,IAAMC,EAAW,QAAM,QAAKD,CAAQ,EACpCH,GAASI,EAAS,IACpB,CACF,CACA,OAAOJ,CACT,CACF,EJ7JO,IAAMK,GAAN,KAAuB,CACpB,UAAwB,CAAC,EACzB,MAER,YAAYC,EAAwBC,EAAmB,CACrD,KAAK,MAAQ,IAAIC,EAAcD,CAAQ,EAEnCD,EACF,KAAK,UAAYA,GAEjB,KAAK,SAAS,IAAIG,CAAc,EAChC,KAAK,SAAS,IAAIC,CAAqB,EAE3C,CAEA,SAASC,EAA0B,CACjC,KAAK,UAAU,KAAKA,CAAQ,CAC9B,CAEA,YAAYC,EAAmC,CAC7C,OAAO,KAAK,UAAU,KAAM,GAAM,EAAE,WAAWA,CAAG,CAAC,CACrD,CAEA,MAAM,QAAQA,EAAaC,EAA8C,CACvE,IAAMF,EAAW,KAAK,YAAYC,CAAG,EAErC,GAAI,CAACD,EAAU,CACb,IAAMG,EAASF,EAAI,MAAM,KAAK,EAAE,CAAC,GAAK,UACtC,MAAM,IAAI,eACR,qCAAqCE,CAAM,MAC3C,mBAAgB,mBAChB,OACA,CACE,IAAAF,EACA,OAAAE,EACA,iBAAkB,CAAC,UAAW,gBAAgB,CAChD,CACF,CACF,CAEA,IAAMC,EAAW,MAAMJ,EAAS,QAAQC,EAAKC,CAAO,EAGpD,OAAKD,EAAI,WAAW,SAAS,GAC3B,MAAM,KAAK,MAAM,IAAIG,CAAQ,EAGxBA,CACT,CAEA,WAAWH,EAAsB,CAC/B,OAAO,KAAK,UAAU,KAAM,GAAM,EAAE,WAAWA,CAAG,CAAC,CACrD,CAEA,qBAAgC,CAC9B,MAAO,CAAC,UAAW,gBAAgB,CACrC,CAEA,MAAM,WAAWA,EAA6B,CACxCA,EACF,MAAM,KAAK,MAAM,OAAOA,CAAG,EAE3B,MAAM,KAAK,MAAM,MAAM,CAE3B,CAEA,MAAM,eAA+E,CACnF,OAAO,KAAK,MAAM,MAAM,CAC1B,CACF,EAKO,SAASI,EAAuBT,EAAqC,CAC1E,OAAO,IAAIF,GAAiB,OAAWE,CAAQ,CACjD,CK5FA,IAAAU,GAAyB,uBACzBC,GAAsB,sBCHtB,IAAAC,GAAuB,uBACvBC,GAAqB,gBAKrB,eAAsBC,GAAWC,EAAoC,CACnE,GAAI,CACF,eAAM,WAAOA,CAAQ,EACd,EACT,MAAQ,CACN,MAAO,EACT,CACF,CAMA,eAAsBC,EAAmBC,EAA4C,CACnF,IAAMC,KAAc,SAAKD,EAAY,QAAS,UAAW,aAAa,EACtE,GAAI,CACF,eAAM,WAAOC,CAAW,EACjBA,CACT,MAAQ,CACN,OAAO,IACT,CACF,CDFA,eAAsBC,GAAWC,EAAsC,CACrE,IAAMC,EAAU,QAAM,aAASD,EAAU,OAAO,EAE1CE,EAASF,EAAS,SAAS,OAAO,EAEpCG,EACJ,GAAI,CACED,EACFC,EAAS,KAAK,MAAMF,CAAO,EAE3BE,EAAc,QAAKF,CAAO,CAE9B,OAASG,EAAO,CACd,IAAMC,EAASH,EAAS,OAAS,OACjC,MAAM,IAAI,MAAM,mBAAmBG,CAAM,iBAAkBD,EAAgB,OAAO,GAAI,CACpF,MAAOA,CACT,CAAC,CACH,CAEA,GACE,CAACD,EAAO,UACR,OAAOA,EAAO,UAAgB,UAC9BA,EAAO,SAAY,KAAK,IAAM,GAE9B,MAAM,IAAI,MACR,yGACkDH,CAAQ,wEAE5D,EAGF,MAAO,CACL,SAAUG,EAAO,SACjB,OAASA,EAAO,QAAyC,CAAC,EAC1D,SAAUA,EAAO,SACjB,eAAgBA,EAAO,eACvB,QAASA,EAAO,OAClB,CACF,CE1DA,IAAAG,GAAyB,uBAOzB,eAAsBC,GAAaC,EAAyC,CAC1E,IAAIC,EACJ,GAAI,CACFA,EAAU,QAAM,aAASD,EAAc,OAAO,CAChD,OAASE,EAAO,CAEd,MADkBA,EACJ,OAAS,SACf,IAAI,MAAM,uBAAuBF,CAAY,GAAI,CAAE,MAAOE,CAAM,CAAC,EAEnE,IAAI,MAAM,4BAA6BA,EAAgB,OAAO,GAAI,CAAE,MAAOA,CAAM,CAAC,CAC1F,CAEA,IAAIC,EACJ,GAAI,CACFA,EAAS,KAAK,MAAMF,CAAO,CAC7B,MAAQ,CACN,MAAM,IAAI,MAAM,6BAA6BD,CAAY,EAAE,CAC7D,CAEA,IAAMI,EAAiB,CACrB,kBACA,QACA,gBACA,SACA,iBACA,WACA,iBACA,iBACF,EAEA,QAAWC,KAASD,EAClB,GAAID,EAAOE,CAAK,IAAM,OACpB,MAAM,IAAI,MAAM,uCAAuCA,CAAK,EAAE,EAIlE,OAAOF,CACT,CAOO,SAASG,GAAqBC,EAA+B,CAClE,GAAI,CAACA,EAAS,OACZ,MAAM,IAAI,MAAM,+DAA+D,EAEjF,GAAI,CAACA,EAAS,eACZ,MAAM,IAAI,MAAM,uEAAuE,EAGzF,MAAO,CACL,SAAUA,EAAS,eACnB,OAAQA,EAAS,OACjB,SAAUA,EAAS,SACnB,eAAgBA,EAAS,cAC3B,CACF,CChEA,IAAAC,GAAkC,uBAClCC,GAAqB,gBACrBC,EAMO,yCAcP,eAAsBC,GACpBC,EACAC,EAAiB,iBACC,CAClB,IAAMC,EAAmB,IAAI,IAEzBC,EACJ,GAAI,CAEFA,GADmB,QAAM,YAAQH,EAAK,CAAE,cAAe,EAAK,CAAC,GACxC,OAAQI,GAAMA,EAAE,OAAO,CAAC,EAAE,IAAKA,GAAMA,EAAE,IAAI,CAClE,OAASC,EAAO,CAEd,MADkBA,EACJ,OAAS,SACf,IAAI,MAAM,wBAAwBL,CAAG,GAAI,CAAE,MAAOK,CAAM,CAAC,EAE3D,IAAI,MAAM,6BAA8BA,EAAgB,OAAO,GAAI,CAAE,MAAOA,CAAM,CAAC,CAC3F,CAEA,QAAWC,KAAYH,EAAS,CAE9B,GAAIG,IAAa,gBAAiB,SAElC,IAAMC,EAAU,QAAM,gBAAS,SAAKP,EAAKM,CAAQ,EAAG,OAAO,EACrDE,KAAO,gBAAaF,EAAUL,CAAM,EAEpCQ,EAAmB,CAAE,QAAAF,EAAS,KAAAC,CAAK,EAEzC,GAAIA,IAAS,WAAY,CACvB,IAAME,KAAS,eAAYH,CAAO,EAC9BG,IACFD,EAAM,OAASC,EAEnB,CAEAR,EAAQ,IAAII,EAAUG,CAAK,CAC7B,CAEA,OAAOP,CACT,CC5DA,IAAAS,GAAiC,uBACjCC,GAA8B,gBAgB9B,eAAsBC,GACpBC,EACAC,EACAC,EACe,CACf,QAAM,UAAMF,EAAK,CAAE,UAAW,EAAK,CAAC,EAEpC,OAAW,CAACG,EAAUC,CAAK,IAAKH,EAAO,MAAO,CAC5C,IAAMI,KAAW,SAAKL,EAAKG,CAAQ,EAE/BD,IAAS,WAAaE,EAAM,OAAS,QACnC,MAAME,GAAWD,CAAQ,IAK/B,QAAM,aAAM,YAAQA,CAAQ,EAAG,CAAE,UAAW,EAAK,CAAC,EAClD,QAAM,cAAUA,EAAUD,EAAM,QAAS,OAAO,EAClD,CACF,CCxCA,IAAAG,GAAoC,gBAU7B,SAASC,EAAoBC,EAAkBC,EAAgBC,EAA0B,CAC9F,IAAMC,EAAqBH,EAAS,QAAQ,QAAS;AAAA,CAAI,EACnDI,EAAmBH,EAAO,QAAQ,QAAS;AAAA,CAAI,EAYrD,SAVc,wBACZ,YAAYC,CAAQ,GACpB,UAAUA,CAAQ,GAClBC,EACAC,EACA,GACA,GACA,CAAE,QAAS,CAAE,CACf,CAGF,CXYO,SAASC,GAAmBC,EAAwB,CAEzD,GADc,CAACA,EAAK,OAAQA,EAAK,YAAY,EAAE,OAAO,OAAO,EACnD,OAAS,EACjB,MAAM,IAAI,MAAM,4EAA4E,EAE9F,GAAIA,EAAK,QAAUA,EAAK,YAAc,IACpC,MAAM,IAAI,MAAM,wEAAwE,CAE5F,CAEO,SAASC,IAA+B,CAkC7C,OAjCY,IAAI,WAAQ,QAAQ,EAC7B,YAAY,gCAAgC,EAC5C,OAAO,sBAAuB,6CAA6C,EAC3E,OAAO,wBAAyB,qCAAsC,GAAG,EACzE,OAAO,uBAAwB,4CAA4C,EAC3E,OAAO,0BAA2B,0CAA2CC,GAAS,CAAC,CAAC,EACxF,eAAe,qBAAsB,kBAAkB,EACvD,OAAO,yBAA0B,8CAA8C,EAC/E,OAAO,UAAW,2DAA2D,EAC7E,OAAO,wBAAyB,gDAAgD,EAChF,OAAO,YAAa,8CAA8C,EAClE,OAAO,SAAU,kCAAkC,EACnD,OAAO,gBAAiB,0BAA0B,EAClD,OAAO,aAAc,yBAAyB,EAC9C,OAAO,iBAAkB,kCAAmC,OAAO,EACnE,OAAO,mBAAoB,yCAAyC,EACpE,OAAO,MAAOC,GAA2B,CACxC,GAAI,CACEA,EAAQ,SAAQA,EAAQ,UAAS,WAAQA,EAAQ,MAAM,GACvDA,EAAQ,eAAcA,EAAQ,gBAAe,WAAQA,EAAQ,YAAY,GAE7EJ,GAAmB,CACjB,OAAQI,EAAQ,OAChB,UAAWA,EAAQ,UACnB,aAAcA,EAAQ,YACxB,CAAC,EACD,IAAMC,EAAW,MAAMC,GAAUF,CAAO,EACxC,QAAQ,KAAKC,CAAQ,CACvB,OAASE,EAAO,CACdC,EAAkBD,CAAK,CACzB,CACF,CAAC,CAGL,CAEA,SAASJ,GAAQM,EAAeC,EAA8B,CAC5D,MAAO,CAAC,GAAGA,EAAUD,CAAK,CAC5B,CAqBO,SAASE,GAAoBC,EAA2C,CAC7E,IAAMC,EAAiC,CAAC,EACxC,GAAI,CAACD,EAAQ,OAAOC,EAEpB,QAAWC,KAASF,EAAQ,CAC1B,IAAMG,EAAUD,EAAM,QAAQ,GAAG,EACjC,GAAIC,EAAU,EAAG,CACf,IAAMC,EAAMF,EAAM,UAAU,EAAGC,CAAO,EAChCN,EAAQK,EAAM,UAAUC,EAAU,CAAC,EACzCF,EAAOG,CAAG,EAAIP,CAChB,CACF,CAEA,OAAOI,CACT,CAEA,eAAeP,GAAUF,EAAyC,CAC5DA,EAAQ,MACHa,GAAY,EAAI,EAGzB,IAAMC,KAAY,WAAQd,EAAQ,MAAM,EAClCe,EAAU,SAASf,EAAQ,QAAS,EAAE,EAE5C,GAAIA,EAAQ,aACV,OAAOgB,GAAsBhB,EAAQ,aAAcA,EAASc,EAAWC,CAAO,EAGhF,GAAIf,EAAQ,OAAQ,CAClB,IAAMiB,KAAe,QAAKH,EAAW,eAAe,EACpD,OAAI,MAAMI,GAAWD,CAAY,EACxBE,GAA2BnB,EAAQ,OAAQA,EAASc,EAAWC,EAASE,CAAY,EAEtFG,GAAoBpB,EAAQ,OAAQA,EAASc,EAAWC,CAAO,CACxE,CAEA,IAAMM,KAAc,WAAQrB,EAAQ,SAAS,EAC7C,OAAOsB,GAAuBD,EAAarB,EAASc,EAAWC,CAAO,CACxE,CAEA,eAAeG,GAAWK,EAAgC,CACxD,GAAI,CACF,eAAM,UAAOA,CAAI,EACV,EACT,MAAQ,CACN,MAAO,EACT,CACF,CAEA,eAAeC,GAAcC,EAAmC,CAC9D,GAAI,CAAC,QAAQ,MAAM,MAAO,MAAO,GACjC,IAAMC,KAAK,oBAAgB,CAAE,MAAO,QAAQ,MAAO,OAAQ,QAAQ,MAAO,CAAC,EAC3E,OAAO,IAAI,QAASC,GAAY,CAC9BD,EAAG,SAAS,GAAGD,CAAO,UAAYG,GAAW,CAC3CF,EAAG,MAAM,EACTC,EAAQC,EAAO,KAAK,EAAE,YAAY,IAAM,GAAG,CAC7C,CAAC,CACH,CAAC,CACH,CAEA,eAAeC,GAAmBC,EAA8B,CAC9D,GAAI,CAEF,OADgB,QAAM,WAAQA,EAAK,CAAE,cAAe,EAAK,CAAC,GAC3C,OAAQC,GAAMA,EAAE,OAAO,GAAKA,EAAE,OAAS,eAAiBA,EAAE,OAAS,aAAa,EAC5F,MACL,MAAQ,CACN,MAAO,EACT,CACF,CAEA,eAAeC,GAAelB,EAAmBd,EAAgD,CAC/F,GAAIA,EAAQ,MAAO,OAAO,KAC1B,IAAMiC,EAAQ,MAAMJ,GAAmBf,CAAS,EAKhD,OAJImB,IAAU,IAEPC,EAAK,6BAA6BD,CAAK,2CAA2C,EACvE,MAAMT,GAAc,UAAU,GAC1B,MAElBxB,EAAQ,KACHmC,EACL,KAAK,UACH,CAAE,QAAS,GAAM,OAAQ,yBAA0B,cAAeF,CAAM,EACxE,KACA,CACF,CACF,EAEOG,EAAK,4CAA4C,EAEnDC,EAAW,iBACpB,CAKA,eAAerB,GACbC,EACAjB,EACAc,EACAC,EACiB,CACVuB,EAAQ,yBAAyBrB,CAAY,EAAE,EACtD,IAAMsB,EAAW,MAAMC,GAAavB,CAAY,EAC1CwB,EAAYC,GAAqBH,CAAQ,EAE3CvC,EAAQ,WACVyC,EAAU,SAAWzC,EAAQ,UAGxB2C,EAAQ,aAAaF,EAAU,QAAQ,EAAE,EACzCH,EAAQ,cAAcG,EAAU,QAAQ,EAAE,EAC1CH,EAAQ,uBAAuB,EAQtC,IAAMM,EAAqB,CACzB,SANe,MADQC,EAAuB,EACR,QAAQJ,EAAU,SAAU,CAClE,QAASzC,EAAQ,QAAU,GAC3B,QAAAe,CACF,CAAC,EAIC,OAAQ0B,EAAU,OAClB,SAAUA,EAAU,SACpB,eAAgBA,EAAU,eAC1B,QAAS,CACP,WAAYzC,EAAQ,UACtB,CACF,EAEOsC,EAAQ,+BAA+B,EAG9C,IAAM7B,EAAS,QADA,iBAAa,EACA,QAAQmC,CAAQ,EAE5C,GAAI5C,EAAQ,OACV,OAAO,MAAM8C,GAAoBrC,EAAQK,EAAWd,EAAQ,IAAI,EAGlE,IAAM+C,EAAU,MAAMf,GAAelB,EAAWd,CAAO,EACvD,OAAI+C,IAAY,KAAaA,GAEtBJ,EAAQ,kBAAkB,EAC1BA,EAAQ,aAAalC,EAAO,MAAM,KAAO,CAAC,wBAAwB,EAEpET,EAAQ,YACJ2C,EAAQ,yBAAyB,EAG1C,MAAMK,GAAkBlC,EAAWL,EAAQ,SAAS,EAEpDwC,GAAmBxC,EAAQK,EAAWd,EAAQ,IAAI,EAE3CqC,EAAW,QACpB,CAKA,eAAef,GACbD,EACArB,EACAc,EACAC,EACiB,CACjB,IAAMmC,EAAc,UAAU7B,CAAW,GAErC8B,EAA6BnD,EAAQ,WAAU,WAAQqB,EAAarB,EAAQ,OAAO,EAAI,KAE3F,GAAImD,GACF,GAAI,CAAE,MAAMjC,GAAWiC,CAAW,EAChC,OAAOhD,EAAM,sBAAsBH,EAAQ,OAAO,EAAE,EAC7CqC,EAAW,yBAGpBc,EAAc,MAAMC,EAAmB/B,CAAW,EAC9C,CAAC8B,EACH,OAAOhD,EAAM,uDAAuD,EAC7DiC,EAAK,kEAAkE,EACvEC,EAAW,iBAItB,IAAMgB,EAAiB,QAAM,YAASF,EAAa,OAAO,EACtD3C,EAAc,QAAK6C,CAAc,EAE/BC,EAAY/C,GAAoBP,EAAQ,KAAK,EACnDQ,EAAS,CAAE,GAAGA,EAAQ,GAAG8C,CAAU,EAEnC,IAAMC,EACJvD,EAAQ,UAAaQ,EAAO,UAAsC,SAAS,KAAK,IAAI,CAAC,GAEhFmC,EAAQ,aAAaO,CAAW,EAAE,EAClCZ,EAAQ,YAAYa,CAAW,EAAE,EACjCb,EAAQ,cAAciB,CAAQ,EAAE,EAEhCjB,EAAQ,uBAAuB,EAOtC,IAAMM,EAAqB,CACzB,SANe,MADQC,EAAuB,EACR,QAAQK,EAAa,CAC3D,QAASlD,EAAQ,QAAU,GAC3B,QAAAe,CACF,CAAC,EAIC,OAAAP,EACA,SAAA+C,EACA,QAAS,CACP,WAAYvD,EAAQ,UACtB,CACF,EAEMwD,KAAS,iBAAa,EAErBlB,EAAQ,sBAAsB,EACrC,IAAM7B,EAAS,MAAM+C,EAAO,QAAQZ,CAAQ,EAE5C,GAAI5C,EAAQ,OACV,OAAO,MAAM8C,GAAoBrC,EAAQK,EAAWd,EAAQ,IAAI,EAGlE,IAAM+C,EAAU,MAAMf,GAAelB,EAAWd,CAAO,EACvD,OAAI+C,IAAY,KAAaA,GAEtBJ,EAAQ,kBAAkB,EAC1BA,EAAQ,aAAalC,EAAO,MAAM,KAAO,CAAC,wBAAwB,EAEpET,EAAQ,YACJ2C,EAAQ,yBAAyB,EAG1C,MAAMK,GAAkBlC,EAAWL,EAAQ,SAAS,EAEpDwC,GAAmBxC,EAAQK,EAAWd,EAAQ,IAAI,EAE3CqC,EAAW,QACpB,CAKA,eAAejB,GACbqC,EACAzD,EACAc,EACAC,EACiB,CACVuB,EAAQ,uBAAuBmB,CAAU,EAAE,EAClD,IAAMhB,EAAY,MAAMiB,GAAWD,CAAU,EAM7C,GAJIzD,EAAQ,WACVyC,EAAU,SAAWzC,EAAQ,UAG3BA,EAAQ,OAASA,EAAQ,MAAM,OAAS,EAAG,CAC7C,IAAMsD,EAAY/C,GAAoBP,EAAQ,KAAK,EACnDyC,EAAU,OAAS,CAAE,GAAIA,EAAU,QAAU,CAAC,EAAI,GAAGa,CAAU,CACjE,CAEOX,EAAQ,aAAaF,EAAU,QAAQ,EAAE,EACzCH,EAAQ,cAAcG,EAAU,QAAQ,EAAE,EAE1CH,EAAQ,uBAAuB,EAOtC,IAAMM,EAAqB,CACzB,SANe,MADQC,EAAuB,EACR,QAAQJ,EAAU,SAAU,CAClE,QAASzC,EAAQ,QAAU,GAC3B,QAAAe,CACF,CAAC,EAIC,OAAQ0B,EAAU,OAClB,SAAUA,EAAU,SACpB,eAAgBA,EAAU,eAC1B,QAAS,CACP,WAAYzC,EAAQ,YAAcyC,EAAU,SAAS,UACvD,CACF,EAEMe,KAAS,iBAAa,EAErBlB,EAAQ,sBAAsB,EACrC,IAAM7B,EAAS,MAAM+C,EAAO,QAAQZ,CAAQ,EAE5C,GAAI5C,EAAQ,OACV,OAAO,MAAM8C,GAAoBrC,EAAQK,EAAWd,EAAQ,IAAI,EAGlE,IAAM+C,EAAU,MAAMf,GAAelB,EAAWd,CAAO,EACvD,OAAI+C,IAAY,KAAaA,GAEtBJ,EAAQ,kBAAkB,EAC1BA,EAAQ,aAAalC,EAAO,MAAM,KAAO,CAAC,wBAAwB,EAEpEmC,EAAS,SAAS,YACdD,EAAQ,yBAAyB,EAG1C,MAAMK,GAAkBlC,EAAWL,EAAQ,SAAS,EAEpDwC,GAAmBxC,EAAQK,EAAWd,EAAQ,IAAI,EAE3CqC,EAAW,QACpB,CAMA,eAAelB,GACbsC,EACAzD,EACAc,EACAC,EACAE,EACiB,CACVqB,EAAQ,iCAAiCrB,CAAY,yBAAoB,EAEhF,IAAM0C,EAAmB,MAAMnB,GAAavB,CAAY,EAEjDqB,EAAQ,sBAAsBxB,CAAS,EAAE,EAChD,IAAM8C,EAAe,MAAMC,GAAuB/C,EAAW6C,EAAiB,cAAc,EAErFrB,EAAQ,uBAAuBmB,CAAU,EAAE,EAClD,IAAMhB,EAAY,MAAMiB,GAAWD,CAAU,EAM7C,GAJIzD,EAAQ,WACVyC,EAAU,SAAWzC,EAAQ,UAG3BA,EAAQ,OAASA,EAAQ,MAAM,OAAS,EAAG,CAC7C,IAAMsD,EAAY/C,GAAoBP,EAAQ,KAAK,EACnDyC,EAAU,OAAS,CAAE,GAAIA,EAAU,QAAU,CAAC,EAAI,GAAGa,CAAU,CACjE,CAEOX,EAAQ,aAAaF,EAAU,QAAQ,EAAE,EACzCH,EAAQ,cAAcG,EAAU,QAAQ,EAAE,EAE1CH,EAAQ,uBAAuB,EAOtC,IAAMM,EAAqB,CACzB,SANe,MADQC,EAAuB,EACR,QAAQJ,EAAU,SAAU,CAClE,QAASzC,EAAQ,QAAU,GAC3B,QAAAe,CACF,CAAC,EAIC,OAAQ0B,EAAU,OAClB,SAAUA,EAAU,SACpB,eAAgBA,EAAU,eAC1B,QAAS,CACP,WAAYzC,EAAQ,YAAcyC,EAAU,SAAS,UACvD,CACF,EAGMhC,EAAS,QADA,iBAAa,EACA,QAAQmC,EAAUgB,EAAcD,CAAgB,EAE5E,OAAIlD,EAAO,eAAiB,CAACT,EAAQ,mBAC5BkC,EAAK,iFAAiF,EAC7F4B,GAAiBrD,EAAO,YAAaT,EAAQ,IAAI,EAC7CA,EAAQ,KACHmC,EACL,KAAK,UACH,CACE,QAAS,GACT,OAAQ,kBACR,cAAe,GACf,YAAa,CAAE,MAAO,CAAC1B,EAAO,YAAY,MAAO,QAASA,EAAO,YAAY,OAAQ,CACvF,EACA,KACA,CACF,CACF,EAEO2B,EAAK,6DAA6D,EAEpEC,EAAW,gBAEhB5B,EAAO,eACFyB,EAAK,2DAA2D,EAGzE4B,GAAiBrD,EAAO,YAAaT,EAAQ,IAAI,EAE7C,CAACS,EAAO,YAAY,OAAS,CAACT,EAAQ,OACpCA,EAAQ,KACHmC,EACL,KAAK,UACH,CACE,QAAS,GACT,OAAQ,gBACR,cAAe1B,EAAO,cACtB,YAAa,CAAE,MAAO,GAAM,QAASA,EAAO,YAAY,OAAQ,CAClE,EACA,KACA,CACF,CACF,EAEO2B,EAAK,uCAAuC,EAE9CC,EAAW,gBAEf5B,EAAO,YAAY,OACfyB,EAAK,qCAAqC,EAG/ClC,EAAQ,OACH+D,GAAoBtD,EAAQmD,EAAc5D,EAAQ,IAAI,GAG/D,MAAMgD,GAAkBlC,EAAWL,EAAQ,SAAS,EAEhDT,EAAQ,KACHmC,EAAK6B,GAAwBvD,CAAM,CAAC,EAEpC2B,EAAK6B,GAAoBxD,CAAM,CAAC,EAGlC4B,EAAW,UACpB,CAIA,eAAe6B,GAAoBC,EAAmBrD,EAAyC,CAC7F,IAAMsD,EAAqB,CAAC,EAC5B,OAAW,CAACC,EAAUC,CAAK,IAAKH,EAAU,CACxC,GAAIE,IAAa,gBAAiB,SAClC,IAAIE,EAAW,GACf,GAAI,CACFA,EAAW,QAAM,eAAS,QAAKzD,EAAWuD,CAAQ,EAAG,OAAO,CAC9D,MAAQ,CAER,CACIE,IAAaD,EAAM,SACvBF,EAAM,KAAK,CAAE,SAAAC,EAAU,KAAMG,EAAoBD,EAAUD,EAAM,QAASD,CAAQ,CAAE,CAAC,CACvF,CACA,OAAOD,CACT,CAEA,SAASK,GAAoBN,EAAmBP,EAAoC,CAClF,IAAMQ,EAAqB,CAAC,EAC5B,OAAW,CAACC,EAAUC,CAAK,IAAKH,EAAU,CACxC,GAAIE,IAAa,gBAAiB,SAElC,IAAMK,EADUd,EAAa,IAAIS,CAAQ,GACT,SAAW,GACvCK,IAAmBJ,EAAM,SAC7BF,EAAM,KAAK,CAAE,SAAAC,EAAU,KAAMG,EAAoBE,EAAgBJ,EAAM,QAASD,CAAQ,CAAE,CAAC,CAC7F,CACA,OAAOD,CACT,CAEA,SAASO,GAAWP,EAA0B,CAC5C,GAAIA,EAAM,SAAW,EAAG,CACfhC,EAAK;AAAA,0BAA6B,EACzC,MACF,CACOA,EAAK;AAAA,EAAKgC,EAAM,MAAM;AAAA,CAAqB,EAClD,QAAWQ,KAAKR,EACPhC,EAAKwC,EAAE,IAAI,CAEtB,CAIA,SAASd,GAAiBe,EAAqBC,EAAsB,CACnE,GAAIA,EAAM,CACD3C,EACL,KAAK,UACH,CACE,MAAO,CAAC0C,EAAO,MACf,QAASA,EAAO,QAChB,QAASA,EAAO,QAAQ,IAAK9C,IAAO,CAClC,KAAMA,EAAE,KACR,KAAMA,EAAE,KACR,QAASA,EAAE,OACb,EAAE,CACJ,EACA,KACA,CACF,CACF,EACA,MACF,CACA,GAAI8C,EAAO,MAAO,CACTlC,EAAQ,qCAAqC,EACpD,MACF,CACA,IAAMoC,EACJF,EAAO,QAAQ,aACfA,EAAO,QAAQ,aACfA,EAAO,QAAQ,gBACfA,EAAO,QAAQ,kBACV3C,EAAK,qBAAqB6C,CAAK,iBAAiB,EACvD,QAAWT,KAASO,EAAO,QAClBzC,EAAK,KAAK4C,GAAaV,EAAM,IAAI,CAAC,KAAKA,EAAM,IAAI,EAAE,EACnDlC,EAAK,OAAOkC,EAAM,OAAO,EAAE,EAE7BlC,EAAK,EAAE,EACPA,EAAK,mDAAmD,CACjE,CAIA,eAAeU,GACbrC,EACAK,EACAgE,EACiB,CACjB,IAAMV,EAAQ,MAAMF,GAAoBzD,EAAO,MAAOK,CAAS,EAC/D,OAAIgE,EACK3C,EACL,KAAK,UACH,CACE,OAAQ,GACR,cAAe1B,EAAO,SAAS,cAC/B,eAAgBA,EAAO,SAAS,eAChC,gBAAiBA,EAAO,SAAS,gBACjC,UAAWA,EAAO,MAAM,KACxB,MAAO,MAAM,KAAKA,EAAO,MAAM,QAAQ,CAAC,EAAE,IAAI,CAAC,CAACc,EAAM+C,CAAK,KAAO,CAChE,KAAA/C,EACA,KAAM,OAAO,WAAW+C,EAAM,QAAS,OAAO,CAChD,EAAE,EACF,QAASF,EAAM,IAAKQ,IAAO,CAAE,SAAUA,EAAE,SAAU,KAAMA,EAAE,IAAK,EAAE,CACpE,EACA,KACA,CACF,CACF,GAEOxC,EAAK,kCAA6B,EAClCA,EAAK,EAAE,EACPA,EAAK,mBAAmB3B,EAAO,SAAS,aAAa,EAAE,EACvD2B,EAAK,mBAAmB3B,EAAO,SAAS,cAAc,EAAE,EACxD2B,EAAK,mBAAmB3B,EAAO,SAAS,eAAe,EAAE,EACzD2B,EAAK,mBAAmB3B,EAAO,MAAM,IAAI,EAAE,EAClDkE,GAAWP,CAAK,GAEX/B,EAAW,OACpB,CAEA,SAASY,GAAmBxC,EAAuBK,EAAmBgE,EAAsB,CAC1F,GAAIA,EACK3C,EACL,KAAK,UACH,CACE,QAAS,GACT,cAAe1B,EAAO,SAAS,cAC/B,UAAWA,EAAO,MAAM,KACxB,UAAAK,EACA,MAAO,MAAM,KAAKL,EAAO,MAAM,QAAQ,CAAC,EAAE,IAAI,CAAC,CAACc,EAAM+C,CAAK,KAAO,CAChE,KAAA/C,EACA,KAAM,OAAO,WAAW+C,EAAM,QAAS,OAAO,CAChD,EAAE,CACJ,EACA,KACA,CACF,CACF,MACK,CACL,IAAMW,EAAkD,CAAC,EACzD,OAAW,CAACC,EAAUZ,CAAK,IAAK7D,EAAO,MACrCwE,EAAS,KAAK,CACZ,KAAMC,EACN,KAAM,OAAO,WAAWZ,EAAM,QAAS,OAAO,CAChD,CAAC,EAEIlC,EAAK,EAAE,EACPA,EAAK,sBAAsBtB,CAAS,EAAE,EACtCsB,EAAK,mBAAmB3B,EAAO,SAAS,aAAa,EAAE,EACvD0E,GAAcF,CAAQ,CAC/B,CACF,CAIA,SAASlB,GAAoBtD,EAAuBmD,EAAuBkB,EAAwB,CACjG,IAAMV,EAAQK,GAAoBhE,EAAO,MAAOmD,CAAY,EAC5D,OAAIkB,EACK3C,EACL,KAAK,UACH,CACE,OAAQ,GACR,eAAgB1B,EAAO,SAAS,eAChC,gBAAiBA,EAAO,SAAS,gBACjC,cAAeA,EAAO,SAAS,cAC/B,YAAaA,EAAO,YACpB,MAAO,MAAM,KAAKA,EAAO,MAAM,QAAQ,CAAC,EAAE,IAAI,CAAC,CAACc,EAAM+C,CAAK,KAAO,CAChE,KAAA/C,EACA,KAAM+C,EAAM,IACd,EAAE,EACF,QAASF,EAAM,IAAKQ,IAAO,CAAE,SAAUA,EAAE,SAAU,KAAMA,EAAE,IAAK,EAAE,CACpE,EACA,KACA,CACF,CACF,GAEOxC,EAAK,kCAA6B,EAClCA,EAAK,EAAE,EACPA,EAAK6B,GAAoBxD,CAAM,CAAC,EACvCkE,GAAWP,CAAK,GAEX/B,EAAW,OACpB,CAEA,SAAS4B,GAAoBxD,EAA+B,CAC1D,IAAM2E,EAAkB,CAAC,EACzBA,EAAM,KACJ,4BAAuB3E,EAAO,SAAS,cAAc,KAAKA,EAAO,SAAS,eAAe,EAC3F,EACA2E,EAAM,KAAK,EAAE,EACb,IAAIC,EAAgB,EAChBC,EAAY,EAChB,OAAW,CAAC,CAAEhB,CAAK,IAAK7D,EAAO,MACzB6D,EAAM,OAAS,WAAYe,IAC1BC,IAEP,OAAAF,EAAM,KAAK,iBAAiBC,CAAa,wBAAwB,EACjED,EAAM,KAAK,iBAAiBE,CAAS,oBAAoB,EACzDF,EAAM,KAAK,EAAE,EACbA,EAAM,KAAK,mBAAmB3E,EAAO,SAAS,aAAa,EAAE,EACtD2E,EAAM,KAAK;AAAA,CAAI,CACxB,CAEA,SAASpB,GAAwBvD,EAA+B,CAC9D,OAAO,KAAK,UACV,CACE,QAAS,GACT,eAAgBA,EAAO,SAAS,eAChC,gBAAiBA,EAAO,SAAS,gBACjC,cAAeA,EAAO,SAAS,cAC/B,YAAaA,EAAO,YACpB,MAAO,MAAM,KAAKA,EAAO,MAAM,QAAQ,CAAC,EAAE,IAAI,CAAC,CAACc,EAAM+C,CAAK,KAAO,CAChE,KAAA/C,EACA,KAAM+C,EAAM,IACd,EAAE,CACJ,EACA,KACA,CACF,CACF,CY5uBA,IAAAiB,GAAwB,qBACxBC,GAA+B,uBAC/BC,GAA8B,gBAC9BC,GAAuB,yBACvBC,GAA6D,yCAMtD,SAASC,IAAiC,CAe/C,OAdY,IAAI,WAAQ,UAAU,EAC/B,YAAY,4CAA4C,EACxD,OAAO,wBAAyB,mCAAoC,GAAG,EACvE,OAAO,WAAY,kBAAkB,EACrC,OAAO,gBAAiB,0BAA0B,EAClD,OAAO,MAAOC,GAA6B,CAC1C,GAAI,CACF,IAAMC,EAAW,MAAMC,GAAYF,CAAO,EAC1C,QAAQ,KAAKC,CAAQ,CACvB,OAASE,EAAO,CACdC,EAAkBD,CAAK,CACzB,CACF,CAAC,CAGL,CAcA,eAAeE,GAAWC,EAAgC,CACxD,GAAI,CACF,eAAM,SAAKA,CAAI,EACR,EACT,MAAQ,CACN,MAAO,EACT,CACF,CAEA,eAAeJ,GAAYF,EAA2C,CACpE,IAAMO,KAAc,YAAQP,EAAQ,SAAS,EACvCQ,EAA4B,CAAC,EAC/BC,EAAY,GAETC,EAAK,2BAA2BH,CAAW,EAAE,EAC7CG,EAAK,EAAE,EAEd,IAAMC,KAAa,SAAKJ,EAAa,aAAa,EAGlD,GAAI,CAFe,MAAMF,GAAWM,CAAU,EAG5C,OAAOR,EAAM,6CAA6C,EACnDS,EAAW,iBAGbC,EAAQ,yBAAyB,EACxC,GAAI,CACF,IAAMC,EAAU,QAAM,aAASH,EAAY,OAAO,EAE9CI,EACJ,GAAI,CACFA,EAAS,KAAK,MAAMD,CAAO,CAC7B,OAASX,EAAO,CACd,MAAM,IAAI,MAAM,iBAAkBA,EAAgB,OAAO,GAAI,CAAE,MAAOA,CAAM,CAAC,CAC/E,CAEA,IAAMa,EAAUD,EAAO,SACvB,GAAI,CAACC,GAAW,OAAOA,GAAY,SACjC,MAAM,IAAI,MAAM,8CAA8C,EAEhE,GAAI,CAACA,EAAQ,MAAW,CAACA,EAAQ,QAC/B,MAAM,IAAI,MAAM,wDAAwD,EAG1E,IAAMC,EAAaF,EAAO,WAC1B,GAAI,CAACE,GAAc,OAAOA,GAAe,SACvC,MAAM,IAAI,MAAM,gDAAgD,KAGlE,gCAA4BA,CAAU,EAC/BC,EAAQ,6BAA6B,EAE5C,IAAMC,EAAQJ,EAAO,MACfK,EAAcL,EAAO,YACrBM,EAAY,OAAO,KAAKF,GAAS,CAAC,CAAC,EAAE,OACrCG,EAAc,OAAO,KAAKF,GAAe,CAAC,CAAC,EAAE,OAEnD,GAAIC,IAAc,GAAKC,IAAgB,EACrCd,EAAO,KAAK,CAAE,KAAM,QAAS,QAAS,mCAAoC,CAAC,EAC3EC,EAAY,OACP,CACL,OAAW,CAACc,EAAST,CAAO,IAAK,OAAO,QAAQK,GAAS,CAAC,CAAC,EACzD,GAAI,CACF,GAAAK,QAAW,WAAWV,CAAO,EACtBD,EAAQ,YAAOU,CAAO,EAAE,CACjC,OAASpB,GAAO,CACdK,EAAO,KAAK,CACV,KAAM,QACN,QAAS,8BAA+BL,GAAgB,OAAO,GAC/D,KAAMoB,CACR,CAAC,EACDd,EAAY,EACd,CAEKS,EACL,SAASG,CAAS,oBACfC,EAAc,EAAI,QAAQA,CAAW,kBAAoB,IAC1D,oBACJ,CACF,CACF,OAASnB,EAAO,CACdK,EAAO,KAAK,CACV,KAAM,QACN,QAAUL,EAAgB,QAC1B,KAAM,aACR,CAAC,EACDM,EAAY,EACd,CAEKT,EAAQ,aACJa,EAAQ,mCAAmC,EAC5B,QAAM,oBAAgB,EAEnCK,EAAQ,oBAAoB,EAEnCV,EAAO,KAAK,CAAE,KAAM,UAAW,QAAS,4CAA6C,CAAC,GAInFE,EAAK,EAAE,EAEd,IAAMe,EAAWjB,EAAO,OAAQkB,GAAMA,EAAE,OAAS,SAAS,EACpDC,EAASnB,EAAO,OAAQkB,GAAMA,EAAE,OAAS,OAAO,EAEtD,GAAID,EAAS,OAAS,EAAG,CAChBG,EAAK,GAAGH,EAAS,MAAM,cAAc,EAC5C,QAAWI,KAASJ,EAAU,CAC5B,IAAMK,EAASD,EAAM,KAAO,GAAGA,EAAM,IAAI,KAAO,GACzCnB,EAAK,YAAOoB,CAAM,GAAGD,EAAM,OAAO,EAAE,CAC7C,CACOnB,EAAK,EAAE,CAChB,CAEA,GAAIiB,EAAO,OAAS,EAAG,CACdxB,EAAM,GAAGwB,EAAO,MAAM,YAAY,EACzC,QAAWE,KAASF,EAAQ,CAC1B,IAAMG,EAASD,EAAM,KAAO,GAAGA,EAAM,IAAI,KAAO,GACzCnB,EAAK,YAAOoB,CAAM,GAAGD,EAAM,OAAO,EAAE,CAC7C,CACOnB,EAAK,EAAE,CAChB,CAEA,OAAID,GACKN,EAAM,4BAA4B,EAClCS,EAAW,kBAGhBZ,EAAQ,QAAUyB,EAAS,OAAS,GAC/BtB,EAAM,0CAA0C,EAChDS,EAAW,mBAGbM,EAAQ,mBAAmB,EAC3BN,EAAW,QACpB,CC9KA,IAAAmB,GAAwB,qBACxBC,EAAoD,uBACpDC,EAA8B,gBAC9BC,GAAsB,sBACtBC,GAAgE,yCAQzD,SAASC,IAA6B,CAiB3C,OAhBY,IAAI,WAAQ,MAAM,EAC3B,YAAY,sCAAsC,EAClD,OAAO,wBAAyB,mCAAoC,GAAG,EACvE,OAAO,mBAAoB,2BAA2B,EACtD,OAAO,WAAY,6CAA6C,EAChE,OAAO,SAAU,+BAA+B,EAChD,OAAO,gBAAiB,wBAAwB,EAChD,OAAO,MAAOC,GAAyB,CACtC,GAAI,CACF,IAAMC,EAAW,MAAMC,GAAQF,CAAO,EACtC,QAAQ,KAAKC,CAAQ,CACvB,OAASE,EAAO,CACdC,EAAkBD,CAAK,CACzB,CACF,CAAC,CAGL,CAiBA,eAAeD,GAAQF,EAAuC,CAC5D,IAAMK,KAAc,WAAQL,EAAQ,SAAS,EACvCM,KAAW,QAAKD,EAAa,OAAO,EACpCE,EAAc,UAAUF,CAAW,GAElCG,EAAK,wBAAwBH,CAAW,EAAE,EAC1CG,EAAK,EAAE,EAEd,IAAIC,EACJ,GAAI,CACF,IAAMC,EAAU,QAAM,WAAQJ,CAAQ,EACtCG,EAAW,CAAC,EACZ,QAAWE,KAASD,EAClB,GAAI,CACF,IAAME,KAAa,QAAKN,EAAUK,EAAO,aAAa,EACtD,QAAM,YAASC,CAAU,EACzBH,EAAS,KAAKE,CAAK,CACrB,MAAQ,CAER,CAEJ,MAAQ,CACN,OAAOR,EAAM,+BAA+BG,CAAQ,EAAE,EAC/CO,EAAW,gBACpB,CAEA,GAAIJ,EAAS,SAAW,EACtB,OAAON,EAAM,wBAAwB,EAC9BK,EAAK,EAAE,EACPA,EAAK,qBAAqB,EAC1BA,EAAK,UAAU,EACfA,EAAK,qBAAqB,EAC1BA,EAAK,mBAAmB,EACxBA,EAAK,iBAAiB,EACtBK,EAAW,iBAGpB,GAAIb,EAAQ,QAAS,CACnB,GAAI,CAACS,EAAS,SAAST,EAAQ,OAAO,EACpC,OAAOG,EAAM,YAAYH,EAAQ,OAAO,aAAa,EAC9CQ,EAAK,uBAAuBC,EAAS,KAAK,IAAI,CAAC,EAAE,EACjDI,EAAW,iBAEpBJ,EAAW,CAACT,EAAQ,OAAO,CAC7B,CAEOQ,EAAK,WAAWC,EAAS,MAAM,gBAAgB,EAC/CD,EAAK,EAAE,EAId,IAAMM,EAAW,MADQC,EAAuB,EACR,QAAQR,EAAa,CAAE,QAAS,EAAK,CAAC,EAExES,KAAS,iBAAa,EACtBC,EAAwB,CAAC,EAE/B,QAAWC,KAAWT,EAAU,CAC9B,IAAMU,KAAa,QAAKb,EAAUY,CAAO,EACnCN,KAAa,QAAKO,EAAY,aAAa,EAC3CC,KAAc,QAAKD,EAAY,UAAU,EAExCE,EAAQ,oBAAoBH,CAAO,EAAE,EAE5C,GAAI,CACF,IAAMI,EAAgB,QAAM,YAASV,EAAY,OAAO,EAClDW,EAAc,QAAKD,CAAa,EAEhCE,EAAqB,CACzB,SAAAV,EACA,OAAAS,EACA,SAAU,QAAQL,CAAO,GACzB,QAAS,CACP,WAAYlB,EAAQ,UACtB,CACF,EAEMyB,GAAS,MAAMT,EAAO,QAAQQ,CAAQ,EAE5C,GAAIxB,EAAQ,OACV,MAAM0B,GAAeN,EAAaK,EAAM,EACjCE,EAAQ,GAAGT,CAAO,WAAW,EACpCD,EAAQ,KAAK,CAAE,KAAMC,EAAS,OAAQ,EAAK,CAAC,MACvC,CACL,GAAM,CAAE,OAAAU,GAAQ,MAAAC,EAAM,EAAI,MAAMC,GAAeV,EAAaK,GAAQzB,EAAQ,IAAI,EAChF,GAAI4B,GAAO,SAAW,EACbD,EAAQ,GAAGT,CAAO,UAAU,EACnCD,EAAQ,KAAK,CAAE,KAAMC,EAAS,OAAQ,EAAK,CAAC,MACvC,CACEf,EAAM,GAAGe,CAAO,UAAU,EACjC,QAAWa,KAAOH,GACTpB,EAAK,OAAOuB,CAAG,EAAE,EAE1B,GAAI/B,EAAQ,MAAQ6B,IAASA,GAAM,OAAS,EAC1C,QAAWG,KAAKH,GAAO,CACdrB,EAAK,kBAAkBwB,EAAE,QAAQ,EAAE,EACnCxB,EAAK,gBAAgBwB,EAAE,QAAQ,EAAE,EACxC,IAAMC,GAAYD,EAAE,KAAK,MAAM;AAAA,CAAI,EAAE,MAAM,CAAC,EAC5C,QAAWE,MAAQD,GACbC,IACK1B,EAAK,KAAK0B,EAAI,EAAE,CAG7B,CAEFjB,EAAQ,KAAK,CAAE,KAAMC,EAAS,OAAQ,GAAO,OAAAU,GAAQ,MAAAC,EAAM,CAAC,CAC9D,CACF,CACF,OAAS1B,EAAO,CACPA,EAAM,GAAGe,CAAO,SAAS,EACzBV,EAAK,OAAQL,EAAgB,OAAO,EAAE,EAC7Cc,EAAQ,KAAK,CAAE,KAAMC,EAAS,OAAQ,GAAO,OAAQ,CAAEf,EAAgB,OAAO,CAAE,CAAC,CACnF,CACF,CAEOK,EAAK,EAAE,EACd,IAAM2B,EAASlB,EAAQ,OAAQmB,GAAMA,EAAE,MAAM,EAAE,OACzCC,EAASpB,EAAQ,OAAQmB,GAAM,CAACA,EAAE,MAAM,EAAE,OAEhD,OAAIC,IAAW,GACNV,EAAQ,OAAOQ,CAAM,oBAAoB,EACzCtB,EAAW,UAEXV,EAAM,GAAGkC,CAAM,uBAAuBF,CAAM,SAAS,EACrDtB,EAAW,iBAEtB,CAMA,SAASyB,GAAsBC,EAAyB,CACtD,OAAOA,EAAQ,QAAQ,qCAAsC,aAAa,CAC5E,CAEA,eAAeb,GAAeN,EAAqBK,EAAsC,CACvF,QAAM,SAAML,EAAa,CAAE,UAAW,EAAK,CAAC,EAC5C,OAAW,CAACoB,EAAU7B,CAAK,IAAKc,EAAO,MAAO,CAC5C,GAAIe,IAAa,gBAAiB,SAClC,IAAMC,KAAa,QAAKrB,EAAaoB,CAAQ,EAC7C,QAAM,aAAUC,EAAYH,GAAsB3B,EAAM,OAAO,EAAG,OAAO,CAC3E,CACF,CAOA,eAAemB,GACbV,EACAK,EACAiB,EACwB,CACxB,IAAMd,EAAmB,CAAC,EACpBC,EAAmD,CAAC,EAE1D,OAAW,CAACW,EAAU7B,CAAK,IAAKc,EAAO,MAAO,CAC5C,GAAIe,IAAa,gBAAiB,SAClC,IAAMG,EAAShC,EAAM,QAEfiC,KAAe,QAAKxB,EAAaoB,CAAQ,EAC/C,GAAI,CACF,IAAMK,EAAW,QAAM,YAASD,EAAc,OAAO,EAC/CE,EAAmBR,GAAsBK,EAAO,KAAK,EAAE,QAAQ,QAAS;AAAA,CAAI,CAAC,EAC7EI,EAAqBT,GAAsBO,EAAS,KAAK,EAAE,QAAQ,QAAS;AAAA,CAAI,CAAC,EAEvF,GAAIC,IAAqBC,IACvBnB,EAAO,KAAK,GAAGY,CAAQ,oBAAoB,EACvCE,GAAc,CAChB,IAAMM,EAAOC,EAAoBF,EAAoBD,EAAkBN,CAAQ,EAC/EX,EAAM,KAAK,CAAE,SAAUW,EAAU,KAAAQ,CAAK,CAAC,CACzC,CAEJ,MAAQ,CACNpB,EAAO,KAAK,GAAGY,CAAQ,2BAA2B,CACpD,CACF,CAEA,GAAI,CACF,IAAMU,EAAgB,QAAM,WAAQ9B,CAAW,EAC/C,QAAW+B,KAAQD,EACb,CAACzB,EAAO,MAAM,IAAI0B,CAAI,GAAKA,IAAS,iBACtCvB,EAAO,KAAK,GAAGuB,CAAI,gCAAgC,CAGzD,MAAQ,CAER,CAEA,MAAO,CAAE,OAAAvB,EAAQ,MAAOc,EAAeb,EAAQ,MAAU,CAC3D,CC3OA,IAAAuB,GAAwB,qBACxBC,EAAuD,uBACvDC,EAAiD,gBACjDC,GAAsB,sBACtBC,EASO,yCCjBP,IAAAC,GAAyC,uBACzCC,GAAyB,gBA0DlB,SAASC,GAAmBC,EAAcC,EAA+B,CAC9E,IAAMC,EAAiBF,EAAK,QAAQ,MAAO,GAAG,EACxCG,KAAW,aAASD,CAAc,EAMxC,OAJID,GAAcC,EAAe,SAASD,EAAW,QAAQ,MAAO,GAAG,CAAC,GAIpEE,IAAa,eAAiBA,IAAa,kBACtC,SAGQ,UAAU,KAAKD,CAAc,GAC9B,CAACC,EAAS,WAAW,GAAG,EAC/B,WAGLA,IAAa,cACR,SAGF,OACT,CASO,SAASC,GAAcC,EAA0BC,EAAwB,CAAC,EAAc,CAC7F,GAAM,CACJ,SAAAC,EACA,QAAAC,EACA,QAAAC,EACA,QAAAC,EAAU,oEACV,WAAAC,EAAa,IACb,WAAAV,CACF,EAAIK,EAEEM,EAAU,GAAAC,QAAS,MAAMR,EAAO,CACpC,QAAAK,EACA,WAAY,GACZ,cAAe,GACf,iBAAkB,CAChB,mBAAoBC,EACpB,aAAc,EAChB,CACF,CAAC,EAEKG,EAAeC,GAA8Bf,GAAiB,CAClE,IAAMgB,EAAWjB,GAAmBC,EAAMC,CAAU,EAGhDM,GACFA,EAHwB,CAAE,KAAAQ,EAAM,KAAAf,EAAM,SAAAgB,CAAS,CAGjC,CAElB,EAEA,OAAAJ,EAAQ,GAAG,SAAUE,EAAY,QAAQ,CAAC,EAC1CF,EAAQ,GAAG,MAAOE,EAAY,KAAK,CAAC,EACpCF,EAAQ,GAAG,SAAUE,EAAY,QAAQ,CAAC,EAEtCN,GACFI,EAAQ,GAAG,QAASJ,CAAO,EAGzBC,GACFG,EAAQ,GAAG,QAAUK,GACnBR,EAAQQ,aAAe,MAAQA,EAAM,IAAI,MAAM,OAAOA,CAAG,CAAC,CAAC,CAC7D,EAGKL,CACT,CD/FO,SAASM,IAA4B,CAuB1C,OAtBY,IAAI,WAAQ,KAAK,EAC1B,YAAY,yCAAyC,EACrD,OAAO,wBAAyB,oCAAoC,EACpE,OACC,kBACA,qEACF,EACC,OAAO,cAAe,iCAAiC,EACvD,OAAO,kBAAmB,yCAAyC,EACnE,OAAO,qBAAsB,4CAA4C,EACzE,OAAO,uBAAwB,0CAA0C,EACzE,eAAe,qBAAsB,qCAAqC,EAC1E,OAAO,YAAa,iCAAiC,EACrD,OAAO,MAAOC,GAAwB,CACrC,GAAI,CACF,IAAMC,EAAW,MAAMC,GAAOF,CAAO,EACrC,QAAQ,KAAKC,CAAQ,CACvB,OAASE,EAAO,CACdC,EAAkBD,CAAK,CACzB,CACF,CAAC,CAGL,CAIA,eAAsBE,GACpBC,EAC6C,CAC7C,GAAI,CACF,QAAM,UAAOA,CAAU,CACzB,MAAQ,CACN,MAAO,CAAE,MAAO,GAAO,MAAO,wBAAwBA,CAAU,EAAG,CACrE,CAEA,IAAMC,KAAiB,QAAKD,EAAY,aAAa,EACrD,GAAI,CACF,eAAM,UAAOC,CAAc,EACpB,CAAE,MAAO,EAAK,CACvB,MAAQ,CACN,MAAO,CAAE,MAAO,GAAO,MAAO,4BAA4BD,CAAU,EAAG,CACzE,CACF,CAEA,eAAsBE,GAAgBC,EAAqBC,EAAuC,CAChG,IAAMC,KAAY,WAAQD,CAAY,EAEtC,GAAI,CACF,QAAM,MAAGC,EAAW,CAAE,UAAW,GAAM,MAAO,EAAK,CAAC,CACtD,MAAQ,CAER,CAEA,eAAM,SAAMA,EAAW,CAAE,UAAW,EAAK,CAAC,EACnCA,CACT,CAEA,SAASC,GAAaZ,EAA8B,CAClD,MAAO,CAAC,EAAEA,EAAQ,QAAUA,EAAQ,IACtC,CAEA,eAAeE,GAAOF,EAAsC,CAC1D,OAAIY,GAAaZ,CAAO,EACfa,GAAiBb,CAAO,EAE1Bc,GAAoBd,CAAO,CACpC,CAMA,eAAea,GAAiBb,EAAsC,CACpE,IAAMe,KAAa,WAAQf,EAAQ,MAAO,EACpCgB,KAAS,WAAQhB,EAAQ,GAAI,EAC7BiB,EAAejB,EAAQ,MAAQ,eAC/BkB,EAAkBlB,EAAQ,QAE5BmB,EACJ,GAAInB,EAAQ,QAAS,CACnBmB,KAAc,WAAQnB,EAAQ,OAAO,EACrC,GAAI,CACF,QAAM,UAAOmB,CAAW,CAC1B,MAAQ,CACN,OAAOhB,EAAM,sBAAsBH,EAAQ,OAAO,EAAE,EAC7CoB,EAAW,gBACpB,CACF,KACE,QAAOjB,EAAM,sCAAsC,EAC5CkB,EAAK,wCAAwC,EAC7CD,EAAW,iBAGpB,IAAMT,EAAY,MAAMH,GAAgB,IAAKR,EAAQ,MAAM,EACrDsB,KAAoB,YAAS,QAAQ,IAAI,EAAGX,CAAS,EAEpDU,EAAK,iBAAc,YAAS,QAAQ,IAAI,EAAGN,CAAU,CAAC,EAAE,EACxDM,EAAK,iBAAc,YAAS,QAAQ,IAAI,EAAGL,CAAM,CAAC,EAAE,EACpDK,EAAK,cAAcC,CAAiB,GAAG,EACvCD,EAAK,iBAAc,YAAS,QAAQ,IAAI,EAAGF,CAAW,CAAC,EAAE,EACzDE,EAAK,EAAE,EAEd,IAAME,KAAS,gBAAa,EAEtBC,EAAmB,SAAY,CACnC,GAAI,CACF,IAAMC,EAAW,MAAMC,GACrBX,EACAC,EACAC,EACAC,CACF,EACA,MAAMS,GAAmBJ,EAAQE,EAAUN,EAAaR,EAAWX,EAAQ,IAAI,CACjF,OAASG,EAAO,CACPA,EAAM,iCAA4B,EAClCkB,EAAK,cAAelB,EAAgB,OAAO,EAAE,CACtD,CACF,EAEA,MAAMqB,EAAiB,EAIvB,IAAMI,EAAUC,GAFG,CAACd,EAAYC,EAAQG,CAAW,EAET,CACxC,WAAYJ,EACZ,SAAWe,GAAsB,EACzB,UACGT,EAAK,iBAAc,YAAS,QAAQ,IAAI,EAAGS,EAAM,IAAI,CAAC,EAAE,EAC/D,MAAMN,EAAiB,KAE3B,EACA,QAAS,IAAM,CACNH,EAAK,qDAAqD,EAC1DA,EAAK,EAAE,CAChB,EACA,QAAUlB,GAAU,CACXA,EAAM,gBAAgBA,EAAM,OAAO,EAAE,CAC9C,CACF,CAAC,EAED,OAAO,IAAI,QAAiB4B,GAAmB,CAC7C,IAAMC,EAAU,IAAM,CACbX,EAAK,EAAE,EACPA,EAAK,6BAA6B,EACpCO,EAAQ,MAAM,EACnBG,EAAeX,EAAW,OAAO,CACnC,EAEA,QAAQ,GAAG,SAAUY,CAAO,EAC5B,QAAQ,GAAG,UAAWA,CAAO,CAC/B,CAAC,CACH,CAEA,SAASC,GAAyBC,EAA6C,CAC7E,IAAMC,EAAQD,EAAW,WACzB,GAAI,CAACC,EAAO,OACZ,IAAMC,EAAcD,EAAM,QAC1B,GAAI,CAACC,GAAe,OAAOA,GAAgB,UAAW,OACtD,IAAMC,EAAMD,EAAY,QACxB,OAAO,OAAOC,GAAQ,SAAWA,EAAM,MACzC,CAEA,eAAeX,GACbX,EACAC,EACAsB,EACAC,EACmB,CACnB,IAAMC,EAAgB,QAAM,YAASzB,EAAY,OAAO,EAGlDmB,EAFY,KAAK,MAAMM,CAAa,EAEb,WAC7B,GAAI,CAACN,GAAc,OAAOA,GAAe,SACvC,MAAM,IAAI,MAAM,mDAAmD,EAGrE,IAAMO,EAAkBF,GAAWN,GAAyBC,CAAU,GAAK,YAErE,CAAE,MAAAQ,EAAO,YAAAC,CAAY,EAAI,MAAMC,GAAW5B,CAAM,EAItD,MAAO,CACL,SAHiC,CAAE,KAAAsB,EAAM,QAASG,CAAgB,EAIlE,WAAAP,EACA,MAAAQ,EACA,YAAAC,EACA,OAAQ,UAAU3B,CAAM,GACxB,QAAS,CAAE,OAAQyB,CAAgB,CACrC,CACF,CAEA,eAAed,GACbJ,EACAE,EACAN,EACAR,EACAkC,EACe,CACf,IAAMC,EAAgB,QAAM,YAAS3B,EAAa,OAAO,EACnD4B,EAAc,QAAKD,CAAa,EAEhCE,EAAqB,CACzB,SAAAvB,EACA,OAAAsB,EACA,SAAU,OAAO,KAAK,IAAI,CAAC,GAC3B,QAAS,CAAE,WAAY,EAAM,CAC/B,EAEME,EAAS,MAAM1B,EAAO,QAAQyB,CAAQ,EAEtCE,EAAoB,MAAM,KAAKD,EAAO,MAAM,KAAK,CAAC,EAAE,OACvDE,GAAMA,IAAM,eACf,EAAE,OACK9B,EAAK,qBAAgB6B,CAAiB,uBAAuB,EAEpE,QAAM,MAAGvC,EAAW,CAAE,UAAW,GAAM,MAAO,EAAK,CAAC,EACpD,QAAM,SAAMA,EAAW,CAAE,UAAW,EAAK,CAAC,EAE1C,IAAIyC,EAAe,EACnB,OAAW,CAACC,EAAUC,CAAK,IAAKL,EAAO,MAAO,CAC5C,GAAII,IAAa,gBAAiB,SAClC,IAAME,KAAa,QAAK5C,EAAW0C,CAAQ,EAC3C,QAAM,YAAM,WAAQE,CAAU,EAAG,CAAE,UAAW,EAAK,CAAC,EACpD,QAAM,aAAUA,EAAYD,EAAM,QAAS,OAAO,EAClDF,GACF,CACO/B,EACL,qBAAgB+B,CAAY,wBAAqB,YAAS,QAAQ,IAAI,EAAGzC,CAAS,CAAC,GACrF,EAEIkC,IAAa,IACf,MAAMW,GAA4BP,EAAQtC,CAAS,CAEvD,CAKA,eAAeG,GAAoBd,EAAsC,CACvE,IAAMM,KAAa,WAAQN,EAAQ,WAAa,GAAG,EAE7CyD,EAAa,MAAMpD,GAAoBC,CAAU,EACvD,GAAI,CAACmD,EAAW,MACd,OAAOtD,EAAMsD,EAAW,KAAM,EACvBrC,EAAW,iBAGpB,IAAID,EACJ,GAAInB,EAAQ,QAAS,CACnBmB,KAAc,WAAQb,EAAYN,EAAQ,OAAO,EACjD,GAAI,CACF,QAAM,UAAOmB,CAAW,CAC1B,MAAQ,CACN,OAAOhB,EAAM,sBAAsBH,EAAQ,OAAO,EAAE,EAC7CoB,EAAW,gBACpB,CACF,KAAO,CACL,IAAMsC,EAAiB,MAAMC,EAAmBrD,CAAU,EAC1D,GAAI,CAACoD,EACH,OAAOvD,EAAM,uDAAuD,EAC7DkB,EAAK,kEAAkE,EACvED,EAAW,iBAEpBD,EAAcuC,CAChB,CAEA,IAAM/C,EAAY,MAAMH,GAAgBF,EAAYN,EAAQ,MAAM,EAC5DsB,KAAoB,YAAS,QAAQ,IAAI,EAAGX,CAAS,EAEpDU,EAAK,iBAAc,YAAS,QAAQ,IAAI,EAAGf,CAAU,CAAC,EAAE,EACxDe,EAAK,cAAcC,CAAiB,GAAG,EACvCD,EAAK,iBAAc,YAAS,QAAQ,IAAI,EAAGF,CAAW,CAAC,EAAE,EACzDE,EAAK,EAAE,EAEd,IAAME,KAAS,gBAAa,EACtBqC,EAAc,UAAUtD,CAAU,GAExC,MAAMuD,GAAetC,EAAQqC,EAAatD,EAAYa,EAAaR,EAAWX,EAAQ,IAAI,EAE1F,IAAM4B,EAAUC,GAAcvB,EAAY,CACxC,SAAWwB,GAAsB,EACzB,SAAY,CAChB,IAAMgC,KAAU,YAASxD,EAAYwB,EAAM,IAAI,EACxCT,EAAK,cAAcyC,CAAO,EAAE,EAE/BhC,EAAM,WAAa,SACrB,MAAMiC,GAAcH,CAAW,GACtB9B,EAAM,WAAa,YAAcA,EAAM,WAAa,WAC7D,MAAM+B,GACJtC,EACAqC,EACAtD,EACAa,EACAR,EACAX,EAAQ,IACV,CAEJ,GAAG,CACL,EACA,QAAS,IAAM,CACNqB,EAAK,qDAAqD,EAC1DA,EAAK,EAAE,CAChB,EACA,QAAUlB,GAAU,CACXA,EAAM,gBAAgBA,EAAM,OAAO,EAAE,CAC9C,CACF,CAAC,EAED,OAAO,IAAI,QAAiB4B,GAAmB,CAC7C,IAAMC,EAAU,IAAM,CACbX,EAAK,EAAE,EACPA,EAAK,6BAA6B,EACpCO,EAAQ,MAAM,EACnBG,EAAeX,EAAW,OAAO,CACnC,EAEA,QAAQ,GAAG,SAAUY,CAAO,EAC5B,QAAQ,GAAG,UAAWA,CAAO,CAC/B,CAAC,CACH,CAEA,eAAe+B,GAAcH,EAAuC,CAClE,GAAI,CAGF,OADiB,MADQI,EAAuB,EACR,QAAQJ,EAAa,CAAE,QAAS,EAAK,CAAC,GACjE,YACJvC,EAAK,gCAA2B,EAChC,KAEFlB,EAAM,kCAA6B,EACnC,GACT,OAASA,EAAO,CACd,OAAOA,EAAM,kCAA6B,EACnCkB,EAAK,cAAelB,EAAgB,OAAO,EAAE,EAC7C,EACT,CACF,CAEA,eAAe0D,GACbtC,EACAqC,EACAtD,EACAa,EACAR,EACAkC,EACe,CAEf,GADc,MAAMkB,GAAcH,CAAW,EAG7C,GAAI,CACF,IAAMd,EAAgB,QAAM,YAAS3B,EAAa,OAAO,EACnD4B,EAAc,QAAKD,CAAa,EAKhCE,EAAqB,CACzB,SAHe,MADQgB,EAAuB,EACR,QAAQJ,EAAa,CAAE,QAAS,EAAK,CAAC,EAI5E,OAAAb,EACA,SAAU,OAAO,KAAK,IAAI,CAAC,GAC3B,QAAS,CAAE,WAAY,EAAM,CAC/B,EAEME,EAAS,MAAM1B,EAAO,QAAQyB,CAAQ,EAEtCE,EAAoB,MAAM,KAAKD,EAAO,MAAM,KAAK,CAAC,EAAE,OACvDE,GAAMA,IAAM,eACf,EAAE,OACK9B,EAAK,qBAAgB6B,CAAiB,uBAAuB,EAEpE,QAAM,MAAGvC,EAAW,CAAE,UAAW,GAAM,MAAO,EAAK,CAAC,EACpD,QAAM,SAAMA,EAAW,CAAE,UAAW,EAAK,CAAC,EAE1C,IAAIyC,EAAe,EACnB,OAAW,CAACC,EAAUC,CAAK,IAAKL,EAAO,MAAO,CAC5C,GAAII,IAAa,gBAAiB,SAClC,IAAME,KAAa,QAAK5C,EAAW0C,CAAQ,EAC3C,QAAM,YAAM,WAAQE,CAAU,EAAG,CAAE,UAAW,EAAK,CAAC,EACpD,QAAM,aAAUA,EAAYD,EAAM,QAAS,OAAO,EAClDF,GACF,CACO/B,EACL,qBAAgB+B,CAAY,wBAAqB,YAAS,QAAQ,IAAI,EAAGzC,CAAS,CAAC,GACrF,EAEIkC,IAAa,IACf,MAAMoB,GAAkB3D,EAAY2C,EAAQtC,CAAS,CAEzD,OAASR,EAAO,CACPA,EAAM,iCAA4B,EAClCkB,EAAK,cAAelB,EAAgB,OAAO,EAAE,EAElDA,aAAiB,eACjBA,EAAM,OAAS,kBAAgB,yBAC/BA,EAAM,SAAU,QAETkB,EACE6C,GACL/D,EAAM,QAAQ,MAChB,CACF,CAEJ,CACF,CAEA,eAAe8D,GACb3D,EACA2C,EACAkB,EACe,CACf,IAAMC,KAAc,QAAK9D,EAAY,QAAS,UAAW,UAAU,EACnE,MAAM+D,GAAyBpB,EAAQmB,CAAW,CACpD,CAEA,eAAeZ,GACbc,EACAH,EACe,CAGjB,CAEA,eAAeE,GAAyBpB,EAAuBmB,EAAoC,CACjG,IAAMG,EAAY,KAAK,IAAI,EAE3B,GAAI,CACF,QAAM,UAAOH,CAAW,CAC1B,MAAQ,CACC/C,EAAK,+DAA0D,EACtE,MACF,CAEA,IAAMmD,EAAiD,CAAC,EAExD,OAAW,CAACnB,EAAUC,CAAK,IAAKL,EAAO,MAAO,CAC5C,GAAII,IAAa,gBAAiB,SAClC,IAAMoB,EAASnB,EAAM,QAEfoB,KAAe,QAAKN,EAAaf,CAAQ,EAC/C,GAAI,CACF,IAAMsB,EAAW,QAAM,YAASD,EAAc,OAAO,EAC/CE,EAAmBH,EAAO,KAAK,EAAE,QAAQ,QAAS;AAAA,CAAI,EACtDI,EAAqBF,EAAS,KAAK,EAAE,QAAQ,QAAS;AAAA,CAAI,EAEhE,GAAIC,IAAqBC,EAAoB,CAC3C,IAAMC,EAAOC,EAAoBF,EAAoBD,EAAkBvB,CAAQ,EAC/EmB,EAAO,KAAK,CAAE,KAAMnB,EAAU,KAAAyB,CAAK,CAAC,CACtC,CACF,MAAQ,CACNN,EAAO,KAAK,CAAE,KAAMnB,CAAS,CAAC,CAChC,CACF,CAEA,IAAM2B,EAAW,KAAK,IAAI,EAAIT,EAE9B,GAAIC,EAAO,SAAW,EACbnD,EAAK,sCAAiC2D,CAAQ,KAAK,MACrD,CACE7E,EAAM,mCAA8B,EAC3C,QAAW8E,KAAOT,EAEhB,GADOnD,EAAK,cAAc4D,EAAI,IAAI,oBAAoB,EAClDA,EAAI,KAAM,CACZ,IAAMC,EAAYD,EAAI,KAAK,MAAM;AAAA,CAAI,EAAE,MAAM,EAAG,EAAE,EAClD,QAAWE,KAAQD,EACbC,GACK9D,EAAK,cAAc8D,CAAI,EAAE,EAGhCF,EAAI,KAAK,MAAM;AAAA,CAAI,EAAE,OAAS,IACzB5D,EAAK,iCAAiC,CAEjD,CAEJ,CACF,CElgBA,IAAA+D,GAAwB,qBACxBC,GAA8B,gBAC9BC,GAA6B,yCAY7B,IAAMC,GAAiB,EAUhB,SAASC,IAA+B,CAc7C,OAbY,IAAI,WAAQ,QAAQ,EAC7B,YAAY,qDAAqD,EACjE,OAAO,wBAAyB,qDAAsD,GAAG,EACzF,OAAO,SAAU,wCAAwC,EACzD,OAAO,MAAOC,GAA2B,CACxC,GAAI,CACF,IAAMC,EAAW,MAAMC,GAAUF,CAAO,EACxC,QAAQ,KAAKC,CAAQ,CACvB,OAASE,EAAO,CACdC,EAAkBD,CAAK,CACzB,CACF,CAAC,CAGL,CAMA,eAAsBD,GAAUF,EAAyC,CACvE,IAAMK,KAAM,YAAQL,EAAQ,SAAS,EAC/BM,KAAe,SAAKD,EAAK,eAAe,EAGvCE,EAAQ,yBAAyBD,CAAY,EAAE,EACtD,IAAME,EAAW,MAAMC,GAAaH,CAAY,EAGzCC,EAAQ,sBAAsBF,CAAG,EAAE,EAC1C,IAAMK,EAAe,MAAMC,GAAuBN,EAAKG,EAAS,cAAc,EAIxEI,EAAS,QADA,iBAAa,EACA,OAAOF,EAAcF,CAAQ,EAGzD,OAAIR,EAAQ,KACHa,EAAKC,GAAsBF,EAAO,WAAW,CAAC,EAE9CC,EAAKE,GAAkBH,EAAO,WAAW,CAAC,EAG5CA,EAAO,YAAY,MAAQI,EAAW,QAAUlB,EACzD,CAMO,SAASiB,GAAkBE,EAA6B,CAC7D,IAAMC,EAAkB,CAAC,EACnBC,EACJF,EAAO,QAAQ,aAAeA,EAAO,QAAQ,aAAeA,EAAO,QAAQ,gBAE7E,GAAIA,EAAO,MACTC,EAAM,KACJ,+BAA0BD,EAAO,QAAQ,UAAU,mBAAmBE,CAAU,SAClF,EACAD,EAAM,KAAK,EAAE,EACbA,EAAM,KAAK,mBAAmBD,EAAO,cAAc,QAAQ,EAAE,MACxD,CACLC,EAAM,KAAK,0BAAqBC,CAAU,eAAe,EACzDD,EAAM,KAAK,EAAE,EAEb,QAAWE,KAASH,EAAO,QAAS,CAClC,IAAMI,EAAMC,GAAaF,EAAM,IAAI,EACnCF,EAAM,KAAK,KAAKG,CAAG,KAAKD,EAAM,IAAI,EAAE,EACpCF,EAAM,KAAK,OAAOE,EAAM,OAAO,EAAE,EACjCF,EAAM,KAAK,EAAE,CACf,CAEAA,EAAM,KACJ,YAAYD,EAAO,QAAQ,UAAU,mBAChCA,EAAO,QAAQ,YAAY,aAC3BA,EAAO,QAAQ,YAAY,YAC7BA,EAAO,QAAQ,gBAAkB,EAAI,KAAKA,EAAO,QAAQ,eAAe,cAAgB,GAC7F,CACF,CAEA,OAAOC,EAAM,KAAK;AAAA,CAAI,CACxB,CAMO,SAASJ,GAAsBG,EAA6B,CACjE,OAAO,KAAK,UAAUA,EAAQ,KAAM,CAAC,CACvC,CrBxGO,SAASM,IAA4B,CAC1C,IAAMC,EAAM,IAAI,WAAQ,KAAK,EAAE,YAAY,qBAAqB,EAAE,wBAAwB,EAE1F,OAAAA,EAAI,WAAWC,GAAoB,CAAC,EACpCD,EAAI,WAAWE,GAAiB,CAAC,EACjCF,EAAI,WAAWG,GAAoB,CAAC,EACpCH,EAAI,WAAWI,GAAsB,CAAC,EACtCJ,EAAI,WAAWK,GAAkB,CAAC,EAElCL,EAAI,WAAWM,GAAoB,CAAC,EAE7BN,CACT,CDjBI,QAAQ,IAAI,UACd,QAAQ,MAAM,QAAQ,IAAI,QAAW,EAIvC,IAAMO,GAAU,uBAKhB,eAAeC,IAAsB,CACnC,IAAMC,EAAU,IAAI,WAEpBA,EACG,KAAK,UAAU,EACf,YAAY,2CAA2C,EACvD,wBAAwB,EACxB,QAAQF,GAAS,gBAAiB,cAAc,EAChD,OAAO,YAAa,uBAAuB,EAC3C,OAAO,cAAe,2BAA2B,EACjD,KAAK,YAAcG,GAAgB,CAClC,IAAMC,EAAOD,EAAY,KAAK,EAC1BC,EAAK,MACPC,GAAa,OAAO,EACXD,EAAK,SACdC,GAAa,SAAS,CAE1B,CAAC,EAGHH,EAAQ,WAAWI,GAAiB,CAAC,EAGrC,MAAMJ,EAAQ,WAAW,QAAQ,IAAI,CACvC,CAGAD,GAAK,EAAE,MAAOM,GAAU,CACtB,QAAQ,MAAM,eAAgBA,CAAK,EACnC,QAAQ,KAAK,CAAC,CAChB,CAAC","names":["import_commander","import_commander","import_commander","import_promises","import_node_path","import_promises","import_node_path","walkSrcDir","rootDir","files","staticFiles","walk","relativePath","currentDir","entries","entry","entryRelative","content","outputPath","import_tfy_infra_engine","currentVerbosity","jsonMode","setVerbosity","level","getVerbosity","setJsonMode","enabled","data","message","success","currentVerbosity","jsonMode","error","info","verbose","warn","formatSize","bytes","formatValidationErrors","errors","driftTypeTag","type","printFileList","files","write","file","EXIT_CODES","handleEngineError","error","fallbackExitCode","getVerbosity","info","key","value","formatValidationErrors","createBundleCommand","options","runBundle","error","handleEngineError","readSchemaPackage","schemaPath","raw","readStdin","parsed","err","obj","resolve","reject","chunks","chunk","extractVersionFromSchema","jsonSchema","props","versionProp","def","schemaPkg","version","srcDir","files","staticFiles","walkSrcDir","metadata","bundle","json","outPath","success","import_commander","import_node_readline","import_promises","import_node_path","yaml","import_tfy_infra_engine","import_tfy_infra_engine","import_promises","import_node_path","import_tfy_infra_engine","FileResolver","uri","_options","fsPath","info","bundlePath","error","nodeError","content","raw","rawMetadata","metadata","jsonSchema","files","staticFiles","version","path","import_tfy_infra_engine","DEFAULT_RESOLVER_OPTIONS","mergeResolverOptions","options","withRetry","fn","retries","baseDelay","lastError","attempt","error","delay","resolve","withTimeout","promise","ms","message","timer","timeout","_","reject","JFROG_TOKEN_ENV","isArtifactoryHost","uri","hostname","buildHeaders","headers","token","fetchBundle","response","hint","unpackBundle","raw","metadata","jsonSchema","files","staticFiles","version","HttpsBundleResolver","options","opts","mergeResolverOptions","withTimeout","withRetry","import_promises","import_node_path","import_node_crypto","import_tfy_infra_engine","getDefaultCacheDir","home","TemplateCache","cacheDir","source","version","key","safeVersion","cachePath","bundlePath","bundleContent","parsed","metadata","jsonSchema","record","files","staticFiles","versionInfo","template","bundle","error","keyPath","sources","templates","size","sourceKeys","versions","versionPath","dir","total","entries","entry","fullPath","fileStat","ResolverRegistry","resolvers","cacheDir","TemplateCache","FileResolver","HttpsBundleResolver","resolver","uri","options","scheme","template","createResolverRegistry","import_promises","yaml","import_promises","import_node_path","fileExists","filePath","findDefaultFixture","versionDir","defaultPath","loadConfig","filePath","content","isJson","parsed","error","format","import_promises","loadManifest","manifestPath","content","error","parsed","requiredFields","field","envelopeFromManifest","manifest","import_promises","import_node_path","import_tfy_infra_engine","readDirectoryToFileMap","dir","prefix","fileMap","entries","e","error","filename","content","zone","entry","header","import_promises","import_node_path","writeEngineOutput","dir","result","mode","filePath","entry","fullPath","fileExists","import_diff","generateUnifiedDiff","expected","actual","filename","normalizedExpected","normalizedActual","validateRenderArgs","args","createRenderCommand","collect","options","exitCode","runRender","error","handleEngineError","value","previous","parseInputOverrides","inputs","result","input","eqIndex","key","setJsonMode","outputDir","timeout","runRenderFromManifest","manifestPath","fileExists","runRenderFromConfigUpgrade","runRenderFromConfig","templateDir","runRenderFromDirectory","path","confirmPrompt","message","rl","resolve","answer","countExistingFiles","dir","e","guardOverwrite","count","warn","data","info","EXIT_CODES","verbose","manifest","loadManifest","rawConfig","envelopeFromManifest","success","envelope","createResolverRegistry","handleInstallDryRun","aborted","writeEngineOutput","printInstallResult","templateUri","fixturePath","findDefaultFixture","fixtureContent","overrides","intentId","engine","configFile","loadConfig","previousManifest","currentFiles","readDirectoryToFileMap","printDriftReport","handleUpgradeDryRun","formatUpgradeResultJson","formatUpgradeResult","computeInstallDiffs","newFiles","diffs","filename","entry","existing","generateUnifiedDiff","computeUpgradeDiffs","currentContent","printDiffs","d","report","json","total","driftTypeTag","fileList","filePath","printFileList","lines","platformCount","userCount","import_commander","import_promises","import_node_path","import_handlebars","import_tfy_infra_engine","createValidateCommand","options","exitCode","runValidate","error","handleEngineError","fileExists","path","templateDir","issues","hasErrors","info","bundlePath","EXIT_CODES","verbose","content","parsed","rawMeta","jsonSchema","success","files","staticFiles","fileCount","staticCount","relPath","Handlebars","warnings","i","errors","warn","issue","prefix","import_commander","import_promises","import_node_path","yaml","import_tfy_infra_engine","createTestCommand","options","exitCode","runTest","error","handleEngineError","templateDir","testsDir","templateUri","info","fixtures","entries","entry","inputsPath","EXIT_CODES","template","createResolverRegistry","engine","results","fixture","fixtureDir","expectedDir","verbose","inputsContent","inputs","envelope","result","updateExpected","success","errors","diffs","compareOutputs","err","d","diffLines","line","passed","r","failed","normalizeStatusSource","content","filePath","outputPath","includeDiffs","actual","expectedPath","expected","normalizedActual","normalizedExpected","diff","generateUnifiedDiff","expectedFiles","file","import_commander","import_promises","import_node_path","yaml","import_tfy_infra_engine","import_chokidar","import_node_path","classifyFileChange","path","schemaFile","normalizedPath","filename","createWatcher","paths","options","onChange","onReady","onError","ignored","debounceMs","watcher","chokidar","handleEvent","type","fileType","err","createDevCommand","options","exitCode","runDev","error","handleEngineError","validateTemplateDir","versionDir","bundleJsonPath","ensureOutputDir","_versionDir","customOutput","outputDir","isSchemaMode","runSchemaModeDev","runDirectoryModeDev","schemaPath","srcDir","templateName","templateVersion","fixturePath","EXIT_CODES","info","relativeOutputDir","engine","renderFromSchema","template","buildTemplateFromSchema","renderWithTemplate","watcher","createWatcher","event","resolvePromise","cleanup","extractVersionFromSchema","jsonSchema","props","versionProp","def","name","version","schemaContent","resolvedVersion","files","staticFiles","walkSrcDir","runTests","inputsContent","inputs","envelope","result","templateFileCount","f","filesWritten","filePath","entry","outputPath","runTestComparisonFromResult","validation","defaultFixture","findDefaultFixture","templateUri","runRenderCycle","relPath","runValidation","createResolverRegistry","runTestComparison","formatValidationErrors","_outputDir","expectedDir","runTestComparisonFromDir","_result","startTime","errors","actual","expectedPath","expected","normalizedActual","normalizedExpected","diff","generateUnifiedDiff","duration","err","diffLines","line","import_commander","import_node_path","import_tfy_infra_engine","DRIFT_DETECTED","createVerifyCommand","options","exitCode","runVerify","error","handleEngineError","dir","manifestPath","verbose","manifest","loadManifest","currentFiles","readDirectoryToFileMap","result","info","formatDriftReportJson","formatDriftReport","EXIT_CODES","report","lines","issueCount","entry","tag","driftTypeTag","createTplCommand","tpl","createBundleCommand","createDevCommand","createRenderCommand","createValidateCommand","createTestCommand","createVerifyCommand","VERSION","main","program","thisCommand","opts","setVerbosity","createTplCommand","error"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/commands/tpl/index.ts","../src/commands/tpl/bundle.ts","../src/utils/walk-src.ts","../src/utils/error-handler.ts","../src/utils/output.ts","../src/commands/tpl/render.ts","../src/resolvers/index.ts","../src/resolvers/file.ts","../src/resolvers/https.ts","../src/resolvers/base.ts","../src/cache/template-cache.ts","../src/utils/config-loader.ts","../src/utils/fs-helpers.ts","../src/utils/manifest-loader.ts","../src/utils/file-reader.ts","../src/utils/file-writer.ts","../src/utils/diff.ts","../src/commands/tpl/validate.ts","../src/commands/tpl/test.ts","../src/commands/tpl/dev.ts","../src/utils/file-watcher.ts","../src/commands/tpl/verify.ts"],"sourcesContent":["/**\n * @truefoundry/tfy-infra-cli\n *\n * CLI for TrueFoundry infrastructure templating engine.\n * Binary: tfy-init\n */\n\nimport { Command } from 'commander';\nimport { createTplCommand } from './commands/tpl/index.js';\nimport { setVerbosity } from './utils/output.js';\n\n// When invoked via `npm run`, npm sets CWD to the package root.\n// INIT_CWD preserves the directory the user actually ran the command from.\nif (process.env['INIT_CWD']) {\n process.chdir(process.env['INIT_CWD']);\n}\n\ndeclare const __CLI_VERSION__: string;\nconst VERSION = __CLI_VERSION__;\n\n/**\n * Main CLI entry point.\n */\nasync function main(): Promise<void> {\n const program = new Command();\n\n program\n .name('tfy-init')\n .description('TrueFoundry infrastructure templating CLI')\n .enablePositionalOptions()\n .version(VERSION, '-v, --version', 'Show version')\n .option('--verbose', 'Enable verbose output')\n .option('-q, --quiet', 'Suppress non-error output')\n .hook('preAction', (thisCommand) => {\n const opts = thisCommand.opts();\n if (opts['quiet']) {\n setVerbosity('quiet');\n } else if (opts['verbose']) {\n setVerbosity('verbose');\n }\n });\n\n // Add command groups\n program.addCommand(createTplCommand());\n\n // Parse and execute\n await program.parseAsync(process.argv);\n}\n\n// Run CLI\nmain().catch((error) => {\n console.error('Fatal error:', error);\n process.exit(1);\n});\n","/**\n * tfy-init tpl command group\n *\n * CHANGE (006): Removed push command (S3 support removed).\n * CHANGE (008): Removed init command (templates scaffolded via CUE).\n */\n\nimport { Command } from 'commander';\nimport { createBundleCommand } from './bundle.js';\nimport { createRenderCommand } from './render.js';\nimport { createValidateCommand } from './validate.js';\nimport { createTestCommand } from './test.js';\n\nimport { createDevCommand } from './dev.js';\nimport { createVerifyCommand } from './verify.js';\n/**\n * Create the tpl command group with all subcommands.\n */\nexport function createTplCommand(): Command {\n const tpl = new Command('tpl').description('Template operations').enablePositionalOptions();\n\n tpl.addCommand(createBundleCommand());\n tpl.addCommand(createDevCommand());\n tpl.addCommand(createRenderCommand());\n tpl.addCommand(createValidateCommand());\n tpl.addCommand(createTestCommand());\n\n tpl.addCommand(createVerifyCommand());\n\n return tpl;\n}\n","/**\n * tfy-init tpl bundle command\n *\n * Accepts a pre-computed schema package (from tfy-schema-tool --schema-package)\n * plus a src/ directory and metadata flags, and produces a bundle.json.\n * No CUE or Go dependencies — pure TypeScript.\n */\n\nimport { Command } from 'commander';\nimport { readFile, writeFile, mkdir } from 'node:fs/promises';\nimport { resolve, dirname } from 'node:path';\nimport { walkSrcDir } from '../../utils/walk-src.js';\nimport { handleEngineError } from '../../utils/error-handler.js';\nimport * as output from '../../utils/output.js';\n\ninterface SchemaPackage {\n jsonSchema: Record<string, unknown>;\n uiSchema: unknown[];\n}\n\ninterface BundleJson {\n metadata: {\n name: string;\n version: string;\n description?: string;\n };\n jsonSchema: Record<string, unknown>;\n uiSchema: unknown[];\n files: Record<string, string>;\n staticFiles: Record<string, string>;\n version: {\n semver: string;\n };\n}\n\ninterface BundleOptions {\n schema: string;\n src: string;\n name: string;\n version?: string;\n description?: string;\n output?: string;\n}\n\nexport function createBundleCommand(): Command {\n const cmd = new Command('bundle')\n .description('Build a bundle.json from a schema package and src/ directory')\n .requiredOption('--schema <path-or-dash>', 'Schema package JSON file, or \"-\" for stdin')\n .requiredOption('--src <dir>', 'Template source directory containing .hbs and static files')\n .requiredOption('--name <string>', 'Template name')\n .option('--description <string>', 'Template description')\n .option('-o, --output <path>', 'Output file path (default: stdout)')\n .action(async (options: BundleOptions) => {\n try {\n await runBundle(options);\n } catch (error) {\n handleEngineError(error);\n }\n });\n\n return cmd;\n}\n\nasync function readSchemaPackage(schemaPath: string): Promise<SchemaPackage> {\n let raw: string;\n\n if (schemaPath === '-') {\n raw = await readStdin();\n } else {\n raw = await readFile(resolve(schemaPath), 'utf-8');\n }\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch (err) {\n throw new Error(`Failed to parse schema package JSON: ${(err as Error).message}`, {\n cause: err,\n });\n }\n\n const obj = parsed as Record<string, unknown>;\n if (!obj['jsonSchema'] || typeof obj['jsonSchema'] !== 'object') {\n throw new Error('Schema package must contain a \"jsonSchema\" object');\n }\n if (!Array.isArray(obj['uiSchema'])) {\n throw new Error('Schema package must contain a \"uiSchema\" array');\n }\n\n return {\n jsonSchema: obj['jsonSchema'] as Record<string, unknown>,\n uiSchema: obj['uiSchema'] as unknown[],\n };\n}\n\nfunction readStdin(): Promise<string> {\n return new Promise((resolve, reject) => {\n const chunks: Buffer[] = [];\n process.stdin.on('data', (chunk: Buffer) => chunks.push(chunk));\n process.stdin.on('end', () => resolve(Buffer.concat(chunks).toString('utf-8')));\n process.stdin.on('error', reject);\n });\n}\n\nfunction extractVersionFromSchema(jsonSchema: Record<string, unknown>): string | undefined {\n const props = jsonSchema['properties'] as Record<string, unknown> | undefined;\n if (!props) return undefined;\n const versionProp = props['version'] as Record<string, unknown> | undefined;\n if (!versionProp) return undefined;\n const def = versionProp['default'];\n return typeof def === 'string' ? def : undefined;\n}\n\nasync function runBundle(options: BundleOptions): Promise<void> {\n const schemaPkg = await readSchemaPackage(options.schema);\n\n const version = options.version ?? extractVersionFromSchema(schemaPkg.jsonSchema);\n if (!version) {\n throw new Error(\n 'Could not determine template version. Provide --version or ensure the schema has a \"version\" property with a default value.'\n );\n }\n\n const srcDir = resolve(options.src);\n const { files, staticFiles } = await walkSrcDir(srcDir);\n\n if (files.size === 0 && staticFiles.size === 0) {\n throw new Error(`No template files found in ${srcDir}`);\n }\n\n const metadata: BundleJson['metadata'] = {\n name: options.name,\n version,\n };\n if (options.description) {\n metadata.description = options.description;\n }\n\n const bundle: BundleJson = {\n metadata,\n jsonSchema: schemaPkg.jsonSchema,\n uiSchema: schemaPkg.uiSchema,\n files: Object.fromEntries(files),\n staticFiles: Object.fromEntries(staticFiles),\n version: { semver: version },\n };\n\n const json = JSON.stringify(bundle, null, 2) + '\\n';\n\n if (options.output) {\n const outPath = resolve(options.output);\n await mkdir(dirname(outPath), { recursive: true });\n await writeFile(outPath, json, 'utf-8');\n output.success(`Bundle written to ${outPath}`);\n } else {\n process.stdout.write(json);\n }\n}\n","/**\n * Recursive directory walker for template src/ directories.\n *\n * Walks a directory tree and classifies files into Handlebars templates\n * (.hbs → rendered) and static files (copied verbatim). Skips entries\n * whose names start with '_'. Map keys are relative paths from the\n * root directory, preserving subdirectory structure.\n */\n\nimport { readdir, readFile } from 'node:fs/promises';\nimport { join } from 'node:path';\n\nexport interface WalkResult {\n files: Map<string, string>;\n staticFiles: Map<string, string>;\n}\n\n/**\n * Recursively walk a directory, reading all files into maps.\n *\n * @param rootDir - The src/ directory to walk\n * @returns Maps of relative-path → content, split by .hbs (rendered) vs static\n */\nexport async function walkSrcDir(rootDir: string): Promise<WalkResult> {\n const files = new Map<string, string>();\n const staticFiles = new Map<string, string>();\n\n await walk(rootDir, '', files, staticFiles);\n\n return { files, staticFiles };\n}\n\nasync function walk(\n rootDir: string,\n relativePath: string,\n files: Map<string, string>,\n staticFiles: Map<string, string>\n): Promise<void> {\n const currentDir = relativePath ? join(rootDir, relativePath) : rootDir;\n const entries = await readdir(currentDir, { withFileTypes: true });\n\n for (const entry of entries) {\n if (entry.name.startsWith('_')) continue;\n\n const entryRelative = relativePath ? `${relativePath}/${entry.name}` : entry.name;\n\n if (entry.isDirectory()) {\n await walk(rootDir, entryRelative, files, staticFiles);\n } else {\n const content = await readFile(join(currentDir, entry.name), 'utf-8');\n if (entry.name.endsWith('.hbs')) {\n const outputPath = entryRelative.replace(/\\.hbs$/, '');\n files.set(outputPath, content);\n } else {\n staticFiles.set(entryRelative, content);\n }\n }\n }\n}\n","/**\n * Shared error handling for CLI commands.\n *\n * Provides consistent error output and exit code mapping across all\n * tfy-init tpl commands.\n */\n\nimport { EngineError, EngineErrorCode } from '@truefoundry/tfy-infra-engine';\nimport * as output from './output.js';\n\n/**\n * Standard exit codes shared across all commands.\n */\nexport const EXIT_CODES = {\n SUCCESS: 0,\n VALIDATION_ERROR: 1,\n FETCH_ERROR: 2,\n RENDER_ERROR: 3,\n FORMAT_ERROR: 4,\n DRIFT_BLOCKED: 5,\n} as const;\n\n/**\n * Handle an error from a CLI command and exit with the appropriate code.\n *\n * Provides consistent behavior:\n * - Displays the error message\n * - Shows validation error details when available\n * - Shows engine error details in verbose mode\n * - Maps EngineErrorCode to standardized exit codes\n * - Shows format hint for tofu-related errors\n *\n * @param error - The caught error\n * @param fallbackExitCode - Exit code for unrecognized errors (default: RENDER_ERROR)\n */\nexport function handleEngineError(\n error: unknown,\n fallbackExitCode: number = EXIT_CODES.RENDER_ERROR\n): never {\n if (error instanceof EngineError) {\n output.error(error.message);\n\n // Show engine error details in verbose mode\n if (output.getVerbosity() === 'verbose' && error.details) {\n output.info('');\n output.info('Details:');\n for (const [key, value] of Object.entries(error.details)) {\n if (key !== 'errors') {\n output.info(` ${key}: ${JSON.stringify(value)}`);\n }\n }\n }\n\n // Show validation error list when available\n if (error.details?.['errors']) {\n output.info('');\n output.info('Errors:');\n output.info(\n output.formatValidationErrors(\n error.details['errors'] as Array<{ path: string; message: string }>\n )\n );\n }\n\n // Map error codes to exit codes\n switch (error.code) {\n case EngineErrorCode.INPUT_VALIDATION_FAILED:\n case EngineErrorCode.SCHEMA_INVALID:\n case EngineErrorCode.ENVELOPE_VALIDATION_FAILED:\n process.exit(EXIT_CODES.VALIDATION_ERROR);\n break;\n\n case EngineErrorCode.TEMPLATE_NOT_FOUND:\n case EngineErrorCode.FILE_NOT_FOUND:\n case EngineErrorCode.NETWORK_ERROR:\n process.exit(EXIT_CODES.FETCH_ERROR);\n break;\n\n case EngineErrorCode.TOFU_NOT_FOUND:\n case EngineErrorCode.TOFU_FMT_FAILED:\n output.info('');\n output.info('Run with --skip-format to skip formatting.');\n process.exit(EXIT_CODES.FORMAT_ERROR);\n break;\n\n case EngineErrorCode.MANIFEST_PARSE_ERROR:\n process.exit(EXIT_CODES.VALIDATION_ERROR);\n break;\n\n default:\n process.exit(fallbackExitCode);\n }\n }\n\n // Generic (non-engine) error\n output.error((error as Error).message);\n if (output.getVerbosity() === 'verbose') {\n console.error((error as Error).stack);\n }\n process.exit(fallbackExitCode);\n}\n","/**\n * Output formatting utilities for CLI.\n *\n * Supports a JSON mode where human-readable output is redirected to stderr\n * so that stdout remains clean for structured machine output via `data()`.\n */\n\nimport type { DriftType } from '@truefoundry/tfy-infra-engine';\n\n/* eslint-disable no-console */\n\nexport type Verbosity = 'quiet' | 'normal' | 'verbose';\n\nlet currentVerbosity: Verbosity = 'normal';\nlet jsonMode = false;\n\nexport function setVerbosity(level: Verbosity): void {\n currentVerbosity = level;\n}\n\nexport function getVerbosity(): Verbosity {\n return currentVerbosity;\n}\n\n/**\n * Enable JSON mode: human-readable output routes to stderr,\n * structured output goes to stdout via `data()`.\n */\nexport function setJsonMode(enabled: boolean): void {\n jsonMode = enabled;\n}\n\nexport function getJsonMode(): boolean {\n return jsonMode;\n}\n\n/**\n * Write structured data to stdout. Use for JSON payloads\n * that machine consumers will parse.\n */\nexport function data(message: string): void {\n process.stdout.write(message + '\\n');\n}\n\nexport function success(message: string): void {\n if (currentVerbosity !== 'quiet') {\n const write = jsonMode ? console.error : console.log;\n write(`✓ ${message}`);\n }\n}\n\nexport function error(message: string): void {\n console.error(`✗ ${message}`);\n}\n\nexport function info(message: string): void {\n if (currentVerbosity !== 'quiet') {\n const write = jsonMode ? console.error : console.log;\n write(message);\n }\n}\n\nexport function verbose(message: string): void {\n if (currentVerbosity === 'verbose') {\n const write = jsonMode ? console.error : console.log;\n write(` ${message}`);\n }\n}\n\nexport function warn(message: string): void {\n if (currentVerbosity !== 'quiet') {\n console.warn(`⚠ ${message}`);\n }\n}\n\nexport function formatSize(bytes: number): string {\n if (bytes < 1024) return `${bytes} B`;\n if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;\n return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;\n}\n\nexport function formatValidationErrors(errors: Array<{ path: string; message: string }>): string {\n return errors.map((e) => ` • ${e.path}: ${e.message}`).join('\\n');\n}\n\nexport function driftTypeTag(type: DriftType): string {\n switch (type) {\n case 'content_drift':\n return 'DRIFTED';\n case 'missing_file':\n return 'MISSING';\n case 'unexpected_file':\n return 'UNEXPECTED';\n case 'metadata_inconsistency':\n return 'INCONSISTENT';\n case 'source_mismatch':\n return 'SOURCE MISMATCH';\n }\n}\n\nexport function printFileList(files: Array<{ path: string; size: number }>): void {\n if (currentVerbosity === 'quiet') return;\n\n const write = jsonMode ? console.error : console.log;\n write('Files:');\n for (const file of files) {\n write(` - ${file.path} (${formatSize(file.size)})`);\n }\n}\n","/**\n * tfy-init tpl render command\n *\n * Unified render command that handles fresh install, upgrade, and\n * manifest re-generation via auto-detection.\n *\n * Config mode auto-detects upgrade when manifest.json exists in the\n * output directory. Manifest mode always performs a fresh install\n * (disaster recovery). Dev mode renders from a local template directory.\n */\n\nimport { Command } from 'commander';\nimport { createInterface } from 'node:readline';\nimport { readFile, readdir, access } from 'node:fs/promises';\nimport { join, resolve } from 'node:path';\nimport * as yaml from 'js-yaml';\nimport { createEngine } from '@truefoundry/tfy-infra-engine';\nimport type {\n Envelope,\n InstallResult,\n UpgradeResult,\n DriftReport,\n FileMap,\n} from '@truefoundry/tfy-infra-engine';\nimport { createResolverRegistry } from '../../resolvers/index.js';\nimport { loadConfig } from '../../utils/config-loader.js';\nimport { loadManifest, envelopeFromManifest } from '../../utils/manifest-loader.js';\nimport { readDirectoryToFileMap } from '../../utils/file-reader.js';\nimport { writeEngineOutput } from '../../utils/file-writer.js';\nimport { handleEngineError, EXIT_CODES } from '../../utils/error-handler.js';\nimport * as output from '../../utils/output.js';\nimport { driftTypeTag } from '../../utils/output.js';\nimport { findDefaultFixture } from '../../utils/fs-helpers.js';\nimport { generateUnifiedDiff, type DiffEntry } from '../../utils/diff.js';\n\nexport { EXIT_CODES };\n\nexport interface RenderArgs {\n config?: string;\n directory?: string;\n fromManifest?: string;\n}\n\nexport function validateRenderArgs(args: RenderArgs): void {\n const modes = [args.config, args.fromManifest].filter(Boolean);\n if (modes.length > 1) {\n throw new Error('--config and --from-manifest are mutually exclusive. Use one or the other.');\n }\n if (args.config && args.directory !== '.') {\n throw new Error('--config and --directory are mutually exclusive. Use one or the other.');\n }\n}\n\nexport function createRenderCommand(): Command {\n const cmd = new Command('render')\n .description('Render a template to HCL files')\n .option('-c, --config <path>', 'Path to envelope config file (YAML or JSON)')\n .option('-d, --directory <dir>', 'Path to template version directory', '.')\n .option('-f, --fixture <path>', 'Input file to use (when using --directory)')\n .option('-i, --input <key=value>', 'Inline input override (can be repeated)', collect, [])\n .requiredOption('-o, --output <dir>', 'Output directory')\n .option('--from-manifest <path>', 'Path to manifest.json to use as input source')\n .option('--force', 'Proceed even if managed files have drifted (upgrade mode)')\n .option('--allow-source-change', 'Allow upgrade when template source has changed')\n .option('--dry-run', 'Show what would change without writing files')\n .option('--json', 'Output result as structured JSON')\n .option('--skip-format', 'Skip tofu fmt formatting')\n .option('--no-cache', 'Force re-fetch template')\n .option('--timeout <ms>', 'Network timeout in milliseconds', '30000')\n .option('--intent-id <id>', 'Cluster identity for integrity tracking')\n .action(async (options: RenderOptions) => {\n try {\n if (options.config) options.config = resolve(options.config);\n if (options.fromManifest) options.fromManifest = resolve(options.fromManifest);\n\n validateRenderArgs({\n config: options.config,\n directory: options.directory,\n fromManifest: options.fromManifest,\n });\n const exitCode = await runRender(options);\n process.exit(exitCode);\n } catch (error) {\n handleEngineError(error);\n }\n });\n\n return cmd;\n}\n\nfunction collect(value: string, previous: string[]): string[] {\n return [...previous, value];\n}\n\ninterface RenderOptions {\n config?: string;\n directory: string;\n fixture?: string;\n input?: string[];\n output: string;\n fromManifest?: string;\n force?: boolean;\n allowSourceChange?: boolean;\n dryRun?: boolean;\n json?: boolean;\n skipFormat?: boolean;\n cache?: boolean;\n timeout: string;\n intentId?: string;\n}\n\nexport { findDefaultFixture } from '../../utils/fs-helpers.js';\n\nexport function parseInputOverrides(inputs?: string[]): Record<string, string> {\n const result: Record<string, string> = {};\n if (!inputs) return result;\n\n for (const input of inputs) {\n const eqIndex = input.indexOf('=');\n if (eqIndex > 0) {\n const key = input.substring(0, eqIndex);\n const value = input.substring(eqIndex + 1);\n result[key] = value;\n }\n }\n\n return result;\n}\n\nasync function runRender(options: RenderOptions): Promise<number> {\n if (options.json) {\n output.setJsonMode(true);\n }\n\n const outputDir = resolve(options.output);\n const timeout = parseInt(options.timeout, 10);\n\n if (options.fromManifest) {\n return runRenderFromManifest(options.fromManifest, options, outputDir, timeout);\n }\n\n if (options.config) {\n const manifestPath = join(outputDir, 'manifest.json');\n if (await fileExists(manifestPath)) {\n return runRenderFromConfigUpgrade(options.config, options, outputDir, timeout, manifestPath);\n }\n return runRenderFromConfig(options.config, options, outputDir, timeout);\n }\n\n const templateDir = resolve(options.directory);\n return runRenderFromDirectory(templateDir, options, outputDir, timeout);\n}\n\nasync function fileExists(path: string): Promise<boolean> {\n try {\n await access(path);\n return true;\n } catch {\n return false;\n }\n}\n\nasync function confirmPrompt(message: string): Promise<boolean> {\n if (!process.stdin.isTTY) return false;\n const rl = createInterface({ input: process.stdin, output: process.stderr });\n return new Promise((resolve) => {\n rl.question(`${message} [y/N] `, (answer) => {\n rl.close();\n resolve(answer.trim().toLowerCase() === 'y');\n });\n });\n}\n\nasync function countExistingFiles(dir: string): Promise<number> {\n try {\n const entries = await readdir(dir, { withFileTypes: true });\n return entries.filter((e) => e.isFile() && e.name !== 'config.yaml' && e.name !== 'config.json')\n .length;\n } catch {\n return 0;\n }\n}\n\nasync function guardOverwrite(outputDir: string, options: RenderOptions): Promise<number | null> {\n if (options.force) return null;\n const count = await countExistingFiles(outputDir);\n if (count === 0) return null;\n\n output.warn(`Output directory contains ${count} existing files that will be overwritten.`);\n const confirmed = await confirmPrompt('Proceed?');\n if (confirmed) return null;\n\n if (options.json) {\n output.data(\n JSON.stringify(\n { aborted: true, reason: 'overwrite_confirmation', existingFiles: count },\n null,\n 2\n )\n );\n } else {\n output.info('Aborted. Use --force to skip confirmation.');\n }\n return EXIT_CODES.VALIDATION_ERROR;\n}\n\n/**\n * Render from a manifest file (re-render / disaster recovery).\n */\nasync function runRenderFromManifest(\n manifestPath: string,\n options: RenderOptions,\n outputDir: string,\n timeout: number\n): Promise<number> {\n output.verbose(`Loading manifest from ${manifestPath}`);\n const manifest = await loadManifest(manifestPath);\n const rawConfig = envelopeFromManifest(manifest);\n\n if (options.intentId) {\n rawConfig.intentId = options.intentId;\n }\n\n output.success(`Template: ${rawConfig.template}`);\n output.verbose(`Intent ID: ${rawConfig.intentId}`);\n output.verbose('Resolving template...');\n\n const resolverRegistry = createResolverRegistry();\n const template = await resolverRegistry.resolve(rawConfig.template, {\n noCache: options.cache === false,\n timeout,\n });\n\n const envelope: Envelope = {\n template,\n inputs: rawConfig.inputs,\n intentId: rawConfig.intentId,\n platformPrefix: rawConfig.platformPrefix,\n options: {\n skipFormat: options.skipFormat,\n },\n };\n\n output.verbose('Re-rendering from manifest...');\n\n const engine = createEngine();\n const result = await engine.install(envelope);\n\n if (options.dryRun) {\n return await handleInstallDryRun(result, outputDir, options.json);\n }\n\n const aborted = await guardOverwrite(outputDir, options);\n if (aborted !== null) return aborted;\n\n output.success('Inputs validated');\n output.success(`Generated ${result.files.size - 1} files + manifest.json`);\n\n if (!options.skipFormat) {\n output.success('Formatted with tofu fmt');\n }\n\n await writeEngineOutput(outputDir, result, 'install');\n\n printInstallResult(result, outputDir, options.json);\n\n return EXIT_CODES.SUCCESS;\n}\n\n/**\n * Render from a template directory with fixture inputs.\n */\nasync function runRenderFromDirectory(\n templateDir: string,\n options: RenderOptions,\n outputDir: string,\n timeout: number\n): Promise<number> {\n const templateUri = `file://${templateDir}`;\n\n let fixturePath: string | null = options.fixture ? resolve(templateDir, options.fixture) : null;\n\n if (fixturePath) {\n if (!(await fileExists(fixturePath))) {\n output.error(`Fixture not found: ${options.fixture}`);\n return EXIT_CODES.VALIDATION_ERROR;\n }\n } else {\n fixturePath = await findDefaultFixture(templateDir);\n if (!fixturePath) {\n output.error('No default fixture found at tests/default/inputs.yaml');\n output.info('Either create a default fixture or use --fixture to specify one.');\n return EXIT_CODES.VALIDATION_ERROR;\n }\n }\n\n const fixtureContent = await readFile(fixturePath, 'utf-8');\n let inputs = yaml.load(fixtureContent) as Record<string, unknown>;\n\n const overrides = parseInputOverrides(options.input);\n inputs = { ...inputs, ...overrides };\n\n const intentId =\n options.intentId ?? (inputs['intentId'] as string | undefined) ?? `local-${Date.now()}`;\n\n output.success(`Template: ${templateUri}`);\n output.verbose(`Fixture: ${fixturePath}`);\n output.verbose(`Intent ID: ${intentId}`);\n\n output.verbose('Resolving template...');\n const resolverRegistry = createResolverRegistry();\n const template = await resolverRegistry.resolve(templateUri, {\n noCache: options.cache === false,\n timeout,\n });\n\n const envelope: Envelope = {\n template,\n inputs,\n intentId,\n options: {\n skipFormat: options.skipFormat,\n },\n };\n\n const engine = createEngine();\n\n output.verbose('Validating inputs...');\n const result = await engine.install(envelope);\n\n if (options.dryRun) {\n return await handleInstallDryRun(result, outputDir, options.json);\n }\n\n const aborted = await guardOverwrite(outputDir, options);\n if (aborted !== null) return aborted;\n\n output.success('Inputs validated');\n output.success(`Generated ${result.files.size - 1} files + manifest.json`);\n\n if (!options.skipFormat) {\n output.success('Formatted with tofu fmt');\n }\n\n await writeEngineOutput(outputDir, result, 'install');\n\n printInstallResult(result, outputDir, options.json);\n\n return EXIT_CODES.SUCCESS;\n}\n\n/**\n * Render from a config file (fresh install).\n */\nasync function runRenderFromConfig(\n configFile: string,\n options: RenderOptions,\n outputDir: string,\n timeout: number\n): Promise<number> {\n output.verbose(`Loading config from ${configFile}`);\n const rawConfig = await loadConfig(configFile);\n\n if (options.intentId) {\n rawConfig.intentId = options.intentId;\n }\n\n if (options.input && options.input.length > 0) {\n const overrides = parseInputOverrides(options.input);\n rawConfig.inputs = { ...(rawConfig.inputs || {}), ...overrides };\n }\n\n output.success(`Template: ${rawConfig.template}`);\n output.verbose(`Intent ID: ${rawConfig.intentId}`);\n\n output.verbose('Resolving template...');\n const resolverRegistry = createResolverRegistry();\n const template = await resolverRegistry.resolve(rawConfig.template, {\n noCache: options.cache === false,\n timeout,\n });\n\n const envelope: Envelope = {\n template,\n inputs: rawConfig.inputs,\n intentId: rawConfig.intentId,\n platformPrefix: rawConfig.platformPrefix,\n options: {\n skipFormat: options.skipFormat ?? rawConfig.options?.skipFormat,\n },\n };\n\n const engine = createEngine();\n\n output.verbose('Validating inputs...');\n const result = await engine.install(envelope);\n\n if (options.dryRun) {\n return await handleInstallDryRun(result, outputDir, options.json);\n }\n\n const aborted = await guardOverwrite(outputDir, options);\n if (aborted !== null) return aborted;\n\n output.success('Inputs validated');\n output.success(`Generated ${result.files.size - 1} files + manifest.json`);\n\n if (!envelope.options?.skipFormat) {\n output.success('Formatted with tofu fmt');\n }\n\n await writeEngineOutput(outputDir, result, 'install');\n\n printInstallResult(result, outputDir, options.json);\n\n return EXIT_CODES.SUCCESS;\n}\n\n/**\n * Upgrade from a config file when manifest.json exists in the output directory.\n * Auto-detected when config mode finds an existing manifest.\n */\nasync function runRenderFromConfigUpgrade(\n configFile: string,\n options: RenderOptions,\n outputDir: string,\n timeout: number,\n manifestPath: string\n): Promise<number> {\n output.verbose(`Existing manifest detected at ${manifestPath} — running upgrade`);\n\n const previousManifest = await loadManifest(manifestPath);\n\n output.verbose(`Reading files from ${outputDir}`);\n const currentFiles = await readDirectoryToFileMap(outputDir, previousManifest.platformPrefix);\n\n output.verbose(`Loading config from ${configFile}`);\n const rawConfig = await loadConfig(configFile);\n\n if (options.intentId) {\n rawConfig.intentId = options.intentId;\n }\n\n if (options.input && options.input.length > 0) {\n const overrides = parseInputOverrides(options.input);\n rawConfig.inputs = { ...(rawConfig.inputs || {}), ...overrides };\n }\n\n output.success(`Template: ${rawConfig.template}`);\n output.verbose(`Intent ID: ${rawConfig.intentId}`);\n\n output.verbose('Resolving template...');\n const resolverRegistry = createResolverRegistry();\n const template = await resolverRegistry.resolve(rawConfig.template, {\n noCache: options.cache === false,\n timeout,\n });\n\n const envelope: Envelope = {\n template,\n inputs: rawConfig.inputs,\n intentId: rawConfig.intentId,\n platformPrefix: rawConfig.platformPrefix,\n options: {\n skipFormat: options.skipFormat ?? rawConfig.options?.skipFormat,\n },\n };\n\n const engine = createEngine();\n const result = await engine.upgrade(envelope, currentFiles, previousManifest);\n\n if (result.sourceBlocked && !options.allowSourceChange) {\n output.warn('Source mismatch: current files were generated from a different template source.');\n printDriftReport(result.driftReport, options.json);\n if (options.json) {\n output.data(\n JSON.stringify(\n {\n blocked: true,\n reason: 'source_mismatch',\n sourceBlocked: true,\n driftReport: { drift: !result.driftReport.valid, summary: result.driftReport.summary },\n },\n null,\n 2\n )\n );\n } else {\n output.info('Use --allow-source-change to proceed with source migration.');\n }\n return EXIT_CODES.DRIFT_BLOCKED;\n }\n if (result.sourceBlocked) {\n output.warn('Proceeding with source migration (--allow-source-change).');\n }\n\n printDriftReport(result.driftReport, options.json);\n\n if (!result.driftReport.valid && !options.force) {\n if (options.json) {\n output.data(\n JSON.stringify(\n {\n blocked: true,\n reason: 'content_drift',\n sourceBlocked: result.sourceBlocked,\n driftReport: { drift: true, summary: result.driftReport.summary },\n },\n null,\n 2\n )\n );\n } else {\n output.info('Use --force to proceed despite drift.');\n }\n return EXIT_CODES.DRIFT_BLOCKED;\n }\n if (!result.driftReport.valid) {\n output.warn('Proceeding despite drift (--force).');\n }\n\n if (options.dryRun) {\n return handleUpgradeDryRun(result, currentFiles, options.json);\n }\n\n await writeEngineOutput(outputDir, result, 'upgrade');\n\n if (options.json) {\n output.data(formatUpgradeResultJson(result));\n } else {\n output.info(formatUpgradeResult(result));\n }\n\n return EXIT_CODES.SUCCESS;\n}\n\n// --- Diff computation helpers ---\n\nasync function computeInstallDiffs(newFiles: FileMap, outputDir: string): Promise<DiffEntry[]> {\n const diffs: DiffEntry[] = [];\n for (const [filename, entry] of newFiles) {\n if (filename === 'manifest.json') continue;\n let existing = '';\n try {\n existing = await readFile(join(outputDir, filename), 'utf-8');\n } catch {\n /* new file */\n }\n if (existing === entry.content) continue;\n diffs.push({ filename, diff: generateUnifiedDiff(existing, entry.content, filename) });\n }\n return diffs;\n}\n\nfunction computeUpgradeDiffs(newFiles: FileMap, currentFiles: FileMap): DiffEntry[] {\n const diffs: DiffEntry[] = [];\n for (const [filename, entry] of newFiles) {\n if (filename === 'manifest.json') continue;\n const current = currentFiles.get(filename);\n const currentContent = current?.content ?? '';\n if (currentContent === entry.content) continue;\n diffs.push({ filename, diff: generateUnifiedDiff(currentContent, entry.content, filename) });\n }\n return diffs;\n}\n\nfunction printDiffs(diffs: DiffEntry[]): void {\n if (diffs.length === 0) {\n output.info('\\nNo file changes detected.');\n return;\n }\n output.info(`\\n${diffs.length} file(s) changed:\\n`);\n for (const d of diffs) {\n output.info(d.diff);\n }\n}\n\n// --- Drift report helper ---\n\nfunction printDriftReport(report: DriftReport, json?: boolean): void {\n if (json) {\n output.data(\n JSON.stringify(\n {\n drift: !report.valid,\n summary: report.summary,\n entries: report.entries.map((e) => ({\n path: e.path,\n type: e.type,\n details: e.details,\n })),\n },\n null,\n 2\n )\n );\n return;\n }\n if (report.valid) {\n output.success('No drift detected in managed files.');\n return;\n }\n const total =\n report.summary.driftedFiles +\n report.summary.missingFiles +\n report.summary.unexpectedFiles +\n report.summary.inconsistentFiles;\n output.warn(`Drift detected in ${total} managed files:`);\n for (const entry of report.entries) {\n output.info(` ${driftTypeTag(entry.type)} ${entry.path}`);\n output.info(` ${entry.details}`);\n }\n output.info('');\n output.info('Use --force to override and proceed with upgrade.');\n}\n\n// --- Install mode output helpers ---\n\nasync function handleInstallDryRun(\n result: InstallResult,\n outputDir: string,\n json?: boolean\n): Promise<number> {\n const diffs = await computeInstallDiffs(result.files, outputDir);\n if (json) {\n output.data(\n JSON.stringify(\n {\n dryRun: true,\n aggregateHash: result.manifest.aggregateHash,\n templateSource: result.manifest.templateSource,\n templateVersion: result.manifest.templateVersion,\n fileCount: result.files.size,\n files: Array.from(result.files.entries()).map(([path, entry]) => ({\n path,\n size: Buffer.byteLength(entry.content, 'utf-8'),\n })),\n changes: diffs.map((d) => ({ filename: d.filename, diff: d.diff })),\n },\n null,\n 2\n )\n );\n } else {\n output.info('Dry run — no files written.');\n output.info('');\n output.info(`Aggregate Hash: ${result.manifest.aggregateHash}`);\n output.info(`Template: ${result.manifest.templateSource}`);\n output.info(`Version: ${result.manifest.templateVersion}`);\n output.info(`Files: ${result.files.size}`);\n printDiffs(diffs);\n }\n return EXIT_CODES.SUCCESS;\n}\n\nfunction printInstallResult(result: InstallResult, outputDir: string, json?: boolean): void {\n if (json) {\n output.data(\n JSON.stringify(\n {\n success: true,\n aggregateHash: result.manifest.aggregateHash,\n fileCount: result.files.size,\n outputDir,\n files: Array.from(result.files.entries()).map(([path, entry]) => ({\n path,\n size: Buffer.byteLength(entry.content, 'utf-8'),\n })),\n },\n null,\n 2\n )\n );\n } else {\n const fileList: Array<{ path: string; size: number }> = [];\n for (const [filePath, entry] of result.files) {\n fileList.push({\n path: filePath,\n size: Buffer.byteLength(entry.content, 'utf-8'),\n });\n }\n output.info('');\n output.info(`Output written to: ${outputDir}`);\n output.info(`Aggregate Hash: ${result.manifest.aggregateHash}`);\n output.printFileList(fileList);\n }\n}\n\n// --- Upgrade mode output helpers ---\n\nfunction handleUpgradeDryRun(result: UpgradeResult, currentFiles: FileMap, json?: boolean): number {\n const diffs = computeUpgradeDiffs(result.files, currentFiles);\n if (json) {\n output.data(\n JSON.stringify(\n {\n dryRun: true,\n templateSource: result.manifest.templateSource,\n templateVersion: result.manifest.templateVersion,\n aggregateHash: result.manifest.aggregateHash,\n driftReport: result.driftReport,\n files: Array.from(result.files.entries()).map(([path, entry]) => ({\n path,\n zone: entry.zone,\n })),\n changes: diffs.map((d) => ({ filename: d.filename, diff: d.diff })),\n },\n null,\n 2\n )\n );\n } else {\n output.info('Dry run — no files written.');\n output.info('');\n output.info(formatUpgradeResult(result));\n printDiffs(diffs);\n }\n return EXIT_CODES.SUCCESS;\n}\n\nfunction formatUpgradeResult(result: UpgradeResult): string {\n const lines: string[] = [];\n lines.push(\n `✓ Upgrade complete: ${result.manifest.templateSource} v${result.manifest.templateVersion}`\n );\n lines.push('');\n let platformCount = 0;\n let userCount = 0;\n for (const [, entry] of result.files) {\n if (entry.zone === 'platform') platformCount++;\n else userCount++;\n }\n lines.push(` Updated: ${platformCount} files (Platform Zone)`);\n lines.push(` Unchanged: ${userCount} files (User Zone)`);\n lines.push('');\n lines.push(`Aggregate Hash: ${result.manifest.aggregateHash}`);\n return lines.join('\\n');\n}\n\nfunction formatUpgradeResultJson(result: UpgradeResult): string {\n return JSON.stringify(\n {\n success: true,\n templateSource: result.manifest.templateSource,\n templateVersion: result.manifest.templateVersion,\n aggregateHash: result.manifest.aggregateHash,\n driftReport: result.driftReport,\n files: Array.from(result.files.entries()).map(([path, entry]) => ({\n path,\n zone: entry.zone,\n })),\n },\n null,\n 2\n );\n}\n","/**\n * Resolver registry for managing template source resolvers.\n *\n * Supports file:// and https://*.json bundle URIs.\n */\n\nimport type { Resolver } from './base.js';\nimport type { Template, ResolverOptions } from '@truefoundry/tfy-infra-engine';\nimport { EngineError, EngineErrorCode } from '@truefoundry/tfy-infra-engine';\nimport { FileResolver } from './file.js';\nimport { HttpsBundleResolver } from './https.js';\nimport { TemplateCache } from '../cache/template-cache.js';\n\nexport type { Resolver } from './base.js';\nexport { FileResolver } from './file.js';\nexport { HttpsBundleResolver } from './https.js';\nexport { TemplateCache, createTemplateCache, getDefaultCacheDir } from '../cache/template-cache.js';\n\n/**\n * Registry for template resolvers.\n * Routes template URIs to the appropriate resolver based on scheme.\n */\nexport class ResolverRegistry {\n private resolvers: Resolver[] = [];\n private cache: TemplateCache;\n\n constructor(resolvers?: Resolver[], cacheDir?: string) {\n this.cache = new TemplateCache(cacheDir);\n\n if (resolvers) {\n this.resolvers = resolvers;\n } else {\n this.register(new FileResolver());\n this.register(new HttpsBundleResolver());\n }\n }\n\n register(resolver: Resolver): void {\n this.resolvers.push(resolver);\n }\n\n getResolver(uri: string): Resolver | undefined {\n return this.resolvers.find((r) => r.canResolve(uri));\n }\n\n async resolve(uri: string, options?: ResolverOptions): Promise<Template> {\n const resolver = this.getResolver(uri);\n\n if (!resolver) {\n const scheme = uri.split('://')[0] || 'unknown';\n throw new EngineError(\n `No resolver found for URI scheme: ${scheme}://`,\n EngineErrorCode.TEMPLATE_NOT_FOUND,\n undefined,\n {\n uri,\n scheme,\n supportedSchemes: ['file://', 'https://*.json'],\n }\n );\n }\n\n const template = await resolver.resolve(uri, options);\n\n // Cache the result (unless it's a file:// URI)\n if (!uri.startsWith('file://')) {\n await this.cache.set(template);\n }\n\n return template;\n }\n\n canResolve(uri: string): boolean {\n return this.resolvers.some((r) => r.canResolve(uri));\n }\n\n getSupportedSchemes(): string[] {\n return ['file://', 'https://*.json'];\n }\n\n async clearCache(uri?: string): Promise<void> {\n if (uri) {\n await this.cache.delete(uri);\n } else {\n await this.cache.clear();\n }\n }\n\n async getCacheStats(): Promise<{ sources: number; templates: number; size: number }> {\n return this.cache.stats();\n }\n}\n\n/**\n * Create a default resolver registry with standard resolvers (file + https bundle).\n */\nexport function createResolverRegistry(cacheDir?: string): ResolverRegistry {\n return new ResolverRegistry(undefined, cacheDir);\n}\n","/**\n * File resolver for local template sources (file://).\n *\n * Supports bundle.json — single-file distribution format.\n *\n * Detection order: if the URI points directly at a .json file, treat it as a\n * bundle; if it's a directory containing bundle.json, load the bundle.\n */\n\nimport { readFile, stat } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport type { Resolver } from './base.js';\nimport type { Template, ResolverOptions, VersionInfo } from '@truefoundry/tfy-infra-engine';\nimport { EngineError, EngineErrorCode } from '@truefoundry/tfy-infra-engine';\n\n/**\n * Resolver for file:// URIs.\n * Loads templates from the local filesystem.\n */\nexport class FileResolver implements Resolver {\n canResolve(uri: string): boolean {\n return uri.startsWith('file://');\n }\n\n async resolve(uri: string, _options?: ResolverOptions): Promise<Template> {\n const fsPath = this.uriToPath(uri);\n\n try {\n const info = await stat(fsPath);\n\n if (info.isFile() && fsPath.endsWith('.json')) {\n return this.resolveBundle(fsPath, uri);\n }\n\n if (info.isDirectory()) {\n const bundlePath = join(fsPath, 'bundle.json');\n if (await this.fileExists(bundlePath)) {\n return this.resolveBundle(bundlePath, uri);\n }\n throw new EngineError(\n `bundle.json not found in ${fsPath}`,\n EngineErrorCode.BUNDLE_NOT_FOUND,\n undefined,\n { path: fsPath }\n );\n }\n\n throw new EngineError(\n `Not a file or directory: ${fsPath}`,\n EngineErrorCode.TEMPLATE_NOT_FOUND,\n undefined,\n { path: fsPath }\n );\n } catch (error) {\n if (error instanceof EngineError) {\n throw error;\n }\n\n const nodeError = error as NodeJS.ErrnoException;\n if (nodeError.code === 'ENOENT') {\n throw new EngineError(\n `Template not found: ${fsPath}`,\n EngineErrorCode.FILE_NOT_FOUND,\n error as Error,\n { path: fsPath, originalCode: nodeError.code }\n );\n }\n\n if (nodeError.code === 'EACCES') {\n throw new EngineError(\n `Permission denied: ${fsPath}`,\n EngineErrorCode.FILE_NOT_FOUND,\n error as Error,\n { path: fsPath, originalCode: nodeError.code }\n );\n }\n\n throw new EngineError(\n `Failed to load template from ${fsPath}: ${(error as Error).message}`,\n EngineErrorCode.FILE_NOT_FOUND,\n error as Error,\n { path: fsPath }\n );\n }\n }\n\n private async resolveBundle(bundlePath: string, uri: string): Promise<Template> {\n const content = await readFile(bundlePath, 'utf-8');\n\n let raw: Record<string, unknown>;\n try {\n raw = JSON.parse(content) as Record<string, unknown>;\n } catch (error) {\n throw new EngineError(\n `Failed to parse bundle.json: ${(error as Error).message}`,\n EngineErrorCode.BUNDLE_INVALID,\n error as Error,\n { path: bundlePath }\n );\n }\n\n const rawMetadata = raw['metadata'] as Record<string, unknown> | undefined;\n if (!rawMetadata || typeof rawMetadata !== 'object') {\n throw new EngineError(\n 'bundle.json must contain a \"metadata\" object',\n EngineErrorCode.BUNDLE_INVALID,\n undefined,\n { path: bundlePath }\n );\n }\n if (!rawMetadata['name'] || !rawMetadata['version']) {\n throw new EngineError(\n 'bundle.json metadata must contain \"name\" and \"version\"',\n EngineErrorCode.BUNDLE_INVALID,\n undefined,\n { path: bundlePath }\n );\n }\n\n const metadata = {\n name: rawMetadata['name'] as string,\n version: rawMetadata['version'] as string,\n ...(rawMetadata['description'] ? { description: rawMetadata['description'] as string } : {}),\n };\n\n const jsonSchema = raw['jsonSchema'] as Record<string, unknown>;\n if (!jsonSchema || typeof jsonSchema !== 'object') {\n throw new EngineError(\n 'bundle.json must contain a \"jsonSchema\" object',\n EngineErrorCode.BUNDLE_INVALID,\n undefined,\n { path: bundlePath }\n );\n }\n\n const files = new Map(Object.entries((raw['files'] as Record<string, string>) ?? {}));\n const staticFiles = new Map(\n Object.entries((raw['staticFiles'] as Record<string, string>) ?? {})\n );\n\n if (files.size === 0 && staticFiles.size === 0) {\n throw new EngineError(\n `Bundle contains no template files: ${bundlePath}`,\n EngineErrorCode.TEMPLATE_NOT_FOUND,\n undefined,\n { path: bundlePath }\n );\n }\n\n const version: VersionInfo = { semver: metadata.version };\n\n return { metadata, jsonSchema, files, staticFiles, source: uri, version };\n }\n\n private uriToPath(uri: string): string {\n if (uri.startsWith('file:///')) {\n return uri.substring(7);\n }\n if (uri.startsWith('file://')) {\n return uri.substring(7);\n }\n return uri;\n }\n\n private async fileExists(path: string): Promise<boolean> {\n try {\n await stat(path);\n return true;\n } catch {\n return false;\n }\n }\n}\n","/**\n * HTTPS bundle resolver for pre-built template bundles (https://*.json).\n *\n * Fetches a bundle JSON file over HTTPS and unpacks it into a Template.\n * Automatically attaches a Bearer token for Artifactory hosts when\n * JFROG_ACCESS_TOKEN is present in the environment.\n */\n\nimport type { Resolver } from './base.js';\nimport type { Template, ResolverOptions, VersionInfo } from '@truefoundry/tfy-infra-engine';\nimport { validateBundle, EngineError, EngineErrorCode } from '@truefoundry/tfy-infra-engine';\nimport { mergeResolverOptions, withRetry, withTimeout } from './base.js';\n\nconst JFROG_TOKEN_ENV = 'JFROG_ACCESS_TOKEN';\n\nfunction isArtifactoryHost(uri: string): boolean {\n try {\n const { hostname } = new URL(uri);\n return hostname.includes('jfrog');\n } catch {\n return false;\n }\n}\n\nfunction buildHeaders(uri: string): Record<string, string> {\n const headers: Record<string, string> = {\n 'User-Agent': 'tfy-infra-cli',\n Accept: 'application/json',\n };\n\n if (isArtifactoryHost(uri)) {\n const token = process.env[JFROG_TOKEN_ENV];\n if (token) {\n headers['Authorization'] = `Bearer ${token}`;\n }\n }\n\n return headers;\n}\n\nasync function fetchBundle(uri: string, headers: Record<string, string>): Promise<Response> {\n const response = await fetch(uri, { headers });\n\n if (response.status === 401 || response.status === 403) {\n const hint = isArtifactoryHost(uri)\n ? ` Set ${JFROG_TOKEN_ENV} in your environment to authenticate.`\n : '';\n throw new EngineError(\n `Authentication failed (HTTP ${response.status}) for ${uri}.${hint}`,\n EngineErrorCode.HTTPS_AUTH_FAILED,\n undefined,\n { uri, status: response.status }\n );\n }\n\n if (!response.ok) {\n throw new EngineError(\n `Bundle download failed: HTTP ${response.status} for ${uri}`,\n EngineErrorCode.NETWORK_ERROR,\n undefined,\n { uri, status: response.status }\n );\n }\n\n return response;\n}\n\nasync function unpackBundle(response: Response, uri: string): Promise<Template> {\n const body: unknown = await response.json();\n const raw = body as Record<string, unknown>;\n\n const { metadata, jsonSchema } = validateBundle(raw);\n\n const files = new Map(Object.entries((raw['files'] as Record<string, string>) ?? {}));\n const staticFiles = new Map(Object.entries((raw['staticFiles'] as Record<string, string>) ?? {}));\n\n if (files.size === 0 && staticFiles.size === 0) {\n throw new EngineError(\n `Bundle contains no template files: ${uri}`,\n EngineErrorCode.TEMPLATE_NOT_FOUND,\n undefined,\n { uri }\n );\n }\n\n const version: VersionInfo = { semver: metadata.version };\n\n return { metadata, jsonSchema, files, staticFiles, source: uri, version };\n}\n\n/**\n * Resolver for HTTPS bundle URLs (https://.../*.json).\n */\nexport class HttpsBundleResolver implements Resolver {\n canResolve(uri: string): boolean {\n return uri.startsWith('https://') && uri.toLowerCase().endsWith('.json');\n }\n\n async resolve(uri: string, options?: ResolverOptions): Promise<Template> {\n const opts = mergeResolverOptions(options);\n const headers = buildHeaders(uri);\n\n const response = await withTimeout(\n withRetry(() => fetchBundle(uri, headers), opts.retries),\n opts.timeout,\n `HTTPS bundle download timed out after ${opts.timeout}ms`\n );\n\n return unpackBundle(response, uri);\n }\n}\n","/**\n * Base resolver interface and common utilities.\n *\n * Moved from @truefoundry/tfy-infra-engine (006).\n * Imports reference the engine package for shared types.\n */\n\nimport type { Template, ResolverOptions } from '@truefoundry/tfy-infra-engine';\n\n/**\n * Interface for template resolvers.\n * Each resolver handles a specific URI scheme (file://, https://).\n */\nexport interface Resolver {\n canResolve(uri: string): boolean;\n resolve(uri: string, options?: ResolverOptions): Promise<Template>;\n}\n\n/**\n * Default resolver options.\n */\nexport const DEFAULT_RESOLVER_OPTIONS: Required<ResolverOptions> = {\n timeout: 30000,\n retries: 3,\n noCache: false,\n};\n\n/**\n * Merge resolver options with defaults.\n */\nexport function mergeResolverOptions(options?: ResolverOptions): Required<ResolverOptions> {\n return {\n ...DEFAULT_RESOLVER_OPTIONS,\n ...options,\n };\n}\n\n/**\n * Retry an async operation with exponential backoff.\n */\nexport async function withRetry<T>(\n fn: () => Promise<T>,\n retries: number,\n baseDelay = 1000\n): Promise<T> {\n let lastError: unknown;\n\n for (let attempt = 0; attempt <= retries; attempt++) {\n try {\n return await fn();\n } catch (error) {\n lastError = error;\n\n if (attempt < retries) {\n const delay = baseDelay * Math.pow(2, attempt) * (0.5 + Math.random() * 0.5);\n await new Promise((resolve) => setTimeout(resolve, delay));\n }\n }\n }\n\n throw lastError instanceof Error ? lastError : new Error(String(lastError));\n}\n\n/**\n * Create a timeout wrapper for promises.\n */\nexport function withTimeout<T>(promise: Promise<T>, ms: number, message?: string): Promise<T> {\n let timer: ReturnType<typeof setTimeout>;\n\n const timeout = new Promise<never>((_, reject) => {\n timer = setTimeout(() => reject(new Error(message ?? `Operation timed out after ${ms}ms`)), ms);\n });\n\n return Promise.race([promise, timeout]).finally(() => clearTimeout(timer));\n}\n","/**\n * Template cache for storing fetched templates locally.\n * Uses filesystem-based caching with version-keyed storage.\n *\n * Stores templates as bundle.json files containing all template data\n * in a single self-contained JSON file.\n */\n\nimport { mkdir, readFile, writeFile, rm, readdir, stat } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport { createHash } from 'node:crypto';\nimport type { Template, VersionInfo } from '@truefoundry/tfy-infra-engine';\nimport { validateBundle, EngineError, EngineErrorCode } from '@truefoundry/tfy-infra-engine';\n\n/**\n * Default cache directory.\n */\nexport function getDefaultCacheDir(): string {\n const home = process.env['HOME'] || process.env['USERPROFILE'] || '~';\n return join(home, '.cache', 'tfy-infra', 'templates');\n}\n\n/**\n * Template cache manager.\n */\nexport class TemplateCache {\n private readonly cacheDir: string;\n\n constructor(cacheDir?: string) {\n this.cacheDir = cacheDir ?? getDefaultCacheDir();\n }\n\n private getCacheKey(source: string): string {\n return createHash('sha256').update(source).digest('hex').substring(0, 16);\n }\n\n private getCachePath(source: string, version: string): string {\n const key = this.getCacheKey(source);\n const safeVersion = version.replace(/\\//g, '-');\n return join(this.cacheDir, key, safeVersion);\n }\n\n async has(source: string, version: string): Promise<boolean> {\n const cachePath = this.getCachePath(source, version);\n try {\n const bundlePath = join(cachePath, 'bundle.json');\n await stat(bundlePath);\n return true;\n } catch {\n return false;\n }\n }\n\n async get(source: string, version: string): Promise<Template | null> {\n const cachePath = this.getCachePath(source, version);\n\n try {\n const bundlePath = join(cachePath, 'bundle.json');\n const bundleContent = await readFile(bundlePath, 'utf-8');\n const parsed: unknown = JSON.parse(bundleContent);\n const { metadata, jsonSchema } = validateBundle(parsed);\n\n const record = parsed as Record<string, unknown>;\n const files = new Map(Object.entries((record['files'] as Record<string, string>) ?? {}));\n const staticFiles = new Map(\n Object.entries((record['staticFiles'] as Record<string, string>) ?? {})\n );\n\n const versionInfo: VersionInfo = {\n semver: metadata.version,\n };\n\n return {\n metadata,\n jsonSchema,\n files,\n staticFiles,\n source,\n version: versionInfo,\n };\n } catch {\n return null;\n }\n }\n\n async set(template: Template): Promise<void> {\n const cachePath = this.getCachePath(template.source, template.version.semver);\n\n try {\n await mkdir(cachePath, { recursive: true });\n\n const bundle = {\n metadata: {\n name: template.metadata.name,\n version: template.metadata.version,\n ...(template.metadata.description ? { description: template.metadata.description } : {}),\n },\n jsonSchema: template.jsonSchema,\n files: Object.fromEntries(template.files),\n staticFiles: Object.fromEntries(template.staticFiles),\n };\n await writeFile(join(cachePath, 'bundle.json'), JSON.stringify(bundle, null, 2), 'utf-8');\n } catch (error) {\n throw new EngineError(\n `Failed to cache template: ${(error as Error).message}`,\n EngineErrorCode.CACHE_ERROR,\n error as Error,\n { source: template.source, version: template.version.semver }\n );\n }\n }\n\n async delete(source: string, version?: string): Promise<void> {\n if (version) {\n const cachePath = this.getCachePath(source, version);\n try {\n await rm(cachePath, { recursive: true, force: true });\n } catch {\n // Ignore if doesn't exist\n }\n } else {\n const key = this.getCacheKey(source);\n const keyPath = join(this.cacheDir, key);\n try {\n await rm(keyPath, { recursive: true, force: true });\n } catch {\n // Ignore if doesn't exist\n }\n }\n }\n\n async clear(): Promise<void> {\n try {\n await rm(this.cacheDir, { recursive: true, force: true });\n } catch {\n // Ignore if doesn't exist\n }\n }\n\n async stats(): Promise<{ sources: number; templates: number; size: number }> {\n let sources = 0;\n let templates = 0;\n let size = 0;\n\n try {\n const sourceKeys = await readdir(this.cacheDir);\n sources = sourceKeys.length;\n\n for (const key of sourceKeys) {\n const keyPath = join(this.cacheDir, key);\n const versions = await readdir(keyPath);\n templates += versions.length;\n\n for (const version of versions) {\n const versionPath = join(keyPath, version);\n size += await this.dirSize(versionPath);\n }\n }\n } catch {\n // Cache doesn't exist or is empty\n }\n\n return { sources, templates, size };\n }\n\n private async dirSize(dir: string): Promise<number> {\n let total = 0;\n const entries = await readdir(dir, { withFileTypes: true });\n for (const entry of entries) {\n const fullPath = join(dir, entry.name);\n if (entry.isDirectory()) {\n total += await this.dirSize(fullPath);\n } else {\n const fileStat = await stat(fullPath);\n total += fileStat.size;\n }\n }\n return total;\n }\n}\n\n/**\n * Create a template cache instance.\n */\nexport function createTemplateCache(cacheDir?: string): TemplateCache {\n return new TemplateCache(cacheDir);\n}\n","/**\n * Configuration file loader for YAML and JSON formats.\n *\n * The CLI command is responsible for resolving the template URI before calling the engine.\n */\n\nimport { readFile } from 'node:fs/promises';\nimport * as yaml from 'js-yaml';\n\n/**\n * Raw configuration from a config file.\n * `template` is a URI string that must be resolved before passing to the engine.\n */\nexport interface RawConfig {\n template: string;\n inputs: Record<string, unknown>;\n intentId: string;\n platformPrefix?: string;\n options?: {\n skipFormat?: boolean;\n };\n}\n\n/**\n * Load a configuration file (YAML or JSON).\n *\n * @param filePath - Path to the config file\n * @returns Raw configuration with template URI string\n */\nexport async function loadConfig(filePath: string): Promise<RawConfig> {\n const content = await readFile(filePath, 'utf-8');\n\n const isJson = filePath.endsWith('.json');\n\n let parsed: Record<string, unknown>;\n try {\n if (isJson) {\n parsed = JSON.parse(content) as Record<string, unknown>;\n } else {\n parsed = yaml.load(content) as Record<string, unknown>;\n }\n } catch (error) {\n const format = isJson ? 'JSON' : 'YAML';\n throw new Error(`Failed to parse ${format} config file: ${(error as Error).message}`, {\n cause: error,\n });\n }\n\n if (\n !parsed['intentId'] ||\n typeof parsed['intentId'] !== 'string' ||\n parsed['intentId'].trim() === ''\n ) {\n throw new Error(\n `Configuration file is missing required field 'intentId'. ` +\n `Add 'intentId: \"<your-cluster-identity>\"' to ${filePath}. ` +\n `The intentId uniquely identifies the cluster for integrity tracking.`\n );\n }\n\n return {\n template: parsed['template'] as string,\n inputs: (parsed['inputs'] as Record<string, unknown>) ?? {},\n intentId: parsed['intentId'],\n platformPrefix: parsed['platformPrefix'] as string | undefined,\n options: parsed['options'] as RawConfig['options'],\n };\n}\n\nexport { fileExists } from './fs-helpers.js';\n","/**\n * Shared filesystem helper utilities.\n */\n\nimport { access } from 'node:fs/promises';\nimport { join } from 'node:path';\n\n/**\n * Check if a file exists on disk.\n */\nexport async function fileExists(filePath: string): Promise<boolean> {\n try {\n await access(filePath);\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Find the default fixture path (tests/default/inputs.yaml) for a template version.\n * Returns null if the file does not exist.\n */\nexport async function findDefaultFixture(versionDir: string): Promise<string | null> {\n const defaultPath = join(versionDir, 'tests', 'default', 'inputs.yaml');\n try {\n await access(defaultPath);\n return defaultPath;\n } catch {\n return null;\n }\n}\n","/**\n * Manifest loading and raw config reconstruction utility.\n *\n * CHANGE (006): envelopeFromManifest now returns RawConfig (with template as\n * URI string) instead of Envelope. The CLI command resolves the URI.\n *\n * Reference: data-model.md §11, research.md R-008\n */\n\nimport { readFile } from 'node:fs/promises';\nimport type { Manifest } from '@truefoundry/tfy-infra-engine';\nimport type { RawConfig } from './config-loader.js';\n\n/**\n * Load and validate a manifest from disk.\n */\nexport async function loadManifest(manifestPath: string): Promise<Manifest> {\n let content: string;\n try {\n content = await readFile(manifestPath, 'utf-8');\n } catch (error) {\n const nodeError = error as NodeJS.ErrnoException;\n if (nodeError.code === 'ENOENT') {\n throw new Error(`Manifest not found: ${manifestPath}`, { cause: error });\n }\n throw new Error(`Failed to read manifest: ${(error as Error).message}`, { cause: error });\n }\n\n let parsed: Record<string, unknown>;\n try {\n parsed = JSON.parse(content) as Record<string, unknown>;\n } catch {\n throw new Error(`Invalid JSON in manifest: ${manifestPath}`);\n }\n\n const requiredFields = [\n 'manifestVersion',\n 'files',\n 'aggregateHash',\n 'inputs',\n 'platformPrefix',\n 'intentId',\n 'templateSource',\n 'templateVersion',\n ] as const;\n\n for (const field of requiredFields) {\n if (parsed[field] === undefined) {\n throw new Error(`Manifest is missing required field: ${field}`);\n }\n }\n\n return parsed as unknown as Manifest;\n}\n\n/**\n * Reconstruct a RawConfig from a manifest for re-render.\n *\n * CHANGE (006): Returns RawConfig with template URI string.\n */\nexport function envelopeFromManifest(manifest: Manifest): RawConfig {\n if (!manifest.inputs) {\n throw new Error('Cannot reconstruct envelope: manifest is missing inputs field');\n }\n if (!manifest.platformPrefix) {\n throw new Error('Cannot reconstruct envelope: manifest is missing platformPrefix field');\n }\n\n return {\n template: manifest.templateSource,\n inputs: manifest.inputs,\n intentId: manifest.intentId,\n platformPrefix: manifest.platformPrefix,\n };\n}\n","/**\n * Disk-to-FileMap reader utility.\n *\n * Reads a cluster directory into an in-memory FileMap for verify and\n * upgrade operations. Classifies files by zone and parses @tfy-status\n * headers for platform files.\n *\n * Reference: data-model.md §9, research.md R-012\n */\n\nimport { readFile, readdir } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport {\n classifyFile,\n parseHeader,\n DEFAULT_PREFIX,\n type FileMap,\n type FileEntry,\n} from '@truefoundry/tfy-infra-engine';\n\n/**\n * Read all files from a cluster directory into a FileMap.\n *\n * Scans the directory, classifies each file by zone, parses @tfy-status\n * headers for platform files, and returns a typed FileMap.\n *\n * Does NOT include manifest.json in the output (callers load it separately).\n *\n * @param dir - Path to cluster directory\n * @param prefix - Platform Zone filename prefix (default: \"tfy_\")\n * @returns FileMap with zone-tagged entries and parsed headers\n */\nexport async function readDirectoryToFileMap(\n dir: string,\n prefix: string = DEFAULT_PREFIX\n): Promise<FileMap> {\n const fileMap: FileMap = new Map();\n\n let entries: string[];\n try {\n const dirEntries = await readdir(dir, { withFileTypes: true });\n entries = dirEntries.filter((e) => e.isFile()).map((e) => e.name);\n } catch (error) {\n const nodeError = error as NodeJS.ErrnoException;\n if (nodeError.code === 'ENOENT') {\n throw new Error(`Directory not found: ${dir}`, { cause: error });\n }\n throw new Error(`Failed to read directory: ${(error as Error).message}`, { cause: error });\n }\n\n for (const filename of entries) {\n // Skip manifest.json — callers load it separately\n if (filename === 'manifest.json') continue;\n\n const content = await readFile(join(dir, filename), 'utf-8');\n const zone = classifyFile(filename, prefix);\n\n const entry: FileEntry = { content, zone };\n\n if (zone === 'platform') {\n const header = parseHeader(content);\n if (header) {\n entry.header = header;\n }\n }\n\n fileMap.set(filename, entry);\n }\n\n return fileMap;\n}\n","/**\n * Engine output-to-disk writer utility.\n *\n * Writes engine results to disk with zone-aware policies.\n *\n * CHANGE (008): Removed sideOutputs handling — no more expose_variable pipeline.\n *\n * Reference: data-model.md §10, spec.md FR-012a\n */\n\nimport { writeFile, mkdir } from 'node:fs/promises';\nimport { join, dirname } from 'node:path';\nimport type { InstallResult, UpgradeResult } from '@truefoundry/tfy-infra-engine';\nimport { fileExists } from './fs-helpers.js';\n\n/**\n * Write engine output to disk with zone-aware policies.\n *\n * Modes:\n * - 'install': Overwrite all files\n * - 'upgrade': Overwrite platform zone files, skip existing user zone files,\n * create new user zone files\n *\n * @param dir - Target directory\n * @param result - InstallResult or UpgradeResult from engine\n * @param mode - 'install' (overwrite all) or 'upgrade' (zone-aware)\n */\nexport async function writeEngineOutput(\n dir: string,\n result: InstallResult | UpgradeResult,\n mode: 'install' | 'upgrade'\n): Promise<void> {\n await mkdir(dir, { recursive: true });\n\n for (const [filePath, entry] of result.files) {\n const fullPath = join(dir, filePath);\n\n if (mode === 'upgrade' && entry.zone === 'user') {\n if (await fileExists(fullPath)) {\n continue;\n }\n }\n\n await mkdir(dirname(fullPath), { recursive: true });\n await writeFile(fullPath, entry.content, 'utf-8');\n }\n}\n","/**\n * Unified diff generation utility\n * Generates unified diff output for test failures\n * @file packages/infra-cli/src/utils/diff.ts\n */\n\nimport { createTwoFilesPatch } from 'diff';\n\n/**\n * Generate a unified diff between expected and actual content.\n *\n * @param expected - Expected file content\n * @param actual - Actual file content\n * @param filename - Name of the file being compared\n * @returns Unified diff string, or empty string if contents match\n */\nexport function generateUnifiedDiff(expected: string, actual: string, filename: string): string {\n const normalizedExpected = expected.replace(/\\r\\n/g, '\\n');\n const normalizedActual = actual.replace(/\\r\\n/g, '\\n');\n\n const patch = createTwoFilesPatch(\n `expected/${filename}`,\n `actual/${filename}`,\n normalizedExpected,\n normalizedActual,\n '', // oldHeader\n '', // newHeader\n { context: 3 }\n );\n\n return patch;\n}\n\n/**\n * Diff entry for a single file\n */\nexport interface DiffEntry {\n filename: string;\n diff: string;\n}\n\n/**\n * Format multiple diffs for display output.\n *\n * @param diffs - Array of diff entries with filenames\n * @returns Formatted string with all diffs\n */\nexport function formatDiffOutput(diffs: DiffEntry[]): string {\n if (diffs.length === 0) {\n return '';\n }\n\n return diffs\n .map((entry) => {\n return ` ${entry.filename}: content mismatch\\n${entry.diff}`;\n })\n .join('\\n');\n}\n","/**\n * tfy-init tpl validate command\n *\n * Validates a bundle.json template package.\n * Detects bundle.json in the specified directory.\n */\n\nimport { Command } from 'commander';\nimport { readFile, stat } from 'node:fs/promises';\nimport { join, resolve } from 'node:path';\nimport Handlebars from 'handlebars';\nimport { validateJsonSchemaStructure, isTofuAvailable } from '@truefoundry/tfy-infra-engine';\nimport { handleEngineError, EXIT_CODES } from '../../utils/error-handler.js';\nimport * as output from '../../utils/output.js';\n\nexport { EXIT_CODES } from '../../utils/error-handler.js';\n\nexport function createValidateCommand(): Command {\n const cmd = new Command('validate')\n .description('Validate a template (for template authors)')\n .option('-d, --directory <dir>', 'Path to local template directory', '.')\n .option('--strict', 'Fail on warnings')\n .option('--skip-format', 'Skip tofu fmt validation')\n .action(async (options: ValidateOptions) => {\n try {\n const exitCode = await runValidate(options);\n process.exit(exitCode);\n } catch (error) {\n handleEngineError(error);\n }\n });\n\n return cmd;\n}\n\ninterface ValidateOptions {\n directory: string;\n strict?: boolean;\n skipFormat?: boolean;\n}\n\ninterface ValidationIssue {\n type: 'error' | 'warning';\n message: string;\n file?: string;\n}\n\nasync function fileExists(path: string): Promise<boolean> {\n try {\n await stat(path);\n return true;\n } catch {\n return false;\n }\n}\n\nasync function runValidate(options: ValidateOptions): Promise<number> {\n const templateDir = resolve(options.directory);\n const issues: ValidationIssue[] = [];\n let hasErrors = false;\n\n output.info(`Validating template at: ${templateDir}`);\n output.info('');\n\n const bundlePath = join(templateDir, 'bundle.json');\n const usesBundle = await fileExists(bundlePath);\n\n if (!usesBundle) {\n output.error('bundle.json not found in template directory');\n return EXIT_CODES.VALIDATION_ERROR;\n }\n\n output.verbose('Checking bundle.json...');\n try {\n const content = await readFile(bundlePath, 'utf-8');\n\n let parsed: Record<string, unknown>;\n try {\n parsed = JSON.parse(content) as Record<string, unknown>;\n } catch (error) {\n throw new Error(`Invalid JSON: ${(error as Error).message}`, { cause: error });\n }\n\n const rawMeta = parsed['metadata'] as Record<string, unknown> | undefined;\n if (!rawMeta || typeof rawMeta !== 'object') {\n throw new Error('bundle.json must contain a \"metadata\" object');\n }\n if (!rawMeta['name'] || !rawMeta['version']) {\n throw new Error('bundle.json metadata must contain \"name\" and \"version\"');\n }\n\n const jsonSchema = parsed['jsonSchema'] as Record<string, unknown> | undefined;\n if (!jsonSchema || typeof jsonSchema !== 'object') {\n throw new Error('bundle.json must contain a \"jsonSchema\" object');\n }\n\n validateJsonSchemaStructure(jsonSchema);\n output.success('bundle.json schema is valid');\n\n const files = parsed['files'] as Record<string, string> | undefined;\n const staticFiles = parsed['staticFiles'] as Record<string, string> | undefined;\n const fileCount = Object.keys(files ?? {}).length;\n const staticCount = Object.keys(staticFiles ?? {}).length;\n\n if (fileCount === 0 && staticCount === 0) {\n issues.push({ type: 'error', message: 'Bundle contains no template files' });\n hasErrors = true;\n } else {\n for (const [relPath, content] of Object.entries(files ?? {})) {\n try {\n Handlebars.precompile(content);\n output.verbose(` ✓ ${relPath}`);\n } catch (error) {\n issues.push({\n type: 'error',\n message: `Invalid Handlebars syntax: ${(error as Error).message}`,\n file: relPath,\n });\n hasErrors = true;\n }\n }\n output.success(\n `Found ${fileCount} HBS template(s)` +\n (staticCount > 0 ? ` and ${staticCount} static file(s)` : '') +\n ' with valid syntax'\n );\n }\n } catch (error) {\n issues.push({\n type: 'error',\n message: (error as Error).message,\n file: 'bundle.json',\n });\n hasErrors = true;\n }\n\n if (!options.skipFormat) {\n output.verbose('Checking tofu fmt availability...');\n const tofuAvailable = await isTofuAvailable();\n if (tofuAvailable) {\n output.success('tofu fmt available');\n } else {\n issues.push({ type: 'warning', message: 'tofu not found - format validation skipped' });\n }\n }\n\n output.info('');\n\n const warnings = issues.filter((i) => i.type === 'warning');\n const errors = issues.filter((i) => i.type === 'error');\n\n if (warnings.length > 0) {\n output.warn(`${warnings.length} warning(s):`);\n for (const issue of warnings) {\n const prefix = issue.file ? `${issue.file}: ` : '';\n output.info(` • ${prefix}${issue.message}`);\n }\n output.info('');\n }\n\n if (errors.length > 0) {\n output.error(`${errors.length} error(s):`);\n for (const issue of errors) {\n const prefix = issue.file ? `${issue.file}: ` : '';\n output.info(` • ${prefix}${issue.message}`);\n }\n output.info('');\n }\n\n if (hasErrors) {\n output.error('Template validation failed');\n return EXIT_CODES.VALIDATION_ERROR;\n }\n\n if (options.strict && warnings.length > 0) {\n output.error('Template validation failed (strict mode)');\n return EXIT_CODES.VALIDATION_ERROR;\n }\n\n output.success('Template is valid');\n return EXIT_CODES.SUCCESS;\n}\n","/**\n * tfy-init tpl test command\n *\n * CHANGE (006): Fetch-first — resolves template via ResolverRegistry\n * before calling engine.install(). Envelope uses Template object.\n */\n\nimport { Command } from 'commander';\nimport { readFile, readdir, writeFile, mkdir } from 'node:fs/promises';\nimport { join, resolve } from 'node:path';\nimport * as yaml from 'js-yaml';\nimport { createEngine, type Envelope, type InstallResult } from '@truefoundry/tfy-infra-engine';\nimport { createResolverRegistry } from '../../resolvers/index.js';\nimport { handleEngineError, EXIT_CODES } from '../../utils/error-handler.js';\nimport * as output from '../../utils/output.js';\nimport { generateUnifiedDiff } from '../../utils/diff.js';\n\nexport { EXIT_CODES } from '../../utils/error-handler.js';\n\nexport function createTestCommand(): Command {\n const cmd = new Command('test')\n .description('Run test fixtures against a template')\n .option('-d, --directory <dir>', 'Path to local template directory', '.')\n .option('--fixture <name>', 'Run specific fixture only')\n .option('--update', 'Update expected outputs with actual results')\n .option('--diff', 'Show unified diff on failures')\n .option('--skip-format', 'Skip tofu fmt in tests')\n .action(async (options: TestOptions) => {\n try {\n const exitCode = await runTest(options);\n process.exit(exitCode);\n } catch (error) {\n handleEngineError(error);\n }\n });\n\n return cmd;\n}\n\ninterface TestOptions {\n directory: string;\n fixture?: string;\n update?: boolean;\n diff?: boolean;\n skipFormat?: boolean;\n}\n\ninterface TestResult {\n name: string;\n passed: boolean;\n errors?: string[];\n diffs?: Array<{ filename: string; diff: string }>;\n}\n\nasync function runTest(options: TestOptions): Promise<number> {\n const templateDir = resolve(options.directory);\n const testsDir = join(templateDir, 'tests');\n const templateUri = `file://${templateDir}`;\n\n output.info(`Testing template at: ${templateDir}`);\n output.info('');\n\n let fixtures: string[];\n try {\n const entries = await readdir(testsDir);\n fixtures = [];\n for (const entry of entries) {\n try {\n const inputsPath = join(testsDir, entry, 'inputs.yaml');\n await readFile(inputsPath);\n fixtures.push(entry);\n } catch {\n // Not a valid fixture directory\n }\n }\n } catch {\n output.error(`No tests directory found at ${testsDir}`);\n return EXIT_CODES.VALIDATION_ERROR;\n }\n\n if (fixtures.length === 0) {\n output.error('No test fixtures found');\n output.info('');\n output.info('Expected structure:');\n output.info(' tests/');\n output.info(' <fixture-name>/');\n output.info(' inputs.yaml');\n output.info(' expected/');\n return EXIT_CODES.VALIDATION_ERROR;\n }\n\n if (options.fixture) {\n if (!fixtures.includes(options.fixture)) {\n output.error(`Fixture '${options.fixture}' not found`);\n output.info(`Available fixtures: ${fixtures.join(', ')}`);\n return EXIT_CODES.VALIDATION_ERROR;\n }\n fixtures = [options.fixture];\n }\n\n output.info(`Running ${fixtures.length} fixture(s)...`);\n output.info('');\n\n // Fetch-first: resolve template once for all fixtures\n const resolverRegistry = createResolverRegistry();\n const template = await resolverRegistry.resolve(templateUri, { noCache: true });\n\n const engine = createEngine();\n const results: TestResult[] = [];\n\n for (const fixture of fixtures) {\n const fixtureDir = join(testsDir, fixture);\n const inputsPath = join(fixtureDir, 'inputs.yaml');\n const expectedDir = join(fixtureDir, 'expected');\n\n output.verbose(`Running fixture: ${fixture}`);\n\n try {\n const inputsContent = await readFile(inputsPath, 'utf-8');\n const inputs = yaml.load(inputsContent) as Record<string, unknown>;\n\n const envelope: Envelope = {\n template,\n inputs,\n intentId: `test-${fixture}`,\n options: {\n skipFormat: options.skipFormat,\n },\n };\n\n const result = await engine.install(envelope);\n\n if (options.update) {\n await updateExpected(expectedDir, result);\n output.success(`${fixture}: updated`);\n results.push({ name: fixture, passed: true });\n } else {\n const { errors, diffs } = await compareOutputs(expectedDir, result, options.diff);\n if (errors.length === 0) {\n output.success(`${fixture}: passed`);\n results.push({ name: fixture, passed: true });\n } else {\n output.error(`${fixture}: failed`);\n for (const err of errors) {\n output.info(` ${err}`);\n }\n if (options.diff && diffs && diffs.length > 0) {\n for (const d of diffs) {\n output.info(` --- expected/${d.filename}`);\n output.info(` +++ actual/${d.filename}`);\n const diffLines = d.diff.split('\\n').slice(4);\n for (const line of diffLines) {\n if (line) {\n output.info(` ${line}`);\n }\n }\n }\n }\n results.push({ name: fixture, passed: false, errors, diffs });\n }\n }\n } catch (error) {\n output.error(`${fixture}: error`);\n output.info(` ${(error as Error).message}`);\n results.push({ name: fixture, passed: false, errors: [(error as Error).message] });\n }\n }\n\n output.info('');\n const passed = results.filter((r) => r.passed).length;\n const failed = results.filter((r) => !r.passed).length;\n\n if (failed === 0) {\n output.success(`All ${passed} fixture(s) passed`);\n return EXIT_CODES.SUCCESS;\n } else {\n output.error(`${failed} fixture(s) failed, ${passed} passed`);\n return EXIT_CODES.VALIDATION_ERROR;\n }\n}\n\n/**\n * Replaces machine-specific `file://` paths in @tfy-status source fields\n * with a stable placeholder so snapshots are portable across environments.\n */\nfunction normalizeStatusSource(content: string): string {\n return content.replace(/(\"source\"\\s*:\\s*)\"file:\\/\\/[^\"]*\"/g, '$1\"<local>\"');\n}\n\nasync function updateExpected(expectedDir: string, result: InstallResult): Promise<void> {\n await mkdir(expectedDir, { recursive: true });\n for (const [filePath, entry] of result.files) {\n if (filePath === 'manifest.json') continue;\n const outputPath = join(expectedDir, filePath);\n await writeFile(outputPath, normalizeStatusSource(entry.content), 'utf-8');\n }\n}\n\ninterface CompareResult {\n errors: string[];\n diffs?: Array<{ filename: string; diff: string }>;\n}\n\nasync function compareOutputs(\n expectedDir: string,\n result: InstallResult,\n includeDiffs?: boolean\n): Promise<CompareResult> {\n const errors: string[] = [];\n const diffs: Array<{ filename: string; diff: string }> = [];\n\n for (const [filePath, entry] of result.files) {\n if (filePath === 'manifest.json') continue;\n const actual = entry.content;\n\n const expectedPath = join(expectedDir, filePath);\n try {\n const expected = await readFile(expectedPath, 'utf-8');\n const normalizedActual = normalizeStatusSource(actual.trim().replace(/\\r\\n/g, '\\n'));\n const normalizedExpected = normalizeStatusSource(expected.trim().replace(/\\r\\n/g, '\\n'));\n\n if (normalizedActual !== normalizedExpected) {\n errors.push(`${filePath}: content mismatch`);\n if (includeDiffs) {\n const diff = generateUnifiedDiff(normalizedExpected, normalizedActual, filePath);\n diffs.push({ filename: filePath, diff });\n }\n }\n } catch {\n errors.push(`${filePath}: expected file not found`);\n }\n }\n\n try {\n const expectedFiles = await readdir(expectedDir);\n for (const file of expectedFiles) {\n if (!result.files.has(file) && file !== 'manifest.json') {\n errors.push(`${file}: unexpected file in expected/`);\n }\n }\n } catch {\n // Expected directory might not exist\n }\n\n return { errors, diffs: includeDiffs ? diffs : undefined };\n}\n","/**\n * tfy-init tpl dev command\n * Watch mode for rapid template iteration.\n *\n * Supports two modes:\n * 1. Schema mode (--schema + --src): reads a pre-computed schema package\n * and a src/ directory. Watches both for changes.\n * 2. Directory mode (--directory): reads bundle.json from a template directory.\n */\n\nimport { Command } from 'commander';\nimport { readFile, mkdir, rm, writeFile, access } from 'node:fs/promises';\nimport { dirname, join, relative, resolve } from 'node:path';\nimport * as yaml from 'js-yaml';\nimport {\n createEngine,\n EngineError,\n EngineErrorCode,\n type Envelope,\n type InstallResult,\n type JSONSchema7,\n type Template,\n type TemplateMetadata,\n} from '@truefoundry/tfy-infra-engine';\nimport { createResolverRegistry } from '../../resolvers/index.js';\nimport { handleEngineError, EXIT_CODES } from '../../utils/error-handler.js';\nimport * as output from '../../utils/output.js';\nimport { findDefaultFixture } from '../../utils/fs-helpers.js';\nimport { walkSrcDir } from '../../utils/walk-src.js';\nimport { createWatcher, type WatchEvent } from '../../utils/file-watcher.js';\nimport { generateUnifiedDiff } from '../../utils/diff.js';\n\nexport { EXIT_CODES } from '../../utils/error-handler.js';\n\nexport interface DevOptions {\n directory: string;\n schema?: string;\n src?: string;\n name?: string;\n version?: string;\n fixture?: string;\n output: string;\n test?: boolean;\n}\n\nexport function createDevCommand(): Command {\n const cmd = new Command('dev')\n .description('Watch mode for rapid template iteration')\n .option('-d, --directory <dir>', 'Path to template version directory')\n .option(\n '--schema <path>',\n 'Path to schema package JSON (from tfy-schema-tool --schema-package)'\n )\n .option('--src <dir>', 'Path to template src/ directory')\n .option('--name <string>', 'Template name (used with --schema mode)')\n .option('--version <semver>', 'Template version (used with --schema mode)')\n .option('-f, --fixture <path>', 'Input file or fixture to use for testing')\n .requiredOption('-o, --output <dir>', 'Output directory for rendered files')\n .option('--no-test', 'Only validate, do not run tests')\n .action(async (options: DevOptions) => {\n try {\n const exitCode = await runDev(options);\n process.exit(exitCode);\n } catch (error) {\n handleEngineError(error);\n }\n });\n\n return cmd;\n}\n\nexport { findDefaultFixture } from '../../utils/fs-helpers.js';\n\nexport async function validateTemplateDir(\n versionDir: string\n): Promise<{ valid: boolean; error?: string }> {\n try {\n await access(versionDir);\n } catch {\n return { valid: false, error: `Directory not found: ${versionDir}` };\n }\n\n const bundleJsonPath = join(versionDir, 'bundle.json');\n try {\n await access(bundleJsonPath);\n return { valid: true };\n } catch {\n return { valid: false, error: `bundle.json not found in ${versionDir}` };\n }\n}\n\nexport async function ensureOutputDir(_versionDir: string, customOutput: string): Promise<string> {\n const outputDir = resolve(customOutput);\n\n try {\n await rm(outputDir, { recursive: true, force: true });\n } catch {\n // Directory might not exist\n }\n\n await mkdir(outputDir, { recursive: true });\n return outputDir;\n}\n\nfunction isSchemaMode(options: DevOptions): boolean {\n return !!(options.schema && options.src);\n}\n\nasync function runDev(options: DevOptions): Promise<number> {\n if (isSchemaMode(options)) {\n return runSchemaModeDev(options);\n }\n return runDirectoryModeDev(options);\n}\n\n/**\n * Schema mode: --schema + --src.\n * Reads schema package from file, walks src/, constructs Template in-memory.\n */\nasync function runSchemaModeDev(options: DevOptions): Promise<number> {\n const schemaPath = resolve(options.schema!);\n const srcDir = resolve(options.src!);\n const templateName = options.name ?? 'dev-template';\n const templateVersion = options.version;\n\n let fixturePath: string;\n if (options.fixture) {\n fixturePath = resolve(options.fixture);\n try {\n await access(fixturePath);\n } catch {\n output.error(`Fixture not found: ${options.fixture}`);\n return EXIT_CODES.VALIDATION_ERROR;\n }\n } else {\n output.error('--fixture is required in schema mode');\n output.info('Use -f to specify an inputs.yaml file.');\n return EXIT_CODES.VALIDATION_ERROR;\n }\n\n const outputDir = await ensureOutputDir('.', options.output);\n const relativeOutputDir = relative(process.cwd(), outputDir);\n\n output.info(`[schema] ${relative(process.cwd(), schemaPath)}`);\n output.info(`[src] ${relative(process.cwd(), srcDir)}`);\n output.info(`[output] ${relativeOutputDir}/`);\n output.info(`[fixture] ${relative(process.cwd(), fixturePath)}`);\n output.info('');\n\n const engine = createEngine();\n\n const renderFromSchema = async () => {\n try {\n const template = await buildTemplateFromSchema(\n schemaPath,\n srcDir,\n templateName,\n templateVersion\n );\n await renderWithTemplate(engine, template, fixturePath, outputDir, options.test);\n } catch (error) {\n output.error(`[render] ✗ Render failed`);\n output.info(` ${(error as Error).message}`);\n }\n };\n\n await renderFromSchema();\n\n const watchPaths = [schemaPath, srcDir, fixturePath];\n\n const watcher = createWatcher(watchPaths, {\n schemaFile: schemaPath,\n onChange: (event: WatchEvent) => {\n void (async () => {\n output.info(`[changed] ${relative(process.cwd(), event.path)}`);\n await renderFromSchema();\n })();\n },\n onReady: () => {\n output.info('[ready] Watching for changes... (Ctrl+C to exit)');\n output.info('');\n },\n onError: (error) => {\n output.error(`Watch error: ${error.message}`);\n },\n });\n\n return new Promise<number>((resolvePromise) => {\n const cleanup = () => {\n output.info('');\n output.info('[stopped] Watch mode ended');\n void watcher.close();\n resolvePromise(EXIT_CODES.SUCCESS);\n };\n\n process.on('SIGINT', cleanup);\n process.on('SIGTERM', cleanup);\n });\n}\n\nfunction extractVersionFromSchema(jsonSchema: JSONSchema7): string | undefined {\n const props = jsonSchema.properties;\n if (!props) return undefined;\n const versionProp = props['version'];\n if (!versionProp || typeof versionProp === 'boolean') return undefined;\n const def = versionProp.default;\n return typeof def === 'string' ? def : undefined;\n}\n\nasync function buildTemplateFromSchema(\n schemaPath: string,\n srcDir: string,\n name: string,\n version?: string\n): Promise<Template> {\n const schemaContent = await readFile(schemaPath, 'utf-8');\n const schemaPkg = JSON.parse(schemaContent) as Record<string, unknown>;\n\n const jsonSchema = schemaPkg['jsonSchema'] as JSONSchema7;\n if (!jsonSchema || typeof jsonSchema !== 'object') {\n throw new Error('Schema package must contain a \"jsonSchema\" object');\n }\n\n const resolvedVersion = version ?? extractVersionFromSchema(jsonSchema) ?? '0.0.0-dev';\n\n const { files, staticFiles } = await walkSrcDir(srcDir);\n\n const metadata: TemplateMetadata = { name, version: resolvedVersion };\n\n return {\n metadata,\n jsonSchema,\n files,\n staticFiles,\n source: `file://${srcDir}`,\n version: { semver: resolvedVersion },\n };\n}\n\nasync function renderWithTemplate(\n engine: ReturnType<typeof createEngine>,\n template: Template,\n fixturePath: string,\n outputDir: string,\n runTests?: boolean\n): Promise<void> {\n const inputsContent = await readFile(fixturePath, 'utf-8');\n const inputs = yaml.load(inputsContent) as Record<string, unknown>;\n\n const envelope: Envelope = {\n template,\n inputs,\n intentId: `dev-${Date.now()}`,\n options: { skipFormat: false },\n };\n\n const result = await engine.install(envelope);\n\n const templateFileCount = Array.from(result.files.keys()).filter(\n (f) => f !== 'manifest.json'\n ).length;\n output.info(`[validate] ✓ ${templateFileCount} template files valid`);\n\n await rm(outputDir, { recursive: true, force: true });\n await mkdir(outputDir, { recursive: true });\n\n let filesWritten = 0;\n for (const [filePath, entry] of result.files) {\n if (filePath === 'manifest.json') continue;\n const outputPath = join(outputDir, filePath);\n await mkdir(dirname(outputPath), { recursive: true });\n await writeFile(outputPath, entry.content, 'utf-8');\n filesWritten++;\n }\n output.info(\n `[render] ✓ ${filesWritten} files written to ${relative(process.cwd(), outputDir)}/`\n );\n\n if (runTests !== false) {\n await runTestComparisonFromResult(result, outputDir);\n }\n}\n\n/**\n * Directory mode: --directory (legacy, uses FileResolver).\n */\nasync function runDirectoryModeDev(options: DevOptions): Promise<number> {\n const versionDir = resolve(options.directory || '.');\n\n const validation = await validateTemplateDir(versionDir);\n if (!validation.valid) {\n output.error(validation.error!);\n return EXIT_CODES.VALIDATION_ERROR;\n }\n\n let fixturePath: string;\n if (options.fixture) {\n fixturePath = resolve(versionDir, options.fixture);\n try {\n await access(fixturePath);\n } catch {\n output.error(`Fixture not found: ${options.fixture}`);\n return EXIT_CODES.VALIDATION_ERROR;\n }\n } else {\n const defaultFixture = await findDefaultFixture(versionDir);\n if (!defaultFixture) {\n output.error('No default fixture found at tests/default/inputs.yaml');\n output.info('Either create a default fixture or use --fixture to specify one.');\n return EXIT_CODES.VALIDATION_ERROR;\n }\n fixturePath = defaultFixture;\n }\n\n const outputDir = await ensureOutputDir(versionDir, options.output);\n const relativeOutputDir = relative(process.cwd(), outputDir);\n\n output.info(`[watching] ${relative(process.cwd(), versionDir)}`);\n output.info(`[output] ${relativeOutputDir}/`);\n output.info(`[fixture] ${relative(process.cwd(), fixturePath)}`);\n output.info('');\n\n const engine = createEngine();\n const templateUri = `file://${versionDir}`;\n\n await runRenderCycle(engine, templateUri, versionDir, fixturePath, outputDir, options.test);\n\n const watcher = createWatcher(versionDir, {\n onChange: (event: WatchEvent) => {\n void (async () => {\n const relPath = relative(versionDir, event.path);\n output.info(`[changed] ${relPath}`);\n\n if (event.fileType === 'schema') {\n await runValidation(templateUri);\n } else if (event.fileType === 'template' || event.fileType === 'inputs') {\n await runRenderCycle(\n engine,\n templateUri,\n versionDir,\n fixturePath,\n outputDir,\n options.test\n );\n }\n })();\n },\n onReady: () => {\n output.info('[ready] Watching for changes... (Ctrl+C to exit)');\n output.info('');\n },\n onError: (error) => {\n output.error(`Watch error: ${error.message}`);\n },\n });\n\n return new Promise<number>((resolvePromise) => {\n const cleanup = () => {\n output.info('');\n output.info('[stopped] Watch mode ended');\n void watcher.close();\n resolvePromise(EXIT_CODES.SUCCESS);\n };\n\n process.on('SIGINT', cleanup);\n process.on('SIGTERM', cleanup);\n });\n}\n\nasync function runValidation(templateUri: string): Promise<boolean> {\n try {\n const resolverRegistry = createResolverRegistry();\n const template = await resolverRegistry.resolve(templateUri, { noCache: true });\n if (template.jsonSchema) {\n output.info('[validate] ✓ schema valid');\n return true;\n }\n output.error('[validate] ✗ schema invalid');\n return false;\n } catch (error) {\n output.error(`[validate] ✗ schema invalid`);\n output.info(` ${(error as Error).message}`);\n return false;\n }\n}\n\nasync function runRenderCycle(\n engine: ReturnType<typeof createEngine>,\n templateUri: string,\n versionDir: string,\n fixturePath: string,\n outputDir: string,\n runTests?: boolean\n): Promise<void> {\n const valid = await runValidation(templateUri);\n if (!valid) return;\n\n try {\n const inputsContent = await readFile(fixturePath, 'utf-8');\n const inputs = yaml.load(inputsContent) as Record<string, unknown>;\n\n const resolverRegistry = createResolverRegistry();\n const template = await resolverRegistry.resolve(templateUri, { noCache: true });\n\n const envelope: Envelope = {\n template,\n inputs,\n intentId: `dev-${Date.now()}`,\n options: { skipFormat: false },\n };\n\n const result = await engine.install(envelope);\n\n const templateFileCount = Array.from(result.files.keys()).filter(\n (f) => f !== 'manifest.json'\n ).length;\n output.info(`[validate] ✓ ${templateFileCount} template files valid`);\n\n await rm(outputDir, { recursive: true, force: true });\n await mkdir(outputDir, { recursive: true });\n\n let filesWritten = 0;\n for (const [filePath, entry] of result.files) {\n if (filePath === 'manifest.json') continue;\n const outputPath = join(outputDir, filePath);\n await mkdir(dirname(outputPath), { recursive: true });\n await writeFile(outputPath, entry.content, 'utf-8');\n filesWritten++;\n }\n output.info(\n `[render] ✓ ${filesWritten} files written to ${relative(process.cwd(), outputDir)}/`\n );\n\n if (runTests !== false) {\n await runTestComparison(versionDir, result, outputDir);\n }\n } catch (error) {\n output.error(`[render] ✗ Render failed`);\n output.info(` ${(error as Error).message}`);\n if (\n error instanceof EngineError &&\n error.code === EngineErrorCode.INPUT_VALIDATION_FAILED &&\n error.details?.['errors']\n ) {\n output.info(\n output.formatValidationErrors(\n error.details['errors'] as Array<{ path: string; message: string }>\n )\n );\n }\n }\n}\n\nasync function runTestComparison(\n versionDir: string,\n result: InstallResult,\n _outputDir: string\n): Promise<void> {\n const expectedDir = join(versionDir, 'tests', 'default', 'expected');\n await runTestComparisonFromDir(result, expectedDir);\n}\n\nasync function runTestComparisonFromResult(\n _result: InstallResult,\n _outputDir: string\n): Promise<void> {\n // In schema mode, we don't have a versionDir to find expected/ in.\n // Test comparison is skipped unless running from directory mode.\n}\n\nasync function runTestComparisonFromDir(result: InstallResult, expectedDir: string): Promise<void> {\n const startTime = Date.now();\n\n try {\n await access(expectedDir);\n } catch {\n output.info('[test] ⚠ No expected/ directory, skipping comparison');\n return;\n }\n\n const errors: Array<{ file: string; diff?: string }> = [];\n\n for (const [filePath, entry] of result.files) {\n if (filePath === 'manifest.json') continue;\n const actual = entry.content;\n\n const expectedPath = join(expectedDir, filePath);\n try {\n const expected = await readFile(expectedPath, 'utf-8');\n const normalizedActual = actual.trim().replace(/\\r\\n/g, '\\n');\n const normalizedExpected = expected.trim().replace(/\\r\\n/g, '\\n');\n\n if (normalizedActual !== normalizedExpected) {\n const diff = generateUnifiedDiff(normalizedExpected, normalizedActual, filePath);\n errors.push({ file: filePath, diff });\n }\n } catch {\n errors.push({ file: filePath });\n }\n }\n\n const duration = Date.now() - startTime;\n\n if (errors.length === 0) {\n output.info(`[test] ✓ default: passed (${duration}ms)`);\n } else {\n output.error(`[test] ✗ default: failed`);\n for (const err of errors) {\n output.info(` ${err.file}: content mismatch`);\n if (err.diff) {\n const diffLines = err.diff.split('\\n').slice(4, 14);\n for (const line of diffLines) {\n if (line) {\n output.info(` ${line}`);\n }\n }\n if (err.diff.split('\\n').length > 14) {\n output.info(` ... (diff truncated)`);\n }\n }\n }\n }\n}\n","/**\n * File watcher utility\n * Watches template directories for changes and emits events\n * @file packages/infra-cli/src/utils/file-watcher.ts\n */\n\nimport chokidar, { type FSWatcher } from 'chokidar';\nimport { basename } from 'node:path';\n\n/**\n * Type of file change that occurred\n */\nexport type FileType = 'schema' | 'template' | 'inputs' | 'other';\n\n/**\n * Event emitted when a file changes\n */\nexport interface WatchEvent {\n type: 'change' | 'add' | 'unlink';\n path: string;\n fileType: FileType;\n}\n\n/**\n * Options for creating a file watcher\n */\nexport interface WatchOptions {\n /**\n * Callback when a file changes\n */\n onChange?: (event: WatchEvent) => void;\n\n /**\n * Callback when watcher is ready\n */\n onReady?: () => void;\n\n /**\n * Callback when an error occurs\n */\n onError?: (error: Error) => void;\n\n /**\n * Patterns to ignore\n */\n ignored?: string | RegExp | ((path: string) => boolean);\n\n /**\n * Debounce delay in milliseconds\n */\n debounceMs?: number;\n\n /**\n * Path to the schema file for classifying changes.\n */\n schemaFile?: string;\n}\n\n/**\n * Classify a file path by its type.\n *\n * @param path - File path to classify\n * @param schemaFile - Optional path to the schema file to match against\n * @returns File type classification\n */\nexport function classifyFileChange(path: string, schemaFile?: string): FileType {\n const normalizedPath = path.replace(/\\\\/g, '/');\n const filename = basename(normalizedPath);\n\n if (schemaFile && normalizedPath.endsWith(schemaFile.replace(/\\\\/g, '/'))) {\n return 'schema';\n }\n\n if (filename === 'bundle.json' || filename === 'schema-pkg.json') {\n return 'schema';\n }\n\n const inSrcDir = /\\/src\\//.test(normalizedPath);\n if (inSrcDir && !filename.startsWith('_')) {\n return 'template';\n }\n\n if (filename === 'inputs.yaml') {\n return 'inputs';\n }\n\n return 'other';\n}\n\n/**\n * Create a file watcher for one or more paths.\n *\n * @param paths - Path(s) to watch (directory or file)\n * @param options - Watcher options\n * @returns FSWatcher instance\n */\nexport function createWatcher(paths: string | string[], options: WatchOptions = {}): FSWatcher {\n const {\n onChange,\n onReady,\n onError,\n ignored = /(^|[/\\\\])\\..|(^|[/\\\\])node_modules($|[/\\\\])|(^|[/\\\\])dev($|[/\\\\])/,\n debounceMs = 100,\n schemaFile,\n } = options;\n\n const watcher = chokidar.watch(paths, {\n ignored,\n persistent: true,\n ignoreInitial: true,\n awaitWriteFinish: {\n stabilityThreshold: debounceMs,\n pollInterval: 50,\n },\n });\n\n const handleEvent = (type: WatchEvent['type']) => (path: string) => {\n const fileType = classifyFileChange(path, schemaFile);\n const event: WatchEvent = { type, path, fileType };\n\n if (onChange) {\n onChange(event);\n }\n };\n\n watcher.on('change', handleEvent('change'));\n watcher.on('add', handleEvent('add'));\n watcher.on('unlink', handleEvent('unlink'));\n\n if (onReady) {\n watcher.on('ready', onReady);\n }\n\n if (onError) {\n watcher.on('error', (err: unknown) =>\n onError(err instanceof Error ? err : new Error(String(err)))\n );\n }\n\n return watcher;\n}\n","/**\n * tfy-init tpl verify command\n *\n * Checks integrity of files on disk against a manifest.json.\n * Wraps engine.verify() to produce human-readable or JSON drift reports.\n *\n */\n\nimport { Command } from 'commander';\nimport { join, resolve } from 'node:path';\nimport { createEngine } from '@truefoundry/tfy-infra-engine';\nimport type { DriftReport } from '@truefoundry/tfy-infra-engine';\nimport { loadManifest } from '../../utils/manifest-loader.js';\nimport { readDirectoryToFileMap } from '../../utils/file-reader.js';\nimport { handleEngineError, EXIT_CODES } from '../../utils/error-handler.js';\nimport * as output from '../../utils/output.js';\nimport { driftTypeTag } from '../../utils/output.js';\n\n// Re-export for backward compatibility\nexport { EXIT_CODES } from '../../utils/error-handler.js';\n\n/** Verify-specific exit code for drift detected (distinct from error). */\nconst DRIFT_DETECTED = 1;\n\ninterface VerifyOptions {\n directory: string;\n json?: boolean;\n}\n\n/**\n * Create the verify command.\n */\nexport function createVerifyCommand(): Command {\n const cmd = new Command('verify')\n .description('Check integrity of files on disk against a manifest')\n .option('-d, --directory <dir>', 'Path to cluster directory containing manifest.json', '.')\n .option('--json', 'Output drift report as structured JSON')\n .action(async (options: VerifyOptions) => {\n try {\n const exitCode = await runVerify(options);\n process.exit(exitCode);\n } catch (error) {\n handleEngineError(error);\n }\n });\n\n return cmd;\n}\n\n/**\n * Execute the verify command.\n * Exported for testing.\n */\nexport async function runVerify(options: VerifyOptions): Promise<number> {\n const dir = resolve(options.directory);\n const manifestPath = join(dir, 'manifest.json');\n\n // Load manifest\n output.verbose(`Loading manifest from ${manifestPath}`);\n const manifest = await loadManifest(manifestPath);\n\n // Read current files on disk\n output.verbose(`Reading files from ${dir}`);\n const currentFiles = await readDirectoryToFileMap(dir, manifest.platformPrefix);\n\n // Create engine and verify\n const engine = createEngine();\n const result = await engine.verify(currentFiles, manifest);\n\n // Output the report\n if (options.json) {\n output.info(formatDriftReportJson(result.driftReport));\n } else {\n output.info(formatDriftReport(result.driftReport));\n }\n\n return result.driftReport.valid ? EXIT_CODES.SUCCESS : DRIFT_DETECTED;\n}\n\n/**\n * Format a drift report as human-readable text.\n * Exported for testing.\n */\nexport function formatDriftReport(report: DriftReport): string {\n const lines: string[] = [];\n const issueCount =\n report.summary.driftedFiles + report.summary.missingFiles + report.summary.unexpectedFiles;\n\n if (report.valid) {\n lines.push(\n `✓ Verification passed: ${report.summary.totalFiles} files checked, ${issueCount} issues`\n );\n lines.push('');\n lines.push(`Aggregate Hash: ${report.aggregateHash.expected}`);\n } else {\n lines.push(`✗ Drift detected: ${issueCount} issues found`);\n lines.push('');\n\n for (const entry of report.entries) {\n const tag = driftTypeTag(entry.type);\n lines.push(` ${tag} ${entry.path}`);\n lines.push(` ${entry.details}`);\n lines.push('');\n }\n\n lines.push(\n `Summary: ${report.summary.totalFiles} files checked, ` +\n `${report.summary.driftedFiles} drifted, ` +\n `${report.summary.missingFiles} missing` +\n (report.summary.unexpectedFiles > 0 ? `, ${report.summary.unexpectedFiles} unexpected` : '')\n );\n }\n\n return lines.join('\\n');\n}\n\n/**\n * Format a drift report as structured JSON.\n * Exported for testing.\n */\nexport function formatDriftReportJson(report: DriftReport): string {\n return JSON.stringify(report, null, 2);\n}\n"],"mappings":"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","names":["import_commander","import_commander","import_commander","import_promises","import_node_path","import_promises","import_node_path","walkSrcDir","rootDir","files","staticFiles","walk","relativePath","currentDir","entries","entry","entryRelative","content","outputPath","import_tfy_infra_engine","currentVerbosity","jsonMode","setVerbosity","level","getVerbosity","setJsonMode","enabled","data","message","success","currentVerbosity","jsonMode","error","info","verbose","warn","formatSize","bytes","formatValidationErrors","errors","driftTypeTag","type","printFileList","files","write","file","EXIT_CODES","handleEngineError","error","fallbackExitCode","getVerbosity","info","key","value","formatValidationErrors","createBundleCommand","options","runBundle","error","handleEngineError","readSchemaPackage","schemaPath","raw","readStdin","parsed","err","obj","resolve","reject","chunks","chunk","extractVersionFromSchema","jsonSchema","props","versionProp","def","schemaPkg","version","srcDir","files","staticFiles","walkSrcDir","metadata","bundle","json","outPath","success","import_commander","import_node_readline","import_promises","import_node_path","yaml","import_tfy_infra_engine","import_tfy_infra_engine","import_promises","import_node_path","import_tfy_infra_engine","FileResolver","uri","_options","fsPath","info","bundlePath","error","nodeError","content","raw","rawMetadata","metadata","jsonSchema","files","staticFiles","version","path","import_tfy_infra_engine","DEFAULT_RESOLVER_OPTIONS","mergeResolverOptions","options","withRetry","fn","retries","baseDelay","lastError","attempt","error","delay","resolve","withTimeout","promise","ms","message","timer","timeout","_","reject","JFROG_TOKEN_ENV","isArtifactoryHost","uri","hostname","buildHeaders","headers","token","fetchBundle","response","hint","unpackBundle","raw","metadata","jsonSchema","files","staticFiles","version","HttpsBundleResolver","options","opts","mergeResolverOptions","withTimeout","withRetry","import_promises","import_node_path","import_node_crypto","import_tfy_infra_engine","getDefaultCacheDir","home","TemplateCache","cacheDir","source","version","key","safeVersion","cachePath","bundlePath","bundleContent","parsed","metadata","jsonSchema","record","files","staticFiles","versionInfo","template","bundle","error","keyPath","sources","templates","size","sourceKeys","versions","versionPath","dir","total","entries","entry","fullPath","fileStat","ResolverRegistry","resolvers","cacheDir","TemplateCache","FileResolver","HttpsBundleResolver","resolver","uri","options","scheme","template","createResolverRegistry","import_promises","yaml","import_promises","import_node_path","fileExists","filePath","findDefaultFixture","versionDir","defaultPath","loadConfig","filePath","content","isJson","parsed","error","format","import_promises","loadManifest","manifestPath","content","error","parsed","requiredFields","field","envelopeFromManifest","manifest","import_promises","import_node_path","import_tfy_infra_engine","readDirectoryToFileMap","dir","prefix","fileMap","entries","e","error","filename","content","zone","entry","header","import_promises","import_node_path","writeEngineOutput","dir","result","mode","filePath","entry","fullPath","fileExists","import_diff","generateUnifiedDiff","expected","actual","filename","normalizedExpected","normalizedActual","validateRenderArgs","args","createRenderCommand","collect","options","exitCode","runRender","error","handleEngineError","value","previous","parseInputOverrides","inputs","result","input","eqIndex","key","setJsonMode","outputDir","timeout","runRenderFromManifest","manifestPath","fileExists","runRenderFromConfigUpgrade","runRenderFromConfig","templateDir","runRenderFromDirectory","path","confirmPrompt","message","rl","resolve","answer","countExistingFiles","dir","e","guardOverwrite","count","warn","data","info","EXIT_CODES","verbose","manifest","loadManifest","rawConfig","envelopeFromManifest","success","envelope","createResolverRegistry","handleInstallDryRun","aborted","writeEngineOutput","printInstallResult","templateUri","fixturePath","findDefaultFixture","fixtureContent","overrides","intentId","engine","configFile","loadConfig","previousManifest","currentFiles","readDirectoryToFileMap","printDriftReport","handleUpgradeDryRun","formatUpgradeResultJson","formatUpgradeResult","computeInstallDiffs","newFiles","diffs","filename","entry","existing","generateUnifiedDiff","computeUpgradeDiffs","currentContent","printDiffs","d","report","json","total","driftTypeTag","fileList","filePath","printFileList","lines","platformCount","userCount","import_commander","import_promises","import_node_path","import_handlebars","import_tfy_infra_engine","createValidateCommand","options","exitCode","runValidate","error","handleEngineError","fileExists","path","templateDir","issues","hasErrors","info","bundlePath","EXIT_CODES","verbose","content","parsed","rawMeta","jsonSchema","success","files","staticFiles","fileCount","staticCount","relPath","Handlebars","warnings","i","errors","warn","issue","prefix","import_commander","import_promises","import_node_path","yaml","import_tfy_infra_engine","createTestCommand","options","exitCode","runTest","error","handleEngineError","templateDir","testsDir","templateUri","info","fixtures","entries","entry","inputsPath","EXIT_CODES","template","createResolverRegistry","engine","results","fixture","fixtureDir","expectedDir","verbose","inputsContent","inputs","envelope","result","updateExpected","success","errors","diffs","compareOutputs","err","d","diffLines","line","passed","r","failed","normalizeStatusSource","content","filePath","outputPath","includeDiffs","actual","expectedPath","expected","normalizedActual","normalizedExpected","diff","generateUnifiedDiff","expectedFiles","file","import_commander","import_promises","import_node_path","yaml","import_tfy_infra_engine","import_chokidar","import_node_path","classifyFileChange","path","schemaFile","normalizedPath","filename","createWatcher","paths","options","onChange","onReady","onError","ignored","debounceMs","watcher","chokidar","handleEvent","type","fileType","err","createDevCommand","options","exitCode","runDev","error","handleEngineError","validateTemplateDir","versionDir","bundleJsonPath","ensureOutputDir","_versionDir","customOutput","outputDir","isSchemaMode","runSchemaModeDev","runDirectoryModeDev","schemaPath","srcDir","templateName","templateVersion","fixturePath","EXIT_CODES","info","relativeOutputDir","engine","renderFromSchema","template","buildTemplateFromSchema","renderWithTemplate","watcher","createWatcher","event","resolvePromise","cleanup","extractVersionFromSchema","jsonSchema","props","versionProp","def","name","version","schemaContent","resolvedVersion","files","staticFiles","walkSrcDir","runTests","inputsContent","inputs","envelope","result","templateFileCount","f","filesWritten","filePath","entry","outputPath","runTestComparisonFromResult","validation","defaultFixture","findDefaultFixture","templateUri","runRenderCycle","relPath","runValidation","createResolverRegistry","runTestComparison","formatValidationErrors","_outputDir","expectedDir","runTestComparisonFromDir","_result","startTime","errors","actual","expectedPath","expected","normalizedActual","normalizedExpected","diff","generateUnifiedDiff","duration","err","diffLines","line","import_commander","import_node_path","import_tfy_infra_engine","DRIFT_DETECTED","createVerifyCommand","options","exitCode","runVerify","error","handleEngineError","dir","manifestPath","verbose","manifest","loadManifest","currentFiles","readDirectoryToFileMap","result","info","formatDriftReportJson","formatDriftReport","EXIT_CODES","report","lines","issueCount","entry","tag","driftTypeTag","createTplCommand","tpl","createBundleCommand","createDevCommand","createRenderCommand","createValidateCommand","createTestCommand","createVerifyCommand","VERSION","main","program","thisCommand","opts","setVerbosity","createTplCommand","error"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@truefoundry/tfy-infra-cli",
3
- "version": "0.1.4-canary.c42a870",
3
+ "version": "0.1.4",
4
4
  "description": "CLI for TrueFoundry infrastructure templating engine",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -19,7 +19,7 @@
19
19
  "prepublishOnly": "yarn build"
20
20
  },
21
21
  "dependencies": {
22
- "@truefoundry/tfy-infra-engine": "0.1.4-canary.c42a870",
22
+ "@truefoundry/tfy-infra-engine": "0.1.4",
23
23
  "chokidar": "^5.0.0",
24
24
  "commander": "^14.0.3",
25
25
  "diff": "^8.0.3",