@clef-sh/core 0.1.7-beta.48 → 0.1.8
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/artifact/packer.d.ts.map +1 -1
- package/dist/artifact/signer.d.ts +66 -0
- package/dist/artifact/signer.d.ts.map +1 -0
- package/dist/artifact/types.d.ts +10 -0
- package/dist/artifact/types.d.ts.map +1 -1
- package/dist/index.d.mts +2 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +119 -10
- package/dist/index.js.map +4 -4
- package/dist/index.mjs +108 -5
- package/dist/index.mjs.map +4 -4
- package/dist/kms/types.d.ts +2 -0
- package/dist/kms/types.d.ts.map +1 -1
- package/dist/types/index.d.ts +2 -0
- package/dist/types/index.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
|
-
"sources": ["../src/types/index.ts", "../src/manifest/parser.ts", "../src/recipients/validator.ts", "../src/scanner/index.ts", "../src/scanner/patterns.ts", "../src/scanner/ignore.ts", "../src/matrix/manager.ts", "../src/pending/metadata.ts", "../src/schema/validator.ts", "../src/diff/engine.ts", "../src/bulk/ops.ts", "../src/git/integration.ts", "../src/sops/client.ts", "../src/sops/resolver.ts", "../src/sops/bundled.ts", "../src/dependencies/checker.ts", "../src/age/keygen.ts", "../src/lint/runner.ts", "../src/consumption/client.ts", "../src/import/index.ts", "../src/import/parsers.ts", "../src/recipients/index.ts", "../src/recipients/requests.ts", "../src/drift/detector.ts", "../src/report/generator.ts", "../src/report/sanitizer.ts", "../src/report/transformer.ts", "../src/report/cloud-client.ts", "../src/report/ci-context.ts", "../src/merge/driver.ts", "../src/service-identity/manager.ts", "../src/artifact/resolve.ts", "../src/artifact/packer.ts"],
|
|
4
|
-
"sourcesContent": ["/** Supported file extensions for encrypted SOPS files managed by Clef. */\nexport const CLEF_SUPPORTED_EXTENSIONS = [\".enc.yaml\", \".enc.json\"] as const;\n\n// \u2500\u2500 Subprocess Runner (dependency injection for sops & git) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/** Result returned by a subprocess invocation. */\nexport interface SubprocessResult {\n stdout: string;\n stderr: string;\n exitCode: number;\n}\n\n/**\n * Abstraction over subprocess execution used throughout the core library.\n * Inject a real implementation (`NodeSubprocessRunner`) in production and a\n * mock via `jest.fn()` in unit tests \u2014 no real subprocess calls in tests.\n */\nexport interface SubprocessRunner {\n run(command: string, args: string[], options?: SubprocessOptions): Promise<SubprocessResult>;\n}\n\n/** Options forwarded to the subprocess. */\nexport interface SubprocessOptions {\n /** Working directory for the child process. */\n cwd?: string;\n /** Data to pipe to stdin. */\n stdin?: string;\n /** Additional environment variables for the child process. */\n env?: Record<string, string>;\n}\n\n// \u2500\u2500 Manifest \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/** Cloud integration configuration stored in the manifest. */\nexport interface ClefCloudConfig {\n integrationId: string;\n}\n\n/** Parsed and validated contents of a `clef.yaml` manifest file. */\nexport interface ClefManifest {\n version: number;\n environments: ClefEnvironment[];\n namespaces: ClefNamespace[];\n sops: SopsConfig;\n file_pattern: string;\n service_identities?: ServiceIdentityDefinition[];\n cloud?: ClefCloudConfig;\n}\n\n/** Per-environment SOPS backend override. */\nexport interface EnvironmentSopsOverride {\n backend: \"age\" | \"awskms\" | \"gcpkms\" | \"azurekv\" | \"pgp\";\n aws_kms_arn?: string;\n gcp_kms_resource_id?: string;\n azure_kv_url?: string;\n pgp_fingerprint?: string;\n}\n\n/** A single deployment environment declared in the manifest. */\nexport interface ClefEnvironment {\n name: string;\n description: string;\n /** When `true`, write operations require explicit confirmation. */\n protected?: boolean;\n /** Per-environment SOPS backend override. Falls back to global `sops` config when absent. */\n sops?: EnvironmentSopsOverride;\n /** Per-environment age recipient overrides. When set, these recipients are used instead of global. */\n recipients?: (string | { key: string; label?: string })[];\n}\n\n/**\n * Resolve the effective backend configuration for an environment.\n * Returns the per-env override if present, otherwise falls back to the global `sops` config.\n */\nexport function resolveBackendConfig(\n manifest: ClefManifest,\n environment: string,\n): EnvironmentSopsOverride {\n const env = manifest.environments.find((e) => e.name === environment);\n if (env?.sops) return env.sops;\n return {\n backend: manifest.sops.default_backend,\n aws_kms_arn: manifest.sops.aws_kms_arn,\n gcp_kms_resource_id: manifest.sops.gcp_kms_resource_id,\n azure_kv_url: manifest.sops.azure_kv_url,\n pgp_fingerprint: manifest.sops.pgp_fingerprint,\n };\n}\n\n/**\n * Resolve per-environment recipients if defined.\n * Returns the environment's `recipients` array if non-empty, otherwise `undefined`\n * (caller should fall back to global recipients).\n */\nexport function resolveRecipientsForEnvironment(\n manifest: ClefManifest,\n environment: string,\n): (string | { key: string; label?: string })[] | undefined {\n const env = manifest.environments.find((e) => e.name === environment);\n if (env?.recipients && env.recipients.length > 0) return env.recipients;\n return undefined;\n}\n\n/** A secrets namespace declared in the manifest. */\nexport interface ClefNamespace {\n name: string;\n description: string;\n /** Optional path to a YAML schema file for this namespace. */\n schema?: string;\n /** Optional list of owner identifiers for this namespace. */\n owners?: string[];\n}\n\n/** SOPS encryption backend configuration from the manifest. */\nexport interface SopsConfig {\n default_backend: \"age\" | \"awskms\" | \"gcpkms\" | \"azurekv\" | \"pgp\";\n aws_kms_arn?: string;\n gcp_kms_resource_id?: string;\n azure_kv_url?: string;\n pgp_fingerprint?: string;\n}\n\n/**\n * Per-developer local config stored in `.clef/config.yaml` (gitignored).\n * Holds settings that must not be committed, such as the age private key path.\n */\nexport interface ClefLocalConfig {\n /** Path to the age private key file for this developer. */\n age_key_file?: string;\n /**\n * Where the age private key was stored during init.\n * - \"keychain\" \u2014 OS keychain (macOS Keychain / Linux libsecret / Windows Credential Manager)\n * - \"file\" \u2014 filesystem at age_key_file path\n *\n * Used to provide targeted guidance when the key cannot be resolved.\n */\n age_key_storage?: \"keychain\" | \"file\";\n /** Label identifying this repo's age key in the OS keychain or filesystem. */\n age_keychain_label?: string;\n}\n\n// \u2500\u2500 Matrix \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/** A single cell in the namespace \u00D7 environment matrix. */\nexport interface MatrixCell {\n namespace: string;\n environment: string;\n /** Absolute path to the encrypted SOPS file for this cell. */\n filePath: string;\n /** Whether the encrypted file exists on disk. */\n exists: boolean;\n}\n\n/** An issue detected within a single matrix cell. */\nexport interface MatrixIssue {\n type: \"missing_keys\" | \"schema_warning\" | \"sops_error\";\n message: string;\n /** The affected key name, if applicable. */\n key?: string;\n}\n\n/** Decrypted status summary for one matrix cell. */\nexport interface MatrixStatus {\n cell: MatrixCell;\n /** Number of keys in the decrypted file. */\n keyCount: number;\n /** Number of keys currently marked as pending placeholders. */\n pendingCount: number;\n /** Timestamp from SOPS metadata, or `null` if unavailable. */\n lastModified: Date | null;\n issues: MatrixIssue[];\n}\n\n// \u2500\u2500 Schema \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/** A namespace schema loaded from a YAML schema file. */\nexport interface NamespaceSchema {\n keys: Record<string, SchemaKey>;\n}\n\n/** Definition for a single key in a namespace schema. */\nexport interface SchemaKey {\n type: \"string\" | \"integer\" | \"boolean\";\n required: boolean;\n /** Regex pattern the value must match (strings only). */\n pattern?: string;\n default?: unknown;\n description?: string;\n /** Maximum numeric value (integers only). */\n max?: number;\n}\n\n/** A hard validation error produced by `SchemaValidator.validate`. */\nexport interface ValidationError {\n key: string;\n message: string;\n rule: \"required\" | \"type\" | \"pattern\";\n}\n\n/** A soft validation warning produced by `SchemaValidator.validate`. */\nexport interface ValidationWarning {\n key: string;\n message: string;\n rule: \"undeclared\" | \"max_exceeded\";\n}\n\n/** Result of validating a set of decrypted values against a namespace schema. */\nexport interface ValidationResult {\n /** `true` when there are no errors (warnings are allowed). */\n valid: boolean;\n errors: ValidationError[];\n warnings: ValidationWarning[];\n}\n\n// \u2500\u2500 Diff \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/** Status of a single key when diffing two environments. */\nexport type DiffStatus = \"changed\" | \"identical\" | \"missing_a\" | \"missing_b\";\n\n/** One row in a diff result representing a single key comparison. */\nexport interface DiffRow {\n key: string;\n /** Value from environment A, or `null` if the key is absent. */\n valueA: string | null;\n /** Value from environment B, or `null` if the key is absent. */\n valueB: string | null;\n status: DiffStatus;\n}\n\n/** The full diff result for a namespace across two environments. */\nexport interface DiffResult {\n namespace: string;\n envA: string;\n envB: string;\n rows: DiffRow[];\n}\n\n// \u2500\u2500 Lint \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/** Severity level of a lint issue. */\nexport type LintSeverity = \"error\" | \"warning\" | \"info\";\n\n/** Category of a lint issue. */\nexport type LintCategory = \"matrix\" | \"schema\" | \"sops\" | \"service-identity\";\n\n/** A single issue reported by `LintRunner`. */\nexport interface LintIssue {\n severity: LintSeverity;\n category: LintCategory;\n /** Path to the affected encrypted file. */\n file: string;\n /** The affected key name, if applicable. */\n key?: string;\n message: string;\n /** CLI command that can auto-fix this issue, if one exists. */\n fixCommand?: string;\n}\n\n/** Aggregate result from a full lint run. */\nexport interface LintResult {\n issues: LintIssue[];\n /** Total number of matrix files checked (including missing ones). */\n fileCount: number;\n /** Total number of keys marked as pending placeholders across all files. */\n pendingCount: number;\n}\n\n// \u2500\u2500 Git \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/** A single git commit. */\nexport interface GitCommit {\n hash: string;\n author: string;\n date: Date;\n message: string;\n}\n\n/** Parsed output of `git status --porcelain`. */\nexport interface GitStatus {\n /** Files with staged (index) changes. */\n staged: string[];\n /** Files with unstaged (work-tree) changes. */\n unstaged: string[];\n untracked: string[];\n}\n\n// \u2500\u2500 SOPS \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/** The in-memory result of decrypting a SOPS-encrypted file. Plaintext never touches disk. */\nexport interface DecryptedFile {\n /** Flat key/value map of all decrypted secrets. */\n values: Record<string, string>;\n metadata: SopsMetadata;\n}\n\n/** SOPS metadata extracted from an encrypted file without decrypting its values. */\nexport interface SopsMetadata {\n backend: \"age\" | \"awskms\" | \"gcpkms\" | \"azurekv\" | \"pgp\";\n /** List of recipient identifiers (age public keys, KMS ARNs, Azure KV URLs, PGP fingerprints). */\n recipients: string[];\n lastModified: Date;\n}\n\n/**\n * Backend-agnostic interface for all encryption/decryption operations.\n *\n * `SopsClient` is the canonical implementation. Consumers should depend on this\n * interface rather than the concrete class so the encryption backend can be\n * replaced without touching call sites.\n */\nexport interface EncryptionBackend {\n /** Decrypt a file and return its values and metadata. */\n decrypt(filePath: string): Promise<DecryptedFile>;\n /** Encrypt a key/value map and write it to a file. */\n encrypt(\n filePath: string,\n values: Record<string, string>,\n manifest: ClefManifest,\n environment?: string,\n ): Promise<void>;\n /** Rotate encryption by adding a new recipient key. */\n reEncrypt(filePath: string, newKey: string): Promise<void>;\n /** Add an age recipient to an encrypted file (rotate + add-age). */\n addRecipient(filePath: string, key: string): Promise<void>;\n /** Remove an age recipient from an encrypted file (rotate + rm-age). */\n removeRecipient(filePath: string, key: string): Promise<void>;\n /** Check whether a file has valid encryption metadata. */\n validateEncryption(filePath: string): Promise<boolean>;\n /** Extract encryption metadata without decrypting. */\n getMetadata(filePath: string): Promise<SopsMetadata>;\n}\n\n// \u2500\u2500 Consumption \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/** Options for `ConsumptionClient.prepareEnvironment`. */\nexport interface ExecOptions {\n /** Inject only these keys (if set, all other keys are excluded). */\n only?: string[];\n /** Prepend this string to every injected environment variable name. */\n prefix?: string;\n /** When `true`, skip keys that already exist in the base environment. */\n noOverride?: boolean;\n}\n\n/** Options for `ConsumptionClient.formatExport`. */\nexport interface ExportOptions {\n format: \"env\";\n /** When `true`, omit the `export` keyword from each line. */\n noExport?: boolean;\n}\n\n// \u2500\u2500 Errors \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/**\n * Base error class for all Clef errors.\n * Carries an optional `fix` hint string describing how to resolve the issue.\n */\nexport class ClefError extends Error {\n constructor(\n message: string,\n public readonly fix?: string,\n ) {\n super(message);\n this.name = \"ClefError\";\n }\n}\n\n/** Thrown when `clef.yaml` fails parsing or schema validation. */\nexport class ManifestValidationError extends ClefError {\n constructor(\n message: string,\n public readonly field?: string,\n ) {\n super(message, field ? `Check the '${field}' field in clef.yaml` : undefined);\n this.name = \"ManifestValidationError\";\n }\n}\n\n/** Thrown when SOPS decryption fails (bad key, corrupt file, etc.). */\nexport class SopsDecryptionError extends ClefError {\n constructor(\n message: string,\n public readonly filePath?: string,\n ) {\n super(\n message,\n filePath\n ? `Ensure you have the correct key configured to decrypt '${filePath}'`\n : \"Ensure your SOPS key is configured correctly\",\n );\n this.name = \"SopsDecryptionError\";\n }\n}\n\n/** Thrown when SOPS encryption or re-encryption fails. */\nexport class SopsEncryptionError extends ClefError {\n constructor(\n message: string,\n public readonly filePath?: string,\n ) {\n super(\n message,\n filePath\n ? `Check your SOPS configuration and key access for '${filePath}'`\n : \"Check your SOPS configuration\",\n );\n this.name = \"SopsEncryptionError\";\n }\n}\n\n/** Thrown when no decryption key is found in the environment. */\nexport class SopsKeyNotFoundError extends ClefError {\n constructor(message: string) {\n super(message, \"Ensure your age key file exists and CLEF_AGE_KEY_FILE is set correctly\");\n this.name = \"SopsKeyNotFoundError\";\n }\n}\n\n/** Thrown when a git subprocess fails. */\nexport class GitOperationError extends ClefError {\n constructor(message: string, fix?: string) {\n super(message, fix ?? \"Ensure you are inside a git repository\");\n this.name = \"GitOperationError\";\n }\n}\n\n/** Thrown when a namespace schema file cannot be read or parsed. */\nexport class SchemaLoadError extends ClefError {\n constructor(\n message: string,\n public readonly filePath?: string,\n ) {\n super(\n message,\n filePath ? `Check the schema file at '${filePath}'` : \"Check your schema file syntax\",\n );\n this.name = \"SchemaLoadError\";\n }\n}\n\n// \u2500\u2500 Dependency errors \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/** Thrown when the `sops` binary is not installed. */\nexport class SopsMissingError extends ClefError {\n constructor(public readonly installHint: string) {\n super(\n \"sops is not installed.\",\n `Install it with: ${installHint}\\nThen run clef doctor to verify your setup.`,\n );\n this.name = \"SopsMissingError\";\n }\n}\n\n/** Thrown when the installed `sops` version is older than the minimum required. */\nexport class SopsVersionError extends ClefError {\n constructor(\n public readonly installed: string,\n public readonly required: string,\n public readonly installHint: string,\n ) {\n super(\n `sops v${installed} is installed but Clef requires v${required} or later.`,\n `Upgrade with: ${installHint}\\nThen run clef doctor to verify your setup.`,\n );\n this.name = \"SopsVersionError\";\n }\n}\n\n// \u2500\u2500 KMS Envelope Encryption \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport type KmsProviderType = \"aws\" | \"gcp\" | \"azure\";\n\nexport interface KmsConfig {\n provider: KmsProviderType;\n keyId: string;\n region?: string;\n}\n\n// \u2500\u2500 Service Identity \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/** Per-environment config for a service identity: either age-only or KMS envelope. */\nexport interface ServiceIdentityEnvironmentConfig {\n /** Age public key (age-only path). Mutually exclusive with `kms`. */\n recipient?: string;\n /** KMS envelope encryption config. Mutually exclusive with `recipient`. */\n kms?: KmsConfig;\n}\n\n/** Type guard: returns true when the environment config uses KMS envelope encryption. */\nexport function isKmsEnvelope(\n cfg: ServiceIdentityEnvironmentConfig,\n): cfg is ServiceIdentityEnvironmentConfig & { kms: KmsConfig } {\n return cfg.kms !== undefined;\n}\n\n/** A machine-oriented identity with scoped namespace access and per-environment age keys. */\nexport interface ServiceIdentityDefinition {\n name: string;\n description: string;\n namespaces: string[];\n environments: Record<string, ServiceIdentityEnvironmentConfig>;\n}\n\n/** A drift issue detected in a service identity configuration. */\nexport interface ServiceIdentityDriftIssue {\n identity: string;\n environment?: string;\n namespace?: string;\n type:\n | \"missing_environment\"\n | \"scope_mismatch\"\n | \"recipient_not_registered\"\n | \"orphaned_recipient\"\n | \"namespace_not_found\";\n message: string;\n fixCommand?: string;\n}\n\n// \u2500\u2500 Cross-repo drift detection \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/** A single drift issue: a key present in some environments but missing from others. */\nexport interface DriftIssue {\n namespace: string;\n key: string;\n /** Environment names where the key exists. */\n presentIn: string[];\n /** Environment names where the key is missing. */\n missingFrom: string[];\n message: string;\n}\n\n/** Result of comparing key sets across two local Clef repos without decryption. */\nexport interface DriftResult {\n issues: DriftIssue[];\n namespacesCompared: number;\n namespacesClean: number;\n localEnvironments: string[];\n remoteEnvironments: string[];\n}\n\n// \u2500\u2500 Dependency check types \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/** Version check result for a single external dependency. */\nexport interface DependencyVersion {\n /** Installed version string, e.g. `\"3.9.1\"`. */\n installed: string;\n /** Minimum required version string. */\n required: string;\n /** `true` when `installed >= required`. */\n satisfied: boolean;\n /** Platform-appropriate install/upgrade command hint. */\n installHint: string;\n /** How the binary was resolved: env override, bundled package, or system PATH. */\n source?: \"env\" | \"bundled\" | \"system\";\n /** Resolved path to the binary. */\n resolvedPath?: string;\n}\n\n/** Combined dependency check result for all required external tools. */\nexport interface DependencyStatus {\n /** `null` if `sops` is not installed or version could not be parsed. */\n sops: DependencyVersion | null;\n /** `null` if `git` is not installed or version could not be parsed. */\n git: DependencyVersion | null;\n}\n\n// \u2500\u2500 Report \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/** Schema version for {@link ClefReport}. Increment when the shape changes. */\nexport const CLEF_REPORT_SCHEMA_VERSION = 1;\n\n/** Repository identity fields included at the top of every report. */\nexport interface ReportRepoIdentity {\n repoOrigin: string;\n commitSha: string;\n branch: string;\n commitTimestamp: string;\n reportGeneratedAt: string;\n clefVersion: string;\n sopsVersion: string | null;\n}\n\n/** Manifest structure summary included in a report. */\nexport interface ReportManifestStructure {\n manifestVersion: number;\n filePattern: string;\n environments: { name: string; protected: boolean }[];\n namespaces: { name: string; hasSchema: boolean; owners: string[] }[];\n defaultBackend: string;\n}\n\n/** SOPS metadata for a single matrix cell. */\nexport interface ReportCellMetadata {\n backend: string;\n recipients: string[];\n lastModified: string | null;\n}\n\n/** A single cell in the matrix as represented in a report. */\nexport interface ReportMatrixCell {\n namespace: string;\n environment: string;\n filePath: string;\n exists: boolean;\n keyCount: number;\n pendingCount: number;\n metadata: ReportCellMetadata | null;\n}\n\n/** A sanitized policy issue \u2014 key names are never present. */\nexport interface ReportPolicyIssue {\n severity: LintSeverity;\n category: string;\n file?: string;\n namespace?: string;\n environment?: string;\n message: string;\n count?: number;\n driftCount?: number;\n sourceEnvironment?: string;\n targetEnvironment?: string;\n}\n\n/** Aggregated counts of policy issues by severity. */\nexport interface ReportIssueCounts {\n error: number;\n warning: number;\n info: number;\n}\n\n/** Policy section of a report: aggregated counts and sanitized issues. */\nexport interface ReportPolicy {\n issueCount: ReportIssueCounts;\n issues: ReportPolicyIssue[];\n}\n\n/** Summary of a single recipient across all matrix cells. */\nexport interface ReportRecipientSummary {\n type: string;\n environments: string[];\n fileCount: number;\n}\n\n/** Top-level structure for a `clef report` output. */\nexport interface ClefReport {\n schemaVersion: number;\n repoIdentity: ReportRepoIdentity;\n manifest: ReportManifestStructure;\n matrix: ReportMatrixCell[];\n policy: ReportPolicy;\n recipients: Record<string, ReportRecipientSummary>;\n}\n\n// \u2500\u2500 Cloud API types \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/** Health status for a single matrix cell in a cloud report. */\nexport type CloudCellHealthStatus = \"healthy\" | \"warning\" | \"critical\" | \"unknown\";\n\n/** A single cell summary sent to the Cloud API. */\nexport interface CloudReportCell {\n namespace: string;\n environment: string;\n healthStatus: CloudCellHealthStatus;\n description: string;\n}\n\n/** Summary section of a cloud API report. */\nexport interface CloudReportSummary {\n filesScanned: number;\n namespaces: string[];\n environments: string[];\n cells: CloudReportCell[];\n violations: number;\n passed: boolean;\n}\n\n/** Drift entry for a single namespace in a cloud report. */\nexport interface CloudReportDrift {\n namespace: string;\n isDrifted: boolean;\n driftCount: number;\n}\n\n/** A single policy result in a cloud report. */\nexport interface CloudPolicyResult {\n ruleId: string;\n ruleName: string;\n passed: boolean;\n severity: string;\n message: string;\n scope?: { namespace?: string; environment?: string };\n}\n\n/** CI context attached to cloud reports when collectCIContext is enabled. */\nexport interface CloudCIContext {\n provider: string;\n pipelineUrl?: string;\n trigger?: string;\n}\n\n/** The report payload sent to the Cloud API. */\nexport interface CloudApiReport {\n commitSha: string;\n branch: string;\n commitTimestamp: number;\n cliVersion: string;\n summary: CloudReportSummary;\n drift: CloudReportDrift[];\n policyResults: CloudPolicyResult[];\n ciContext?: CloudCIContext;\n}\n\n/** Batch payload for backfill submissions (max 500, oldest\u2192newest). */\nexport interface CloudBatchPayload {\n reports: CloudApiReport[];\n}\n\n/** Response from GET /api/v1/integrations/:integrationId. */\nexport interface CloudIntegrationResponse {\n lastCommitSha: string | null;\n config: {\n collectCIContext: boolean;\n };\n}\n\n/** Response from POST /api/v1/reports. */\nexport interface CloudReportResponse {\n id: string;\n commitSha: string;\n}\n\n/** Response from POST /api/v1/reports/batch. */\nexport interface CloudBatchResponse {\n accepted: number;\n reportIds: string[];\n}\n\n/** Thrown when a Clef Pro API request fails. */\nexport class CloudApiError extends ClefError {\n constructor(\n message: string,\n public readonly statusCode: number,\n fix?: string,\n ) {\n super(message, fix);\n this.name = \"CloudApiError\";\n }\n}\n", "/**\n * TIER 1 MODULE \u2014 Security and correctness critical.\n *\n * This module requires exhaustive test coverage. Before\n * adding or modifying code here:\n * 1. Add tests for the happy path\n * 2. Add tests for all documented error paths\n * 3. Add at least one boundary/edge case test\n *\n * Coverage threshold: 95% lines/functions, 90% branches.\n * See docs/contributing/testing.md for the rationale.\n */\nimport * as fs from \"fs\";\nimport * as YAML from \"yaml\";\nimport {\n ClefManifest,\n ClefCloudConfig,\n ClefEnvironment,\n ManifestValidationError,\n ServiceIdentityDefinition,\n ServiceIdentityEnvironmentConfig,\n} from \"../types\";\nimport { validateAgePublicKey } from \"../recipients/validator\";\n\n/**\n * Canonical filename for the Clef manifest.\n * All code that references this filename must import this constant.\n */\nexport const CLEF_MANIFEST_FILENAME = \"clef.yaml\";\n\nconst VALID_BACKENDS = [\"age\", \"awskms\", \"gcpkms\", \"azurekv\", \"pgp\"] as const;\nconst VALID_TOP_LEVEL_KEYS = [\n \"version\",\n \"environments\",\n \"namespaces\",\n \"sops\",\n \"file_pattern\",\n \"service_identities\",\n \"cloud\",\n];\nconst ENV_NAME_PATTERN = /^[a-z][a-z0-9_-]*$/;\nconst FILE_PATTERN_REQUIRED_TOKENS = [\"{namespace}\", \"{environment}\"];\n\n/**\n * Parses and validates `clef.yaml` manifest files.\n *\n * @example\n * ```ts\n * const parser = new ManifestParser();\n * const manifest = parser.parse(\"/path/to/clef.yaml\");\n * ```\n */\nexport class ManifestParser {\n /**\n * Read and validate a `clef.yaml` file from disk.\n *\n * @param filePath - Absolute or relative path to the manifest file.\n * @returns Validated {@link ClefManifest}.\n * @throws {@link ManifestValidationError} If the file cannot be read, contains invalid YAML,\n * or fails schema validation.\n */\n parse(filePath: string): ClefManifest {\n let raw: string;\n try {\n raw = fs.readFileSync(filePath, \"utf-8\");\n } catch {\n throw new ManifestValidationError(\n `Could not read manifest file at '${filePath}'. Run 'clef init' to create one.`,\n );\n }\n\n let parsed: unknown;\n try {\n parsed = YAML.parse(raw);\n } catch {\n throw new ManifestValidationError(\n \"Manifest file contains invalid YAML. Check for syntax errors in clef.yaml.\",\n );\n }\n\n return this.validate(parsed);\n }\n\n /**\n * Validate an already-parsed object against the manifest schema.\n *\n * @param input - Raw value returned by `YAML.parse`.\n * @returns Validated {@link ClefManifest}.\n * @throws {@link ManifestValidationError} On any schema violation.\n */\n validate(input: unknown): ClefManifest {\n if (input === null || input === undefined || typeof input !== \"object\") {\n throw new ManifestValidationError(\n \"Manifest must be a YAML object, not null or a scalar value.\",\n \"root\",\n );\n }\n\n const obj = input as Record<string, unknown>;\n\n // Check for unknown top-level keys\n for (const key of Object.keys(obj)) {\n if (!VALID_TOP_LEVEL_KEYS.includes(key)) {\n throw new ManifestValidationError(\n `Unknown top-level key '${key}' in manifest. Valid keys are: ${VALID_TOP_LEVEL_KEYS.join(\", \")}.`,\n key,\n );\n }\n }\n\n // version\n if (obj.version === undefined) {\n throw new ManifestValidationError(\"Missing required field 'version'.\", \"version\");\n }\n if (typeof obj.version !== \"number\" || obj.version !== 1) {\n throw new ManifestValidationError(\n \"Field 'version' must be 1. Only version 1 is currently supported.\",\n \"version\",\n );\n }\n\n // environments\n if (!obj.environments) {\n throw new ManifestValidationError(\n \"Missing required field 'environments'. Define at least one environment.\",\n \"environments\",\n );\n }\n if (!Array.isArray(obj.environments) || obj.environments.length === 0) {\n throw new ManifestValidationError(\n \"Field 'environments' must be a non-empty array.\",\n \"environments\",\n );\n }\n const environments: ClefEnvironment[] = obj.environments.map((env: unknown, i: number) => {\n if (typeof env !== \"object\" || env === null) {\n throw new ManifestValidationError(\n `Environment at index ${i} must be an object with 'name' and 'description'.`,\n \"environments\",\n );\n }\n const envObj = env as Record<string, unknown>;\n if (!envObj.name || typeof envObj.name !== \"string\") {\n throw new ManifestValidationError(\n `Environment at index ${i} is missing a 'name' string.`,\n \"environments\",\n );\n }\n if (!ENV_NAME_PATTERN.test(envObj.name)) {\n throw new ManifestValidationError(\n `Environment name '${envObj.name}' is invalid. Names must start with a lowercase letter and contain only lowercase letters, digits, hyphens, and underscores.`,\n \"environments\",\n );\n }\n if (!envObj.description || typeof envObj.description !== \"string\") {\n throw new ManifestValidationError(\n `Environment '${envObj.name}' is missing a 'description' string.`,\n \"environments\",\n );\n }\n\n const result: ClefEnvironment = {\n name: envObj.name,\n description: envObj.description,\n ...(typeof envObj.protected === \"boolean\" ? { protected: envObj.protected } : {}),\n };\n\n // Parse optional per-environment sops override\n if (envObj.sops !== undefined) {\n if (typeof envObj.sops !== \"object\" || envObj.sops === null) {\n throw new ManifestValidationError(\n `Environment '${envObj.name}' has an invalid 'sops' field. It must be an object.`,\n \"environments\",\n );\n }\n const sopsOverride = envObj.sops as Record<string, unknown>;\n if (!sopsOverride.backend || typeof sopsOverride.backend !== \"string\") {\n throw new ManifestValidationError(\n `Environment '${envObj.name}' sops override is missing 'backend'. Must be one of: ${VALID_BACKENDS.join(\", \")}.`,\n \"environments\",\n );\n }\n if (!(VALID_BACKENDS as readonly string[]).includes(sopsOverride.backend)) {\n throw new ManifestValidationError(\n `Environment '${envObj.name}' has invalid sops backend '${sopsOverride.backend}'. Must be one of: ${VALID_BACKENDS.join(\", \")}.`,\n \"environments\",\n );\n }\n const backend = sopsOverride.backend as (typeof VALID_BACKENDS)[number];\n\n // Validate required fields per backend\n if (backend === \"awskms\" && typeof sopsOverride.aws_kms_arn !== \"string\") {\n throw new ManifestValidationError(\n `Environment '${envObj.name}' uses 'awskms' backend but is missing 'aws_kms_arn'.`,\n \"environments\",\n );\n }\n if (backend === \"gcpkms\" && typeof sopsOverride.gcp_kms_resource_id !== \"string\") {\n throw new ManifestValidationError(\n `Environment '${envObj.name}' uses 'gcpkms' backend but is missing 'gcp_kms_resource_id'.`,\n \"environments\",\n );\n }\n if (backend === \"azurekv\" && typeof sopsOverride.azure_kv_url !== \"string\") {\n throw new ManifestValidationError(\n `Environment '${envObj.name}' uses 'azurekv' backend but is missing 'azure_kv_url'.`,\n \"environments\",\n );\n }\n if (backend === \"pgp\" && typeof sopsOverride.pgp_fingerprint !== \"string\") {\n throw new ManifestValidationError(\n `Environment '${envObj.name}' uses 'pgp' backend but is missing 'pgp_fingerprint'.`,\n \"environments\",\n );\n }\n\n result.sops = {\n backend,\n ...(typeof sopsOverride.aws_kms_arn === \"string\"\n ? { aws_kms_arn: sopsOverride.aws_kms_arn }\n : {}),\n ...(typeof sopsOverride.gcp_kms_resource_id === \"string\"\n ? { gcp_kms_resource_id: sopsOverride.gcp_kms_resource_id }\n : {}),\n ...(typeof sopsOverride.azure_kv_url === \"string\"\n ? { azure_kv_url: sopsOverride.azure_kv_url }\n : {}),\n ...(typeof sopsOverride.pgp_fingerprint === \"string\"\n ? { pgp_fingerprint: sopsOverride.pgp_fingerprint }\n : {}),\n };\n }\n\n // Parse optional per-environment recipients\n if (envObj.recipients !== undefined) {\n if (!Array.isArray(envObj.recipients)) {\n throw new ManifestValidationError(\n `Environment '${envObj.name}' has an invalid 'recipients' field. It must be an array.`,\n \"environments\",\n );\n }\n const parsedRecipients: (string | { key: string; label?: string })[] = [];\n for (let ri = 0; ri < envObj.recipients.length; ri++) {\n const entry = envObj.recipients[ri];\n if (typeof entry === \"string\") {\n const validation = validateAgePublicKey(entry);\n if (!validation.valid) {\n throw new ManifestValidationError(\n `Environment '${envObj.name}' recipient at index ${ri}: ${validation.error}`,\n \"environments\",\n );\n }\n parsedRecipients.push(validation.key!);\n } else if (typeof entry === \"object\" && entry !== null) {\n const entryObj = entry as Record<string, unknown>;\n if (typeof entryObj.key !== \"string\") {\n throw new ManifestValidationError(\n `Environment '${envObj.name}' recipient at index ${ri} must have a 'key' string.`,\n \"environments\",\n );\n }\n const validation = validateAgePublicKey(entryObj.key);\n if (!validation.valid) {\n throw new ManifestValidationError(\n `Environment '${envObj.name}' recipient at index ${ri}: ${validation.error}`,\n \"environments\",\n );\n }\n const recipientObj: { key: string; label?: string } = { key: validation.key! };\n if (typeof entryObj.label === \"string\") {\n recipientObj.label = entryObj.label;\n }\n parsedRecipients.push(recipientObj);\n } else {\n throw new ManifestValidationError(\n `Environment '${envObj.name}' recipient at index ${ri} must be a string or object.`,\n \"environments\",\n );\n }\n }\n if (parsedRecipients.length > 0) {\n result.recipients = parsedRecipients;\n }\n }\n\n return result;\n });\n\n // Check for duplicate environment names\n const envNames = new Set<string>();\n for (const env of environments) {\n if (envNames.has(env.name)) {\n throw new ManifestValidationError(\n `Duplicate environment name '${env.name}'. Each environment must have a unique name.`,\n \"environments\",\n );\n }\n envNames.add(env.name);\n }\n\n // namespaces\n // Design decision: all namespaces are encrypted. There is no `encrypted: false`\n // option on namespace definitions. This is intentional \u2014 see docs/guide/concepts.md\n // \"Design decision: all namespaces are encrypted\" for the full rationale.\n if (!obj.namespaces) {\n throw new ManifestValidationError(\n \"Missing required field 'namespaces'. Define at least one namespace.\",\n \"namespaces\",\n );\n }\n if (!Array.isArray(obj.namespaces) || obj.namespaces.length === 0) {\n throw new ManifestValidationError(\n \"Field 'namespaces' must be a non-empty array.\",\n \"namespaces\",\n );\n }\n const namespaces = obj.namespaces.map((ns: unknown, i: number) => {\n if (typeof ns !== \"object\" || ns === null) {\n throw new ManifestValidationError(\n `Namespace at index ${i} must be an object with 'name' and 'description'.`,\n \"namespaces\",\n );\n }\n const nsObj = ns as Record<string, unknown>;\n if (!nsObj.name || typeof nsObj.name !== \"string\") {\n throw new ManifestValidationError(\n `Namespace at index ${i} is missing a 'name' string.`,\n \"namespaces\",\n );\n }\n if (!nsObj.description || typeof nsObj.description !== \"string\") {\n throw new ManifestValidationError(\n `Namespace '${nsObj.name}' is missing a 'description' string.`,\n \"namespaces\",\n );\n }\n return {\n name: nsObj.name,\n description: nsObj.description,\n ...(typeof nsObj.schema === \"string\" ? { schema: nsObj.schema } : {}),\n ...(Array.isArray(nsObj.owners) ? { owners: nsObj.owners as string[] } : {}),\n };\n });\n\n // Check for duplicate namespace names\n const nsNames = new Set<string>();\n for (const ns of namespaces) {\n if (nsNames.has(ns.name)) {\n throw new ManifestValidationError(\n `Duplicate namespace name '${ns.name}'. Each namespace must have a unique name.`,\n \"namespaces\",\n );\n }\n nsNames.add(ns.name);\n }\n\n // sops\n if (!obj.sops) {\n throw new ManifestValidationError(\n \"Missing required field 'sops'. Configure at least 'default_backend'.\",\n \"sops\",\n );\n }\n if (typeof obj.sops !== \"object\" || obj.sops === null) {\n throw new ManifestValidationError(\"Field 'sops' must be an object.\", \"sops\");\n }\n const sopsObj = obj.sops as Record<string, unknown>;\n if (!sopsObj.default_backend || typeof sopsObj.default_backend !== \"string\") {\n throw new ManifestValidationError(\n \"Field 'sops.default_backend' is required and must be one of: age, awskms, gcpkms, azurekv, pgp.\",\n \"sops.default_backend\",\n );\n }\n if (!(VALID_BACKENDS as readonly string[]).includes(sopsObj.default_backend)) {\n throw new ManifestValidationError(\n `Invalid sops.default_backend '${sopsObj.default_backend}'. Must be one of: ${VALID_BACKENDS.join(\", \")}.`,\n \"sops.default_backend\",\n );\n }\n\n const sopsConfig = {\n default_backend: sopsObj.default_backend as (typeof VALID_BACKENDS)[number],\n ...(typeof sopsObj.aws_kms_arn === \"string\" ? { aws_kms_arn: sopsObj.aws_kms_arn } : {}),\n ...(typeof sopsObj.gcp_kms_resource_id === \"string\"\n ? { gcp_kms_resource_id: sopsObj.gcp_kms_resource_id }\n : {}),\n ...(typeof sopsObj.azure_kv_url === \"string\" ? { azure_kv_url: sopsObj.azure_kv_url } : {}),\n ...(typeof sopsObj.pgp_fingerprint === \"string\"\n ? { pgp_fingerprint: sopsObj.pgp_fingerprint }\n : {}),\n };\n\n // Post-processing: validate per-env recipients are only used with age backend\n for (const env of environments) {\n if (env.recipients && env.recipients.length > 0) {\n const effectiveBackend = env.sops?.backend ?? sopsConfig.default_backend;\n if (effectiveBackend !== \"age\") {\n throw new ManifestValidationError(\n `Environment '${env.name}' has per-environment recipients but uses '${effectiveBackend}' backend. Per-environment recipients are only supported with the 'age' backend.`,\n \"environments\",\n );\n }\n }\n }\n\n // file_pattern\n if (!obj.file_pattern || typeof obj.file_pattern !== \"string\") {\n throw new ManifestValidationError(\n \"Missing required field 'file_pattern'. Example: '{namespace}/{environment}.enc.yaml'.\",\n \"file_pattern\",\n );\n }\n for (const token of FILE_PATTERN_REQUIRED_TOKENS) {\n if (!obj.file_pattern.includes(token)) {\n throw new ManifestValidationError(\n `file_pattern must contain '${token}'. Got: '${obj.file_pattern}'.`,\n \"file_pattern\",\n );\n }\n }\n\n // service_identities (optional)\n let serviceIdentities: ServiceIdentityDefinition[] | undefined;\n if (obj.service_identities !== undefined) {\n if (!Array.isArray(obj.service_identities)) {\n throw new ManifestValidationError(\n \"Field 'service_identities' must be an array.\",\n \"service_identities\",\n );\n }\n serviceIdentities = obj.service_identities.map((si: unknown, i: number) => {\n if (typeof si !== \"object\" || si === null) {\n throw new ManifestValidationError(\n `Service identity at index ${i} must be an object.`,\n \"service_identities\",\n );\n }\n const siObj = si as Record<string, unknown>;\n\n if (!siObj.name || typeof siObj.name !== \"string\") {\n throw new ManifestValidationError(\n `Service identity at index ${i} is missing a 'name' string.`,\n \"service_identities\",\n );\n }\n const siName = siObj.name;\n\n if (!siObj.description || typeof siObj.description !== \"string\") {\n throw new ManifestValidationError(\n `Service identity '${siName}' is missing a 'description' string.`,\n \"service_identities\",\n );\n }\n\n // namespaces\n if (!Array.isArray(siObj.namespaces) || siObj.namespaces.length === 0) {\n throw new ManifestValidationError(\n `Service identity '${siName}' must have a non-empty 'namespaces' array.`,\n \"service_identities\",\n );\n }\n for (const ns of siObj.namespaces) {\n if (typeof ns !== \"string\") {\n throw new ManifestValidationError(\n `Service identity '${siName}' has a non-string entry in 'namespaces'.`,\n \"service_identities\",\n );\n }\n if (!nsNames.has(ns)) {\n throw new ManifestValidationError(\n `Service identity '${siName}' references unknown namespace '${ns}'.`,\n \"service_identities\",\n );\n }\n }\n\n // environments\n if (\n !siObj.environments ||\n typeof siObj.environments !== \"object\" ||\n Array.isArray(siObj.environments)\n ) {\n throw new ManifestValidationError(\n `Service identity '${siName}' must have an 'environments' object.`,\n \"service_identities\",\n );\n }\n const siEnvs = siObj.environments as Record<string, unknown>;\n const parsedEnvs: Record<string, ServiceIdentityEnvironmentConfig> = {};\n\n // Validate that all declared environments are covered\n for (const env of environments) {\n if (!(env.name in siEnvs)) {\n throw new ManifestValidationError(\n `Service identity '${siName}' is missing environment '${env.name}'. Service identities must cover all declared environments.`,\n \"service_identities\",\n );\n }\n }\n\n for (const [envName, envVal] of Object.entries(siEnvs)) {\n if (!envNames.has(envName)) {\n throw new ManifestValidationError(\n `Service identity '${siName}' references unknown environment '${envName}'.`,\n \"service_identities\",\n );\n }\n if (typeof envVal !== \"object\" || envVal === null) {\n throw new ManifestValidationError(\n `Service identity '${siName}' environment '${envName}' must be an object with 'recipient' or 'kms'.`,\n \"service_identities\",\n );\n }\n const envObj = envVal as Record<string, unknown>;\n const hasRecipient = envObj.recipient !== undefined;\n const hasKms = envObj.kms !== undefined;\n\n if (hasRecipient && hasKms) {\n throw new ManifestValidationError(\n `Service identity '${siName}' environment '${envName}': 'recipient' and 'kms' are mutually exclusive.`,\n \"service_identities\",\n );\n }\n if (!hasRecipient && !hasKms) {\n throw new ManifestValidationError(\n `Service identity '${siName}' environment '${envName}' must have either 'recipient' or 'kms'.`,\n \"service_identities\",\n );\n }\n\n if (hasRecipient) {\n if (typeof envObj.recipient !== \"string\") {\n throw new ManifestValidationError(\n `Service identity '${siName}' environment '${envName}': 'recipient' must be a string.`,\n \"service_identities\",\n );\n }\n const recipientValidation = validateAgePublicKey(envObj.recipient);\n if (!recipientValidation.valid) {\n throw new ManifestValidationError(\n `Service identity '${siName}' environment '${envName}': ${recipientValidation.error}`,\n \"service_identities\",\n );\n }\n parsedEnvs[envName] = { recipient: recipientValidation.key! };\n } else {\n const kmsObj = envObj.kms as Record<string, unknown>;\n if (typeof kmsObj !== \"object\" || kmsObj === null) {\n throw new ManifestValidationError(\n `Service identity '${siName}' environment '${envName}': 'kms' must be an object.`,\n \"service_identities\",\n );\n }\n const validProviders = [\"aws\", \"gcp\", \"azure\"];\n if (!kmsObj.provider || !validProviders.includes(kmsObj.provider as string)) {\n throw new ManifestValidationError(\n `Service identity '${siName}' environment '${envName}': kms.provider must be one of: ${validProviders.join(\", \")}.`,\n \"service_identities\",\n );\n }\n if (!kmsObj.keyId || typeof kmsObj.keyId !== \"string\") {\n throw new ManifestValidationError(\n `Service identity '${siName}' environment '${envName}': kms.keyId must be a non-empty string.`,\n \"service_identities\",\n );\n }\n parsedEnvs[envName] = {\n kms: {\n provider: kmsObj.provider as \"aws\" | \"gcp\" | \"azure\",\n keyId: kmsObj.keyId,\n region: typeof kmsObj.region === \"string\" ? kmsObj.region : undefined,\n },\n };\n }\n }\n\n return {\n name: siName,\n description: siObj.description as string,\n namespaces: siObj.namespaces as string[],\n environments: parsedEnvs,\n };\n });\n\n // Check for duplicate identity names\n const siNames = new Set<string>();\n for (const si of serviceIdentities) {\n if (siNames.has(si.name)) {\n throw new ManifestValidationError(\n `Duplicate service identity name '${si.name}'.`,\n \"service_identities\",\n );\n }\n siNames.add(si.name);\n }\n }\n\n // cloud (optional)\n let cloud: ClefCloudConfig | undefined;\n if (obj.cloud !== undefined) {\n if (typeof obj.cloud !== \"object\" || obj.cloud === null || Array.isArray(obj.cloud)) {\n throw new ManifestValidationError(\"Field 'cloud' must be an object.\", \"cloud\");\n }\n const cloudObj = obj.cloud as Record<string, unknown>;\n if (typeof cloudObj.integrationId !== \"string\" || cloudObj.integrationId.length === 0) {\n throw new ManifestValidationError(\n \"Field 'cloud.integrationId' is required and must be a non-empty string.\",\n \"cloud\",\n );\n }\n cloud = { integrationId: cloudObj.integrationId };\n }\n\n return {\n version: 1,\n environments,\n namespaces,\n sops: sopsConfig,\n file_pattern: obj.file_pattern,\n ...(serviceIdentities ? { service_identities: serviceIdentities } : {}),\n ...(cloud ? { cloud } : {}),\n };\n }\n\n /**\n * Watch a manifest file for changes and invoke a callback on each successful parse.\n *\n * @param filePath - Path to the manifest file to watch.\n * @param onChange - Called with the newly parsed manifest on each valid change.\n * @returns Unsubscribe function \u2014 call it to stop watching.\n */\n watch(filePath: string, onChange: (manifest: ClefManifest) => void): () => void {\n const watcher = fs.watch(filePath, () => {\n try {\n const manifest = this.parse(filePath);\n onChange(manifest);\n } catch {\n // Ignore parse errors during watch \u2014 file may be mid-save\n }\n });\n\n return () => {\n watcher.close();\n };\n }\n}\n", "export interface AgeKeyValidation {\n valid: boolean;\n key?: string;\n error?: string;\n}\n\nconst BECH32_CHARSET = \"qpzry9x8gf2tvdw0s3jn54khce6mua7l\";\nconst AGE_PREFIX = \"age1\";\nconst MIN_LENGTH = 10;\n\n/**\n * Validate that a string is a well-formed age public key (bech32, `age1` prefix).\n *\n * @param input - The string to validate.\n * @returns `{ valid: true, key: trimmedKey }` or `{ valid: false, error: message }`.\n */\nexport function validateAgePublicKey(input: string): AgeKeyValidation {\n const trimmed = input.trim();\n\n if (!trimmed.startsWith(AGE_PREFIX)) {\n return {\n valid: false,\n error: `age public key must start with '${AGE_PREFIX}'. Got: '${trimmed.slice(0, 10)}...'`,\n };\n }\n\n if (trimmed.length < MIN_LENGTH) {\n return {\n valid: false,\n error: `age public key is too short. Expected at least ${MIN_LENGTH} characters, got ${trimmed.length}.`,\n };\n }\n\n const body = trimmed.slice(AGE_PREFIX.length);\n for (const ch of body) {\n if (!BECH32_CHARSET.includes(ch)) {\n return {\n valid: false,\n error: `Invalid character '${ch}' in age public key. Only bech32 characters are allowed after the 'age1' prefix.`,\n };\n }\n }\n\n return { valid: true, key: trimmed };\n}\n\n/**\n * Return a short display preview of an age public key: `age1\u2026last8chars`.\n *\n * @param key - Full age public key string.\n */\nexport function keyPreview(key: string): string {\n const last8 = key.slice(-8);\n return `age1\\u2026${last8}`;\n}\n", "import * as fs from \"fs\";\nimport * as path from \"path\";\nimport type { ClefManifest, SubprocessRunner } from \"../types\";\nimport { matchPatterns, isHighEntropy, shannonEntropy, redactValue, ScanMatch } from \"./patterns\";\nimport { loadIgnoreRules, shouldIgnoreFile, shouldIgnoreMatch } from \"./ignore\";\n\nexport type { ScanMatch } from \"./patterns\";\nexport type { ClefIgnoreRules } from \"./ignore\";\nexport { shannonEntropy, isHighEntropy, matchPatterns, redactValue } from \"./patterns\";\nexport { loadIgnoreRules, shouldIgnoreFile, shouldIgnoreMatch, parseIgnoreContent } from \"./ignore\";\n\nexport interface ScanResult {\n matches: ScanMatch[];\n filesScanned: number;\n filesSkipped: number;\n unencryptedMatrixFiles: string[];\n durationMs: number;\n}\n\nexport interface ScanOptions {\n stagedOnly?: boolean;\n paths?: string[];\n severity?: \"all\" | \"high\";\n}\n\nconst ALWAYS_SKIP_EXTENSIONS = [\".enc.yaml\", \".enc.json\"] as const;\nconst ALWAYS_SKIP_NAMES = [\".clef-meta.yaml\"] as const;\nconst ALWAYS_SKIP_DIRS = [\"node_modules\", \".git\"] as const;\nconst MAX_FILE_SIZE = 1024 * 1024; // 1 MB\n\n/**\n * Scans repository files for plaintext secrets using pattern matching and entropy detection.\n *\n * @example\n * ```ts\n * const scanner = new ScanRunner(runner);\n * const result = await scanner.scan(repoRoot, manifest, { stagedOnly: true });\n * ```\n */\nexport class ScanRunner {\n constructor(private readonly runner: SubprocessRunner) {}\n\n /**\n * Scan tracked (or staged) files for secret-like values and unencrypted matrix files.\n *\n * The scan respects `.clefignore` rules and inline `# clef-ignore` suppressions.\n *\n * @param repoRoot - Absolute path to the repository root.\n * @param manifest - Parsed manifest used to identify matrix file paths.\n * @param options - Optional scan filters.\n */\n async scan(\n repoRoot: string,\n manifest: ClefManifest,\n options: ScanOptions = {},\n ): Promise<ScanResult> {\n const startMs = Date.now();\n const ignoreRules = loadIgnoreRules(repoRoot);\n const matches: ScanMatch[] = [];\n const unencryptedMatrixFiles: string[] = [];\n let filesScanned = 0;\n let filesSkipped = 0;\n\n // \u2500\u2500 Check 1: unencrypted matrix files \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n for (const ns of manifest.namespaces) {\n for (const env of manifest.environments) {\n const relPath = manifest.file_pattern\n .replace(\"{namespace}\", ns.name)\n .replace(\"{environment}\", env.name);\n const absPath = path.join(repoRoot, relPath);\n if (fs.existsSync(absPath)) {\n const content = fs.readFileSync(absPath, \"utf-8\");\n if (!content.includes(\"sops:\") && !content.includes('\"sops\"')) {\n unencryptedMatrixFiles.push(relPath);\n }\n }\n }\n }\n\n // \u2500\u2500 Determine files to scan \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n let filesToScan: string[];\n if (options.stagedOnly) {\n filesToScan = await this.getStagedFiles(repoRoot);\n } else if (options.paths && options.paths.length > 0) {\n filesToScan = await this.getFilesInPaths(repoRoot, options.paths);\n } else {\n filesToScan = await this.getAllTrackedFiles(repoRoot);\n }\n\n // \u2500\u2500 Check 2: secret-looking values \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n for (const relFile of filesToScan) {\n const absFile = path.isAbsolute(relFile) ? relFile : path.join(repoRoot, relFile);\n const relPath = path.relative(repoRoot, absFile).replace(/\\\\/g, \"/\");\n\n if (this.shouldAlwaysSkip(relPath)) {\n filesSkipped++;\n continue;\n }\n\n if (shouldIgnoreFile(relPath, ignoreRules)) {\n filesSkipped++;\n continue;\n }\n\n if (!fs.existsSync(absFile)) {\n filesSkipped++;\n continue;\n }\n\n let stat: fs.Stats;\n try {\n stat = fs.statSync(absFile);\n } catch {\n filesSkipped++;\n continue;\n }\n\n if (stat.size > MAX_FILE_SIZE) {\n filesSkipped++;\n continue;\n }\n\n if (this.isBinary(absFile)) {\n filesSkipped++;\n continue;\n }\n\n filesScanned++;\n const content = fs.readFileSync(absFile, \"utf-8\");\n const lines = content.split(\"\\n\");\n\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i];\n const lineNum = i + 1;\n\n // Inline suppress: # clef-ignore on the same line\n if (line.includes(\"# clef-ignore\")) continue;\n\n // Pattern matching\n const patternHits = matchPatterns(line, lineNum, relPath);\n for (const m of patternHits) {\n if (!shouldIgnoreMatch(m, ignoreRules)) {\n matches.push(m);\n }\n }\n\n // Entropy detection (skip when severity === 'high')\n if (options.severity !== \"high\") {\n const entropyHit = this.detectEntropy(line, lineNum, relPath);\n if (entropyHit && !shouldIgnoreMatch(entropyHit, ignoreRules)) {\n matches.push(entropyHit);\n }\n }\n }\n }\n\n return {\n matches,\n filesScanned,\n filesSkipped,\n unencryptedMatrixFiles,\n durationMs: Date.now() - startMs,\n };\n }\n\n private shouldAlwaysSkip(relPath: string): boolean {\n for (const dir of ALWAYS_SKIP_DIRS) {\n if (relPath === dir || relPath.startsWith(dir + \"/\")) return true;\n }\n for (const ext of ALWAYS_SKIP_EXTENSIONS) {\n if (relPath.endsWith(ext)) return true;\n }\n for (const name of ALWAYS_SKIP_NAMES) {\n if (relPath.endsWith(name)) return true;\n }\n return false;\n }\n\n private isBinary(filePath: string): boolean {\n try {\n const fd = fs.openSync(filePath, \"r\");\n const buf = Buffer.alloc(512);\n const bytesRead = fs.readSync(fd, buf, 0, 512, 0);\n fs.closeSync(fd);\n for (let i = 0; i < bytesRead; i++) {\n if (buf[i] === 0) return true;\n }\n return false;\n } catch {\n return false;\n }\n }\n\n private detectEntropy(line: string, lineNum: number, filePath: string): ScanMatch | null {\n // Look for values appearing after = or : (assignment positions)\n const valuePattern = /(?:=|:\\s*)[\"']?([A-Za-z0-9+/=_-]{20,})[\"']?/;\n const match = valuePattern.exec(line);\n if (!match) return null;\n\n const value = match[1];\n const entropy = shannonEntropy(value);\n\n if (!isHighEntropy(value)) return null;\n\n // Extract variable name for the preview\n const varMatch = /(\\w+)\\s*(?:=|:)/.exec(line);\n const varName = varMatch ? varMatch[1] : \"\";\n const preview = varName\n ? `${varName}=\\u2022\\u2022\\u2022\\u2022\\u2022\\u2022\\u2022\\u2022`\n : redactValue(value);\n\n return {\n file: filePath,\n line: lineNum,\n column: match.index + 1,\n matchType: \"entropy\",\n entropy,\n preview,\n };\n }\n\n private async getStagedFiles(repoRoot: string): Promise<string[]> {\n const result = await this.runner.run(\n \"git\",\n [\"diff\", \"--cached\", \"--name-only\", \"--diff-filter=ACM\"],\n { cwd: repoRoot },\n );\n if (result.exitCode !== 0 || !result.stdout.trim()) return [];\n return result.stdout.trim().split(\"\\n\");\n }\n\n private async getFilesInPaths(repoRoot: string, paths: string[]): Promise<string[]> {\n const files: string[] = [];\n for (const p of paths) {\n const absPath = path.isAbsolute(p) ? p : path.join(repoRoot, p);\n if (!fs.existsSync(absPath)) continue;\n const stat = fs.statSync(absPath);\n if (stat.isDirectory()) {\n files.push(...this.walkDir(absPath, repoRoot));\n } else {\n files.push(path.relative(repoRoot, absPath).replace(/\\\\/g, \"/\"));\n }\n }\n return files;\n }\n\n private async getAllTrackedFiles(repoRoot: string): Promise<string[]> {\n // Use git ls-files to respect .gitignore automatically\n const result = await this.runner.run(\"git\", [\"ls-files\"], { cwd: repoRoot });\n if (result.exitCode !== 0) {\n return this.walkDir(repoRoot, repoRoot);\n }\n return result.stdout.trim() ? result.stdout.trim().split(\"\\n\") : [];\n }\n\n private walkDir(dir: string, repoRoot: string): string[] {\n const files: string[] = [];\n let entries: fs.Dirent[];\n try {\n entries = fs.readdirSync(dir, { withFileTypes: true });\n } catch {\n return files;\n }\n for (const entry of entries) {\n const fullPath = path.join(dir, entry.name);\n const relPath = path.relative(repoRoot, fullPath).replace(/\\\\/g, \"/\");\n if (entry.isDirectory()) {\n if (!(ALWAYS_SKIP_DIRS as readonly string[]).includes(entry.name)) {\n files.push(...this.walkDir(fullPath, repoRoot));\n }\n } else {\n files.push(relPath);\n }\n }\n return files;\n }\n}\n", "/**\n * TIER 1 MODULE \u2014 Security and correctness critical.\n *\n * This module requires exhaustive test coverage. Before\n * adding or modifying code here:\n * 1. Add tests for the happy path\n * 2. Add tests for all documented error paths\n * 3. Add at least one boundary/edge case test\n *\n * Coverage threshold: 95% lines/functions, 90% branches.\n * See docs/contributing/testing.md for the rationale.\n */\nexport interface ScanMatch {\n file: string;\n line: number;\n column: number;\n matchType: \"pattern\" | \"entropy\";\n patternName?: string;\n entropy?: number;\n preview: string;\n}\n\ninterface PatternDef {\n name: string;\n regex: RegExp;\n}\n\nconst PATTERNS: PatternDef[] = [\n { name: \"AWS access key\", regex: /AKIA[0-9A-Z]{16}/ },\n { name: \"Stripe live key\", regex: /sk_live_[0-9a-zA-Z]{24,}/ },\n { name: \"Stripe test key\", regex: /sk_test_[0-9a-zA-Z]{24,}/ },\n { name: \"GitHub personal access token\", regex: /ghp_[0-9a-zA-Z]{36}/ },\n { name: \"GitHub OAuth token\", regex: /gho_[0-9a-zA-Z]{36}/ },\n { name: \"GitHub Actions token\", regex: /ghs_[0-9a-zA-Z]{36}/ },\n { name: \"Slack token\", regex: /xox[baprs]-[0-9a-zA-Z-]{10,}/ },\n {\n name: \"Private key header\",\n regex: /-----BEGIN (?:RSA |EC |DSA |OPENSSH )?PRIVATE KEY-----/,\n },\n {\n name: \"Generic API key\",\n regex: /(?:API_KEY|SECRET_KEY|ACCESS_TOKEN|AUTH_TOKEN)\\s*=\\s*\\S{8,}/,\n },\n { name: \"Database URL\", regex: /(?:postgres|mysql|mongodb|redis):\\/\\/[^:]+:[^@]+@/ },\n];\n\n/**\n * Calculate Shannon entropy (bits per character) of a string.\n */\nexport function shannonEntropy(str: string): number {\n if (str.length === 0) return 0;\n const freq = new Map<string, number>();\n for (const c of str) {\n freq.set(c, (freq.get(c) ?? 0) + 1);\n }\n let entropy = 0;\n for (const count of freq.values()) {\n const p = count / str.length;\n entropy -= p * Math.log2(p);\n }\n return entropy;\n}\n\n/**\n * Returns true if a string has sufficiently high entropy to be considered a potential secret.\n * Threshold: > 4.5 bits/char, minimum 20 characters.\n */\nexport function isHighEntropy(value: string, threshold = 4.5, minLength = 20): boolean {\n return value.length >= minLength && shannonEntropy(value) > threshold;\n}\n\n/**\n * Redact a matched secret value \u2014 show first 4 characters, mask the rest.\n * Never exposes more than 4 characters of any secret.\n */\nexport function redactValue(value: string): string {\n if (value.length <= 4) return \"\\u2022\\u2022\\u2022\\u2022\\u2022\\u2022\\u2022\\u2022\";\n return value.slice(0, 4) + \"\\u2022\\u2022\\u2022\\u2022\\u2022\\u2022\\u2022\\u2022\";\n}\n\n/**\n * Match a line against all known secret patterns.\n * Returns one ScanMatch per matched pattern.\n */\nexport function matchPatterns(line: string, lineNumber: number, filePath: string): ScanMatch[] {\n const matches: ScanMatch[] = [];\n for (const { name, regex } of PATTERNS) {\n const match = regex.exec(line);\n if (match) {\n matches.push({\n file: filePath,\n line: lineNumber,\n column: match.index + 1,\n matchType: \"pattern\",\n patternName: name,\n preview: redactValue(match[0]),\n });\n }\n }\n return matches;\n}\n", "import * as fs from \"fs\";\nimport * as path from \"path\";\nimport type { ScanMatch } from \"./patterns\";\n\nexport interface ClefIgnoreRules {\n files: string[];\n patterns: string[];\n paths: string[];\n}\n\n/**\n * Load .clefignore rules from the repo root.\n * Returns empty rules if the file does not exist.\n */\nexport function loadIgnoreRules(repoRoot: string): ClefIgnoreRules {\n const ignorePath = path.join(repoRoot, \".clefignore\");\n try {\n const content = fs.readFileSync(ignorePath, \"utf-8\");\n return parseIgnoreContent(content);\n } catch {\n return { files: [], patterns: [], paths: [] };\n }\n}\n\n/**\n * Parse raw `.clefignore` content into structured rules.\n * Lines starting with `ignore-pattern:` suppress named patterns; lines ending with `/`\n * suppress entire directory paths; all other lines are treated as file glob patterns.\n *\n * @param content - Raw `.clefignore` file content.\n */\nexport function parseIgnoreContent(content: string): ClefIgnoreRules {\n const files: string[] = [];\n const patterns: string[] = [];\n const paths: string[] = [];\n\n for (const rawLine of content.split(\"\\n\")) {\n const line = rawLine.trim();\n if (!line || line.startsWith(\"#\")) continue;\n\n if (line.startsWith(\"ignore-pattern:\")) {\n const patternName = line.slice(\"ignore-pattern:\".length).trim();\n if (patternName) patterns.push(patternName);\n } else if (line.endsWith(\"/\")) {\n paths.push(line.slice(0, -1));\n } else {\n files.push(line);\n }\n }\n\n return { files, patterns, paths };\n}\n\n/**\n * Returns true if a file path should be ignored per .clefignore rules.\n */\nexport function shouldIgnoreFile(filePath: string, rules: ClefIgnoreRules): boolean {\n const normalized = filePath.replace(/\\\\/g, \"/\");\n\n for (const p of rules.paths) {\n const dir = p.replace(/\\\\/g, \"/\");\n if (normalized === dir || normalized.startsWith(dir + \"/\")) return true;\n }\n\n for (const pattern of rules.files) {\n if (matchesGlob(normalized, pattern)) return true;\n }\n\n return false;\n}\n\n/**\n * Returns true if a scan match should be suppressed per .clefignore rules.\n */\nexport function shouldIgnoreMatch(match: ScanMatch, rules: ClefIgnoreRules): boolean {\n if (match.matchType === \"pattern\" && match.patternName) {\n return rules.patterns.includes(match.patternName);\n }\n return false;\n}\n\nfunction matchesGlob(filePath: string, pattern: string): boolean {\n // Convert glob to regex: support *, **, and ? wildcards.\n // Step 1: stash ** segments, Step 2: escape all regex metacharacters,\n // Step 3: restore wildcards as their regex equivalents.\n const DOUBLE_STAR = \"\\x00DS\\x00\";\n const SINGLE_STAR = \"\\x00SS\\x00\";\n const QUESTION = \"\\x00QM\\x00\";\n\n const escaped = pattern\n .replace(/\\*\\*/g, DOUBLE_STAR)\n .replace(/\\*/g, SINGLE_STAR)\n .replace(/\\?/g, QUESTION)\n .replace(/[.+^${}()|[\\]\\\\]/g, \"\\\\$&\")\n .replace(DOUBLE_STAR, \".*\")\n .replace(SINGLE_STAR, \"[^/]*\")\n .replace(QUESTION, \"[^/]\");\n\n const regex = new RegExp(\"^\" + escaped + \"$\");\n // Also match if the pattern matches a prefix directory\n const prefixRegex = new RegExp(\"^\" + escaped + \"/\");\n return regex.test(filePath) || prefixRegex.test(filePath);\n}\n", "import * as fs from \"fs\";\nimport * as path from \"path\";\nimport * as YAML from \"yaml\";\nimport { ClefManifest, MatrixCell, MatrixIssue, MatrixStatus } from \"../types\";\nimport { EncryptionBackend } from \"../types\";\nimport { getPendingKeys } from \"../pending/metadata\";\n\n/**\n * Resolves and manages the namespace \u00D7 environment matrix of encrypted files.\n *\n * @example\n * ```ts\n * const manager = new MatrixManager();\n * const cells = manager.resolveMatrix(manifest, repoRoot);\n * ```\n */\nexport class MatrixManager {\n /**\n * Build the full grid of {@link MatrixCell} objects from the manifest.\n * Each cell reflects whether its encrypted file exists on disk.\n *\n * @param manifest - Parsed manifest.\n * @param repoRoot - Absolute path to the repository root.\n */\n resolveMatrix(manifest: ClefManifest, repoRoot: string): MatrixCell[] {\n const cells: MatrixCell[] = [];\n\n for (const ns of manifest.namespaces) {\n for (const env of manifest.environments) {\n const relativePath = manifest.file_pattern\n .replace(\"{namespace}\", ns.name)\n .replace(\"{environment}\", env.name);\n const filePath = path.join(repoRoot, relativePath);\n\n cells.push({\n namespace: ns.name,\n environment: env.name,\n filePath,\n exists: fs.existsSync(filePath),\n });\n }\n }\n\n return cells;\n }\n\n /**\n * Return only the cells whose encrypted files do not yet exist on disk.\n *\n * @param manifest - Parsed manifest.\n * @param repoRoot - Absolute path to the repository root.\n */\n detectMissingCells(manifest: ClefManifest, repoRoot: string): MatrixCell[] {\n return this.resolveMatrix(manifest, repoRoot).filter((cell) => !cell.exists);\n }\n\n /**\n * Create an empty encrypted SOPS file for a missing matrix cell.\n *\n * @param cell - The cell to scaffold (must not already exist).\n * @param sopsClient - SOPS client used to write the initial encrypted file.\n * @param manifest - Parsed manifest used to determine the encryption backend.\n */\n async scaffoldCell(\n cell: MatrixCell,\n sopsClient: EncryptionBackend,\n manifest: ClefManifest,\n ): Promise<void> {\n const dir = path.dirname(cell.filePath);\n if (!fs.existsSync(dir)) {\n fs.mkdirSync(dir, { recursive: true });\n }\n\n await sopsClient.encrypt(cell.filePath, {}, manifest, cell.environment);\n }\n\n /**\n * Decrypt each cell and return key counts, pending counts, and cross-environment issues.\n *\n * @param manifest - Parsed manifest.\n * @param repoRoot - Absolute path to the repository root.\n * @param sopsClient - SOPS client used to decrypt each cell.\n */\n async getMatrixStatus(\n manifest: ClefManifest,\n repoRoot: string,\n _sopsClient: EncryptionBackend,\n ): Promise<MatrixStatus[]> {\n const cells = this.resolveMatrix(manifest, repoRoot);\n const statuses: MatrixStatus[] = [];\n\n // First pass: read key names from plaintext YAML (no decryption)\n const cellKeys = new Map<string, string[]>();\n for (const cell of cells) {\n if (cell.exists) {\n cellKeys.set(cell.filePath, this.readKeyNames(cell.filePath));\n }\n }\n\n for (const cell of cells) {\n if (!cell.exists) {\n statuses.push({\n cell,\n keyCount: 0,\n pendingCount: 0,\n lastModified: null,\n issues: [\n {\n type: \"missing_keys\",\n message: `File '${cell.filePath}' does not exist. Run 'clef init' to scaffold missing files.`,\n },\n ],\n });\n continue;\n }\n\n // Read pending count from metadata (plaintext, no decryption needed)\n let pendingCount = 0;\n try {\n const pending = await getPendingKeys(cell.filePath);\n pendingCount = pending.length;\n } catch {\n // Metadata file missing or unreadable \u2014 0 pending\n }\n\n const keys = cellKeys.get(cell.filePath) ?? [];\n const keyCount = keys.length;\n const lastModified = this.readLastModified(cell.filePath);\n const issues: MatrixIssue[] = [];\n\n // Cross-environment key drift (using plaintext key names, no decrypt)\n const siblingCells = cells.filter(\n (c) => c.namespace === cell.namespace && c.environment !== cell.environment && c.exists,\n );\n for (const sibling of siblingCells) {\n const siblingKeys = cellKeys.get(sibling.filePath) ?? [];\n const missingKeys = siblingKeys.filter((k) => !keys.includes(k));\n for (const mk of missingKeys) {\n issues.push({\n type: \"missing_keys\",\n message: `Key '${mk}' exists in ${sibling.environment} but is missing here.`,\n key: mk,\n });\n }\n }\n\n statuses.push({ cell, keyCount, pendingCount, lastModified, issues });\n }\n\n return statuses;\n }\n\n /**\n * Read top-level key names from a SOPS file without decryption.\n * SOPS stores key names in plaintext \u2014 only values are encrypted.\n */\n private readKeyNames(filePath: string): string[] {\n try {\n const raw = fs.readFileSync(filePath, \"utf-8\");\n const parsed = YAML.parse(raw);\n if (!parsed || typeof parsed !== \"object\") return [];\n return Object.keys(parsed as Record<string, unknown>).filter((k) => k !== \"sops\");\n } catch {\n return [];\n }\n }\n\n /**\n * Read the lastModified timestamp from SOPS metadata without decryption.\n */\n private readLastModified(filePath: string): Date | null {\n try {\n const raw = fs.readFileSync(filePath, \"utf-8\");\n const parsed = YAML.parse(raw) as Record<string, unknown>;\n const sops = parsed?.sops as Record<string, unknown> | undefined;\n if (sops?.lastmodified) return new Date(String(sops.lastmodified));\n return null;\n } catch {\n return null;\n }\n }\n\n /**\n * Check whether an environment has the `protected` flag set in the manifest.\n *\n * @param manifest - Parsed manifest.\n * @param environment - Environment name to check.\n */\n isProtectedEnvironment(manifest: ClefManifest, environment: string): boolean {\n const env = manifest.environments.find((e) => e.name === environment);\n return env?.protected === true;\n }\n}\n", "/**\n * TIER 1 MODULE \u2014 Security and correctness critical.\n *\n * This module requires exhaustive test coverage. Before\n * adding or modifying code here:\n * 1. Add tests for the happy path\n * 2. Add tests for all documented error paths\n * 3. Add at least one boundary/edge case test\n *\n * Coverage threshold: 95% lines/functions, 90% branches.\n * See docs/contributing/testing.md for the rationale.\n */\nimport * as fs from \"fs\";\nimport * as path from \"path\";\nimport * as crypto from \"crypto\";\nimport * as YAML from \"yaml\";\n\ninterface PendingKey {\n key: string;\n since: Date;\n setBy: string;\n}\n\ninterface PendingMetadata {\n version: 1;\n pending: PendingKey[];\n}\n\n/**\n * Derive the `.clef-meta.yaml` path from an `.enc.yaml` path.\n * Example: `database/dev.enc.yaml` \u2192 `database/dev.clef-meta.yaml`\n */\nfunction metadataPath(encryptedFilePath: string): string {\n const dir = path.dirname(encryptedFilePath);\n const base = path.basename(encryptedFilePath).replace(/\\.enc\\.(yaml|json)$/, \"\");\n return path.join(dir, `${base}.clef-meta.yaml`);\n}\n\nconst HEADER_COMMENT = \"# Managed by Clef. Do not edit manually.\\n\";\n\n/** Load pending-key metadata for an encrypted file. Returns empty metadata if the file is missing. */\nasync function loadMetadata(filePath: string): Promise<PendingMetadata> {\n const metaPath = metadataPath(filePath);\n try {\n if (!fs.existsSync(metaPath)) {\n return { version: 1, pending: [] };\n }\n const content = fs.readFileSync(metaPath, \"utf-8\");\n const parsed = YAML.parse(content);\n if (!parsed || !Array.isArray(parsed.pending)) {\n return { version: 1, pending: [] };\n }\n return {\n version: 1,\n pending: parsed.pending.map((p: { key: string; since: string; setBy: string }) => ({\n key: p.key,\n since: new Date(p.since),\n setBy: p.setBy,\n })),\n };\n } catch {\n return { version: 1, pending: [] };\n }\n}\n\n/** Write pending-key metadata to disk. Creates parent directories if needed. */\nasync function saveMetadata(filePath: string, metadata: PendingMetadata): Promise<void> {\n const metaPath = metadataPath(filePath);\n const dir = path.dirname(metaPath);\n if (!fs.existsSync(dir)) {\n fs.mkdirSync(dir, { recursive: true });\n }\n const data = {\n version: metadata.version,\n pending: metadata.pending.map((p) => ({\n key: p.key,\n since: p.since.toISOString(),\n setBy: p.setBy,\n })),\n };\n fs.writeFileSync(metaPath, HEADER_COMMENT + YAML.stringify(data), \"utf-8\");\n}\n\n/**\n * Mark one or more keys as pending (placeholder value) for an encrypted file.\n * If a key is already pending, its timestamp and `setBy` are updated.\n *\n * @param filePath - Path to the encrypted file.\n * @param keys - Key names to mark as pending.\n * @param setBy - Identifier of the actor setting these keys (e.g. a username or CI job).\n */\nasync function markPending(filePath: string, keys: string[], setBy: string): Promise<void> {\n const metadata = await loadMetadata(filePath);\n const now = new Date();\n for (const key of keys) {\n const existing = metadata.pending.findIndex((p) => p.key === key);\n if (existing >= 0) {\n // Upsert: update timestamp and setBy on re-randomization\n metadata.pending[existing] = { key, since: now, setBy };\n } else {\n metadata.pending.push({ key, since: now, setBy });\n }\n }\n await saveMetadata(filePath, metadata);\n}\n\n/** Remove keys from the pending list after they have received real values. */\nasync function markResolved(filePath: string, keys: string[]): Promise<void> {\n const metadata = await loadMetadata(filePath);\n metadata.pending = metadata.pending.filter((p) => !keys.includes(p.key));\n await saveMetadata(filePath, metadata);\n}\n\n/** Return the list of key names that are still pending for the given encrypted file. */\nasync function getPendingKeys(filePath: string): Promise<string[]> {\n const metadata = await loadMetadata(filePath);\n return metadata.pending.map((p) => p.key);\n}\n\n/** Check whether a single key is currently pending for the given encrypted file. */\nasync function isPending(filePath: string, key: string): Promise<boolean> {\n const metadata = await loadMetadata(filePath);\n return metadata.pending.some((p) => p.key === key);\n}\n\n/** Generate a cryptographically random 64-character hex string for use as a placeholder value. */\nfunction generateRandomValue(): string {\n return crypto.randomBytes(32).toString(\"hex\");\n}\n\n/**\n * Same as {@link markPending} but retries once after `retryDelayMs` on transient failure.\n *\n * @param filePath - Path to the encrypted file.\n * @param keys - Key names to mark as pending.\n * @param setBy - Identifier of the actor setting these keys.\n * @param retryDelayMs - Delay in milliseconds before the single retry (default: 200).\n */\nasync function markPendingWithRetry(\n filePath: string,\n keys: string[],\n setBy: string,\n retryDelayMs = 200,\n): Promise<void> {\n try {\n await markPending(filePath, keys, setBy);\n } catch {\n // One retry after short delay for transient failures\n await new Promise((r) => setTimeout(r, retryDelayMs));\n await markPending(filePath, keys, setBy);\n }\n}\n\nexport {\n PendingKey,\n PendingMetadata,\n metadataPath,\n loadMetadata,\n saveMetadata,\n markPending,\n markPendingWithRetry,\n markResolved,\n getPendingKeys,\n isPending,\n generateRandomValue,\n};\n", "import * as fs from \"fs\";\nimport * as YAML from \"yaml\";\nimport {\n NamespaceSchema,\n SchemaLoadError,\n ValidationError,\n ValidationResult,\n ValidationWarning,\n} from \"../types\";\n\n/**\n * Loads namespace schemas and validates decrypted key/value maps against them.\n *\n * @example\n * ```ts\n * const validator = new SchemaValidator();\n * const schema = validator.loadSchema(\"schemas/app.yaml\");\n * const result = validator.validate(decrypted.values, schema);\n * ```\n */\nexport class SchemaValidator {\n /**\n * Read and parse a YAML schema file from disk.\n *\n * @param filePath - Path to the schema YAML file.\n * @returns Parsed {@link NamespaceSchema}.\n * @throws {@link SchemaLoadError} If the file cannot be read or contains invalid YAML.\n */\n loadSchema(filePath: string): NamespaceSchema {\n let raw: string;\n try {\n raw = fs.readFileSync(filePath, \"utf-8\");\n } catch {\n throw new SchemaLoadError(`Could not read schema file at '${filePath}'.`, filePath);\n }\n\n let parsed: unknown;\n try {\n parsed = YAML.parse(raw);\n } catch {\n throw new SchemaLoadError(`Schema file '${filePath}' contains invalid YAML.`, filePath);\n }\n\n if (!parsed || typeof parsed !== \"object\") {\n throw new SchemaLoadError(\n `Schema file '${filePath}' must be a YAML object with a 'keys' map.`,\n filePath,\n );\n }\n\n const obj = parsed as Record<string, unknown>;\n if (!obj.keys || typeof obj.keys !== \"object\") {\n throw new SchemaLoadError(\n `Schema file '${filePath}' is missing the required 'keys' map.`,\n filePath,\n );\n }\n\n const keys: NamespaceSchema[\"keys\"] = {};\n const keysObj = obj.keys as Record<string, unknown>;\n\n for (const [keyName, keyDef] of Object.entries(keysObj)) {\n if (!keyDef || typeof keyDef !== \"object\") {\n throw new SchemaLoadError(\n `Schema key '${keyName}' must be an object with at least 'type' and 'required'.`,\n filePath,\n );\n }\n\n const def = keyDef as Record<string, unknown>;\n const type = def.type as string;\n if (![\"string\", \"integer\", \"boolean\"].includes(type)) {\n throw new SchemaLoadError(\n `Schema key '${keyName}' has invalid type '${type}'. Must be 'string', 'integer', or 'boolean'.`,\n filePath,\n );\n }\n\n keys[keyName] = {\n type: type as \"string\" | \"integer\" | \"boolean\",\n required: def.required === true,\n ...(typeof def.pattern === \"string\" ? { pattern: def.pattern } : {}),\n ...(def.default !== undefined ? { default: def.default } : {}),\n ...(typeof def.description === \"string\" ? { description: def.description } : {}),\n ...(typeof def.max === \"number\" ? { max: def.max } : {}),\n };\n }\n\n return { keys };\n }\n\n /**\n * Validate a set of decrypted values against a loaded namespace schema.\n *\n * @param values - Flat key/value map from a decrypted SOPS file.\n * @param schema - Schema loaded via {@link loadSchema}.\n * @returns {@link ValidationResult} with `valid: false` when any errors are present.\n */\n validate(values: Record<string, string>, schema: NamespaceSchema): ValidationResult {\n const errors: ValidationError[] = [];\n const warnings: ValidationWarning[] = [];\n\n // Check required keys and type/pattern validation\n for (const [keyName, keyDef] of Object.entries(schema.keys)) {\n const value = values[keyName];\n\n if (value === undefined || value === null) {\n if (keyDef.required) {\n errors.push({\n key: keyName,\n message: `Required key '${keyName}' is missing.`,\n rule: \"required\",\n });\n }\n continue;\n }\n\n // Type validation\n switch (keyDef.type) {\n case \"integer\": {\n const num = Number(value);\n if (!Number.isInteger(num) || value.trim() === \"\") {\n errors.push({\n key: keyName,\n message: `Key '${keyName}' must be an integer, got '${value}'.`,\n rule: \"type\",\n });\n } else if (keyDef.max !== undefined && num > keyDef.max) {\n warnings.push({\n key: keyName,\n message: `Key '${keyName}' value ${num} exceeds maximum ${keyDef.max}.`,\n rule: \"max_exceeded\",\n });\n }\n break;\n }\n case \"boolean\": {\n const lower = value.toLowerCase();\n if (![\"true\", \"false\"].includes(lower)) {\n errors.push({\n key: keyName,\n message: `Key '${keyName}' must be a boolean ('true' or 'false'), got '${value}'.`,\n rule: \"type\",\n });\n }\n break;\n }\n case \"string\":\n // Strings are always valid type-wise\n break;\n }\n\n // Pattern validation (only for strings)\n if (keyDef.pattern && keyDef.type === \"string\") {\n const regex = new RegExp(keyDef.pattern);\n if (!regex.test(value)) {\n errors.push({\n key: keyName,\n message: `Key '${keyName}' value does not match required pattern '${keyDef.pattern}'.`,\n rule: \"pattern\",\n });\n }\n }\n }\n\n // Check for undeclared keys\n for (const keyName of Object.keys(values)) {\n if (!(keyName in schema.keys)) {\n warnings.push({\n key: keyName,\n message: `Key '${keyName}' is not declared in the schema.`,\n rule: \"undeclared\",\n });\n }\n }\n\n return {\n valid: errors.length === 0,\n errors,\n warnings,\n };\n }\n}\n", "/**\n * TIER 1 MODULE \u2014 Security and correctness critical.\n *\n * This module requires exhaustive test coverage. Before\n * adding or modifying code here:\n * 1. Add tests for the happy path\n * 2. Add tests for all documented error paths\n * 3. Add at least one boundary/edge case test\n *\n * Coverage threshold: 95% lines/functions, 90% branches.\n * See docs/contributing/testing.md for the rationale.\n */\nimport * as path from \"path\";\nimport { ClefManifest, DiffResult, DiffRow, DiffStatus } from \"../types\";\nimport { EncryptionBackend } from \"../types\";\n\n/**\n * Compares decrypted values between two environments or two arbitrary key/value maps.\n *\n * @example\n * ```ts\n * const engine = new DiffEngine();\n * const result = await engine.diffFiles(\"app\", \"staging\", \"production\", manifest, sopsClient, repoRoot);\n * ```\n */\nexport class DiffEngine {\n /**\n * Compare two in-memory value maps and produce a sorted diff result.\n *\n * Rows are sorted with missing and changed keys first, identical keys last.\n *\n * @param valuesA - Decrypted values from environment A.\n * @param valuesB - Decrypted values from environment B.\n * @param envA - Name of environment A.\n * @param envB - Name of environment B.\n * @param namespace - Namespace label included in the result (optional).\n */\n diff(\n valuesA: Record<string, string>,\n valuesB: Record<string, string>,\n envA: string,\n envB: string,\n namespace: string = \"\",\n ): DiffResult {\n const allKeys = new Set([...Object.keys(valuesA), ...Object.keys(valuesB)]);\n const rows: DiffRow[] = [];\n\n for (const key of allKeys) {\n const inA = key in valuesA;\n const inB = key in valuesB;\n\n let status: DiffStatus;\n if (inA && inB) {\n status = valuesA[key] === valuesB[key] ? \"identical\" : \"changed\";\n } else if (inA && !inB) {\n status = \"missing_b\";\n } else {\n status = \"missing_a\";\n }\n\n rows.push({\n key,\n valueA: inA ? valuesA[key] : null,\n valueB: inB ? valuesB[key] : null,\n status,\n });\n }\n\n // Sort: missing and changed first, then identical\n rows.sort((a, b) => {\n const order: Record<DiffStatus, number> = {\n missing_a: 0,\n missing_b: 0,\n changed: 1,\n identical: 2,\n };\n return order[a.status] - order[b.status];\n });\n\n return { namespace, envA, envB, rows };\n }\n\n /**\n * Decrypt two matrix cells and diff their values.\n *\n * @param namespace - Namespace containing both cells.\n * @param envA - Name of environment A.\n * @param envB - Name of environment B.\n * @param manifest - Parsed manifest used to resolve file paths.\n * @param sopsClient - SOPS client used to decrypt both files.\n * @param repoRoot - Absolute path to the repository root.\n * @throws {@link SopsDecryptionError} If either file cannot be decrypted.\n */\n async diffFiles(\n namespace: string,\n envA: string,\n envB: string,\n manifest: ClefManifest,\n sopsClient: EncryptionBackend,\n repoRoot: string,\n ): Promise<DiffResult> {\n const fileA = path.join(\n repoRoot,\n manifest.file_pattern.replace(\"{namespace}\", namespace).replace(\"{environment}\", envA),\n );\n const fileB = path.join(\n repoRoot,\n manifest.file_pattern.replace(\"{namespace}\", namespace).replace(\"{environment}\", envB),\n );\n\n const [decryptedA, decryptedB] = await Promise.all([\n sopsClient.decrypt(fileA),\n sopsClient.decrypt(fileB),\n ]);\n\n return this.diff(decryptedA.values, decryptedB.values, envA, envB, namespace);\n }\n}\n", "import * as path from \"path\";\nimport { ClefManifest, MatrixCell } from \"../types\";\nimport { EncryptionBackend } from \"../types\";\n\n/**\n * Performs bulk set, delete, and copy operations across multiple environments.\n *\n * @example\n * ```ts\n * const bulk = new BulkOps();\n * await bulk.setAcrossEnvironments(\"app\", \"DATABASE_URL\", { staging: \"...\", production: \"...\" }, manifest, sopsClient, repoRoot);\n * ```\n */\nexport class BulkOps {\n /**\n * Set a key to different values in multiple environments at once.\n *\n * @param namespace - Target namespace.\n * @param key - Secret key name to set.\n * @param values - Map of `{ environment: value }` pairs.\n * @param manifest - Parsed manifest.\n * @param sopsClient - SOPS client used to decrypt and re-encrypt each file.\n * @param repoRoot - Absolute path to the repository root.\n * @throws `Error` with details if any environment fails.\n */\n async setAcrossEnvironments(\n namespace: string,\n key: string,\n values: Record<string, string>,\n manifest: ClefManifest,\n sopsClient: EncryptionBackend,\n repoRoot: string,\n ): Promise<void> {\n const errors: Array<{ environment: string; error: Error }> = [];\n\n for (const env of manifest.environments) {\n if (!(env.name in values)) {\n continue;\n }\n\n const filePath = path.join(\n repoRoot,\n manifest.file_pattern.replace(\"{namespace}\", namespace).replace(\"{environment}\", env.name),\n );\n\n try {\n const decrypted = await sopsClient.decrypt(filePath);\n decrypted.values[key] = values[env.name];\n await sopsClient.encrypt(filePath, decrypted.values, manifest, env.name);\n } catch (err) {\n errors.push({ environment: env.name, error: err as Error });\n }\n }\n\n if (errors.length > 0) {\n const details = errors.map((e) => ` - ${e.environment}: ${e.error.message}`).join(\"\\n\");\n throw new Error(\n `Failed to set key '${key}' in ${errors.length} environment(s):\\n${details}\\n` +\n `Successfully updated ${Object.keys(values).length - errors.length} environment(s).`,\n );\n }\n }\n\n /**\n * Delete a key from every environment in a namespace.\n *\n * @param namespace - Target namespace.\n * @param key - Secret key name to delete.\n * @param manifest - Parsed manifest.\n * @param sopsClient - SOPS client.\n * @param repoRoot - Absolute path to the repository root.\n * @throws `Error` with details if any environment fails.\n */\n async deleteAcrossEnvironments(\n namespace: string,\n key: string,\n manifest: ClefManifest,\n sopsClient: EncryptionBackend,\n repoRoot: string,\n ): Promise<void> {\n const errors: Array<{ environment: string; error: Error }> = [];\n\n for (const env of manifest.environments) {\n const filePath = path.join(\n repoRoot,\n manifest.file_pattern.replace(\"{namespace}\", namespace).replace(\"{environment}\", env.name),\n );\n\n try {\n const decrypted = await sopsClient.decrypt(filePath);\n if (key in decrypted.values) {\n delete decrypted.values[key];\n await sopsClient.encrypt(filePath, decrypted.values, manifest, env.name);\n }\n } catch (err) {\n errors.push({ environment: env.name, error: err as Error });\n }\n }\n\n if (errors.length > 0) {\n const details = errors.map((e) => ` - ${e.environment}: ${e.error.message}`).join(\"\\n\");\n throw new Error(\n `Failed to delete key '${key}' in ${errors.length} environment(s):\\n${details}`,\n );\n }\n }\n\n /**\n * Copy a single key's value from one matrix cell to another.\n *\n * @param key - Secret key name to copy.\n * @param fromCell - Source matrix cell.\n * @param toCell - Destination matrix cell.\n * @param sopsClient - SOPS client.\n * @param manifest - Parsed manifest.\n * @throws `Error` if the key does not exist in the source cell.\n */\n async copyValue(\n key: string,\n fromCell: MatrixCell,\n toCell: MatrixCell,\n sopsClient: EncryptionBackend,\n manifest: ClefManifest,\n ): Promise<void> {\n const source = await sopsClient.decrypt(fromCell.filePath);\n\n if (!(key in source.values)) {\n throw new Error(\n `Key '${key}' does not exist in ${fromCell.namespace}/${fromCell.environment}.`,\n );\n }\n\n const dest = await sopsClient.decrypt(toCell.filePath);\n dest.values[key] = source.values[key];\n await sopsClient.encrypt(toCell.filePath, dest.values, manifest, toCell.environment);\n }\n}\n", "import * as fs from \"fs\";\nimport * as path from \"path\";\nimport { GitCommit, GitOperationError, GitStatus, SubprocessRunner } from \"../types\";\n\nconst PRE_COMMIT_HOOK = `#!/bin/sh\n# Clef pre-commit hook \u2014 blocks commits of files missing SOPS encryption metadata\n# and scans staged files for plaintext secrets.\n# Installed by: clef hooks install\n\nSTAGED_FILES=$(git diff --cached --name-only --diff-filter=ACM)\nEXIT_CODE=0\n\nfor FILE in $STAGED_FILES; do\n case \"$FILE\" in\n *.enc.yaml|*.enc.json)\n if ! grep -q '\"sops\":' \"$FILE\" && ! grep -q 'sops:' \"$FILE\"; then\n echo \"ERROR: $FILE appears to be missing SOPS metadata.\"\n echo \" This file may contain unencrypted secrets.\"\n echo \" Encrypt it with 'sops encrypt -i $FILE' before committing.\"\n EXIT_CODE=1\n fi\n ;;\n esac\ndone\n\nif [ $EXIT_CODE -eq 0 ]; then\n # Scan staged files for plaintext secrets\n if command -v clef >/dev/null 2>&1; then\n clef scan --staged\n SCAN_EXIT=$?\n if [ $SCAN_EXIT -ne 0 ]; then\n echo \"\"\n echo \"clef scan found potential secrets in staged files.\"\n echo \"Review the findings above before committing.\"\n echo \"To bypass (use with caution): git commit --no-verify\"\n EXIT_CODE=1\n fi\n fi\nfi\n\nexit $EXIT_CODE\n`;\n\n/**\n * Wraps git operations: staging, committing, log, diff, status, and hook installation.\n *\n * @example\n * ```ts\n * const git = new GitIntegration(runner);\n * await git.stageFiles([\"secrets/app/production.enc.yaml\"], repoRoot);\n * const hash = await git.commit(\"chore(secrets): rotate production keys\", repoRoot);\n * ```\n */\nexport class GitIntegration {\n constructor(private readonly runner: SubprocessRunner) {}\n\n /**\n * Stage one or more file paths with `git add`.\n *\n * @param filePaths - Paths to stage (relative or absolute).\n * @param repoRoot - Working directory for the git command.\n * @throws {@link GitOperationError} On failure.\n */\n async stageFiles(filePaths: string[], repoRoot: string): Promise<void> {\n if (filePaths.length === 0) return;\n\n const result = await this.runner.run(\"git\", [\"add\", ...filePaths], { cwd: repoRoot });\n\n if (result.exitCode !== 0) {\n throw new GitOperationError(\n `Failed to stage files: ${result.stderr.trim()}`,\n \"Check that the files exist and you are inside a git repository.\",\n );\n }\n }\n\n /**\n * Create a commit with the given message.\n *\n * @param message - Commit message.\n * @param repoRoot - Working directory for the git command.\n * @returns The short commit hash, or an empty string if parsing fails.\n * @throws {@link GitOperationError} On failure.\n */\n async commit(message: string, repoRoot: string): Promise<string> {\n const result = await this.runner.run(\"git\", [\"commit\", \"-m\", message], { cwd: repoRoot });\n\n if (result.exitCode !== 0) {\n throw new GitOperationError(\n `Failed to commit: ${result.stderr.trim()}`,\n \"Ensure there are staged changes and your git user is configured.\",\n );\n }\n\n // Extract commit hash from output\n const hashMatch = result.stdout.match(/\\[[\\w/-]+ ([a-f0-9]+)\\]/);\n return hashMatch ? hashMatch[1] : \"\";\n }\n\n /**\n * Retrieve recent commits for a specific file.\n *\n * @param filePath - Path to the file (relative to `repoRoot`).\n * @param repoRoot - Working directory for the git command.\n * @param limit - Maximum number of commits to return (default: 20).\n * @throws {@link GitOperationError} On failure.\n */\n async getLog(filePath: string, repoRoot: string, limit: number = 20): Promise<GitCommit[]> {\n const result = await this.runner.run(\n \"git\",\n [\"log\", `--max-count=${limit}`, \"--format=%H|%an|%aI|%s\", \"--\", filePath],\n { cwd: repoRoot },\n );\n\n if (result.exitCode !== 0) {\n throw new GitOperationError(\n `Failed to get git log for '${filePath}': ${result.stderr.trim()}`,\n );\n }\n\n if (!result.stdout.trim()) return [];\n\n return result.stdout\n .trim()\n .split(\"\\n\")\n .map((line) => {\n const [hash, author, dateStr, ...messageParts] = line.split(\"|\");\n return {\n hash,\n author,\n date: new Date(dateStr),\n message: messageParts.join(\"|\"),\n };\n });\n }\n\n /**\n * Get the staged diff (`git diff --cached`).\n *\n * @param repoRoot - Working directory for the git command.\n * @returns Raw diff output as a string.\n * @throws {@link GitOperationError} On failure.\n */\n async getDiff(repoRoot: string): Promise<string> {\n const result = await this.runner.run(\"git\", [\"diff\", \"--cached\"], { cwd: repoRoot });\n\n if (result.exitCode !== 0) {\n throw new GitOperationError(`Failed to get git diff: ${result.stderr.trim()}`);\n }\n\n return result.stdout;\n }\n\n /**\n * Parse `git status --porcelain` into staged, unstaged, and untracked lists.\n *\n * @param repoRoot - Working directory for the git command.\n * @throws {@link GitOperationError} On failure.\n */\n async getStatus(repoRoot: string): Promise<GitStatus> {\n const result = await this.runner.run(\"git\", [\"status\", \"--porcelain\"], { cwd: repoRoot });\n\n if (result.exitCode !== 0) {\n throw new GitOperationError(`Failed to get git status: ${result.stderr.trim()}`);\n }\n\n const staged: string[] = [];\n const unstaged: string[] = [];\n const untracked: string[] = [];\n\n if (!result.stdout.trim()) {\n return { staged, unstaged, untracked };\n }\n\n for (const line of result.stdout.trim().split(\"\\n\")) {\n const indexStatus = line[0];\n const workTreeStatus = line[1];\n const filePath = line.substring(3);\n\n if (indexStatus === \"?\") {\n untracked.push(filePath);\n } else {\n if (indexStatus !== \" \" && indexStatus !== \"?\") {\n staged.push(filePath);\n }\n if (workTreeStatus !== \" \" && workTreeStatus !== \"?\") {\n unstaged.push(filePath);\n }\n }\n }\n\n return { staged, unstaged, untracked };\n }\n\n /**\n * Configure the SOPS-aware git merge driver so that encrypted files\n * are merged at the plaintext level instead of producing ciphertext conflicts.\n *\n * Sets two things:\n * 1. `.gitattributes` \u2014 tells git which files use the custom driver.\n * 2. `.git/config [merge \"sops\"]` \u2014 tells git what command to run.\n *\n * Both operations are idempotent \u2014 safe to call repeatedly.\n *\n * @param repoRoot - Absolute path to the repository root.\n * @throws {@link GitOperationError} On failure.\n */\n async installMergeDriver(repoRoot: string): Promise<void> {\n // 1. Configure git merge driver in local config\n const configResult = await this.runner.run(\n \"git\",\n [\"config\", \"merge.sops.name\", \"SOPS-aware merge driver\"],\n { cwd: repoRoot },\n );\n if (configResult.exitCode !== 0) {\n throw new GitOperationError(\n `Failed to configure merge driver name: ${configResult.stderr.trim()}`,\n \"Ensure you are inside a git repository.\",\n );\n }\n\n const driverResult = await this.runner.run(\n \"git\",\n [\"config\", \"merge.sops.driver\", \"clef merge-driver %O %A %B\"],\n { cwd: repoRoot },\n );\n if (driverResult.exitCode !== 0) {\n throw new GitOperationError(\n `Failed to configure merge driver command: ${driverResult.stderr.trim()}`,\n \"Ensure you are inside a git repository.\",\n );\n }\n\n // 2. Ensure .gitattributes contains the rule\n await this.ensureGitattributes(repoRoot);\n }\n\n /**\n * Check whether the SOPS merge driver is configured in both\n * `.git/config` and `.gitattributes`.\n *\n * @param repoRoot - Absolute path to the repository root.\n * @returns An object indicating which parts are configured.\n */\n async checkMergeDriver(\n repoRoot: string,\n ): Promise<{ gitConfig: boolean; gitattributes: boolean }> {\n // Check git config\n const configResult = await this.runner.run(\"git\", [\"config\", \"--get\", \"merge.sops.driver\"], {\n cwd: repoRoot,\n });\n const gitConfig = configResult.exitCode === 0 && configResult.stdout.trim().length > 0;\n\n // Check .gitattributes\n const attrFilePath = path.join(repoRoot, \".gitattributes\");\n const attrContent = fs.existsSync(attrFilePath) ? fs.readFileSync(attrFilePath, \"utf-8\") : \"\";\n const gitattributes = attrContent.includes(\"merge=sops\");\n\n return { gitConfig, gitattributes };\n }\n\n private async ensureGitattributes(repoRoot: string): Promise<void> {\n const attrPath = path.join(repoRoot, \".gitattributes\");\n const mergeRule = \"*.enc.yaml merge=sops\\n*.enc.json merge=sops\";\n\n // Read existing content\n const existing = fs.existsSync(attrPath) ? fs.readFileSync(attrPath, \"utf-8\") : \"\";\n\n if (existing.includes(\"merge=sops\")) {\n return; // Already configured\n }\n\n const newContent = existing.trimEnd()\n ? `${existing.trimEnd()}\\n\\n# Clef: SOPS-aware merge driver for encrypted files\\n${mergeRule}\\n`\n : `# Clef: SOPS-aware merge driver for encrypted files\\n${mergeRule}\\n`;\n\n const writeResult = await this.runner.run(\"tee\", [attrPath], {\n stdin: newContent,\n cwd: repoRoot,\n });\n\n if (writeResult.exitCode !== 0) {\n throw new GitOperationError(`Failed to write .gitattributes: ${writeResult.stderr.trim()}`);\n }\n }\n\n /**\n * Write and chmod the Clef pre-commit hook into `.git/hooks/pre-commit`.\n * The hook blocks commits of unencrypted matrix files and scans staged files for secrets.\n *\n * @param repoRoot - Absolute path to the repository root.\n * @throws {@link GitOperationError} On failure.\n */\n async installPreCommitHook(repoRoot: string): Promise<void> {\n const hookPath = path.join(repoRoot, \".git\", \"hooks\", \"pre-commit\");\n\n // Write the hook using the subprocess runner to avoid direct fs writes in the integration\n const result = await this.runner.run(\"tee\", [hookPath], {\n stdin: PRE_COMMIT_HOOK,\n cwd: repoRoot,\n });\n\n if (result.exitCode !== 0) {\n throw new GitOperationError(\n `Failed to install pre-commit hook: ${result.stderr.trim()}`,\n \"Ensure .git/hooks/ directory exists.\",\n );\n }\n\n // Make it executable\n const chmodResult = await this.runner.run(\"chmod\", [\"+x\", hookPath], { cwd: repoRoot });\n\n if (chmodResult.exitCode !== 0) {\n throw new GitOperationError(\n `Failed to make pre-commit hook executable: ${chmodResult.stderr.trim()}`,\n );\n }\n }\n}\n", "/**\n * TIER 1 MODULE \u2014 Security and correctness critical.\n *\n * This module requires exhaustive test coverage. Before\n * adding or modifying code here:\n * 1. Add tests for the happy path\n * 2. Add tests for all documented error paths\n * 3. Add at least one boundary/edge case test\n *\n * Coverage threshold: 95% lines/functions, 90% branches.\n * See docs/contributing/testing.md for the rationale.\n */\nimport * as fs from \"fs\";\nimport * as net from \"net\";\nimport { randomBytes } from \"crypto\";\nimport * as YAML from \"yaml\";\nimport {\n ClefManifest,\n DecryptedFile,\n EncryptionBackend,\n SopsDecryptionError,\n SopsEncryptionError,\n SopsKeyNotFoundError,\n SopsMetadata,\n SubprocessRunner,\n resolveBackendConfig,\n} from \"../types\";\nimport { assertSops } from \"../dependencies/checker\";\nimport { deriveAgePublicKey } from \"../age/keygen\";\nimport { resolveSopsPath } from \"./resolver\";\n\nfunction formatFromPath(filePath: string): \"yaml\" | \"json\" {\n return filePath.endsWith(\".json\") ? \"json\" : \"yaml\";\n}\n\n/**\n * On Windows, /dev/stdin does not exist. Create a named pipe that sops can open\n * as its input file, feed the content through it, and return the pipe path.\n * The returned cleanup function closes the server once sops is done reading.\n *\n * Go's os.Open / CreateFile can open \\\\.\\pipe\\... paths directly, so sops\n * reads from the pipe exactly as it would from a regular file.\n */\nfunction openWindowsInputPipe(content: string): Promise<{ inputArg: string; cleanup: () => void }> {\n const pipeName = `\\\\\\\\.\\\\pipe\\\\clef-sops-${randomBytes(8).toString(\"hex\")}`;\n\n return new Promise((resolve, reject) => {\n const server = net.createServer((socket) => {\n // On Windows, socket.end() does not reliably signal EOF to named pipe\n // clients because libuv's uv_shutdown is a no-op for pipes. Write the\n // content and then force-destroy the socket so the pipe handle is closed,\n // which the Go client (sops) sees as ERROR_BROKEN_PIPE \u2192 io.EOF.\n socket.write(content, () => {\n socket.destroy();\n });\n });\n server.maxConnections = 1;\n server.on(\"error\", reject);\n server.listen(pipeName, () => {\n resolve({\n inputArg: pipeName,\n cleanup: () => server.close(),\n });\n });\n });\n}\n\n/**\n * Wraps the `sops` binary for encryption, decryption, re-encryption, and metadata extraction.\n * All decrypt/encrypt operations are piped via stdin/stdout \u2014 plaintext never touches disk.\n *\n * @example\n * ```ts\n * const client = new SopsClient(runner, \"/home/user/.age/key.txt\");\n * const decrypted = await client.decrypt(\"secrets/production.enc.yaml\");\n * ```\n */\nexport class SopsClient implements EncryptionBackend {\n private readonly sopsCommand: string;\n\n /**\n * @param runner - Subprocess runner used to invoke the `sops` binary.\n * @param ageKeyFile - Optional path to an age private key file. Passed as\n * `SOPS_AGE_KEY_FILE` to the subprocess environment.\n * @param ageKey - Optional inline age private key. Passed as `SOPS_AGE_KEY`\n * to the subprocess environment.\n * @param sopsPath - Optional explicit path to the sops binary. When omitted,\n * resolved automatically via {@link resolveSopsPath}.\n */\n constructor(\n private readonly runner: SubprocessRunner,\n private readonly ageKeyFile?: string,\n private readonly ageKey?: string,\n sopsPath?: string,\n ) {\n this.sopsCommand = sopsPath ?? resolveSopsPath().path;\n }\n\n private buildSopsEnv(): Record<string, string> | undefined {\n const env: Record<string, string> = {};\n if (this.ageKey) {\n env.SOPS_AGE_KEY = this.ageKey;\n }\n if (this.ageKeyFile) {\n env.SOPS_AGE_KEY_FILE = this.ageKeyFile;\n }\n return Object.keys(env).length > 0 ? env : undefined;\n }\n\n /**\n * Decrypt a SOPS-encrypted file and return its values and metadata.\n *\n * @param filePath - Path to the `.enc.yaml` or `.enc.json` file.\n * @returns {@link DecryptedFile} with plaintext values in memory only.\n * @throws {@link SopsKeyNotFoundError} If no matching decryption key is available.\n * @throws {@link SopsDecryptionError} On any other decryption failure.\n */\n async decrypt(filePath: string): Promise<DecryptedFile> {\n await assertSops(this.runner, this.sopsCommand);\n const fmt = formatFromPath(filePath);\n const env = this.buildSopsEnv();\n const result = await this.runner.run(\n this.sopsCommand,\n [\"decrypt\", \"--output-type\", fmt, filePath],\n {\n ...(env ? { env } : {}),\n },\n );\n\n if (result.exitCode !== 0) {\n const errorType = await this.classifyDecryptError(filePath);\n if (errorType === \"key-not-found\") {\n throw new SopsKeyNotFoundError(\n `No decryption key found for '${filePath}'. ${result.stderr.trim()}`,\n );\n }\n throw new SopsDecryptionError(\n `Failed to decrypt '${filePath}': ${result.stderr.trim()}`,\n filePath,\n );\n }\n\n let parsed: Record<string, unknown>;\n try {\n parsed = YAML.parse(result.stdout) ?? {};\n } catch {\n throw new SopsDecryptionError(\n `Decrypted content of '${filePath}' is not valid YAML.`,\n filePath,\n );\n }\n\n const values: Record<string, string> = {};\n for (const [key, value] of Object.entries(parsed)) {\n values[key] = String(value);\n }\n\n const metadata = await this.getMetadata(filePath);\n\n return { values, metadata };\n }\n\n /**\n * Encrypt a key/value map and write it to an encrypted SOPS file.\n *\n * @param filePath - Destination path for the encrypted file.\n * @param values - Flat key/value map to encrypt.\n * @param manifest - Manifest used to determine the encryption backend and key configuration.\n * @param environment - Optional environment name. When provided, per-env backend overrides\n * are resolved from the manifest. When omitted, the global `sops.default_backend` is used.\n * @throws {@link SopsEncryptionError} On encryption or write failure.\n */\n async encrypt(\n filePath: string,\n values: Record<string, string>,\n manifest: ClefManifest,\n environment?: string,\n ): Promise<void> {\n await assertSops(this.runner, this.sopsCommand);\n const fmt = formatFromPath(filePath);\n const content = fmt === \"json\" ? JSON.stringify(values, null, 2) : YAML.stringify(values);\n const args = this.buildEncryptArgs(filePath, manifest, environment);\n const env = this.buildSopsEnv();\n\n // sops requires an explicit input path \u2014 it does not read from stdin implicitly.\n // On Unix we pass /dev/stdin (a special file backed by the process's stdin pipe).\n // On Windows /dev/stdin does not exist, so we create a named pipe, feed content\n // through it, and pass the pipe path as the input file instead.\n let inputArg: string;\n let pipeCleanup: (() => void) | undefined;\n\n if (process.platform === \"win32\") {\n const pipe = await openWindowsInputPipe(content);\n inputArg = pipe.inputArg;\n pipeCleanup = pipe.cleanup;\n } else {\n inputArg = \"/dev/stdin\";\n }\n\n let result;\n try {\n result = await this.runner.run(\n this.sopsCommand,\n [\n \"encrypt\",\n ...args,\n \"--input-type\",\n fmt,\n \"--output-type\",\n fmt,\n \"--filename-override\",\n filePath,\n inputArg,\n ],\n {\n // stdin is still piped on Unix (/dev/stdin reads from it);\n // on Windows the named pipe server feeds content directly.\n ...(process.platform !== \"win32\" ? { stdin: content } : {}),\n ...(env ? { env } : {}),\n },\n );\n } finally {\n pipeCleanup?.();\n }\n\n if (result.exitCode !== 0) {\n throw new SopsEncryptionError(\n `Failed to encrypt '${filePath}': ${result.stderr.trim()}`,\n filePath,\n );\n }\n\n // Write the encrypted output to the file (using fs directly \u2014 tee is not available on Windows)\n try {\n fs.writeFileSync(filePath, result.stdout);\n } catch {\n throw new SopsEncryptionError(`Failed to write encrypted data to '${filePath}'.`, filePath);\n }\n }\n\n /**\n * Rotate encryption by adding a new age recipient key to an existing SOPS file.\n *\n * @param filePath - Path to the encrypted file to re-encrypt.\n * @param newKey - New age public key to add as a recipient.\n * @throws {@link SopsEncryptionError} On failure.\n */\n async reEncrypt(filePath: string, newKey: string): Promise<void> {\n await assertSops(this.runner, this.sopsCommand);\n const env = this.buildSopsEnv();\n const result = await this.runner.run(\n this.sopsCommand,\n [\"rotate\", \"-i\", \"--add-age\", newKey, filePath],\n {\n ...(env ? { env } : {}),\n },\n );\n\n if (result.exitCode !== 0) {\n throw new SopsEncryptionError(\n `Failed to re-encrypt '${filePath}': ${result.stderr.trim()}`,\n filePath,\n );\n }\n }\n\n /**\n * Add an age recipient to an existing SOPS file.\n *\n * @param filePath - Path to the encrypted file.\n * @param key - age public key to add as a recipient.\n * @throws {@link SopsEncryptionError} On failure.\n */\n async addRecipient(filePath: string, key: string): Promise<void> {\n await assertSops(this.runner, this.sopsCommand);\n const env = this.buildSopsEnv();\n const result = await this.runner.run(\n this.sopsCommand,\n [\"rotate\", \"-i\", \"--add-age\", key, filePath],\n {\n ...(env ? { env } : {}),\n },\n );\n\n if (result.exitCode !== 0) {\n throw new SopsEncryptionError(\n `Failed to add recipient to '${filePath}': ${result.stderr.trim()}`,\n filePath,\n );\n }\n }\n\n /**\n * Remove an age recipient from an existing SOPS file.\n *\n * @param filePath - Path to the encrypted file.\n * @param key - age public key to remove.\n * @throws {@link SopsEncryptionError} On failure.\n */\n async removeRecipient(filePath: string, key: string): Promise<void> {\n await assertSops(this.runner, this.sopsCommand);\n const env = this.buildSopsEnv();\n const result = await this.runner.run(\n this.sopsCommand,\n [\"rotate\", \"-i\", \"--rm-age\", key, filePath],\n {\n ...(env ? { env } : {}),\n },\n );\n\n if (result.exitCode !== 0) {\n throw new SopsEncryptionError(\n `Failed to remove recipient from '${filePath}': ${result.stderr.trim()}`,\n filePath,\n );\n }\n }\n\n /**\n * Check whether a file contains valid SOPS encryption metadata.\n *\n * @param filePath - Path to the file to check.\n * @returns `true` if valid SOPS metadata is present; `false` otherwise. Never throws.\n */\n async validateEncryption(filePath: string): Promise<boolean> {\n await assertSops(this.runner, this.sopsCommand);\n try {\n await this.getMetadata(filePath);\n return true;\n } catch {\n return false;\n }\n }\n\n /**\n * Extract SOPS metadata (backend, recipients, last-modified timestamp) from an encrypted file\n * without decrypting its values.\n *\n * @param filePath - Path to the encrypted file.\n * @returns {@link SopsMetadata} parsed from the file's `sops:` block.\n * @throws {@link SopsDecryptionError} If the file cannot be read or parsed.\n */\n async getMetadata(filePath: string): Promise<SopsMetadata> {\n await assertSops(this.runner, this.sopsCommand);\n const env = this.buildSopsEnv();\n const result = await this.runner.run(this.sopsCommand, [\"filestatus\", filePath], {\n ...(env ? { env } : {}),\n });\n\n // filestatus returns JSON with encrypted status; if it fails, try parsing the file directly\n if (result.exitCode !== 0) {\n // Fall back to reading SOPS metadata from the encrypted file\n return this.parseMetadataFromFile(filePath);\n }\n\n return this.parseMetadataFromFile(filePath);\n }\n\n /**\n * Determine whether a decrypt failure is caused by a missing/mismatched key (vs. some other\n * SOPS error) without relying on stderr message text.\n *\n * For age backends: reads the file's recipient list and checks whether any of the configured\n * private keys derive to a matching public key. For non-age backends (pgp, kms) we cannot\n * perform an equivalent check, so those always return \"other\".\n */\n private async classifyDecryptError(filePath: string): Promise<\"key-not-found\" | \"other\"> {\n let metadata: SopsMetadata;\n try {\n metadata = this.parseMetadataFromFile(filePath);\n } catch {\n return \"other\";\n }\n\n if (metadata.backend !== \"age\") return \"other\";\n\n // No age key configured at all\n if (!this.ageKey && !this.ageKeyFile) return \"key-not-found\";\n\n // Obtain the private key material from the constructor params\n let keyContent: string;\n try {\n keyContent = this.ageKey ?? fs.readFileSync(this.ageKeyFile!, \"utf-8\");\n } catch {\n return \"key-not-found\";\n }\n\n // Key files may contain multiple AGE-SECRET-KEY-1... lines (plus comments/blank lines)\n const privateKeys = keyContent\n .split(\"\\n\")\n .map((line) => line.trim())\n .filter((line) => line.startsWith(\"AGE-SECRET-KEY-\"));\n\n if (privateKeys.length === 0) return \"key-not-found\";\n\n try {\n const publicKeys = await Promise.all(privateKeys.map((k) => deriveAgePublicKey(k)));\n const recipients = new Set(metadata.recipients);\n return publicKeys.some((pk) => recipients.has(pk)) ? \"other\" : \"key-not-found\";\n } catch {\n return \"other\";\n }\n }\n\n private parseMetadataFromFile(filePath: string): SopsMetadata {\n let content: string;\n try {\n content = fs.readFileSync(filePath, \"utf-8\");\n } catch {\n throw new SopsDecryptionError(\n `Could not read file '${filePath}' to extract SOPS metadata.`,\n filePath,\n );\n }\n\n let parsed: Record<string, unknown>;\n try {\n parsed = YAML.parse(content);\n } catch {\n throw new SopsDecryptionError(\n `File '${filePath}' is not valid YAML. Cannot extract SOPS metadata.`,\n filePath,\n );\n }\n\n const sops = parsed?.sops as Record<string, unknown> | undefined;\n if (!sops) {\n throw new SopsDecryptionError(\n `File '${filePath}' does not contain SOPS metadata. It may not be encrypted.`,\n filePath,\n );\n }\n\n const backend = this.detectBackend(sops);\n const recipients = this.extractRecipients(sops, backend);\n const lastModified = sops.lastmodified ? new Date(sops.lastmodified as string) : new Date();\n\n return { backend, recipients, lastModified };\n }\n\n private detectBackend(\n sops: Record<string, unknown>,\n ): \"age\" | \"awskms\" | \"gcpkms\" | \"azurekv\" | \"pgp\" {\n if (sops.age && Array.isArray(sops.age) && (sops.age as unknown[]).length > 0) return \"age\";\n if (sops.kms && Array.isArray(sops.kms) && (sops.kms as unknown[]).length > 0) return \"awskms\";\n if (sops.gcp_kms && Array.isArray(sops.gcp_kms) && (sops.gcp_kms as unknown[]).length > 0)\n return \"gcpkms\";\n if (sops.azure_kv && Array.isArray(sops.azure_kv) && (sops.azure_kv as unknown[]).length > 0)\n return \"azurekv\";\n if (sops.pgp && Array.isArray(sops.pgp) && (sops.pgp as unknown[]).length > 0) return \"pgp\";\n return \"age\"; // Interpretation: default to age when metadata is ambiguous\n }\n\n private extractRecipients(\n sops: Record<string, unknown>,\n backend: \"age\" | \"awskms\" | \"gcpkms\" | \"azurekv\" | \"pgp\",\n ): string[] {\n switch (backend) {\n case \"age\": {\n const entries = sops.age as Array<Record<string, unknown>> | undefined;\n return entries?.map((e) => String(e.recipient ?? \"\")) ?? [];\n }\n case \"awskms\": {\n const entries = sops.kms as Array<Record<string, unknown>> | undefined;\n return entries?.map((e) => String(e.arn ?? \"\")) ?? [];\n }\n case \"gcpkms\": {\n const entries = sops.gcp_kms as Array<Record<string, unknown>> | undefined;\n return entries?.map((e) => String(e.resource_id ?? \"\")) ?? [];\n }\n case \"azurekv\": {\n const entries = sops.azure_kv as Array<Record<string, unknown>> | undefined;\n return (\n entries?.map((e) => {\n const vaultUrl = String(e.vaultUrl ?? e.vault_url ?? \"\");\n const name = String(e.name ?? e.key ?? \"\");\n // Return the composite Key Vault key identifier\n return vaultUrl && name ? `${vaultUrl}/keys/${name}` : vaultUrl || name;\n }) ?? []\n );\n }\n case \"pgp\": {\n const entries = sops.pgp as Array<Record<string, unknown>> | undefined;\n return entries?.map((e) => String(e.fp ?? \"\")) ?? [];\n }\n }\n }\n\n private buildEncryptArgs(\n filePath: string,\n manifest: ClefManifest,\n environment?: string,\n ): string[] {\n const args: string[] = [];\n\n const config = environment\n ? resolveBackendConfig(manifest, environment)\n : {\n backend: manifest.sops.default_backend,\n aws_kms_arn: manifest.sops.aws_kms_arn,\n gcp_kms_resource_id: manifest.sops.gcp_kms_resource_id,\n azure_kv_url: manifest.sops.azure_kv_url,\n pgp_fingerprint: manifest.sops.pgp_fingerprint,\n };\n\n switch (config.backend) {\n case \"age\":\n // Key injection is handled via buildSopsEnv() \u2014 no extra args needed here\n break;\n case \"awskms\":\n if (config.aws_kms_arn) {\n args.push(\"--kms\", config.aws_kms_arn);\n }\n break;\n case \"gcpkms\":\n if (config.gcp_kms_resource_id) {\n args.push(\"--gcp-kms\", config.gcp_kms_resource_id);\n }\n break;\n case \"azurekv\":\n if (config.azure_kv_url) {\n args.push(\"--azure-kv\", config.azure_kv_url);\n }\n break;\n case \"pgp\":\n if (config.pgp_fingerprint) {\n args.push(\"--pgp\", config.pgp_fingerprint);\n }\n break;\n }\n\n return args;\n }\n}\n", "/**\n * Resolves the path to the `sops` binary using a three-tier resolution chain:\n *\n * 1. `CLEF_SOPS_PATH` environment variable (explicit user override)\n * 2. Bundled platform-specific package (`@clef-sh/sops-{os}-{arch}`)\n * 3. System PATH fallback (bare `\"sops\"` command name)\n *\n * The result is cached after the first call \u2014 subsequent calls return the\n * same resolution without re-probing the filesystem.\n */\nimport * as fs from \"fs\";\nimport * as path from \"path\";\nimport { tryBundled } from \"./bundled\";\n\nfunction validateSopsPath(candidate: string): void {\n if (!path.isAbsolute(candidate)) {\n throw new Error(`CLEF_SOPS_PATH must be an absolute path, got '${candidate}'.`);\n }\n const segments = candidate.split(/[/\\\\]/);\n if (segments.includes(\"..\")) {\n throw new Error(\n `CLEF_SOPS_PATH contains '..' path segments ('${candidate}'). ` +\n \"Use an absolute path without directory traversal.\",\n );\n }\n}\n\nexport type SopsSource = \"env\" | \"bundled\" | \"system\";\n\nexport interface SopsResolution {\n /** Absolute path to the sops binary, or \"sops\" for system PATH fallback. */\n path: string;\n /** How the binary was located. */\n source: SopsSource;\n}\n\nlet cached: SopsResolution | undefined;\n\n/**\n * Resolve the sops binary path.\n *\n * Resolution order:\n * 1. `CLEF_SOPS_PATH` env var \u2014 explicit override, used as-is\n * 2. Bundled `@clef-sh/sops-{platform}-{arch}` package\n * 3. System PATH fallback \u2014 returns bare `\"sops\"`\n *\n * The result is cached module-wide. Call {@link resetSopsResolution} in tests\n * to clear the cache.\n */\nexport function resolveSopsPath(): SopsResolution {\n if (cached) return cached;\n\n // 1. Explicit environment override\n const envPath = process.env.CLEF_SOPS_PATH?.trim();\n if (envPath) {\n validateSopsPath(envPath);\n if (!fs.existsSync(envPath)) {\n throw new Error(`CLEF_SOPS_PATH points to '${envPath}' but the file does not exist.`);\n }\n cached = { path: envPath, source: \"env\" };\n return cached;\n }\n\n // 2. Bundled platform package\n const bundledPath = tryBundled();\n if (bundledPath) {\n cached = { path: bundledPath, source: \"bundled\" };\n return cached;\n }\n\n // 3. System PATH fallback\n cached = { path: \"sops\", source: \"system\" };\n return cached;\n}\n\n/**\n * Clear the cached resolution. Only intended for use in tests.\n */\nexport function resetSopsResolution(): void {\n cached = undefined;\n}\n", "/**\n * Locates the bundled sops binary from the platform-specific npm package.\n * Extracted into its own module for testability \u2014 the resolver imports this\n * so tests can mock it without overriding require.resolve.\n */\nimport * as fs from \"fs\";\nimport * as path from \"path\";\n\n/**\n * Try to locate the bundled sops binary from the platform-specific npm package.\n * Returns the resolved path or null if the package is not installed.\n */\nexport function tryBundled(): string | null {\n const platform = process.platform;\n const arch = process.arch;\n\n // Map Node.js arch names to our package names\n const archName = arch === \"x64\" ? \"x64\" : arch === \"arm64\" ? \"arm64\" : null;\n if (!archName) return null;\n\n // Map Node.js platform names to our package names\n const platformName =\n platform === \"darwin\"\n ? \"darwin\"\n : platform === \"linux\"\n ? \"linux\"\n : platform === \"win32\"\n ? \"win32\"\n : null;\n if (!platformName) return null;\n\n const packageName = `@clef-sh/sops-${platformName}-${archName}`;\n const binName = platform === \"win32\" ? \"sops.exe\" : \"sops\";\n\n try {\n // Use createRequire to resolve the platform package.\n const packageMain = require.resolve(`${packageName}/package.json`);\n const packageDir = path.dirname(packageMain);\n const binPath = path.join(packageDir, \"bin\", binName);\n return fs.existsSync(binPath) ? binPath : null;\n } catch {\n return null;\n }\n}\n", "import {\n DependencyStatus,\n DependencyVersion,\n SopsMissingError,\n SopsVersionError,\n SubprocessRunner,\n} from \"../types\";\nimport { resolveSopsPath } from \"../sops/resolver\";\n\n// Minimum versions \u2014 update .github/workflows/ci.yml when these change\nexport const REQUIREMENTS = {\n sops: \"3.8.0\",\n git: \"2.28.0\",\n} as const;\n\n/**\n * Parse a version string like \"3.8.1\" into [major, minor, patch].\n * Returns null if the string is not a valid semver-like version.\n */\nfunction parseSemver(version: string): [number, number, number] | null {\n const match = version.match(/^(\\d+)\\.(\\d+)\\.(\\d+)/);\n if (!match) return null;\n return [parseInt(match[1], 10), parseInt(match[2], 10), parseInt(match[3], 10)];\n}\n\n/**\n * Returns true if `installed` >= `required` using semver comparison.\n */\nfunction semverSatisfied(installed: string, required: string): boolean {\n const a = parseSemver(installed);\n const b = parseSemver(required);\n if (!a || !b) return false;\n\n if (a[0] !== b[0]) return a[0] > b[0];\n if (a[1] !== b[1]) return a[1] > b[1];\n return a[2] >= b[2];\n}\n\n/**\n * Extract version from sops output.\n * Format: \"sops 3.8.1 (latest)\" or \"sops 3.9.4\"\n */\nfunction parseSopsVersion(stdout: string): string | null {\n const match = stdout.match(/sops\\s+(\\d+\\.\\d+\\.\\d+)/);\n return match ? match[1] : null;\n}\n\n/**\n * Extract version from git output.\n * Format: \"git version 2.43.0\" or \"git version 2.50.1 (Apple Git-155)\"\n */\nfunction parseGitVersion(stdout: string): string | null {\n const match = stdout.match(/git version\\s+(\\d+\\.\\d+\\.\\d+)/);\n return match ? match[1] : null;\n}\n\n/**\n * Get the platform-appropriate install hint for a binary.\n */\nfunction getInstallHint(name: \"sops\" | \"git\"): string {\n const platform = process.platform;\n\n switch (name) {\n case \"sops\":\n if (platform === \"darwin\") return \"brew install sops\";\n return \"see https://github.com/getsops/sops/releases\";\n case \"git\":\n if (platform === \"darwin\") return \"brew install git\";\n if (platform === \"linux\") return \"apt install git\";\n return \"see https://git-scm.com/downloads\";\n }\n}\n\n/**\n * Check a single dependency. Returns null if the binary is not found.\n * Never throws.\n */\nexport async function checkDependency(\n name: \"sops\" | \"git\",\n runner: SubprocessRunner,\n commandOverride?: string,\n): Promise<DependencyVersion | null> {\n try {\n // For sops, use the resolver to find the binary path (unless overridden)\n const resolution = name === \"sops\" && !commandOverride ? resolveSopsPath() : undefined;\n const command = commandOverride ?? (resolution ? resolution.path : name);\n\n const result = await runner.run(command, [\"--version\"]);\n\n if (result.exitCode !== 0) {\n return null;\n }\n\n const output = result.stdout.trim() || result.stderr.trim();\n let installed: string | null = null;\n\n switch (name) {\n case \"sops\":\n installed = parseSopsVersion(output);\n break;\n case \"git\":\n installed = parseGitVersion(output);\n break;\n }\n\n if (!installed) {\n return null;\n }\n\n const required = REQUIREMENTS[name];\n return {\n installed,\n required,\n satisfied: semverSatisfied(installed, required),\n installHint: getInstallHint(name),\n source: resolution?.source,\n resolvedPath: resolution?.path,\n };\n } catch {\n return null;\n }\n}\n\n/**\n * Check sops and git dependencies in parallel.\n */\nexport async function checkAll(runner: SubprocessRunner): Promise<DependencyStatus> {\n const [sops, git] = await Promise.all([\n checkDependency(\"sops\", runner),\n checkDependency(\"git\", runner),\n ]);\n\n return { sops, git };\n}\n\n/**\n * Assert that sops is installed and meets the minimum version.\n * Throws SopsMissingError or SopsVersionError.\n */\nexport async function assertSops(runner: SubprocessRunner, command?: string): Promise<void> {\n const dep = await checkDependency(\"sops\", runner, command);\n\n if (!dep) {\n throw new SopsMissingError(getInstallHint(\"sops\"));\n }\n\n if (!dep.satisfied) {\n throw new SopsVersionError(dep.installed, dep.required, getInstallHint(\"sops\"));\n }\n}\n\n// Exported for testing\nexport { parseSopsVersion, parseGitVersion, semverSatisfied };\n", "/**\n * age key generation using the age-encryption npm package.\n * Dynamic import() is required: age-encryption is ESM-only; this package compiles to CJS.\n */\n\nexport interface AgeIdentity {\n /** AGE-SECRET-KEY-1... armored private key string */\n privateKey: string;\n /** age1... bech32 public key string */\n publicKey: string;\n}\n\n/**\n * Generate a new age key pair using the `age-encryption` npm package.\n *\n * @returns Private key (`AGE-SECRET-KEY-1...` format) and derived public key (`age1...` bech32 format).\n */\nexport async function generateAgeIdentity(): Promise<AgeIdentity> {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any -- dynamic ESM import of CJS-incompatible package\n const { generateIdentity, identityToRecipient } = await import(\"age-encryption\" as any);\n const privateKey = (await generateIdentity()) as string;\n const publicKey = (await identityToRecipient(privateKey)) as string;\n return { privateKey, publicKey };\n}\n\n/**\n * Derive the age public key (`age1...`) from an existing private key (`AGE-SECRET-KEY-1...`).\n */\nexport async function deriveAgePublicKey(privateKey: string): Promise<string> {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any -- dynamic ESM import of CJS-incompatible package\n const { identityToRecipient } = await import(\"age-encryption\" as any);\n return (await identityToRecipient(privateKey)) as string;\n}\n\n/**\n * Format an age private key and public key into the standard key file format.\n * The output includes a `created` timestamp comment and is ready to write to disk.\n *\n * @param privateKey - `AGE-SECRET-KEY-1...` armored private key string.\n * @param publicKey - `age1...` bech32 public key string.\n */\nexport function formatAgeKeyFile(privateKey: string, publicKey: string): string {\n const now = new Date().toISOString();\n return `# created: ${now}\\n# public key: ${publicKey}\\n${privateKey}\\n`;\n}\n", "import * as path from \"path\";\nimport {\n ClefManifest,\n LintIssue,\n LintResult,\n resolveRecipientsForEnvironment,\n ServiceIdentityDefinition,\n} from \"../types\";\nimport { MatrixManager } from \"../matrix/manager\";\nimport { SchemaValidator } from \"../schema/validator\";\nimport { EncryptionBackend } from \"../types\";\nimport { getPendingKeys } from \"../pending/metadata\";\n\n/**\n * Runs matrix completeness, schema validation, SOPS integrity, and key-drift checks.\n *\n * @example\n * ```ts\n * const runner = new LintRunner(matrixManager, schemaValidator, sopsClient);\n * const result = await runner.run(manifest, repoRoot);\n * ```\n */\nexport class LintRunner {\n constructor(\n private readonly matrixManager: MatrixManager,\n private readonly schemaValidator: SchemaValidator,\n private readonly sopsClient: EncryptionBackend,\n ) {}\n\n /**\n * Lint the entire matrix: check missing files, schema errors, SOPS integrity,\n * single-recipient warnings, and cross-environment key drift.\n *\n * @param manifest - Parsed manifest.\n * @param repoRoot - Absolute path to the repository root.\n */\n async run(manifest: ClefManifest, repoRoot: string): Promise<LintResult> {\n const issues: LintIssue[] = [];\n const cells = this.matrixManager.resolveMatrix(manifest, repoRoot);\n let fileCount = 0;\n let pendingCount = 0;\n\n // Category 1: Matrix completeness\n const missingCells = cells.filter((c) => !c.exists);\n for (const cell of missingCells) {\n issues.push({\n severity: \"error\",\n category: \"matrix\",\n file: cell.filePath,\n message: `Missing encrypted file for ${cell.namespace}/${cell.environment}.`,\n fixCommand: `clef init`,\n });\n }\n\n const existingCells = cells.filter((c) => c.exists);\n fileCount = existingCells.length;\n\n // Build a map of keys per namespace to detect cross-env drift\n const namespaceKeys: Record<string, Record<string, Set<string>>> = {};\n\n for (const cell of existingCells) {\n // Category 3: SOPS integrity\n try {\n const isValid = await this.sopsClient.validateEncryption(cell.filePath);\n if (!isValid) {\n issues.push({\n severity: \"error\",\n category: \"sops\",\n file: cell.filePath,\n message: `File is missing valid SOPS encryption metadata.`,\n fixCommand: `sops encrypt -i ${cell.filePath}`,\n });\n continue;\n }\n } catch {\n issues.push({\n severity: \"error\",\n category: \"sops\",\n file: cell.filePath,\n message: `Could not validate SOPS metadata. The file may be corrupted.`,\n });\n continue;\n }\n\n // Decrypt for schema and key-drift checks\n try {\n const decrypted = await this.sopsClient.decrypt(cell.filePath);\n const keys = Object.keys(decrypted.values);\n\n // Track keys per namespace/environment\n if (!namespaceKeys[cell.namespace]) {\n namespaceKeys[cell.namespace] = {};\n }\n namespaceKeys[cell.namespace][cell.environment] = new Set(keys);\n\n // Check SOPS metadata for single-recipient warning\n if (decrypted.metadata.recipients.length <= 1) {\n issues.push({\n severity: \"info\",\n category: \"sops\",\n file: cell.filePath,\n message: `File is encrypted with only ${decrypted.metadata.recipients.length} recipient(s). Consider adding a backup key.`,\n });\n }\n\n // Per-environment recipient drift check\n const envRecipients = resolveRecipientsForEnvironment(manifest, cell.environment);\n if (envRecipients) {\n const expectedKeys = new Set(\n envRecipients.map((r) => (typeof r === \"string\" ? r : r.key)),\n );\n const actualKeys = new Set(decrypted.metadata.recipients);\n for (const expected of expectedKeys) {\n if (!actualKeys.has(expected)) {\n issues.push({\n severity: \"warning\",\n category: \"sops\",\n file: cell.filePath,\n message: `Expected recipient '${expected.slice(0, 4)}\u2026${expected.slice(-8)}' is missing from encrypted file.`,\n fixCommand: `clef recipients add ${expected} -e ${cell.environment}`,\n });\n }\n }\n for (const actual of actualKeys) {\n if (!expectedKeys.has(actual)) {\n issues.push({\n severity: \"warning\",\n category: \"sops\",\n file: cell.filePath,\n message: `Unexpected recipient '${actual.slice(0, 4)}\u2026${actual.slice(-8)}' found in encrypted file.`,\n fixCommand: `clef recipients remove ${actual} -e ${cell.environment}`,\n });\n }\n }\n }\n\n // Category 2: Schema validation\n const ns = manifest.namespaces.find((n) => n.name === cell.namespace);\n if (ns?.schema) {\n const schemaPath = path.join(repoRoot, ns.schema);\n try {\n const schema = this.schemaValidator.loadSchema(schemaPath);\n const result = this.schemaValidator.validate(decrypted.values, schema);\n\n for (const err of result.errors) {\n issues.push({\n severity: \"error\",\n category: \"schema\",\n file: cell.filePath,\n key: err.key,\n message: err.message,\n fixCommand: `clef set ${cell.namespace}/${cell.environment} ${err.key} <value>`,\n });\n }\n\n for (const warn of result.warnings) {\n issues.push({\n severity: \"warning\",\n category: \"schema\",\n file: cell.filePath,\n key: warn.key,\n message: warn.message,\n });\n }\n } catch {\n issues.push({\n severity: \"warning\",\n category: \"schema\",\n file: cell.filePath,\n message: `Could not load schema '${ns.schema}' for validation.`,\n });\n }\n } else {\n // No schema \u2014 flag keys with no schema as info\n for (const key of keys) {\n issues.push({\n severity: \"info\",\n category: \"schema\",\n file: cell.filePath,\n key,\n message: `Key '${key}' has no schema definition. Consider adding a schema for namespace '${cell.namespace}'.`,\n });\n }\n }\n\n // Check for pending keys\n try {\n const pendingKeys = await getPendingKeys(cell.filePath);\n pendingCount += pendingKeys.length;\n for (const pendingKey of pendingKeys) {\n issues.push({\n severity: \"warning\",\n category: \"schema\",\n file: cell.filePath,\n key: pendingKey,\n message: `Value is a random placeholder \\u2014 replace with the real secret.`,\n fixCommand: `clef set ${cell.namespace}/${cell.environment} ${pendingKey}`,\n });\n }\n } catch {\n // Metadata unreadable \u2014 skip pending check\n }\n } catch {\n issues.push({\n severity: \"error\",\n category: \"sops\",\n file: cell.filePath,\n message: `Failed to decrypt file. Ensure you have the correct decryption key.`,\n });\n }\n }\n\n // Detect cross-environment key drift\n for (const [nsName, envKeys] of Object.entries(namespaceKeys)) {\n const allKeys = new Set<string>();\n for (const keys of Object.values(envKeys)) {\n for (const k of keys) allKeys.add(k);\n }\n\n for (const [envName, keys] of Object.entries(envKeys)) {\n for (const key of allKeys) {\n if (!keys.has(key)) {\n const presentIn = Object.entries(envKeys)\n .filter(([, ks]) => ks.has(key))\n .map(([e]) => e);\n const cell = existingCells.find(\n (c) => c.namespace === nsName && c.environment === envName,\n );\n if (cell) {\n issues.push({\n severity: \"warning\",\n category: \"matrix\",\n file: cell.filePath,\n key,\n message: `Key '${key}' is missing in ${envName} but present in ${presentIn.join(\", \")}.`,\n fixCommand: `clef set ${nsName}/${envName} ${key} <value>`,\n });\n }\n }\n }\n }\n }\n\n // Service identity drift checks\n if (manifest.service_identities && manifest.service_identities.length > 0) {\n const siIssues = await this.lintServiceIdentities(\n manifest.service_identities,\n manifest,\n repoRoot,\n existingCells,\n );\n issues.push(...siIssues);\n }\n\n return { issues, fileCount: fileCount + missingCells.length, pendingCount };\n }\n\n /**\n * Lint service identity configurations for drift issues.\n */\n private async lintServiceIdentities(\n identities: ServiceIdentityDefinition[],\n manifest: ClefManifest,\n repoRoot: string,\n existingCells: { namespace: string; environment: string; filePath: string }[],\n ): Promise<LintIssue[]> {\n const issues: LintIssue[] = [];\n const declaredEnvNames = new Set(manifest.environments.map((e) => e.name));\n const declaredNsNames = new Set(manifest.namespaces.map((ns) => ns.name));\n\n for (const si of identities) {\n // Namespace references\n for (const ns of si.namespaces) {\n if (!declaredNsNames.has(ns)) {\n issues.push({\n severity: \"error\",\n category: \"service-identity\",\n file: \"clef.yaml\",\n message: `Service identity '${si.name}' references non-existent namespace '${ns}'.`,\n });\n }\n }\n\n // Environment coverage\n for (const envName of declaredEnvNames) {\n if (!(envName in si.environments)) {\n issues.push({\n severity: \"error\",\n category: \"service-identity\",\n file: \"clef.yaml\",\n message: `Service identity '${si.name}' is missing environment '${envName}'. Manually add an age key pair for this environment in clef.yaml.`,\n });\n }\n }\n\n // Recipient registration on scoped files\n // (KMS-backed environments skip recipient checks)\n for (const cell of existingCells) {\n const envConfig = si.environments[cell.environment];\n if (!envConfig) continue;\n if (!envConfig.recipient) continue;\n\n if (si.namespaces.includes(cell.namespace)) {\n try {\n const metadata = await this.sopsClient.getMetadata(cell.filePath);\n if (!metadata.recipients.includes(envConfig.recipient)) {\n issues.push({\n severity: \"warning\",\n category: \"service-identity\",\n file: cell.filePath,\n message: `Service identity '${si.name}' recipient is not registered in ${cell.namespace}/${cell.environment}.`,\n fixCommand: `clef service create ${si.name} --namespaces ${si.namespaces.join(\",\")}`,\n });\n }\n } catch {\n // Cannot read metadata \u2014 skip\n }\n } else {\n try {\n const metadata = await this.sopsClient.getMetadata(cell.filePath);\n if (metadata.recipients.includes(envConfig.recipient)) {\n issues.push({\n severity: \"warning\",\n category: \"service-identity\",\n file: cell.filePath,\n message: `Service identity '${si.name}' recipient found in ${cell.namespace}/${cell.environment} but namespace is not in scope.`,\n fixCommand: `clef recipients remove ${envConfig.recipient} -e ${cell.environment}`,\n });\n }\n } catch {\n // Cannot read metadata \u2014 skip\n }\n }\n }\n }\n\n return issues;\n }\n\n /**\n * Auto-fix safe issues (scaffold missing matrix files), then re-run lint.\n *\n * @param manifest - Parsed manifest.\n * @param repoRoot - Absolute path to the repository root.\n */\n async fix(manifest: ClefManifest, repoRoot: string): Promise<LintResult> {\n // Auto-fix safe issues: scaffold missing files\n const missingCells = this.matrixManager.detectMissingCells(manifest, repoRoot);\n\n for (const cell of missingCells) {\n await this.matrixManager.scaffoldCell(cell, this.sopsClient, manifest);\n }\n\n // Re-run lint after fixes\n return this.run(manifest, repoRoot);\n }\n}\n", "import { DecryptedFile, ExecOptions, ExportOptions } from \"../types\";\n\n/**\n * Prepares decrypted secrets for consumption via environment injection or shell export.\n *\n * @example\n * ```ts\n * const client = new ConsumptionClient();\n * const env = client.prepareEnvironment(decrypted, process.env, { prefix: \"APP_\" });\n * ```\n */\nexport class ConsumptionClient {\n /**\n * Merges decrypted values into a base environment, respecting --only, --prefix, and --no-override.\n * Returns a new environment record suitable for child_process.spawn.\n */\n prepareEnvironment(\n decryptedFile: DecryptedFile,\n baseEnv: Record<string, string | undefined>,\n options: ExecOptions = {},\n ): Record<string, string> {\n const result: Record<string, string> = {};\n\n // Copy base environment\n for (const [k, v] of Object.entries(baseEnv)) {\n if (v !== undefined) {\n result[k] = v;\n }\n }\n\n let entries = Object.entries(decryptedFile.values);\n\n // --only: filter to specified keys\n if (options.only && options.only.length > 0) {\n const allowed = new Set(options.only);\n entries = entries.filter(([key]) => allowed.has(key));\n }\n\n // Inject values with optional prefix\n for (const [key, value] of entries) {\n const envKey = options.prefix ? `${options.prefix}${key}` : key;\n\n // --no-override: skip keys that already exist in the base environment\n if (options.noOverride && envKey in result) {\n continue;\n }\n\n result[envKey] = value;\n }\n\n return result;\n }\n\n /**\n * Formats decrypted values for stdout output.\n * Values are single-quoted; embedded single quotes are escaped as '\\''.\n */\n formatExport(\n decryptedFile: DecryptedFile,\n format: ExportOptions[\"format\"],\n noExport: boolean,\n ): string {\n if (format !== \"env\") {\n throw new Error(\n `Unsupported export format '${format}'. Only 'env' is supported.\\n` +\n \"Clef does not support formats that encourage writing plaintext secrets to disk.\\n\" +\n \"Use 'clef exec' to inject secrets directly into a process, or 'clef export --format env' to print shell export statements to stdout.\",\n );\n }\n\n const lines: string[] = [];\n const prefix = noExport ? \"\" : \"export \";\n\n for (const [key, value] of Object.entries(decryptedFile.values)) {\n // Single-quote the value; escape embedded single quotes as '\\''\n const escaped = value.replace(/'/g, \"'\\\\''\");\n lines.push(`${prefix}${key}='${escaped}'`);\n }\n\n return lines.join(\"\\n\") + \"\\n\";\n }\n}\n", "import * as path from \"path\";\nimport { ClefManifest } from \"../types\";\nimport { EncryptionBackend } from \"../types\";\nimport { parse, ImportFormat } from \"./parsers\";\nexport type { ImportFormat, ParsedImport } from \"./parsers\";\n\nexport interface ImportOptions {\n format?: ImportFormat;\n prefix?: string;\n keys?: string[];\n overwrite?: boolean;\n dryRun?: boolean;\n stdin?: boolean;\n}\n\nexport interface ImportResult {\n imported: string[];\n skipped: string[];\n failed: Array<{ key: string; error: string }>;\n warnings: string[];\n dryRun: boolean;\n}\n\n/**\n * Imports secrets from `.env`, JSON, or YAML files into encrypted matrix cells.\n *\n * @example\n * ```ts\n * const runner = new ImportRunner(sopsClient);\n * const result = await runner.import(\"app/staging\", null, envContent, manifest, repoRoot, { format: \"dotenv\" });\n * ```\n */\nexport class ImportRunner {\n constructor(private readonly sopsClient: EncryptionBackend) {}\n\n /**\n * Parse a source file and import its key/value pairs into a target `namespace/environment` cell.\n *\n * @param target - Target cell in `namespace/environment` format.\n * @param sourcePath - Source file path used for format detection (pass `null` when reading from stdin).\n * @param content - Raw file content to import.\n * @param manifest - Parsed manifest.\n * @param repoRoot - Absolute path to the repository root.\n * @param options - Import options (format, prefix, key filter, overwrite, dry-run).\n */\n async import(\n target: string,\n sourcePath: string | null,\n content: string,\n manifest: ClefManifest,\n repoRoot: string,\n options: ImportOptions,\n ): Promise<ImportResult> {\n const [ns, env] = target.split(\"/\");\n const filePath = path.join(\n repoRoot,\n manifest.file_pattern.replace(\"{namespace}\", ns).replace(\"{environment}\", env),\n );\n\n // Parse content\n const parsed = parse(content, options.format ?? \"auto\", sourcePath ?? \"\");\n\n // Build candidate key/value pairs\n let candidates = Object.entries(parsed.pairs);\n\n // Apply prefix filter\n if (options.prefix) {\n const prefix = options.prefix;\n candidates = candidates.filter(([key]) => key.startsWith(prefix));\n }\n\n // Apply keys filter\n if (options.keys && options.keys.length > 0) {\n const keySet = new Set(options.keys);\n candidates = candidates.filter(([key]) => keySet.has(key));\n }\n\n const imported: string[] = [];\n const skipped: string[] = [];\n const failed: Array<{ key: string; error: string }> = [];\n const warnings = [...parsed.warnings];\n\n if (options.dryRun) {\n // Dry run: check existing keys but never call encrypt\n let existingKeys: Set<string>;\n try {\n const decrypted = await this.sopsClient.decrypt(filePath);\n existingKeys = new Set(Object.keys(decrypted.values));\n } catch {\n // File may not exist or be inaccessible \u2014 treat as empty\n existingKeys = new Set<string>();\n }\n\n for (const [key] of candidates) {\n if (existingKeys.has(key) && !options.overwrite) {\n skipped.push(key);\n } else {\n imported.push(key);\n }\n }\n\n return { imported, skipped, failed, warnings, dryRun: true };\n }\n\n // Real import\n const decrypted = await this.sopsClient.decrypt(filePath);\n let currentValues: Record<string, string> = { ...decrypted.values };\n const existingKeys = new Set(Object.keys(decrypted.values));\n\n for (const [key, value] of candidates) {\n if (existingKeys.has(key) && !options.overwrite) {\n skipped.push(key);\n continue;\n }\n\n try {\n const newValues = { ...currentValues, [key]: value };\n await this.sopsClient.encrypt(filePath, newValues, manifest, env);\n currentValues = newValues;\n imported.push(key);\n } catch (err) {\n const message = err instanceof Error ? err.message : \"Encryption failed\";\n failed.push({ key, error: message });\n // Do NOT update currentValues, do NOT rollback previous encrypts. Continue with rest.\n }\n }\n\n return { imported, skipped, failed, warnings, dryRun: false };\n }\n}\n", "import * as path from \"path\";\nimport * as YAML from \"yaml\";\n\nexport type ImportFormat = \"dotenv\" | \"json\" | \"yaml\" | \"auto\";\n\nexport interface ParsedImport {\n pairs: Record<string, string>;\n format: Exclude<ImportFormat, \"auto\">;\n skipped: string[];\n warnings: string[];\n}\n\n/**\n * Auto-detect the format of a file from its extension, basename, and content heuristics.\n *\n * @param filePath - File path used for extension and basename detection.\n * @param content - Raw file content used as a fallback heuristic.\n * @returns Detected format (`\"dotenv\"`, `\"json\"`, or `\"yaml\"`).\n */\nexport function detectFormat(filePath: string, content: string): Exclude<ImportFormat, \"auto\"> {\n const base = path.basename(filePath);\n const ext = path.extname(filePath).toLowerCase();\n\n // basename is \".env\" or starts with \".env.\"\n if (base === \".env\" || base.startsWith(\".env.\")) {\n return \"dotenv\";\n }\n\n // ends with \".env\"\n if (base.endsWith(\".env\")) {\n return \"dotenv\";\n }\n\n // extension-based\n if (ext === \".json\") return \"json\";\n if (ext === \".yaml\" || ext === \".yml\") return \"yaml\";\n\n // content heuristics\n const trimmed = content.trimStart();\n if (trimmed.startsWith(\"{\")) {\n return \"json\";\n }\n\n // try JSON.parse \u2014 if it's a non-array object, it's JSON\n try {\n const parsed = JSON.parse(content);\n if (parsed !== null && typeof parsed === \"object\" && !Array.isArray(parsed)) {\n return \"json\";\n }\n } catch {\n // not JSON\n }\n\n // try YAML.parse \u2014 if it's a non-array object, it's YAML\n try {\n const parsed = YAML.parse(content);\n if (parsed !== null && typeof parsed === \"object\" && !Array.isArray(parsed)) {\n return \"yaml\";\n }\n } catch {\n // not YAML\n }\n\n // fallback\n return \"dotenv\";\n}\n\n/**\n * Parse dotenv-formatted content into flat key/value pairs.\n * Supports `export KEY=VALUE`, inline comments, and both single- and double-quoted values.\n */\nexport function parseDotenv(content: string): ParsedImport {\n const pairs: Record<string, string> = {};\n const skipped: string[] = [];\n const warnings: string[] = [];\n\n const lines = content.split(\"\\n\");\n for (const rawLine of lines) {\n let line = rawLine.trim();\n\n // Skip blank lines and comments\n if (!line || line.startsWith(\"#\")) {\n continue;\n }\n\n // Strip \"export \" prefix\n if (line.startsWith(\"export \")) {\n line = line.slice(7);\n }\n\n // Must have KEY=VALUE format\n const eqIdx = line.indexOf(\"=\");\n if (eqIdx === -1) {\n continue;\n }\n\n const key = line.slice(0, eqIdx).trim();\n if (!key) {\n continue;\n }\n\n let value = line.slice(eqIdx + 1);\n\n // Strip inline comments: everything after \" #\" (space-hash)\n const inlineCommentIdx = value.indexOf(\" #\");\n if (inlineCommentIdx !== -1) {\n value = value.slice(0, inlineCommentIdx);\n }\n\n // Strip matching outer quotes (\" or ')\n if (\n (value.startsWith('\"') && value.endsWith('\"')) ||\n (value.startsWith(\"'\") && value.endsWith(\"'\"))\n ) {\n value = value.slice(1, -1);\n }\n\n pairs[key] = value;\n }\n\n return { pairs, format: \"dotenv\", skipped, warnings };\n}\n\n/**\n * Parse a JSON object into flat string key/value pairs.\n * Non-string values (numbers, booleans, nulls, arrays, objects) are skipped with warnings.\n *\n * @throws `Error` If the content is not valid JSON or the root is not an object.\n */\nexport function parseJson(content: string): ParsedImport {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n let parsed: any;\n try {\n parsed = JSON.parse(content);\n } catch (err) {\n throw new Error(`Invalid JSON: ${(err as Error).message}`);\n }\n\n if (Array.isArray(parsed)) {\n throw new Error(\n \"JSON root must be an object, not an array. Clef keys are flat key/value pairs.\",\n );\n }\n\n if (parsed === null || typeof parsed !== \"object\") {\n throw new Error(\"JSON root must be an object. Clef keys are flat key/value pairs.\");\n }\n\n const pairs: Record<string, string> = {};\n const skipped: string[] = [];\n const warnings: string[] = [];\n\n for (const [key, value] of Object.entries(parsed as Record<string, unknown>)) {\n if (typeof value === \"string\") {\n pairs[key] = value;\n } else if (value === null) {\n skipped.push(key);\n warnings.push(`${key}: skipped \u2014 value is null, not string`);\n } else if (Array.isArray(value)) {\n skipped.push(key);\n warnings.push(`${key}: skipped \u2014 value is array, not string`);\n } else if (typeof value === \"object\") {\n skipped.push(key);\n warnings.push(`${key}: skipped \u2014 value is nested object, not string`);\n } else {\n // number, boolean\n skipped.push(key);\n warnings.push(`${key}: skipped \u2014 value is ${typeof value}, not string`);\n }\n }\n\n return { pairs, format: \"json\", skipped, warnings };\n}\n\n/**\n * Parse a YAML mapping into flat string key/value pairs.\n * Non-string values are skipped with warnings.\n *\n * @throws `Error` If the content is not valid YAML or the root is not a mapping.\n */\nexport function parseYaml(content: string): ParsedImport {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n let parsed: any;\n try {\n parsed = YAML.parse(content);\n } catch (err) {\n throw new Error(`Invalid YAML: ${(err as Error).message}`);\n }\n\n if (Array.isArray(parsed)) {\n throw new Error(\n \"YAML root must be a mapping, not a sequence. Clef keys are flat key/value pairs.\",\n );\n }\n\n if (parsed === null || typeof parsed !== \"object\") {\n throw new Error(\"YAML root must be a mapping. Clef keys are flat key/value pairs.\");\n }\n\n const pairs: Record<string, string> = {};\n const skipped: string[] = [];\n const warnings: string[] = [];\n\n for (const [key, value] of Object.entries(parsed as Record<string, unknown>)) {\n if (typeof value === \"string\") {\n pairs[key] = value;\n } else if (value === null) {\n skipped.push(key);\n warnings.push(`${key}: skipped \u2014 value is null, not string`);\n } else if (Array.isArray(value)) {\n skipped.push(key);\n warnings.push(`${key}: skipped \u2014 value is array, not string`);\n } else if (typeof value === \"object\") {\n skipped.push(key);\n warnings.push(`${key}: skipped \u2014 value is nested object, not string`);\n } else {\n // number, boolean\n skipped.push(key);\n warnings.push(`${key}: skipped \u2014 value is ${typeof value}, not string`);\n }\n }\n\n return { pairs, format: \"yaml\", skipped, warnings };\n}\n\n/**\n * Parse content in the given format (or auto-detect) and return flat key/value pairs.\n *\n * @param content - Raw file content to parse.\n * @param format - Explicit format, or `\"auto\"` to detect from `filePath` and content.\n * @param filePath - File path used for format detection when `format` is `\"auto\"`.\n */\nexport function parse(content: string, format: ImportFormat, filePath?: string): ParsedImport {\n const resolved: Exclude<ImportFormat, \"auto\"> =\n format === \"auto\" ? detectFormat(filePath ?? \"\", content) : format;\n\n switch (resolved) {\n case \"dotenv\":\n return parseDotenv(content);\n case \"json\":\n return parseJson(content);\n case \"yaml\":\n return parseYaml(content);\n }\n}\n", "import * as fs from \"fs\";\nimport * as path from \"path\";\nimport * as YAML from \"yaml\";\nimport { ClefManifest, EncryptionBackend } from \"../types\";\nimport { MatrixManager } from \"../matrix/manager\";\nimport { validateAgePublicKey, keyPreview } from \"./validator\";\nimport { CLEF_MANIFEST_FILENAME } from \"../manifest/parser\";\n\nexport interface Recipient {\n key: string;\n preview: string;\n label?: string;\n}\n\nexport interface RecipientsResult {\n added?: Recipient;\n removed?: Recipient;\n recipients: Recipient[];\n reEncryptedFiles: string[];\n failedFiles: string[];\n warnings: string[];\n}\n\ninterface RawRecipientEntry {\n key: string;\n label?: string;\n}\n\nfunction parseRecipientEntry(entry: unknown): RawRecipientEntry {\n if (typeof entry === \"string\") {\n return { key: entry };\n }\n if (typeof entry === \"object\" && entry !== null) {\n const obj = entry as Record<string, unknown>;\n return {\n key: String(obj.key ?? \"\"),\n ...(typeof obj.label === \"string\" ? { label: obj.label } : {}),\n };\n }\n return { key: \"\" };\n}\n\nfunction toRecipient(entry: RawRecipientEntry): Recipient {\n return {\n key: entry.key,\n preview: keyPreview(entry.key),\n ...(entry.label ? { label: entry.label } : {}),\n };\n}\n\nfunction readManifestYaml(repoRoot: string): Record<string, unknown> {\n const manifestPath = path.join(repoRoot, CLEF_MANIFEST_FILENAME);\n const raw = fs.readFileSync(manifestPath, \"utf-8\");\n return YAML.parse(raw) as Record<string, unknown>;\n}\n\nfunction writeManifestYaml(repoRoot: string, doc: Record<string, unknown>): void {\n const manifestPath = path.join(repoRoot, CLEF_MANIFEST_FILENAME);\n fs.writeFileSync(manifestPath, YAML.stringify(doc), \"utf-8\");\n}\n\nfunction getRecipientsArray(doc: Record<string, unknown>): unknown[] {\n const sops = doc.sops as Record<string, unknown> | undefined;\n if (!sops) return [];\n const age = sops.age as Record<string, unknown> | undefined;\n if (!age) return [];\n const recipients = age.recipients;\n if (!Array.isArray(recipients)) return [];\n return recipients;\n}\n\nfunction ensureRecipientsArray(doc: Record<string, unknown>): unknown[] {\n if (!doc.sops || typeof doc.sops !== \"object\") {\n doc.sops = {};\n }\n const sops = doc.sops as Record<string, unknown>;\n if (!sops.age || typeof sops.age !== \"object\") {\n sops.age = {};\n }\n const age = sops.age as Record<string, unknown>;\n if (!Array.isArray(age.recipients)) {\n age.recipients = [];\n }\n return age.recipients as unknown[];\n}\n\nfunction getEnvironmentRecipientsArray(doc: Record<string, unknown>, envName: string): unknown[] {\n const environments = doc.environments as Record<string, unknown>[] | undefined;\n if (!Array.isArray(environments)) return [];\n const env = environments.find((e) => (e as Record<string, unknown>).name === envName) as\n | Record<string, unknown>\n | undefined;\n if (!env) return [];\n const recipients = env.recipients;\n if (!Array.isArray(recipients)) return [];\n return recipients;\n}\n\nfunction ensureEnvironmentRecipientsArray(\n doc: Record<string, unknown>,\n envName: string,\n): unknown[] {\n const environments = doc.environments as Record<string, unknown>[] | undefined;\n if (!Array.isArray(environments)) {\n throw new Error(`No environments array in manifest.`);\n }\n const env = environments.find((e) => (e as Record<string, unknown>).name === envName) as\n | Record<string, unknown>\n | undefined;\n if (!env) {\n throw new Error(`Environment '${envName}' not found in manifest.`);\n }\n if (!Array.isArray(env.recipients)) {\n env.recipients = [];\n }\n return env.recipients as unknown[];\n}\n\n/**\n * Manages age recipient keys in the manifest and re-encrypts matrix files on add/remove.\n * All add/remove operations are transactional \u2014 a failure triggers a full rollback.\n *\n * @example\n * ```ts\n * const manager = new RecipientManager(runner, matrixManager);\n * const result = await manager.add(\"age1...\", \"Alice\", manifest, repoRoot);\n * ```\n */\nexport class RecipientManager {\n constructor(\n private readonly encryption: EncryptionBackend,\n private readonly matrixManager: MatrixManager,\n ) {}\n\n /**\n * List all age recipients declared in the manifest.\n *\n * @param manifest - Parsed manifest.\n * @param repoRoot - Absolute path to the repository root.\n * @param environment - Optional environment name to list per-env recipients.\n */\n async list(manifest: ClefManifest, repoRoot: string, environment?: string): Promise<Recipient[]> {\n if (environment) {\n const env = manifest.environments.find((e) => e.name === environment);\n if (!env) {\n throw new Error(`Environment '${environment}' not found in manifest.`);\n }\n }\n const doc = readManifestYaml(repoRoot);\n const entries = environment\n ? getEnvironmentRecipientsArray(doc, environment)\n : getRecipientsArray(doc);\n return entries.map((entry) => toRecipient(parseRecipientEntry(entry)));\n }\n\n /**\n * Add a new age recipient and re-encrypt all existing matrix files.\n * Rolls back the manifest and any already-re-encrypted files on failure.\n *\n * @param key - age public key to add (`age1...`).\n * @param label - Optional human-readable label for the recipient.\n * @param manifest - Parsed manifest.\n * @param repoRoot - Absolute path to the repository root.\n * @param environment - Optional environment name to scope the operation.\n * @throws `Error` If the key is invalid or already present.\n */\n async add(\n key: string,\n label: string | undefined,\n manifest: ClefManifest,\n repoRoot: string,\n environment?: string,\n ): Promise<RecipientsResult> {\n const validation = validateAgePublicKey(key);\n if (!validation.valid) {\n throw new Error(validation.error);\n }\n const normalizedKey = validation.key!;\n\n if (environment) {\n const env = manifest.environments.find((e) => e.name === environment);\n if (!env) {\n throw new Error(`Environment '${environment}' not found in manifest.`);\n }\n }\n\n // Read current manifest\n const doc = readManifestYaml(repoRoot);\n const currentEntries = environment\n ? getEnvironmentRecipientsArray(doc, environment)\n : getRecipientsArray(doc);\n const currentKeys = currentEntries.map((e) => parseRecipientEntry(e).key);\n\n if (currentKeys.includes(normalizedKey)) {\n throw new Error(`Recipient '${keyPreview(normalizedKey)}' is already present.`);\n }\n\n // Save backup of manifest for rollback\n const manifestPath = path.join(repoRoot, CLEF_MANIFEST_FILENAME);\n const manifestBackup = fs.readFileSync(manifestPath, \"utf-8\");\n\n // Add new recipient to manifest\n const recipients = environment\n ? ensureEnvironmentRecipientsArray(doc, environment)\n : ensureRecipientsArray(doc);\n if (label) {\n recipients.push({ key: normalizedKey, label });\n } else {\n recipients.push(normalizedKey);\n }\n writeManifestYaml(repoRoot, doc);\n\n // Re-encrypt matching files\n const allCells = this.matrixManager.resolveMatrix(manifest, repoRoot).filter((c) => c.exists);\n const cells = environment ? allCells.filter((c) => c.environment === environment) : allCells;\n const reEncryptedFiles: string[] = [];\n const failedFiles: string[] = [];\n const fileBackups = new Map<string, string>();\n\n for (const cell of cells) {\n try {\n // Save file backup before re-encryption\n fileBackups.set(cell.filePath, fs.readFileSync(cell.filePath, \"utf-8\"));\n\n await this.encryption.addRecipient(cell.filePath, normalizedKey);\n\n reEncryptedFiles.push(cell.filePath);\n } catch {\n failedFiles.push(cell.filePath);\n\n // Rollback: restore manifest\n fs.writeFileSync(manifestPath, manifestBackup, \"utf-8\");\n\n // Rollback: restore previously re-encrypted files\n for (const reEncryptedFile of reEncryptedFiles) {\n const backup = fileBackups.get(reEncryptedFile);\n if (backup) {\n fs.writeFileSync(reEncryptedFile, backup, \"utf-8\");\n }\n }\n\n // Re-read the restored manifest for the result\n const restoredDoc = readManifestYaml(repoRoot);\n const restoredEntries = environment\n ? getEnvironmentRecipientsArray(restoredDoc, environment)\n : getRecipientsArray(restoredDoc);\n const restoredRecipients = restoredEntries.map((e) => toRecipient(parseRecipientEntry(e)));\n\n return {\n added: toRecipient({ key: normalizedKey, label }),\n recipients: restoredRecipients,\n reEncryptedFiles: [],\n failedFiles,\n warnings: [\"Rollback completed: manifest and re-encrypted files have been restored.\"],\n };\n }\n }\n\n // Build final recipient list\n const updatedDoc = readManifestYaml(repoRoot);\n const updatedEntries = environment\n ? getEnvironmentRecipientsArray(updatedDoc, environment)\n : getRecipientsArray(updatedDoc);\n const finalRecipients = updatedEntries.map((e) => toRecipient(parseRecipientEntry(e)));\n\n return {\n added: toRecipient({ key: normalizedKey, label }),\n recipients: finalRecipients,\n reEncryptedFiles,\n failedFiles,\n warnings: [],\n };\n }\n\n /**\n * Remove an age recipient and re-encrypt all existing matrix files.\n * Rolls back on failure. Note: re-encryption removes _future_ access only;\n * rotate secret values to fully revoke access.\n *\n * @param key - age public key to remove.\n * @param manifest - Parsed manifest.\n * @param repoRoot - Absolute path to the repository root.\n * @param environment - Optional environment name to scope the operation.\n * @throws `Error` If the key is not in the manifest.\n */\n async remove(\n key: string,\n manifest: ClefManifest,\n repoRoot: string,\n environment?: string,\n ): Promise<RecipientsResult> {\n const trimmedKey = key.trim();\n\n if (environment) {\n const env = manifest.environments.find((e) => e.name === environment);\n if (!env) {\n throw new Error(`Environment '${environment}' not found in manifest.`);\n }\n }\n\n // Read current manifest\n const doc = readManifestYaml(repoRoot);\n const currentEntries = environment\n ? getEnvironmentRecipientsArray(doc, environment)\n : getRecipientsArray(doc);\n const parsed = currentEntries.map((e) => parseRecipientEntry(e));\n const matchIndex = parsed.findIndex((p) => p.key === trimmedKey);\n\n if (matchIndex === -1) {\n throw new Error(`Recipient '${keyPreview(trimmedKey)}' is not in the manifest.`);\n }\n\n const removedEntry = parsed[matchIndex];\n\n // Save backup of manifest for rollback\n const manifestPath = path.join(repoRoot, CLEF_MANIFEST_FILENAME);\n const manifestBackup = fs.readFileSync(manifestPath, \"utf-8\");\n\n // Remove recipient from manifest\n const recipients = environment\n ? ensureEnvironmentRecipientsArray(doc, environment)\n : ensureRecipientsArray(doc);\n recipients.splice(matchIndex, 1);\n writeManifestYaml(repoRoot, doc);\n\n // Re-encrypt matching files\n const allCells = this.matrixManager.resolveMatrix(manifest, repoRoot).filter((c) => c.exists);\n const cells = environment ? allCells.filter((c) => c.environment === environment) : allCells;\n const reEncryptedFiles: string[] = [];\n const failedFiles: string[] = [];\n const fileBackups = new Map<string, string>();\n\n for (const cell of cells) {\n try {\n // Save file backup before re-encryption\n fileBackups.set(cell.filePath, fs.readFileSync(cell.filePath, \"utf-8\"));\n\n await this.encryption.removeRecipient(cell.filePath, trimmedKey);\n\n reEncryptedFiles.push(cell.filePath);\n } catch {\n failedFiles.push(cell.filePath);\n\n // Rollback: restore manifest\n fs.writeFileSync(manifestPath, manifestBackup, \"utf-8\");\n\n // Rollback: restore previously re-encrypted files\n for (const reEncryptedFile of reEncryptedFiles) {\n const backup = fileBackups.get(reEncryptedFile);\n if (backup) {\n fs.writeFileSync(reEncryptedFile, backup, \"utf-8\");\n }\n }\n\n // Re-read the restored manifest for the result\n const restoredDoc = readManifestYaml(repoRoot);\n const restoredEntries = environment\n ? getEnvironmentRecipientsArray(restoredDoc, environment)\n : getRecipientsArray(restoredDoc);\n const restoredRecipients = restoredEntries.map((e) => toRecipient(parseRecipientEntry(e)));\n\n return {\n removed: toRecipient(removedEntry),\n recipients: restoredRecipients,\n reEncryptedFiles: [],\n failedFiles,\n warnings: [\n \"Rollback completed: manifest and re-encrypted files have been restored.\",\n \"Re-encryption removes future access, not past access. Rotate secret values to complete revocation.\",\n ],\n };\n }\n }\n\n // Build final recipient list\n const updatedDoc = readManifestYaml(repoRoot);\n const updatedEntries = environment\n ? getEnvironmentRecipientsArray(updatedDoc, environment)\n : getRecipientsArray(updatedDoc);\n const finalRecipients = updatedEntries.map((e) => toRecipient(parseRecipientEntry(e)));\n\n return {\n removed: toRecipient(removedEntry),\n recipients: finalRecipients,\n reEncryptedFiles,\n failedFiles,\n warnings: [\n \"Re-encryption removes future access, not past access. Rotate secret values to complete revocation.\",\n ],\n };\n }\n}\n", "import * as fs from \"fs\";\nimport * as path from \"path\";\nimport * as YAML from \"yaml\";\n\nexport const REQUESTS_FILENAME = \".clef-requests.yaml\";\n\nconst HEADER_COMMENT =\n \"# Pending recipient access requests. Approve with: clef recipients approve <label>\\n\";\n\nexport interface RecipientRequest {\n key: string;\n label: string;\n requestedAt: Date;\n environment?: string;\n}\n\ninterface RawRequest {\n key: string;\n label: string;\n requested_at: string;\n environment?: string;\n}\n\nexport function requestsFilePath(repoRoot: string): string {\n return path.join(repoRoot, REQUESTS_FILENAME);\n}\n\n/** Load all pending requests. Returns empty array if file is missing or malformed. */\nexport function loadRequests(repoRoot: string): RecipientRequest[] {\n const filePath = requestsFilePath(repoRoot);\n try {\n if (!fs.existsSync(filePath)) return [];\n const content = fs.readFileSync(filePath, \"utf-8\");\n const parsed = YAML.parse(content);\n if (!parsed || !Array.isArray(parsed.requests)) return [];\n return parsed.requests.map((r: RawRequest) => ({\n key: r.key,\n label: r.label,\n requestedAt: new Date(r.requested_at),\n environment: r.environment,\n }));\n } catch {\n return [];\n }\n}\n\n/** Save requests to disk. Deletes the file if no requests remain. */\nexport function saveRequests(repoRoot: string, requests: RecipientRequest[]): void {\n const filePath = requestsFilePath(repoRoot);\n if (requests.length === 0) {\n try {\n fs.unlinkSync(filePath);\n } catch {\n // File may not exist\n }\n return;\n }\n const data = {\n requests: requests.map((r) => {\n const raw: RawRequest = {\n key: r.key,\n label: r.label,\n requested_at: r.requestedAt.toISOString(),\n };\n if (r.environment) raw.environment = r.environment;\n return raw;\n }),\n };\n fs.writeFileSync(filePath, HEADER_COMMENT + YAML.stringify(data), \"utf-8\");\n}\n\n/**\n * Add or update a request. If a request with the same key already exists,\n * update its label, timestamp, and environment. Returns the upserted request.\n */\nexport function upsertRequest(\n repoRoot: string,\n key: string,\n label: string,\n environment?: string,\n): RecipientRequest {\n const requests = loadRequests(repoRoot);\n const now = new Date();\n const request: RecipientRequest = { key, label, requestedAt: now, environment };\n\n const existingIndex = requests.findIndex((r) => r.key === key);\n if (existingIndex >= 0) {\n requests[existingIndex] = request;\n } else {\n requests.push(request);\n }\n\n saveRequests(repoRoot, requests);\n return request;\n}\n\n/**\n * Remove a request by matching against label (case-insensitive) or key (exact).\n * Returns the removed request, or null if not found.\n */\nexport function removeRequest(repoRoot: string, identifier: string): RecipientRequest | null {\n const requests = loadRequests(repoRoot);\n const match = findInList(requests, identifier);\n if (!match) return null;\n\n const filtered = requests.filter((r) => r.key !== match.key);\n saveRequests(repoRoot, filtered);\n return match;\n}\n\n/**\n * Find a request by label (case-insensitive) or key (exact match).\n * Returns null if not found.\n */\nexport function findRequest(repoRoot: string, identifier: string): RecipientRequest | null {\n const requests = loadRequests(repoRoot);\n return findInList(requests, identifier);\n}\n\nfunction findInList(requests: RecipientRequest[], identifier: string): RecipientRequest | null {\n const lower = identifier.toLowerCase();\n // Match by label (case-insensitive)\n const byLabel = requests.find((r) => r.label.toLowerCase() === lower);\n if (byLabel) return byLabel;\n // Match by key (exact)\n const byKey = requests.find((r) => r.key === identifier);\n return byKey ?? null;\n}\n", "import * as fs from \"fs\";\nimport * as path from \"path\";\nimport * as YAML from \"yaml\";\nimport { ManifestParser, CLEF_MANIFEST_FILENAME } from \"../manifest/parser\";\nimport { MatrixManager } from \"../matrix/manager\";\nimport { DriftIssue, DriftResult } from \"../types\";\n\n/**\n * Compares key sets across two local Clef repositories without decryption.\n *\n * SOPS files store key names in plaintext \u2014 only values are encrypted.\n * This means drift detection works without the `sops` binary or any\n * decryption keys. The detector reads `.enc.yaml` files as plain YAML,\n * strips the `sops` metadata key, and compares the remaining top-level\n * keys across all environments from both repos within each shared namespace.\n */\nexport class DriftDetector {\n private parser = new ManifestParser();\n private matrix = new MatrixManager();\n\n /**\n * Compare key sets between two Clef repos.\n *\n * @param localRoot - Path to the first (local) Clef repository.\n * @param remoteRoot - Path to the second (remote/other) Clef repository.\n * @param namespaceFilter - Optional list of namespace names to scope comparison.\n * @returns Drift result with any issues found.\n */\n detect(localRoot: string, remoteRoot: string, namespaceFilter?: string[]): DriftResult {\n const localManifest = this.parser.parse(path.join(localRoot, CLEF_MANIFEST_FILENAME));\n const remoteManifest = this.parser.parse(path.join(remoteRoot, CLEF_MANIFEST_FILENAME));\n\n const localCells = this.matrix.resolveMatrix(localManifest, localRoot);\n const remoteCells = this.matrix.resolveMatrix(remoteManifest, remoteRoot);\n\n const localEnvNames = localManifest.environments.map((e) => e.name);\n const remoteEnvNames = remoteManifest.environments.map((e) => e.name);\n\n // Find shared namespaces\n const localNsNames = new Set(localManifest.namespaces.map((n) => n.name));\n const remoteNsNames = new Set(remoteManifest.namespaces.map((n) => n.name));\n let sharedNamespaces = [...localNsNames].filter((n) => remoteNsNames.has(n));\n\n if (namespaceFilter && namespaceFilter.length > 0) {\n const filterSet = new Set(namespaceFilter);\n sharedNamespaces = sharedNamespaces.filter((n) => filterSet.has(n));\n }\n\n const issues: DriftIssue[] = [];\n let namespacesClean = 0;\n\n for (const ns of sharedNamespaces) {\n // Collect key \u2192 environment sets across both repos\n const keyEnvs = new Map<string, Set<string>>();\n const allEnvs = new Set<string>();\n\n // Read keys from local cells\n const localNsCells = localCells.filter((c) => c.namespace === ns);\n for (const cell of localNsCells) {\n const keys = this.readKeysFromFile(cell.filePath);\n if (keys === null) continue;\n allEnvs.add(cell.environment);\n for (const key of keys) {\n if (!keyEnvs.has(key)) keyEnvs.set(key, new Set());\n keyEnvs.get(key)!.add(cell.environment);\n }\n }\n\n // Read keys from remote cells\n const remoteNsCells = remoteCells.filter((c) => c.namespace === ns);\n for (const cell of remoteNsCells) {\n const keys = this.readKeysFromFile(cell.filePath);\n if (keys === null) continue;\n allEnvs.add(cell.environment);\n for (const key of keys) {\n if (!keyEnvs.has(key)) keyEnvs.set(key, new Set());\n keyEnvs.get(key)!.add(cell.environment);\n }\n }\n\n // Compare: a key must exist in every environment that has a readable file\n const envList = [...allEnvs];\n let nsClean = true;\n\n for (const [key, envSet] of keyEnvs) {\n const missingFrom = envList.filter((e) => !envSet.has(e));\n if (missingFrom.length > 0) {\n nsClean = false;\n const presentIn = [...envSet].sort();\n issues.push({\n namespace: ns,\n key,\n presentIn,\n missingFrom: missingFrom.sort(),\n message: `Key '${key}' in namespace '${ns}' exists in [${presentIn.join(\", \")}] but is missing from [${missingFrom.sort().join(\", \")}]`,\n });\n }\n }\n\n if (nsClean) namespacesClean++;\n }\n\n return {\n issues,\n namespacesCompared: sharedNamespaces.length,\n namespacesClean,\n localEnvironments: localEnvNames,\n remoteEnvironments: remoteEnvNames,\n };\n }\n\n /**\n * Read top-level key names from an encrypted SOPS YAML file,\n * filtering out the `sops` metadata key.\n *\n * @returns Array of key names, or `null` if the file cannot be read.\n */\n private readKeysFromFile(filePath: string): string[] | null {\n try {\n if (!fs.existsSync(filePath)) return null;\n const raw = fs.readFileSync(filePath, \"utf-8\");\n const parsed = YAML.parse(raw);\n if (parsed === null || parsed === undefined || typeof parsed !== \"object\") return null;\n return Object.keys(parsed).filter((k) => k !== \"sops\");\n } catch {\n return null;\n }\n }\n}\n", "import * as fs from \"fs\";\nimport * as path from \"path\";\nimport * as YAML from \"yaml\";\nimport {\n ClefManifest,\n ClefReport,\n CLEF_REPORT_SCHEMA_VERSION,\n EncryptionBackend,\n MatrixCell,\n ReportCellMetadata,\n ReportManifestStructure,\n ReportMatrixCell,\n ReportPolicy,\n ReportRecipientSummary,\n ReportRepoIdentity,\n SubprocessRunner,\n} from \"../types\";\nimport { ManifestParser } from \"../manifest/parser\";\nimport { MatrixManager } from \"../matrix/manager\";\nimport { SchemaValidator } from \"../schema/validator\";\nimport { LintRunner } from \"../lint/runner\";\nimport { getPendingKeys } from \"../pending/metadata\";\nimport { checkDependency } from \"../dependencies/checker\";\nimport { ReportSanitizer } from \"./sanitizer\";\n\n/**\n * Orchestrates all data-gathering for a `clef report` invocation.\n * Matrix key counts are read from SOPS YAML directly (no decryption).\n * Policy issues are gathered via LintRunner then sanitized.\n */\nexport class ReportGenerator {\n constructor(\n private readonly runner: SubprocessRunner,\n private readonly sopsClient: EncryptionBackend,\n private readonly matrixManager: MatrixManager,\n private readonly schemaValidator: SchemaValidator,\n ) {}\n\n /**\n * Generate a full {@link ClefReport} for the given repository root.\n * Each section gathers data independently \u2014 partial failures return empty\n * values rather than aborting the entire report.\n *\n * @param repoRoot - Absolute path to the repository root.\n * @param clefVersion - The running CLI version string.\n * @param options - Optional namespace/environment filters.\n */\n async generate(\n repoRoot: string,\n clefVersion: string,\n options?: { namespaceFilter?: string[]; environmentFilter?: string[] },\n ): Promise<ClefReport> {\n let manifest: ClefManifest | null = null;\n try {\n const parser = new ManifestParser();\n manifest = parser.parse(path.join(repoRoot, \"clef.yaml\"));\n } catch {\n // Manifest parse failure \u2014 return minimal report\n const emptyManifest: ReportManifestStructure = {\n manifestVersion: 0,\n filePattern: \"\",\n environments: [],\n namespaces: [],\n defaultBackend: \"\",\n };\n return {\n schemaVersion: CLEF_REPORT_SCHEMA_VERSION,\n repoIdentity: await this.buildRepoIdentity(repoRoot, clefVersion),\n manifest: emptyManifest,\n matrix: [],\n policy: { issueCount: { error: 0, warning: 0, info: 0 }, issues: [] },\n recipients: {},\n };\n }\n\n const [repoIdentity, matrixCells, policy] = await Promise.all([\n this.buildRepoIdentity(repoRoot, clefVersion),\n this.buildMatrixCells(manifest, repoRoot, options),\n this.buildPolicy(manifest, repoRoot),\n ]);\n\n return {\n schemaVersion: CLEF_REPORT_SCHEMA_VERSION,\n repoIdentity,\n manifest: this.buildManifestStructure(manifest),\n matrix: matrixCells,\n policy,\n recipients: this.buildRecipients(matrixCells),\n };\n }\n\n private async buildRepoIdentity(\n repoRoot: string,\n clefVersion: string,\n ): Promise<ReportRepoIdentity> {\n let repoOrigin = \"\";\n let commitSha = \"\";\n let branch = \"\";\n let commitTimestamp = \"\";\n let sopsVersion: string | null = null;\n\n try {\n const r = await this.runner.run(\"git\", [\"remote\", \"get-url\", \"origin\"], { cwd: repoRoot });\n if (r.exitCode === 0) repoOrigin = this.normalizeRepoOrigin(r.stdout.trim());\n } catch {\n /* ignore */\n }\n\n try {\n const r = await this.runner.run(\"git\", [\"rev-parse\", \"HEAD\"], { cwd: repoRoot });\n if (r.exitCode === 0) commitSha = r.stdout.trim();\n } catch {\n /* ignore */\n }\n\n try {\n const r = await this.runner.run(\"git\", [\"branch\", \"--show-current\"], { cwd: repoRoot });\n if (r.exitCode === 0) branch = r.stdout.trim();\n } catch {\n /* ignore */\n }\n\n try {\n const r = await this.runner.run(\"git\", [\"log\", \"-1\", \"--format=%cI\"], { cwd: repoRoot });\n if (r.exitCode === 0) commitTimestamp = r.stdout.trim();\n } catch {\n /* ignore */\n }\n\n try {\n const dep = await checkDependency(\"sops\", this.runner);\n sopsVersion = dep?.installed ?? null;\n } catch {\n /* ignore */\n }\n\n return {\n repoOrigin,\n commitSha,\n branch,\n commitTimestamp,\n reportGeneratedAt: new Date().toISOString(),\n clefVersion,\n sopsVersion,\n };\n }\n\n private normalizeRepoOrigin(rawUrl: string): string {\n const sshMatch = rawUrl.match(/^git@([^:]+):(.+?)(?:\\.git)?$/);\n if (sshMatch) return `${sshMatch[1]}/${sshMatch[2]}`;\n const httpsMatch = rawUrl.match(/^https?:\\/\\/([^/]+)\\/(.+?)(?:\\.git)?$/);\n if (httpsMatch) return `${httpsMatch[1]}/${httpsMatch[2]}`;\n return rawUrl.replace(/\\.git$/, \"\");\n }\n\n private buildManifestStructure(manifest: ClefManifest): ReportManifestStructure {\n return {\n manifestVersion: manifest.version,\n filePattern: manifest.file_pattern,\n environments: manifest.environments.map((e) => ({\n name: e.name,\n protected: e.protected ?? false,\n })),\n namespaces: manifest.namespaces.map((ns) => ({\n name: ns.name,\n hasSchema: !!ns.schema,\n owners: ns.owners ?? [],\n })),\n defaultBackend: manifest.sops.default_backend,\n };\n }\n\n private async buildMatrixCells(\n manifest: ClefManifest,\n repoRoot: string,\n options?: { namespaceFilter?: string[]; environmentFilter?: string[] },\n ): Promise<ReportMatrixCell[]> {\n const allCells = this.matrixManager.resolveMatrix(manifest, repoRoot);\n const cells = allCells.filter((cell) => {\n const nsOk =\n !options?.namespaceFilter?.length || options.namespaceFilter.includes(cell.namespace);\n const envOk =\n !options?.environmentFilter?.length || options.environmentFilter.includes(cell.environment);\n return nsOk && envOk;\n });\n\n const result: ReportMatrixCell[] = [];\n\n for (const cell of cells) {\n result.push(await this.buildCell(cell));\n }\n\n return result;\n }\n\n private async buildCell(cell: MatrixCell): Promise<ReportMatrixCell> {\n if (!cell.exists) {\n return {\n namespace: cell.namespace,\n environment: cell.environment,\n filePath: cell.filePath,\n exists: false,\n keyCount: 0,\n pendingCount: 0,\n metadata: null,\n };\n }\n\n const keyCount = this.readKeyCount(cell.filePath);\n\n let pendingCount = 0;\n try {\n const pending = await getPendingKeys(cell.filePath);\n pendingCount = pending.length;\n } catch {\n /* ignore */\n }\n\n let metadata: ReportCellMetadata | null = null;\n try {\n const sopsMetadata = await this.sopsClient.getMetadata(cell.filePath);\n metadata = {\n backend: sopsMetadata.backend,\n recipients: sopsMetadata.recipients,\n lastModified: sopsMetadata.lastModified?.toISOString() ?? null,\n };\n } catch {\n /* ignore */\n }\n\n return {\n namespace: cell.namespace,\n environment: cell.environment,\n filePath: cell.filePath,\n exists: true,\n keyCount,\n pendingCount,\n metadata,\n };\n }\n\n private readKeyCount(filePath: string): number {\n try {\n if (!fs.existsSync(filePath)) return 0;\n const raw = fs.readFileSync(filePath, \"utf-8\");\n const parsed: unknown = YAML.parse(raw);\n if (parsed === null || parsed === undefined || typeof parsed !== \"object\") return 0;\n return Object.keys(parsed as Record<string, unknown>).filter((k) => k !== \"sops\").length;\n } catch {\n return 0;\n }\n }\n\n private async buildPolicy(manifest: ClefManifest, repoRoot: string): Promise<ReportPolicy> {\n try {\n const lintRunner = new LintRunner(this.matrixManager, this.schemaValidator, this.sopsClient);\n const lintResult = await lintRunner.run(manifest, repoRoot);\n return new ReportSanitizer().sanitize(lintResult.issues);\n } catch {\n return { issueCount: { error: 0, warning: 0, info: 0 }, issues: [] };\n }\n }\n\n private buildRecipients(cells: ReportMatrixCell[]): Record<string, ReportRecipientSummary> {\n const recipientMap = new Map<\n string,\n { type: string; environments: Set<string>; fileCount: number }\n >();\n\n for (const cell of cells) {\n if (!cell.metadata) continue;\n for (const recipient of cell.metadata.recipients) {\n const type = this.inferRecipientType(recipient);\n const existing = recipientMap.get(recipient);\n if (existing) {\n existing.environments.add(cell.environment);\n existing.fileCount++;\n } else {\n recipientMap.set(recipient, {\n type,\n environments: new Set([cell.environment]),\n fileCount: 1,\n });\n }\n }\n }\n\n const result: Record<string, ReportRecipientSummary> = {};\n for (const [recipient, data] of recipientMap.entries()) {\n result[recipient] = {\n type: data.type,\n environments: Array.from(data.environments),\n fileCount: data.fileCount,\n };\n }\n return result;\n }\n\n private inferRecipientType(recipient: string): string {\n if (recipient.startsWith(\"age1\")) return \"age\";\n if (recipient.startsWith(\"arn:aws:kms:\")) return \"awskms\";\n if (recipient.includes(\"projects/\") && recipient.includes(\"cryptoKeys/\")) return \"gcpkms\";\n return \"pgp\";\n }\n}\n", "import { LintIssue, ReportIssueCounts, ReportPolicy, ReportPolicyIssue } from \"../types\";\n\n/**\n * Transforms raw `LintIssue[]` from LintRunner into `ReportPolicy` with all\n * key names stripped and similar issues aggregated into counts.\n *\n * This is the trust boundary for Clef Pro: nothing emitted by this class\n * should contain a secret key name.\n */\nexport class ReportSanitizer {\n sanitize(lintIssues: LintIssue[]): ReportPolicy {\n const output: ReportPolicyIssue[] = [];\n\n // \u2500\u2500 Schema issues with a key field \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n const schemaWithKey = lintIssues.filter((i) => i.category === \"schema\" && i.key !== undefined);\n\n // Schema errors \u2192 group by file: \"N keys fail schema validation\"\n const schemaErrors = schemaWithKey.filter((i) => i.severity === \"error\");\n this.groupByFile(schemaErrors).forEach((issues, file) => {\n const n = issues.length;\n output.push({\n severity: \"error\",\n category: \"schema\",\n file,\n count: n,\n message: `${n} key${n !== 1 ? \"s\" : \"\"} fail schema validation`,\n });\n });\n\n // Schema warnings that are pending placeholders \u2192 group by file, reclassify to info/matrix\n const pendingWarnings = schemaWithKey.filter(\n (i) => i.severity === \"warning\" && i.message.includes(\"placeholder\"),\n );\n this.groupByFile(pendingWarnings).forEach((issues, file) => {\n const n = issues.length;\n output.push({\n severity: \"info\",\n category: \"matrix\",\n file,\n count: n,\n message: `${n} pending key${n !== 1 ? \"s\" : \"\"} awaiting values`,\n });\n });\n\n // Schema warnings that are NOT pending \u2192 group by file: \"N keys have schema warnings\"\n const schemaWarnings = schemaWithKey.filter(\n (i) => i.severity === \"warning\" && !i.message.includes(\"placeholder\"),\n );\n this.groupByFile(schemaWarnings).forEach((issues, file) => {\n const n = issues.length;\n output.push({\n severity: \"warning\",\n category: \"schema\",\n file,\n count: n,\n message: n === 1 ? \"1 key has schema warnings\" : `${n} keys have schema warnings`,\n });\n });\n\n // Schema info with key \u2192 DROP entirely (per-key noise that leaks key names)\n\n // \u2500\u2500 Schema issues without a key field \u2014 pass through \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n for (const issue of lintIssues.filter((i) => i.category === \"schema\" && i.key === undefined)) {\n output.push({\n severity: issue.severity,\n category: issue.category,\n file: issue.file,\n message: issue.message,\n });\n }\n\n // \u2500\u2500 Matrix issues \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n const matrixIssues = lintIssues.filter((i) => i.category === \"matrix\");\n\n // Matrix with key = cross-env drift \u2192 group by (namespace, targetEnv, sourceEnvs)\n const driftIssues = matrixIssues.filter((i) => i.key !== undefined);\n const driftGroups = new Map<\n string,\n { namespace: string; targetEnv: string; sourceEnvs: string; count: number }\n >();\n for (const issue of driftIssues) {\n // Use indexOf/lastIndexOf instead of a regex with unbounded quantifiers to\n // avoid ReDoS on uncontrolled `issue.message` input.\n const prefix = \"is missing in \";\n const middle = \" but present in \";\n const pi = issue.message.indexOf(prefix);\n if (pi === -1) continue;\n const afterPrefix = issue.message.indexOf(middle, pi + prefix.length);\n if (afterPrefix === -1) continue;\n const targetEnv = issue.message.slice(pi + prefix.length, afterPrefix);\n if (!targetEnv || /\\s/.test(targetEnv)) continue;\n const rest = issue.message.slice(afterPrefix + middle.length);\n if (!rest.endsWith(\".\")) continue;\n const sourceEnvs = rest.slice(0, -1);\n const namespace = this.extractNamespace(issue.file);\n const groupKey = `${namespace}|${targetEnv}|${sourceEnvs}`;\n const existing = driftGroups.get(groupKey);\n if (existing) {\n existing.count++;\n } else {\n driftGroups.set(groupKey, { namespace, targetEnv, sourceEnvs, count: 1 });\n }\n }\n for (const group of driftGroups.values()) {\n const n = group.count;\n output.push({\n severity: \"warning\",\n category: \"drift\",\n namespace: group.namespace,\n environment: group.targetEnv,\n sourceEnvironment: group.sourceEnvs,\n driftCount: n,\n message: `${n} key${n !== 1 ? \"s\" : \"\"} in [${group.sourceEnvs}] missing from ${group.targetEnv}`,\n });\n }\n\n // Matrix without key (missing file) \u2192 pass through\n for (const issue of matrixIssues.filter((i) => i.key === undefined)) {\n output.push({\n severity: issue.severity,\n category: issue.category,\n file: issue.file,\n message: issue.message,\n });\n }\n\n // \u2500\u2500 SOPS issues \u2014 pass through \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n for (const issue of lintIssues.filter((i) => i.category === \"sops\")) {\n output.push({\n severity: issue.severity,\n category: issue.category,\n file: issue.file,\n message: issue.message,\n });\n }\n\n // \u2500\u2500 Service-identity issues \u2014 pass through \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n for (const issue of lintIssues.filter((i) => i.category === \"service-identity\")) {\n output.push({\n severity: issue.severity,\n category: issue.category,\n file: issue.file,\n message: issue.message,\n });\n }\n\n const issueCount: ReportIssueCounts = {\n error: output.filter((i) => i.severity === \"error\").length,\n warning: output.filter((i) => i.severity === \"warning\").length,\n info: output.filter((i) => i.severity === \"info\").length,\n };\n\n return { issueCount, issues: output };\n }\n\n private groupByFile(issues: LintIssue[]): Map<string, LintIssue[]> {\n const map = new Map<string, LintIssue[]>();\n for (const issue of issues) {\n const arr = map.get(issue.file) ?? [];\n arr.push(issue);\n map.set(issue.file, arr);\n }\n return map;\n }\n\n private extractNamespace(filePath: string): string {\n const normalized = filePath.replace(/\\\\/g, \"/\");\n const parts = normalized.split(\"/\");\n return parts.length >= 2 ? (parts[parts.length - 2] ?? \"\") : (parts[0] ?? \"\");\n }\n}\n", "import {\n ClefReport,\n CloudApiReport,\n CloudCellHealthStatus,\n CloudPolicyResult,\n CloudReportCell,\n CloudReportDrift,\n CloudReportSummary,\n ReportMatrixCell,\n ReportPolicyIssue,\n} from \"../types\";\n\n/**\n * Transforms a local {@link ClefReport} into the {@link CloudApiReport} payload\n * expected by the Clef Pro API. Mapping is deterministic and side-effect-free.\n */\nexport class ReportTransformer {\n transform(report: ClefReport): CloudApiReport {\n const summary = this.buildSummary(report);\n const drift = this.buildDrift(report);\n const policyResults = this.buildPolicyResults(report.policy.issues);\n\n return {\n commitSha: report.repoIdentity.commitSha,\n branch: report.repoIdentity.branch,\n commitTimestamp: new Date(report.repoIdentity.commitTimestamp).getTime(),\n cliVersion: report.repoIdentity.clefVersion,\n summary,\n drift,\n policyResults,\n };\n }\n\n private buildSummary(report: ClefReport): CloudReportSummary {\n const namespaces = [...new Set(report.matrix.map((c) => c.namespace))];\n const environments = [...new Set(report.matrix.map((c) => c.environment))];\n const cells = report.matrix.map((cell) => this.buildCell(cell, report.policy.issues));\n const violations = report.policy.issues.filter((i) => i.severity === \"error\").length;\n\n return {\n filesScanned: report.matrix.length,\n namespaces,\n environments,\n cells,\n violations,\n passed: violations === 0,\n };\n }\n\n private buildCell(cell: ReportMatrixCell, issues: ReportPolicyIssue[]): CloudReportCell {\n const healthStatus = this.computeHealthStatus(cell, issues);\n const description = this.describeCell(cell, healthStatus);\n\n return {\n namespace: cell.namespace,\n environment: cell.environment,\n healthStatus,\n description,\n };\n }\n\n private computeHealthStatus(\n cell: ReportMatrixCell,\n issues: ReportPolicyIssue[],\n ): CloudCellHealthStatus {\n if (!cell.exists) return \"unknown\";\n\n const cellIssues = issues.filter(\n (i) =>\n (i.namespace === cell.namespace && i.environment === cell.environment) ||\n (i.file !== undefined &&\n i.file.includes(cell.namespace) &&\n i.file.includes(cell.environment)),\n );\n\n if (cellIssues.some((i) => i.severity === \"error\")) return \"critical\";\n if (cellIssues.some((i) => i.severity === \"warning\") || cell.pendingCount > 0) return \"warning\";\n return \"healthy\";\n }\n\n private describeCell(cell: ReportMatrixCell, status: CloudCellHealthStatus): string {\n switch (status) {\n case \"unknown\":\n return \"File does not exist\";\n case \"critical\":\n return \"Has error-severity policy issues\";\n case \"warning\":\n return cell.pendingCount > 0\n ? `${cell.pendingCount} pending key(s) awaiting values`\n : \"Has warning-severity policy issues\";\n case \"healthy\":\n return `${cell.keyCount} key(s), no issues`;\n }\n }\n\n private buildDrift(report: ClefReport): CloudReportDrift[] {\n const namespaces = [...new Set(report.matrix.map((c) => c.namespace))];\n const driftIssues = report.policy.issues.filter((i) => i.category === \"drift\");\n\n return namespaces.map((namespace) => {\n const nsIssues = driftIssues.filter((i) => i.namespace === namespace);\n const totalDrift = nsIssues.reduce((sum, i) => sum + (i.driftCount ?? 1), 0);\n return {\n namespace,\n isDrifted: totalDrift > 0,\n driftCount: totalDrift,\n };\n });\n }\n\n private buildPolicyResults(issues: ReportPolicyIssue[]): CloudPolicyResult[] {\n return issues.map((issue) => ({\n ruleId: `${issue.category}/${issue.severity}`,\n ruleName: issue.category,\n passed: issue.severity !== \"error\",\n severity: issue.severity,\n message: issue.message,\n ...(issue.namespace || issue.environment\n ? {\n scope: {\n ...(issue.namespace ? { namespace: issue.namespace } : {}),\n ...(issue.environment ? { environment: issue.environment } : {}),\n },\n }\n : {}),\n }));\n }\n}\n", "import {\n CloudApiError,\n CloudApiReport,\n CloudBatchPayload,\n CloudBatchResponse,\n CloudIntegrationResponse,\n CloudReportResponse,\n} from \"../types\";\n\nconst DEFAULT_RETRY_DELAY_MS = 1000;\n\n/**\n * HTTP client for the Clef Pro API.\n * Uses native `fetch()` (Node 18+). Retries once on 5xx or network errors.\n */\nexport class CloudClient {\n private readonly retryDelayMs: number;\n\n constructor(options?: { retryDelayMs?: number }) {\n this.retryDelayMs = options?.retryDelayMs ?? DEFAULT_RETRY_DELAY_MS;\n }\n async fetchIntegration(\n apiUrl: string,\n apiKey: string,\n integrationId: string,\n ): Promise<CloudIntegrationResponse> {\n const url = `${apiUrl}/api/v1/integrations/${encodeURIComponent(integrationId)}`;\n return this.request<CloudIntegrationResponse>(\"GET\", url, apiKey);\n }\n\n async submitReport(\n apiUrl: string,\n apiKey: string,\n report: CloudApiReport,\n ): Promise<CloudReportResponse> {\n const url = `${apiUrl}/api/v1/reports`;\n return this.request<CloudReportResponse>(\"POST\", url, apiKey, report);\n }\n\n async submitBatchReports(\n apiUrl: string,\n apiKey: string,\n batch: CloudBatchPayload,\n ): Promise<CloudBatchResponse> {\n const url = `${apiUrl}/api/v1/reports/batch`;\n return this.request<CloudBatchResponse>(\"POST\", url, apiKey, batch);\n }\n\n private async request<T>(\n method: string,\n url: string,\n apiKey: string,\n body?: unknown,\n ): Promise<T> {\n const headers: Record<string, string> = {\n Authorization: `Bearer ${apiKey}`,\n \"Content-Type\": \"application/json\",\n };\n\n const init: RequestInit = {\n method,\n headers,\n ...(body !== undefined ? { body: JSON.stringify(body) } : {}),\n };\n\n let response: Response;\n try {\n response = await fetch(url, init);\n } catch {\n // Network error \u2014 retry once\n await this.delay(this.retryDelayMs);\n try {\n response = await fetch(url, init);\n } catch (retryErr) {\n throw new CloudApiError(\n `Network error contacting Clef Pro: ${(retryErr as Error).message}`,\n 0,\n \"Check your network connection and CLEF_API_URL.\",\n );\n }\n }\n\n if (response.ok) {\n return (await response.json()) as T;\n }\n\n // 5xx \u2014 retry once\n if (response.status >= 500 && response.status < 600) {\n await this.delay(this.retryDelayMs);\n const retryResponse = await fetch(url, init);\n if (retryResponse.ok) {\n return (await retryResponse.json()) as T;\n }\n throw this.buildError(retryResponse);\n }\n\n // 4xx \u2014 do not retry\n throw this.buildError(response);\n }\n\n private buildError(response: Response): CloudApiError {\n const hint =\n response.status === 401 || response.status === 403\n ? \"Check your API token (--api-token or CLEF_API_TOKEN).\"\n : response.status === 404\n ? \"Check your cloud.integrationId in clef.yaml.\"\n : undefined;\n\n return new CloudApiError(\n `Clef Pro API returned ${response.status} ${response.statusText}`,\n response.status,\n hint,\n );\n }\n\n private delay(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n }\n}\n", "import { CloudCIContext } from \"../types\";\n\n/**\n * Detects the current CI provider from environment variables and returns\n * a {@link CloudCIContext} with provider, pipeline URL, and trigger info.\n *\n * Returns `undefined` when not running in a CI environment.\n */\nexport function collectCIContext(): CloudCIContext | undefined {\n const env = process.env;\n\n if (env.GITHUB_ACTIONS) {\n const serverUrl = env.GITHUB_SERVER_URL ?? \"https://github.com\";\n const repo = env.GITHUB_REPOSITORY ?? \"\";\n const runId = env.GITHUB_RUN_ID ?? \"\";\n const pipelineUrl = repo && runId ? `${serverUrl}/${repo}/actions/runs/${runId}` : undefined;\n return {\n provider: \"github-actions\",\n pipelineUrl,\n trigger: env.GITHUB_EVENT_NAME,\n };\n }\n\n if (env.GITLAB_CI) {\n return {\n provider: \"gitlab-ci\",\n pipelineUrl: env.CI_PIPELINE_URL,\n trigger: env.CI_PIPELINE_SOURCE,\n };\n }\n\n if (env.CIRCLECI) {\n return {\n provider: \"circleci\",\n pipelineUrl: env.CIRCLE_BUILD_URL,\n };\n }\n\n if (env.CI) {\n return {\n provider: \"unknown\",\n };\n }\n\n return undefined;\n}\n", "import { EncryptionBackend } from \"../types\";\n\n/** Status of a single key in a three-way merge. */\nexport type MergeKeyStatus = \"unchanged\" | \"ours\" | \"theirs\" | \"both_added\" | \"conflict\";\n\n/** One key's resolution in the three-way merge. */\nexport interface MergeKey {\n key: string;\n status: MergeKeyStatus;\n /** Resolved value when status is not \"conflict\". `null` for deletions or unresolvable conflicts. */\n value: string | null;\n /** Base value (common ancestor). `null` if the key did not exist in base. */\n baseValue: string | null;\n /** Value from ours. `null` if the key was deleted or absent in ours. */\n oursValue: string | null;\n /** Value from theirs. `null` if the key was deleted or absent in theirs. */\n theirsValue: string | null;\n}\n\n/** Result of a three-way merge. */\nexport interface MergeResult {\n /** `true` when all keys merged cleanly with no conflicts. */\n clean: boolean;\n /** The merged key/value map. Only complete when `clean` is `true`. */\n merged: Record<string, string>;\n /** Per-key resolution details. */\n keys: MergeKey[];\n /** Keys that could not be auto-resolved. Empty when `clean` is `true`. */\n conflicts: MergeKey[];\n}\n\n/**\n * Three-way merge driver for SOPS-encrypted files.\n *\n * Decrypts the base (common ancestor), ours (current branch), and theirs (incoming branch)\n * versions of a file, performs a three-way merge on the plaintext key/value maps, and\n * returns the merged result for re-encryption.\n *\n * @example\n * ```ts\n * const driver = new SopsMergeDriver(sopsClient);\n * const result = await driver.mergeFiles(basePath, oursPath, theirsPath);\n * if (result.clean) {\n * await sopsClient.encrypt(oursPath, result.merged, manifest, environment);\n * }\n * ```\n */\nexport class SopsMergeDriver {\n constructor(private readonly sopsClient: EncryptionBackend) {}\n\n /**\n * Perform a three-way merge on three in-memory key/value maps.\n *\n * Algorithm: For each key across all three maps, compare ours and theirs against base.\n * - If only one side changed relative to base, take that side's value.\n * - If both sides made the same change, take either (they agree).\n * - If both sides made different changes to the same key, it's a conflict.\n * - If a key was added on both sides with the same value, accept it.\n * - If a key was added on both sides with different values, it's a conflict.\n */\n merge(\n base: Record<string, string>,\n ours: Record<string, string>,\n theirs: Record<string, string>,\n ): MergeResult {\n const allKeys = new Set([...Object.keys(base), ...Object.keys(ours), ...Object.keys(theirs)]);\n\n const merged: Record<string, string> = {};\n const keys: MergeKey[] = [];\n const conflicts: MergeKey[] = [];\n\n for (const key of allKeys) {\n const inBase = key in base;\n const inOurs = key in ours;\n const inTheirs = key in theirs;\n const baseVal = inBase ? base[key] : null;\n const oursVal = inOurs ? ours[key] : null;\n const theirsVal = inTheirs ? theirs[key] : null;\n\n const oursChanged = oursVal !== baseVal;\n const theirsChanged = theirsVal !== baseVal;\n\n let status: MergeKeyStatus;\n let value: string | null;\n\n if (!oursChanged && !theirsChanged) {\n // Neither side changed this key relative to base\n status = \"unchanged\";\n value = baseVal;\n } else if (oursChanged && !theirsChanged) {\n // Only ours changed (including additions and deletions)\n status = \"ours\";\n value = oursVal;\n } else if (!oursChanged && theirsChanged) {\n // Only theirs changed (including additions and deletions)\n status = \"theirs\";\n value = theirsVal;\n } else if (oursVal === theirsVal) {\n // Both changed to the same value (or both deleted)\n status = !inBase && inOurs && inTheirs ? \"both_added\" : \"ours\";\n value = oursVal;\n } else {\n // Both changed to different values \u2014 conflict\n status = \"conflict\";\n value = null;\n }\n\n const mergeKey: MergeKey = {\n key,\n status,\n value,\n baseValue: baseVal,\n oursValue: oursVal,\n theirsValue: theirsVal,\n };\n keys.push(mergeKey);\n\n if (status === \"conflict\") {\n conflicts.push(mergeKey);\n } else if (value !== null) {\n merged[key] = value;\n }\n // value === null && status !== \"conflict\" means the key was deleted \u2014 omit from merged\n }\n\n // Sort keys alphabetically for stable output\n keys.sort((a, b) => a.key.localeCompare(b.key));\n conflicts.sort((a, b) => a.key.localeCompare(b.key));\n\n return { clean: conflicts.length === 0, merged, keys, conflicts };\n }\n\n /**\n * Decrypt three file versions and perform a three-way merge.\n *\n * @param basePath - Path to the common ancestor file (git %O).\n * @param oursPath - Path to the current branch file (git %A).\n * @param theirsPath - Path to the incoming branch file (git %B).\n * @returns The merge result. When `clean` is `true`, `merged` contains the resolved values.\n */\n async mergeFiles(basePath: string, oursPath: string, theirsPath: string): Promise<MergeResult> {\n const [baseDecrypted, oursDecrypted, theirsDecrypted] = await Promise.all([\n this.sopsClient.decrypt(basePath),\n this.sopsClient.decrypt(oursPath),\n this.sopsClient.decrypt(theirsPath),\n ]);\n\n return this.merge(baseDecrypted.values, oursDecrypted.values, theirsDecrypted.values);\n }\n}\n", "import * as fs from \"fs\";\nimport * as os from \"os\";\nimport * as path from \"path\";\nimport * as YAML from \"yaml\";\nimport {\n ClefManifest,\n EncryptionBackend,\n KmsConfig,\n ServiceIdentityDefinition,\n ServiceIdentityDriftIssue,\n ServiceIdentityEnvironmentConfig,\n isKmsEnvelope,\n} from \"../types\";\nimport { generateAgeIdentity } from \"../age/keygen\";\nimport { MatrixManager } from \"../matrix/manager\";\nimport { CLEF_MANIFEST_FILENAME } from \"../manifest/parser\";\n\n/**\n * Thrown when key rotation partially completes before a failure.\n * Contains the private keys for environments that were successfully rotated.\n */\nexport class PartialRotationError extends Error {\n constructor(\n message: string,\n public readonly rotatedKeys: Record<string, string>,\n ) {\n super(message);\n this.name = \"PartialRotationError\";\n }\n}\n\n/**\n * Manages service identities: creation, listing, key rotation, and drift validation.\n *\n * @example\n * ```ts\n * const manager = new ServiceIdentityManager(sopsClient, matrixManager);\n * const result = await manager.create(\"api-gw\", [\"api\"], \"API gateway\", manifest, repoRoot);\n * ```\n */\nexport class ServiceIdentityManager {\n constructor(\n private readonly encryption: EncryptionBackend,\n private readonly matrixManager: MatrixManager,\n ) {}\n\n /**\n * Create a new service identity with per-environment age key pairs or KMS envelope config.\n * For age-only: generates keys, updates the manifest, and registers public keys as SOPS recipients.\n * For KMS: stores KMS config in manifest, no age keys generated.\n *\n * @param kmsEnvConfigs - Optional per-environment KMS config. When provided, those envs use\n * KMS envelope encryption instead of generating age keys.\n * @returns The created identity definition and the per-environment private keys (empty for KMS envs).\n */\n async create(\n name: string,\n namespaces: string[],\n description: string,\n manifest: ClefManifest,\n repoRoot: string,\n kmsEnvConfigs?: Record<string, KmsConfig>,\n ): Promise<{\n identity: ServiceIdentityDefinition;\n privateKeys: Record<string, string>;\n }> {\n // Validate name uniqueness\n if (manifest.service_identities?.some((si) => si.name === name)) {\n throw new Error(`Service identity '${name}' already exists.`);\n }\n\n // Validate namespace references\n const validNamespaces = new Set(manifest.namespaces.map((ns) => ns.name));\n for (const ns of namespaces) {\n if (!validNamespaces.has(ns)) {\n throw new Error(`Namespace '${ns}' not found in manifest.`);\n }\n }\n\n // Generate per-environment config\n const environments: Record<string, ServiceIdentityEnvironmentConfig> = {};\n const privateKeys: Record<string, string> = {};\n\n for (const env of manifest.environments) {\n const kmsConfig = kmsEnvConfigs?.[env.name];\n if (kmsConfig) {\n // KMS envelope path \u2014 no age keys generated\n environments[env.name] = { kms: kmsConfig };\n } else {\n // Age-only path\n const identity = await generateAgeIdentity();\n environments[env.name] = { recipient: identity.publicKey };\n privateKeys[env.name] = identity.privateKey;\n }\n }\n\n const definition: ServiceIdentityDefinition = {\n name,\n description,\n namespaces,\n environments,\n };\n\n // Register public keys as SOPS recipients on scoped files BEFORE writing\n // the manifest, so a registration failure doesn't leave orphaned state.\n // (Only for age-only environments \u2014 KMS envs have no recipient to register.)\n await this.registerRecipients(definition, manifest, repoRoot);\n\n // Update manifest on disk\n const manifestPath = path.join(repoRoot, CLEF_MANIFEST_FILENAME);\n const raw = fs.readFileSync(manifestPath, \"utf-8\");\n const doc = YAML.parse(raw) as Record<string, unknown>;\n\n if (!Array.isArray(doc.service_identities)) {\n doc.service_identities = [];\n }\n (doc.service_identities as unknown[]).push({\n name,\n description,\n namespaces,\n environments,\n });\n const tmpCreate = path.join(os.tmpdir(), `clef-manifest-${process.pid}-${Date.now()}.tmp`);\n try {\n fs.writeFileSync(tmpCreate, YAML.stringify(doc), \"utf-8\");\n fs.renameSync(tmpCreate, manifestPath);\n } finally {\n try {\n fs.unlinkSync(tmpCreate);\n } catch {\n // Already renamed or never written \u2014 ignore\n }\n }\n\n return { identity: definition, privateKeys };\n }\n\n /**\n * List all service identities from the manifest.\n */\n list(manifest: ClefManifest): ServiceIdentityDefinition[] {\n return manifest.service_identities ?? [];\n }\n\n /**\n * Get a single service identity by name.\n */\n get(manifest: ClefManifest, name: string): ServiceIdentityDefinition | undefined {\n return manifest.service_identities?.find((si) => si.name === name);\n }\n\n /**\n * Delete a service identity: remove its recipients from scoped SOPS files\n * and remove it from the manifest.\n */\n async delete(name: string, manifest: ClefManifest, repoRoot: string): Promise<void> {\n const identity = this.get(manifest, name);\n if (!identity) {\n throw new Error(`Service identity '${name}' not found.`);\n }\n\n // Remove age recipients from scoped files\n const cells = this.matrixManager.resolveMatrix(manifest, repoRoot).filter((c) => c.exists);\n for (const cell of cells) {\n if (!identity.namespaces.includes(cell.namespace)) continue;\n const envConfig = identity.environments[cell.environment];\n if (!envConfig?.recipient) continue;\n if (isKmsEnvelope(envConfig)) continue;\n\n try {\n await this.encryption.removeRecipient(cell.filePath, envConfig.recipient);\n } catch {\n // May not be a current recipient\n }\n }\n\n // Remove from manifest on disk\n const manifestPath = path.join(repoRoot, CLEF_MANIFEST_FILENAME);\n const raw = fs.readFileSync(manifestPath, \"utf-8\");\n const doc = YAML.parse(raw) as Record<string, unknown>;\n const identities = doc.service_identities as Record<string, unknown>[];\n if (Array.isArray(identities)) {\n doc.service_identities = identities.filter(\n (si) => (si as Record<string, unknown>).name !== name,\n );\n }\n const tmp = path.join(os.tmpdir(), `clef-manifest-${process.pid}-${Date.now()}.tmp`);\n try {\n fs.writeFileSync(tmp, YAML.stringify(doc), \"utf-8\");\n fs.renameSync(tmp, manifestPath);\n } finally {\n try {\n fs.unlinkSync(tmp);\n } catch {\n // Already renamed or never written \u2014 ignore\n }\n }\n }\n\n /**\n * Update environment backends on an existing service identity.\n * Switches age \u2192 KMS (removes old recipient) or updates KMS config.\n * Returns new private keys for any environments switched from KMS \u2192 age.\n */\n async updateEnvironments(\n name: string,\n kmsEnvConfigs: Record<string, KmsConfig>,\n manifest: ClefManifest,\n repoRoot: string,\n ): Promise<{ privateKeys: Record<string, string> }> {\n const identity = this.get(manifest, name);\n if (!identity) {\n throw new Error(`Service identity '${name}' not found.`);\n }\n\n const manifestPath = path.join(repoRoot, CLEF_MANIFEST_FILENAME);\n const raw = fs.readFileSync(manifestPath, \"utf-8\");\n const doc = YAML.parse(raw) as Record<string, unknown>;\n const identities = doc.service_identities as Record<string, unknown>[];\n const siDoc = identities.find((si) => (si as Record<string, unknown>).name === name) as Record<\n string,\n unknown\n >;\n const envs = siDoc.environments as Record<string, Record<string, unknown>>;\n\n const cells = this.matrixManager.resolveMatrix(manifest, repoRoot).filter((c) => c.exists);\n const privateKeys: Record<string, string> = {};\n\n for (const [envName, kmsConfig] of Object.entries(kmsEnvConfigs)) {\n const oldConfig = identity.environments[envName];\n if (!oldConfig) {\n throw new Error(`Environment '${envName}' not found on identity '${name}'.`);\n }\n\n // If switching from age \u2192 KMS, remove the old age recipient from scoped files\n if (oldConfig.recipient) {\n const scopedCells = cells.filter(\n (c) => identity.namespaces.includes(c.namespace) && c.environment === envName,\n );\n for (const cell of scopedCells) {\n try {\n await this.encryption.removeRecipient(cell.filePath, oldConfig.recipient);\n } catch {\n // May not be a current recipient\n }\n }\n }\n\n // Update manifest entry to KMS\n envs[envName] = { kms: kmsConfig };\n identity.environments[envName] = { kms: kmsConfig };\n }\n\n // Write updated manifest\n const tmp = path.join(os.tmpdir(), `clef-manifest-${process.pid}-${Date.now()}.tmp`);\n try {\n fs.writeFileSync(tmp, YAML.stringify(doc), \"utf-8\");\n fs.renameSync(tmp, manifestPath);\n } finally {\n try {\n fs.unlinkSync(tmp);\n } catch {\n // Already renamed or never written \u2014 ignore\n }\n }\n\n return { privateKeys };\n }\n\n /**\n * Register a service identity's public keys as SOPS recipients on scoped matrix files.\n */\n async registerRecipients(\n identity: ServiceIdentityDefinition,\n manifest: ClefManifest,\n repoRoot: string,\n ): Promise<void> {\n const cells = this.matrixManager.resolveMatrix(manifest, repoRoot).filter((c) => c.exists);\n\n for (const cell of cells) {\n if (!identity.namespaces.includes(cell.namespace)) continue;\n\n const envConfig = identity.environments[cell.environment];\n if (!envConfig) continue;\n\n // KMS-backed environments have no recipient to register\n if (isKmsEnvelope(envConfig)) continue;\n if (!envConfig.recipient) continue;\n\n try {\n await this.encryption.addRecipient(cell.filePath, envConfig.recipient);\n } catch (err) {\n // SOPS exits non-zero for duplicate recipients \u2014 safe to ignore.\n // Re-throw genuine I/O or corruption errors.\n const message = err instanceof Error ? err.message : String(err);\n if (!message.includes(\"already\")) {\n throw err;\n }\n }\n }\n }\n\n /**\n * Rotate the age key for a service identity (all envs or a specific env).\n * Returns the new private keys.\n */\n async rotateKey(\n name: string,\n manifest: ClefManifest,\n repoRoot: string,\n environment?: string,\n ): Promise<Record<string, string>> {\n const identity = this.get(manifest, name);\n if (!identity) {\n throw new Error(`Service identity '${name}' not found.`);\n }\n\n const manifestPath = path.join(repoRoot, CLEF_MANIFEST_FILENAME);\n const raw = fs.readFileSync(manifestPath, \"utf-8\");\n const doc = YAML.parse(raw) as Record<string, unknown>;\n const identities = doc.service_identities as Record<string, unknown>[];\n const siDoc = identities.find((si) => (si as Record<string, unknown>).name === name) as Record<\n string,\n unknown\n >;\n const envs = siDoc.environments as Record<string, Record<string, string>>;\n\n const newPrivateKeys: Record<string, string> = {};\n const envsToRotate = environment ? [environment] : Object.keys(identity.environments);\n\n const cells = this.matrixManager.resolveMatrix(manifest, repoRoot).filter((c) => c.exists);\n\n try {\n for (const envName of envsToRotate) {\n const envConfig = identity.environments[envName];\n if (!envConfig) {\n throw new Error(`Environment '${envName}' not found on identity '${name}'.`);\n }\n // KMS-backed environments don't have age keys to rotate\n if (isKmsEnvelope(envConfig)) continue;\n const oldRecipient = envConfig.recipient;\n if (!oldRecipient) {\n throw new Error(`Environment '${envName}' not found on identity '${name}'.`);\n }\n\n const newIdentity = await generateAgeIdentity();\n newPrivateKeys[envName] = newIdentity.privateKey;\n\n // Update manifest\n envs[envName] = { recipient: newIdentity.publicKey };\n\n // Swap recipients on scoped files\n const scopedCells = cells.filter(\n (c) => identity.namespaces.includes(c.namespace) && c.environment === envName,\n );\n for (const cell of scopedCells) {\n try {\n await this.encryption.removeRecipient(cell.filePath, oldRecipient);\n } catch {\n // May not be a current recipient\n }\n try {\n await this.encryption.addRecipient(cell.filePath, newIdentity.publicKey);\n } catch (addErr) {\n // Attempt rollback: re-add old recipient\n try {\n await this.encryption.addRecipient(cell.filePath, oldRecipient);\n } catch {\n throw new Error(\n `Failed to add new recipient to ${cell.namespace}/${cell.environment} and rollback also failed. ` +\n `File may be in an inconsistent state. ` +\n `Old key: ${oldRecipient.slice(0, 12)}..., New key: ${newIdentity.publicKey.slice(0, 12)}...`,\n );\n }\n throw addErr;\n }\n }\n }\n } catch (err) {\n if (Object.keys(newPrivateKeys).length > 0) {\n const partialErr = new PartialRotationError(\n `Rotation failed after rotating ${Object.keys(newPrivateKeys).join(\", \")}: ${(err as Error).message}`,\n newPrivateKeys,\n );\n throw partialErr;\n }\n throw err;\n }\n\n const tmpRotate = path.join(os.tmpdir(), `clef-manifest-${process.pid}-${Date.now()}.tmp`);\n try {\n fs.writeFileSync(tmpRotate, YAML.stringify(doc), \"utf-8\");\n fs.renameSync(tmpRotate, manifestPath);\n } finally {\n try {\n fs.unlinkSync(tmpRotate);\n } catch {\n // Already renamed or never written \u2014 ignore\n }\n }\n return newPrivateKeys;\n }\n\n /**\n * Validate service identities and return drift issues.\n */\n async validate(manifest: ClefManifest, repoRoot: string): Promise<ServiceIdentityDriftIssue[]> {\n const issues: ServiceIdentityDriftIssue[] = [];\n const identities = manifest.service_identities ?? [];\n\n if (identities.length === 0) return issues;\n\n const declaredEnvNames = new Set(manifest.environments.map((e) => e.name));\n const declaredNsNames = new Set(manifest.namespaces.map((ns) => ns.name));\n const cells = this.matrixManager.resolveMatrix(manifest, repoRoot).filter((c) => c.exists);\n\n for (const si of identities) {\n // Check namespace references\n for (const ns of si.namespaces) {\n if (!declaredNsNames.has(ns)) {\n issues.push({\n identity: si.name,\n namespace: ns,\n type: \"namespace_not_found\",\n message: `Service identity '${si.name}' references non-existent namespace '${ns}'.`,\n });\n }\n }\n\n // Check environment coverage\n for (const envName of declaredEnvNames) {\n if (!(envName in si.environments)) {\n issues.push({\n identity: si.name,\n environment: envName,\n type: \"missing_environment\",\n message: `Service identity '${si.name}' is missing environment '${envName}'. Manually add an age key pair for this environment in clef.yaml.`,\n });\n }\n }\n\n // Check recipient registration on scoped files\n // (KMS-backed environments skip recipient checks \u2014 no recipient to register)\n for (const cell of cells) {\n const envConfig = si.environments[cell.environment];\n if (!envConfig) continue;\n if (isKmsEnvelope(envConfig)) continue;\n if (!envConfig.recipient) continue;\n\n if (si.namespaces.includes(cell.namespace)) {\n // Should be registered\n try {\n const metadata = await this.encryption.getMetadata(cell.filePath);\n if (!metadata.recipients.includes(envConfig.recipient)) {\n issues.push({\n identity: si.name,\n environment: cell.environment,\n namespace: cell.namespace,\n type: \"recipient_not_registered\",\n message: `Service identity '${si.name}' recipient is not registered in ${cell.namespace}/${cell.environment}.`,\n fixCommand: `clef service create ${si.name} --namespaces ${si.namespaces.join(\",\")}`,\n });\n }\n } catch {\n // Cannot read metadata \u2014 skip\n }\n } else {\n // Should NOT be registered (scope mismatch)\n try {\n const metadata = await this.encryption.getMetadata(cell.filePath);\n if (metadata.recipients.includes(envConfig.recipient)) {\n issues.push({\n identity: si.name,\n environment: cell.environment,\n namespace: cell.namespace,\n type: \"scope_mismatch\",\n message: `Service identity '${si.name}' recipient found in ${cell.namespace}/${cell.environment} but namespace '${cell.namespace}' is not in scope.`,\n fixCommand: `clef recipients remove ${envConfig.recipient} -e ${cell.environment}`,\n });\n }\n } catch {\n // Cannot read metadata \u2014 skip\n }\n }\n }\n }\n\n return issues;\n }\n}\n", "import {\n ClefManifest,\n EncryptionBackend,\n ServiceIdentityDefinition,\n ServiceIdentityEnvironmentConfig,\n} from \"../types\";\nimport { MatrixManager } from \"../matrix/manager\";\n\n/** Resolved identity secrets: merged key/value map plus metadata. */\nexport interface ResolvedSecrets {\n /** Flat key/value map (namespace-prefixed if multi-namespace). */\n values: Record<string, string>;\n /** The matched service identity definition. */\n identity: ServiceIdentityDefinition;\n /** Age public key for the target environment (undefined for KMS identities). */\n recipient?: string;\n /** Full environment config (age-only or KMS). */\n envConfig: ServiceIdentityEnvironmentConfig;\n}\n\n/**\n * Decrypt and merge scoped SOPS files for a service identity + environment.\n *\n * Shared by `ArtifactPacker` (and any future consumers) to avoid duplicating\n * the decrypt-merge-collision-check logic.\n */\nexport async function resolveIdentitySecrets(\n identityName: string,\n environment: string,\n manifest: ClefManifest,\n repoRoot: string,\n encryption: EncryptionBackend,\n matrixManager: MatrixManager,\n): Promise<ResolvedSecrets> {\n const identity = manifest.service_identities?.find((si) => si.name === identityName);\n if (!identity) {\n throw new Error(`Service identity '${identityName}' not found in manifest.`);\n }\n\n const envConfig = identity.environments[environment];\n if (!envConfig) {\n throw new Error(\n `Environment '${environment}' not found on service identity '${identityName}'.`,\n );\n }\n\n const allValues: Record<string, string> = {};\n const cells = matrixManager\n .resolveMatrix(manifest, repoRoot)\n .filter(\n (c) => c.exists && identity.namespaces.includes(c.namespace) && c.environment === environment,\n );\n\n const isMultiNamespace = identity.namespaces.length > 1;\n const collisions: string[] = [];\n\n for (const cell of cells) {\n const decrypted = await encryption.decrypt(cell.filePath);\n for (const [key, value] of Object.entries(decrypted.values)) {\n const qualifiedKey = isMultiNamespace ? `${cell.namespace}__${key}` : key;\n if (qualifiedKey in allValues && allValues[qualifiedKey] !== value) {\n collisions.push(qualifiedKey);\n }\n allValues[qualifiedKey] = value;\n }\n }\n\n if (collisions.length > 0) {\n throw new Error(\n `Key collision detected in bundle: ${collisions.join(\", \")}. ` +\n \"Keys with the same name but different values exist across namespaces.\",\n );\n }\n\n return {\n values: allValues,\n identity,\n recipient: envConfig.recipient,\n envConfig,\n };\n}\n", "import * as fs from \"fs\";\nimport * as path from \"path\";\nimport * as crypto from \"crypto\";\nimport { ClefManifest, EncryptionBackend, isKmsEnvelope } from \"../types\";\nimport { KmsProvider } from \"../kms\";\nimport { MatrixManager } from \"../matrix/manager\";\nimport { PackConfig, PackResult, PackedArtifact } from \"./types\";\nimport { resolveIdentitySecrets } from \"./resolve\";\n\n/**\n * Packs an encrypted artifact for a service identity + environment.\n *\n * The artifact is a JSON envelope containing age-encrypted secrets that can\n * be fetched by the runtime agent via HTTP or local file.\n */\nexport class ArtifactPacker {\n constructor(\n private readonly encryption: EncryptionBackend,\n private readonly matrixManager: MatrixManager,\n private readonly kms?: KmsProvider,\n ) {}\n\n /**\n * Pack an artifact: decrypt scoped SOPS files, age-encrypt the merged\n * values to the service identity's recipient, and write a JSON envelope.\n */\n async pack(config: PackConfig, manifest: ClefManifest, repoRoot: string): Promise<PackResult> {\n const resolved = await resolveIdentitySecrets(\n config.identity,\n config.environment,\n manifest,\n repoRoot,\n this.encryption,\n this.matrixManager,\n );\n\n const plaintext = JSON.stringify(resolved.values);\n\n let ciphertext: string;\n let artifact: PackedArtifact;\n\n if (isKmsEnvelope(resolved.envConfig)) {\n // KMS envelope path\n if (!this.kms) {\n throw new Error(\"KMS provider required for envelope encryption but none was provided.\");\n }\n\n // Generate ephemeral age key pair\n const { generateIdentity, identityToRecipient, Encrypter } = await import(\n // eslint-disable-next-line @typescript-eslint/no-explicit-any -- dynamic ESM import of CJS-incompatible package\n \"age-encryption\" as any\n );\n const ephemeralPrivateKey = (await generateIdentity()) as string;\n const ephemeralPublicKey = (await identityToRecipient(ephemeralPrivateKey)) as string;\n\n try {\n const e = new Encrypter();\n e.addRecipient(ephemeralPublicKey);\n const encrypted = await e.encrypt(plaintext);\n ciphertext = Buffer.from(encrypted as Uint8Array).toString(\"base64\");\n } catch {\n throw new Error(\"Failed to age-encrypt artifact with ephemeral key.\");\n }\n\n // Wrap the ephemeral private key with KMS\n const kmsConfig = resolved.envConfig.kms;\n const wrapped = await this.kms.wrap(kmsConfig.keyId, Buffer.from(ephemeralPrivateKey));\n\n const revision = `${Date.now()}-${crypto.randomBytes(4).toString(\"hex\")}`;\n const ciphertextHash = crypto.createHash(\"sha256\").update(ciphertext).digest(\"hex\");\n\n artifact = {\n version: 1,\n identity: config.identity,\n environment: config.environment,\n packedAt: new Date().toISOString(),\n revision,\n ciphertextHash,\n ciphertext,\n keys: Object.keys(resolved.values),\n envelope: {\n provider: kmsConfig.provider,\n keyId: kmsConfig.keyId,\n wrappedKey: wrapped.wrappedKey.toString(\"base64\"),\n algorithm: wrapped.algorithm,\n },\n };\n } else {\n // Age-only path (v1, unchanged)\n try {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any -- dynamic ESM import of CJS-incompatible package\n const { Encrypter } = await import(\"age-encryption\" as any);\n const e = new Encrypter();\n e.addRecipient(resolved.recipient!);\n const encrypted = await e.encrypt(plaintext);\n ciphertext = Buffer.from(encrypted as Uint8Array).toString(\"base64\");\n } catch {\n throw new Error(\"Failed to age-encrypt artifact. Check recipient key.\");\n }\n\n const revision = `${Date.now()}-${crypto.randomBytes(4).toString(\"hex\")}`;\n const ciphertextHash = crypto.createHash(\"sha256\").update(ciphertext).digest(\"hex\");\n\n artifact = {\n version: 1,\n identity: config.identity,\n environment: config.environment,\n packedAt: new Date().toISOString(),\n revision,\n ciphertextHash,\n ciphertext,\n keys: Object.keys(resolved.values),\n };\n }\n\n const outputDir = path.dirname(config.outputPath);\n if (!fs.existsSync(outputDir)) {\n fs.mkdirSync(outputDir, { recursive: true });\n }\n\n if (config.ttl && config.ttl > 0) {\n artifact.expiresAt = new Date(Date.now() + config.ttl * 1000).toISOString();\n }\n\n const json = JSON.stringify(artifact, null, 2);\n const tmpOutput = `${config.outputPath}.tmp.${process.pid}`;\n fs.writeFileSync(tmpOutput, json, \"utf-8\");\n fs.renameSync(tmpOutput, config.outputPath);\n\n return {\n outputPath: config.outputPath,\n namespaceCount: resolved.identity.namespaces.length,\n keyCount: Object.keys(resolved.values).length,\n artifactSize: Buffer.byteLength(json, \"utf-8\"),\n revision: artifact.revision,\n };\n }\n}\n"],
|
|
5
|
-
"mappings": "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",
|
|
6
|
-
"names": ["fs", "path", "fs", "fs", "path", "YAML", "fs", "path", "YAML", "fs", "YAML", "path", "path", "fs", "path", "fs", "randomBytes", "YAML", "fs", "path", "fs", "path", "randomBytes", "path", "path", "path", "YAML", "parse", "parse", "existingKeys", "decrypted", "fs", "path", "YAML", "fs", "path", "YAML", "HEADER_COMMENT", "fs", "path", "YAML", "fs", "path", "YAML", "fs", "path", "YAML", "fs", "path", "crypto"]
|
|
3
|
+
"sources": ["../src/types/index.ts", "../src/manifest/parser.ts", "../src/recipients/validator.ts", "../src/scanner/index.ts", "../src/scanner/patterns.ts", "../src/scanner/ignore.ts", "../src/matrix/manager.ts", "../src/pending/metadata.ts", "../src/schema/validator.ts", "../src/diff/engine.ts", "../src/bulk/ops.ts", "../src/git/integration.ts", "../src/sops/client.ts", "../src/sops/resolver.ts", "../src/sops/bundled.ts", "../src/dependencies/checker.ts", "../src/age/keygen.ts", "../src/lint/runner.ts", "../src/consumption/client.ts", "../src/import/index.ts", "../src/import/parsers.ts", "../src/recipients/index.ts", "../src/recipients/requests.ts", "../src/drift/detector.ts", "../src/report/generator.ts", "../src/report/sanitizer.ts", "../src/report/transformer.ts", "../src/report/cloud-client.ts", "../src/report/ci-context.ts", "../src/merge/driver.ts", "../src/service-identity/manager.ts", "../src/artifact/resolve.ts", "../src/artifact/packer.ts", "../src/artifact/signer.ts"],
|
|
4
|
+
"sourcesContent": ["/** Supported file extensions for encrypted SOPS files managed by Clef. */\nexport const CLEF_SUPPORTED_EXTENSIONS = [\".enc.yaml\", \".enc.json\"] as const;\n\n// \u2500\u2500 Subprocess Runner (dependency injection for sops & git) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/** Result returned by a subprocess invocation. */\nexport interface SubprocessResult {\n stdout: string;\n stderr: string;\n exitCode: number;\n}\n\n/**\n * Abstraction over subprocess execution used throughout the core library.\n * Inject a real implementation (`NodeSubprocessRunner`) in production and a\n * mock via `jest.fn()` in unit tests \u2014 no real subprocess calls in tests.\n */\nexport interface SubprocessRunner {\n run(command: string, args: string[], options?: SubprocessOptions): Promise<SubprocessResult>;\n}\n\n/** Options forwarded to the subprocess. */\nexport interface SubprocessOptions {\n /** Working directory for the child process. */\n cwd?: string;\n /** Data to pipe to stdin. */\n stdin?: string;\n /** Additional environment variables for the child process. */\n env?: Record<string, string>;\n}\n\n// \u2500\u2500 Manifest \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/** Cloud integration configuration stored in the manifest. */\nexport interface ClefCloudConfig {\n integrationId: string;\n}\n\n/** Parsed and validated contents of a `clef.yaml` manifest file. */\nexport interface ClefManifest {\n version: number;\n environments: ClefEnvironment[];\n namespaces: ClefNamespace[];\n sops: SopsConfig;\n file_pattern: string;\n service_identities?: ServiceIdentityDefinition[];\n cloud?: ClefCloudConfig;\n}\n\n/** Per-environment SOPS backend override. */\nexport interface EnvironmentSopsOverride {\n backend: \"age\" | \"awskms\" | \"gcpkms\" | \"azurekv\" | \"pgp\";\n aws_kms_arn?: string;\n gcp_kms_resource_id?: string;\n azure_kv_url?: string;\n pgp_fingerprint?: string;\n}\n\n/** A single deployment environment declared in the manifest. */\nexport interface ClefEnvironment {\n name: string;\n description: string;\n /** When `true`, write operations require explicit confirmation. */\n protected?: boolean;\n /** Per-environment SOPS backend override. Falls back to global `sops` config when absent. */\n sops?: EnvironmentSopsOverride;\n /** Per-environment age recipient overrides. When set, these recipients are used instead of global. */\n recipients?: (string | { key: string; label?: string })[];\n}\n\n/**\n * Resolve the effective backend configuration for an environment.\n * Returns the per-env override if present, otherwise falls back to the global `sops` config.\n */\nexport function resolveBackendConfig(\n manifest: ClefManifest,\n environment: string,\n): EnvironmentSopsOverride {\n const env = manifest.environments.find((e) => e.name === environment);\n if (env?.sops) return env.sops;\n return {\n backend: manifest.sops.default_backend,\n aws_kms_arn: manifest.sops.aws_kms_arn,\n gcp_kms_resource_id: manifest.sops.gcp_kms_resource_id,\n azure_kv_url: manifest.sops.azure_kv_url,\n pgp_fingerprint: manifest.sops.pgp_fingerprint,\n };\n}\n\n/**\n * Resolve per-environment recipients if defined.\n * Returns the environment's `recipients` array if non-empty, otherwise `undefined`\n * (caller should fall back to global recipients).\n */\nexport function resolveRecipientsForEnvironment(\n manifest: ClefManifest,\n environment: string,\n): (string | { key: string; label?: string })[] | undefined {\n const env = manifest.environments.find((e) => e.name === environment);\n if (env?.recipients && env.recipients.length > 0) return env.recipients;\n return undefined;\n}\n\n/** A secrets namespace declared in the manifest. */\nexport interface ClefNamespace {\n name: string;\n description: string;\n /** Optional path to a YAML schema file for this namespace. */\n schema?: string;\n /** Optional list of owner identifiers for this namespace. */\n owners?: string[];\n}\n\n/** SOPS encryption backend configuration from the manifest. */\nexport interface SopsConfig {\n default_backend: \"age\" | \"awskms\" | \"gcpkms\" | \"azurekv\" | \"pgp\";\n aws_kms_arn?: string;\n gcp_kms_resource_id?: string;\n azure_kv_url?: string;\n pgp_fingerprint?: string;\n}\n\n/**\n * Per-developer local config stored in `.clef/config.yaml` (gitignored).\n * Holds settings that must not be committed, such as the age private key path.\n */\nexport interface ClefLocalConfig {\n /** Path to the age private key file for this developer. */\n age_key_file?: string;\n /**\n * Where the age private key was stored during init.\n * - \"keychain\" \u2014 OS keychain (macOS Keychain / Linux libsecret / Windows Credential Manager)\n * - \"file\" \u2014 filesystem at age_key_file path\n *\n * Used to provide targeted guidance when the key cannot be resolved.\n */\n age_key_storage?: \"keychain\" | \"file\";\n /** Label identifying this repo's age key in the OS keychain or filesystem. */\n age_keychain_label?: string;\n}\n\n// \u2500\u2500 Matrix \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/** A single cell in the namespace \u00D7 environment matrix. */\nexport interface MatrixCell {\n namespace: string;\n environment: string;\n /** Absolute path to the encrypted SOPS file for this cell. */\n filePath: string;\n /** Whether the encrypted file exists on disk. */\n exists: boolean;\n}\n\n/** An issue detected within a single matrix cell. */\nexport interface MatrixIssue {\n type: \"missing_keys\" | \"schema_warning\" | \"sops_error\";\n message: string;\n /** The affected key name, if applicable. */\n key?: string;\n}\n\n/** Decrypted status summary for one matrix cell. */\nexport interface MatrixStatus {\n cell: MatrixCell;\n /** Number of keys in the decrypted file. */\n keyCount: number;\n /** Number of keys currently marked as pending placeholders. */\n pendingCount: number;\n /** Timestamp from SOPS metadata, or `null` if unavailable. */\n lastModified: Date | null;\n issues: MatrixIssue[];\n}\n\n// \u2500\u2500 Schema \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/** A namespace schema loaded from a YAML schema file. */\nexport interface NamespaceSchema {\n keys: Record<string, SchemaKey>;\n}\n\n/** Definition for a single key in a namespace schema. */\nexport interface SchemaKey {\n type: \"string\" | \"integer\" | \"boolean\";\n required: boolean;\n /** Regex pattern the value must match (strings only). */\n pattern?: string;\n default?: unknown;\n description?: string;\n /** Maximum numeric value (integers only). */\n max?: number;\n}\n\n/** A hard validation error produced by `SchemaValidator.validate`. */\nexport interface ValidationError {\n key: string;\n message: string;\n rule: \"required\" | \"type\" | \"pattern\";\n}\n\n/** A soft validation warning produced by `SchemaValidator.validate`. */\nexport interface ValidationWarning {\n key: string;\n message: string;\n rule: \"undeclared\" | \"max_exceeded\";\n}\n\n/** Result of validating a set of decrypted values against a namespace schema. */\nexport interface ValidationResult {\n /** `true` when there are no errors (warnings are allowed). */\n valid: boolean;\n errors: ValidationError[];\n warnings: ValidationWarning[];\n}\n\n// \u2500\u2500 Diff \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/** Status of a single key when diffing two environments. */\nexport type DiffStatus = \"changed\" | \"identical\" | \"missing_a\" | \"missing_b\";\n\n/** One row in a diff result representing a single key comparison. */\nexport interface DiffRow {\n key: string;\n /** Value from environment A, or `null` if the key is absent. */\n valueA: string | null;\n /** Value from environment B, or `null` if the key is absent. */\n valueB: string | null;\n status: DiffStatus;\n}\n\n/** The full diff result for a namespace across two environments. */\nexport interface DiffResult {\n namespace: string;\n envA: string;\n envB: string;\n rows: DiffRow[];\n}\n\n// \u2500\u2500 Lint \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/** Severity level of a lint issue. */\nexport type LintSeverity = \"error\" | \"warning\" | \"info\";\n\n/** Category of a lint issue. */\nexport type LintCategory = \"matrix\" | \"schema\" | \"sops\" | \"service-identity\";\n\n/** A single issue reported by `LintRunner`. */\nexport interface LintIssue {\n severity: LintSeverity;\n category: LintCategory;\n /** Path to the affected encrypted file. */\n file: string;\n /** The affected key name, if applicable. */\n key?: string;\n message: string;\n /** CLI command that can auto-fix this issue, if one exists. */\n fixCommand?: string;\n}\n\n/** Aggregate result from a full lint run. */\nexport interface LintResult {\n issues: LintIssue[];\n /** Total number of matrix files checked (including missing ones). */\n fileCount: number;\n /** Total number of keys marked as pending placeholders across all files. */\n pendingCount: number;\n}\n\n// \u2500\u2500 Git \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/** A single git commit. */\nexport interface GitCommit {\n hash: string;\n author: string;\n date: Date;\n message: string;\n}\n\n/** Parsed output of `git status --porcelain`. */\nexport interface GitStatus {\n /** Files with staged (index) changes. */\n staged: string[];\n /** Files with unstaged (work-tree) changes. */\n unstaged: string[];\n untracked: string[];\n}\n\n// \u2500\u2500 SOPS \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/** The in-memory result of decrypting a SOPS-encrypted file. Plaintext never touches disk. */\nexport interface DecryptedFile {\n /** Flat key/value map of all decrypted secrets. */\n values: Record<string, string>;\n metadata: SopsMetadata;\n}\n\n/** SOPS metadata extracted from an encrypted file without decrypting its values. */\nexport interface SopsMetadata {\n backend: \"age\" | \"awskms\" | \"gcpkms\" | \"azurekv\" | \"pgp\";\n /** List of recipient identifiers (age public keys, KMS ARNs, Azure KV URLs, PGP fingerprints). */\n recipients: string[];\n lastModified: Date;\n}\n\n/**\n * Backend-agnostic interface for all encryption/decryption operations.\n *\n * `SopsClient` is the canonical implementation. Consumers should depend on this\n * interface rather than the concrete class so the encryption backend can be\n * replaced without touching call sites.\n */\nexport interface EncryptionBackend {\n /** Decrypt a file and return its values and metadata. */\n decrypt(filePath: string): Promise<DecryptedFile>;\n /** Encrypt a key/value map and write it to a file. */\n encrypt(\n filePath: string,\n values: Record<string, string>,\n manifest: ClefManifest,\n environment?: string,\n ): Promise<void>;\n /** Rotate encryption by adding a new recipient key. */\n reEncrypt(filePath: string, newKey: string): Promise<void>;\n /** Add an age recipient to an encrypted file (rotate + add-age). */\n addRecipient(filePath: string, key: string): Promise<void>;\n /** Remove an age recipient from an encrypted file (rotate + rm-age). */\n removeRecipient(filePath: string, key: string): Promise<void>;\n /** Check whether a file has valid encryption metadata. */\n validateEncryption(filePath: string): Promise<boolean>;\n /** Extract encryption metadata without decrypting. */\n getMetadata(filePath: string): Promise<SopsMetadata>;\n}\n\n// \u2500\u2500 Consumption \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/** Options for `ConsumptionClient.prepareEnvironment`. */\nexport interface ExecOptions {\n /** Inject only these keys (if set, all other keys are excluded). */\n only?: string[];\n /** Prepend this string to every injected environment variable name. */\n prefix?: string;\n /** When `true`, skip keys that already exist in the base environment. */\n noOverride?: boolean;\n}\n\n/** Options for `ConsumptionClient.formatExport`. */\nexport interface ExportOptions {\n format: \"env\";\n /** When `true`, omit the `export` keyword from each line. */\n noExport?: boolean;\n}\n\n// \u2500\u2500 Errors \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/**\n * Base error class for all Clef errors.\n * Carries an optional `fix` hint string describing how to resolve the issue.\n */\nexport class ClefError extends Error {\n constructor(\n message: string,\n public readonly fix?: string,\n ) {\n super(message);\n this.name = \"ClefError\";\n }\n}\n\n/** Thrown when `clef.yaml` fails parsing or schema validation. */\nexport class ManifestValidationError extends ClefError {\n constructor(\n message: string,\n public readonly field?: string,\n ) {\n super(message, field ? `Check the '${field}' field in clef.yaml` : undefined);\n this.name = \"ManifestValidationError\";\n }\n}\n\n/** Thrown when SOPS decryption fails (bad key, corrupt file, etc.). */\nexport class SopsDecryptionError extends ClefError {\n constructor(\n message: string,\n public readonly filePath?: string,\n ) {\n super(\n message,\n filePath\n ? `Ensure you have the correct key configured to decrypt '${filePath}'`\n : \"Ensure your SOPS key is configured correctly\",\n );\n this.name = \"SopsDecryptionError\";\n }\n}\n\n/** Thrown when SOPS encryption or re-encryption fails. */\nexport class SopsEncryptionError extends ClefError {\n constructor(\n message: string,\n public readonly filePath?: string,\n ) {\n super(\n message,\n filePath\n ? `Check your SOPS configuration and key access for '${filePath}'`\n : \"Check your SOPS configuration\",\n );\n this.name = \"SopsEncryptionError\";\n }\n}\n\n/** Thrown when no decryption key is found in the environment. */\nexport class SopsKeyNotFoundError extends ClefError {\n constructor(message: string) {\n super(message, \"Ensure your age key file exists and CLEF_AGE_KEY_FILE is set correctly\");\n this.name = \"SopsKeyNotFoundError\";\n }\n}\n\n/** Thrown when a git subprocess fails. */\nexport class GitOperationError extends ClefError {\n constructor(message: string, fix?: string) {\n super(message, fix ?? \"Ensure you are inside a git repository\");\n this.name = \"GitOperationError\";\n }\n}\n\n/** Thrown when a namespace schema file cannot be read or parsed. */\nexport class SchemaLoadError extends ClefError {\n constructor(\n message: string,\n public readonly filePath?: string,\n ) {\n super(\n message,\n filePath ? `Check the schema file at '${filePath}'` : \"Check your schema file syntax\",\n );\n this.name = \"SchemaLoadError\";\n }\n}\n\n// \u2500\u2500 Dependency errors \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/** Thrown when the `sops` binary is not installed. */\nexport class SopsMissingError extends ClefError {\n constructor(public readonly installHint: string) {\n super(\n \"sops is not installed.\",\n `Install it with: ${installHint}\\nThen run clef doctor to verify your setup.`,\n );\n this.name = \"SopsMissingError\";\n }\n}\n\n/** Thrown when the installed `sops` version is older than the minimum required. */\nexport class SopsVersionError extends ClefError {\n constructor(\n public readonly installed: string,\n public readonly required: string,\n public readonly installHint: string,\n ) {\n super(\n `sops v${installed} is installed but Clef requires v${required} or later.`,\n `Upgrade with: ${installHint}\\nThen run clef doctor to verify your setup.`,\n );\n this.name = \"SopsVersionError\";\n }\n}\n\n// \u2500\u2500 KMS Envelope Encryption \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport type KmsProviderType = \"aws\" | \"gcp\" | \"azure\";\n\nexport interface KmsConfig {\n provider: KmsProviderType;\n keyId: string;\n region?: string;\n}\n\n// \u2500\u2500 Service Identity \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/** Per-environment config for a service identity: either age-only or KMS envelope. */\nexport interface ServiceIdentityEnvironmentConfig {\n /** Age public key (age-only path). Mutually exclusive with `kms`. */\n recipient?: string;\n /** KMS envelope encryption config. Mutually exclusive with `recipient`. */\n kms?: KmsConfig;\n /** Public key for artifact signature verification (base64-encoded DER SPKI). */\n verifyKey?: string;\n}\n\n/** Type guard: returns true when the environment config uses KMS envelope encryption. */\nexport function isKmsEnvelope(\n cfg: ServiceIdentityEnvironmentConfig,\n): cfg is ServiceIdentityEnvironmentConfig & { kms: KmsConfig } {\n return cfg.kms !== undefined;\n}\n\n/** A machine-oriented identity with scoped namespace access and per-environment age keys. */\nexport interface ServiceIdentityDefinition {\n name: string;\n description: string;\n namespaces: string[];\n environments: Record<string, ServiceIdentityEnvironmentConfig>;\n}\n\n/** A drift issue detected in a service identity configuration. */\nexport interface ServiceIdentityDriftIssue {\n identity: string;\n environment?: string;\n namespace?: string;\n type:\n | \"missing_environment\"\n | \"scope_mismatch\"\n | \"recipient_not_registered\"\n | \"orphaned_recipient\"\n | \"namespace_not_found\";\n message: string;\n fixCommand?: string;\n}\n\n// \u2500\u2500 Cross-repo drift detection \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/** A single drift issue: a key present in some environments but missing from others. */\nexport interface DriftIssue {\n namespace: string;\n key: string;\n /** Environment names where the key exists. */\n presentIn: string[];\n /** Environment names where the key is missing. */\n missingFrom: string[];\n message: string;\n}\n\n/** Result of comparing key sets across two local Clef repos without decryption. */\nexport interface DriftResult {\n issues: DriftIssue[];\n namespacesCompared: number;\n namespacesClean: number;\n localEnvironments: string[];\n remoteEnvironments: string[];\n}\n\n// \u2500\u2500 Dependency check types \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/** Version check result for a single external dependency. */\nexport interface DependencyVersion {\n /** Installed version string, e.g. `\"3.9.1\"`. */\n installed: string;\n /** Minimum required version string. */\n required: string;\n /** `true` when `installed >= required`. */\n satisfied: boolean;\n /** Platform-appropriate install/upgrade command hint. */\n installHint: string;\n /** How the binary was resolved: env override, bundled package, or system PATH. */\n source?: \"env\" | \"bundled\" | \"system\";\n /** Resolved path to the binary. */\n resolvedPath?: string;\n}\n\n/** Combined dependency check result for all required external tools. */\nexport interface DependencyStatus {\n /** `null` if `sops` is not installed or version could not be parsed. */\n sops: DependencyVersion | null;\n /** `null` if `git` is not installed or version could not be parsed. */\n git: DependencyVersion | null;\n}\n\n// \u2500\u2500 Report \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/** Schema version for {@link ClefReport}. Increment when the shape changes. */\nexport const CLEF_REPORT_SCHEMA_VERSION = 1;\n\n/** Repository identity fields included at the top of every report. */\nexport interface ReportRepoIdentity {\n repoOrigin: string;\n commitSha: string;\n branch: string;\n commitTimestamp: string;\n reportGeneratedAt: string;\n clefVersion: string;\n sopsVersion: string | null;\n}\n\n/** Manifest structure summary included in a report. */\nexport interface ReportManifestStructure {\n manifestVersion: number;\n filePattern: string;\n environments: { name: string; protected: boolean }[];\n namespaces: { name: string; hasSchema: boolean; owners: string[] }[];\n defaultBackend: string;\n}\n\n/** SOPS metadata for a single matrix cell. */\nexport interface ReportCellMetadata {\n backend: string;\n recipients: string[];\n lastModified: string | null;\n}\n\n/** A single cell in the matrix as represented in a report. */\nexport interface ReportMatrixCell {\n namespace: string;\n environment: string;\n filePath: string;\n exists: boolean;\n keyCount: number;\n pendingCount: number;\n metadata: ReportCellMetadata | null;\n}\n\n/** A sanitized policy issue \u2014 key names are never present. */\nexport interface ReportPolicyIssue {\n severity: LintSeverity;\n category: string;\n file?: string;\n namespace?: string;\n environment?: string;\n message: string;\n count?: number;\n driftCount?: number;\n sourceEnvironment?: string;\n targetEnvironment?: string;\n}\n\n/** Aggregated counts of policy issues by severity. */\nexport interface ReportIssueCounts {\n error: number;\n warning: number;\n info: number;\n}\n\n/** Policy section of a report: aggregated counts and sanitized issues. */\nexport interface ReportPolicy {\n issueCount: ReportIssueCounts;\n issues: ReportPolicyIssue[];\n}\n\n/** Summary of a single recipient across all matrix cells. */\nexport interface ReportRecipientSummary {\n type: string;\n environments: string[];\n fileCount: number;\n}\n\n/** Top-level structure for a `clef report` output. */\nexport interface ClefReport {\n schemaVersion: number;\n repoIdentity: ReportRepoIdentity;\n manifest: ReportManifestStructure;\n matrix: ReportMatrixCell[];\n policy: ReportPolicy;\n recipients: Record<string, ReportRecipientSummary>;\n}\n\n// \u2500\u2500 Cloud API types \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/** Health status for a single matrix cell in a cloud report. */\nexport type CloudCellHealthStatus = \"healthy\" | \"warning\" | \"critical\" | \"unknown\";\n\n/** A single cell summary sent to the Cloud API. */\nexport interface CloudReportCell {\n namespace: string;\n environment: string;\n healthStatus: CloudCellHealthStatus;\n description: string;\n}\n\n/** Summary section of a cloud API report. */\nexport interface CloudReportSummary {\n filesScanned: number;\n namespaces: string[];\n environments: string[];\n cells: CloudReportCell[];\n violations: number;\n passed: boolean;\n}\n\n/** Drift entry for a single namespace in a cloud report. */\nexport interface CloudReportDrift {\n namespace: string;\n isDrifted: boolean;\n driftCount: number;\n}\n\n/** A single policy result in a cloud report. */\nexport interface CloudPolicyResult {\n ruleId: string;\n ruleName: string;\n passed: boolean;\n severity: string;\n message: string;\n scope?: { namespace?: string; environment?: string };\n}\n\n/** CI context attached to cloud reports when collectCIContext is enabled. */\nexport interface CloudCIContext {\n provider: string;\n pipelineUrl?: string;\n trigger?: string;\n}\n\n/** The report payload sent to the Cloud API. */\nexport interface CloudApiReport {\n commitSha: string;\n branch: string;\n commitTimestamp: number;\n cliVersion: string;\n summary: CloudReportSummary;\n drift: CloudReportDrift[];\n policyResults: CloudPolicyResult[];\n ciContext?: CloudCIContext;\n}\n\n/** Batch payload for backfill submissions (max 500, oldest\u2192newest). */\nexport interface CloudBatchPayload {\n reports: CloudApiReport[];\n}\n\n/** Response from GET /api/v1/integrations/:integrationId. */\nexport interface CloudIntegrationResponse {\n lastCommitSha: string | null;\n config: {\n collectCIContext: boolean;\n };\n}\n\n/** Response from POST /api/v1/reports. */\nexport interface CloudReportResponse {\n id: string;\n commitSha: string;\n}\n\n/** Response from POST /api/v1/reports/batch. */\nexport interface CloudBatchResponse {\n accepted: number;\n reportIds: string[];\n}\n\n/** Thrown when a Clef Pro API request fails. */\nexport class CloudApiError extends ClefError {\n constructor(\n message: string,\n public readonly statusCode: number,\n fix?: string,\n ) {\n super(message, fix);\n this.name = \"CloudApiError\";\n }\n}\n", "/**\n * TIER 1 MODULE \u2014 Security and correctness critical.\n *\n * This module requires exhaustive test coverage. Before\n * adding or modifying code here:\n * 1. Add tests for the happy path\n * 2. Add tests for all documented error paths\n * 3. Add at least one boundary/edge case test\n *\n * Coverage threshold: 95% lines/functions, 90% branches.\n * See docs/contributing/testing.md for the rationale.\n */\nimport * as fs from \"fs\";\nimport * as YAML from \"yaml\";\nimport {\n ClefManifest,\n ClefCloudConfig,\n ClefEnvironment,\n ManifestValidationError,\n ServiceIdentityDefinition,\n ServiceIdentityEnvironmentConfig,\n} from \"../types\";\nimport { validateAgePublicKey } from \"../recipients/validator\";\n\n/**\n * Canonical filename for the Clef manifest.\n * All code that references this filename must import this constant.\n */\nexport const CLEF_MANIFEST_FILENAME = \"clef.yaml\";\n\nconst VALID_BACKENDS = [\"age\", \"awskms\", \"gcpkms\", \"azurekv\", \"pgp\"] as const;\nconst VALID_TOP_LEVEL_KEYS = [\n \"version\",\n \"environments\",\n \"namespaces\",\n \"sops\",\n \"file_pattern\",\n \"service_identities\",\n \"cloud\",\n];\nconst ENV_NAME_PATTERN = /^[a-z][a-z0-9_-]*$/;\nconst FILE_PATTERN_REQUIRED_TOKENS = [\"{namespace}\", \"{environment}\"];\n\n/**\n * Parses and validates `clef.yaml` manifest files.\n *\n * @example\n * ```ts\n * const parser = new ManifestParser();\n * const manifest = parser.parse(\"/path/to/clef.yaml\");\n * ```\n */\nexport class ManifestParser {\n /**\n * Read and validate a `clef.yaml` file from disk.\n *\n * @param filePath - Absolute or relative path to the manifest file.\n * @returns Validated {@link ClefManifest}.\n * @throws {@link ManifestValidationError} If the file cannot be read, contains invalid YAML,\n * or fails schema validation.\n */\n parse(filePath: string): ClefManifest {\n let raw: string;\n try {\n raw = fs.readFileSync(filePath, \"utf-8\");\n } catch {\n throw new ManifestValidationError(\n `Could not read manifest file at '${filePath}'. Run 'clef init' to create one.`,\n );\n }\n\n let parsed: unknown;\n try {\n parsed = YAML.parse(raw);\n } catch {\n throw new ManifestValidationError(\n \"Manifest file contains invalid YAML. Check for syntax errors in clef.yaml.\",\n );\n }\n\n return this.validate(parsed);\n }\n\n /**\n * Validate an already-parsed object against the manifest schema.\n *\n * @param input - Raw value returned by `YAML.parse`.\n * @returns Validated {@link ClefManifest}.\n * @throws {@link ManifestValidationError} On any schema violation.\n */\n validate(input: unknown): ClefManifest {\n if (input === null || input === undefined || typeof input !== \"object\") {\n throw new ManifestValidationError(\n \"Manifest must be a YAML object, not null or a scalar value.\",\n \"root\",\n );\n }\n\n const obj = input as Record<string, unknown>;\n\n // Check for unknown top-level keys\n for (const key of Object.keys(obj)) {\n if (!VALID_TOP_LEVEL_KEYS.includes(key)) {\n throw new ManifestValidationError(\n `Unknown top-level key '${key}' in manifest. Valid keys are: ${VALID_TOP_LEVEL_KEYS.join(\", \")}.`,\n key,\n );\n }\n }\n\n // version\n if (obj.version === undefined) {\n throw new ManifestValidationError(\"Missing required field 'version'.\", \"version\");\n }\n if (typeof obj.version !== \"number\" || obj.version !== 1) {\n throw new ManifestValidationError(\n \"Field 'version' must be 1. Only version 1 is currently supported.\",\n \"version\",\n );\n }\n\n // environments\n if (!obj.environments) {\n throw new ManifestValidationError(\n \"Missing required field 'environments'. Define at least one environment.\",\n \"environments\",\n );\n }\n if (!Array.isArray(obj.environments) || obj.environments.length === 0) {\n throw new ManifestValidationError(\n \"Field 'environments' must be a non-empty array.\",\n \"environments\",\n );\n }\n const environments: ClefEnvironment[] = obj.environments.map((env: unknown, i: number) => {\n if (typeof env !== \"object\" || env === null) {\n throw new ManifestValidationError(\n `Environment at index ${i} must be an object with 'name' and 'description'.`,\n \"environments\",\n );\n }\n const envObj = env as Record<string, unknown>;\n if (!envObj.name || typeof envObj.name !== \"string\") {\n throw new ManifestValidationError(\n `Environment at index ${i} is missing a 'name' string.`,\n \"environments\",\n );\n }\n if (!ENV_NAME_PATTERN.test(envObj.name)) {\n throw new ManifestValidationError(\n `Environment name '${envObj.name}' is invalid. Names must start with a lowercase letter and contain only lowercase letters, digits, hyphens, and underscores.`,\n \"environments\",\n );\n }\n if (!envObj.description || typeof envObj.description !== \"string\") {\n throw new ManifestValidationError(\n `Environment '${envObj.name}' is missing a 'description' string.`,\n \"environments\",\n );\n }\n\n const result: ClefEnvironment = {\n name: envObj.name,\n description: envObj.description,\n ...(typeof envObj.protected === \"boolean\" ? { protected: envObj.protected } : {}),\n };\n\n // Parse optional per-environment sops override\n if (envObj.sops !== undefined) {\n if (typeof envObj.sops !== \"object\" || envObj.sops === null) {\n throw new ManifestValidationError(\n `Environment '${envObj.name}' has an invalid 'sops' field. It must be an object.`,\n \"environments\",\n );\n }\n const sopsOverride = envObj.sops as Record<string, unknown>;\n if (!sopsOverride.backend || typeof sopsOverride.backend !== \"string\") {\n throw new ManifestValidationError(\n `Environment '${envObj.name}' sops override is missing 'backend'. Must be one of: ${VALID_BACKENDS.join(\", \")}.`,\n \"environments\",\n );\n }\n if (!(VALID_BACKENDS as readonly string[]).includes(sopsOverride.backend)) {\n throw new ManifestValidationError(\n `Environment '${envObj.name}' has invalid sops backend '${sopsOverride.backend}'. Must be one of: ${VALID_BACKENDS.join(\", \")}.`,\n \"environments\",\n );\n }\n const backend = sopsOverride.backend as (typeof VALID_BACKENDS)[number];\n\n // Validate required fields per backend\n if (backend === \"awskms\" && typeof sopsOverride.aws_kms_arn !== \"string\") {\n throw new ManifestValidationError(\n `Environment '${envObj.name}' uses 'awskms' backend but is missing 'aws_kms_arn'.`,\n \"environments\",\n );\n }\n if (backend === \"gcpkms\" && typeof sopsOverride.gcp_kms_resource_id !== \"string\") {\n throw new ManifestValidationError(\n `Environment '${envObj.name}' uses 'gcpkms' backend but is missing 'gcp_kms_resource_id'.`,\n \"environments\",\n );\n }\n if (backend === \"azurekv\" && typeof sopsOverride.azure_kv_url !== \"string\") {\n throw new ManifestValidationError(\n `Environment '${envObj.name}' uses 'azurekv' backend but is missing 'azure_kv_url'.`,\n \"environments\",\n );\n }\n if (backend === \"pgp\" && typeof sopsOverride.pgp_fingerprint !== \"string\") {\n throw new ManifestValidationError(\n `Environment '${envObj.name}' uses 'pgp' backend but is missing 'pgp_fingerprint'.`,\n \"environments\",\n );\n }\n\n result.sops = {\n backend,\n ...(typeof sopsOverride.aws_kms_arn === \"string\"\n ? { aws_kms_arn: sopsOverride.aws_kms_arn }\n : {}),\n ...(typeof sopsOverride.gcp_kms_resource_id === \"string\"\n ? { gcp_kms_resource_id: sopsOverride.gcp_kms_resource_id }\n : {}),\n ...(typeof sopsOverride.azure_kv_url === \"string\"\n ? { azure_kv_url: sopsOverride.azure_kv_url }\n : {}),\n ...(typeof sopsOverride.pgp_fingerprint === \"string\"\n ? { pgp_fingerprint: sopsOverride.pgp_fingerprint }\n : {}),\n };\n }\n\n // Parse optional per-environment recipients\n if (envObj.recipients !== undefined) {\n if (!Array.isArray(envObj.recipients)) {\n throw new ManifestValidationError(\n `Environment '${envObj.name}' has an invalid 'recipients' field. It must be an array.`,\n \"environments\",\n );\n }\n const parsedRecipients: (string | { key: string; label?: string })[] = [];\n for (let ri = 0; ri < envObj.recipients.length; ri++) {\n const entry = envObj.recipients[ri];\n if (typeof entry === \"string\") {\n const validation = validateAgePublicKey(entry);\n if (!validation.valid) {\n throw new ManifestValidationError(\n `Environment '${envObj.name}' recipient at index ${ri}: ${validation.error}`,\n \"environments\",\n );\n }\n parsedRecipients.push(validation.key!);\n } else if (typeof entry === \"object\" && entry !== null) {\n const entryObj = entry as Record<string, unknown>;\n if (typeof entryObj.key !== \"string\") {\n throw new ManifestValidationError(\n `Environment '${envObj.name}' recipient at index ${ri} must have a 'key' string.`,\n \"environments\",\n );\n }\n const validation = validateAgePublicKey(entryObj.key);\n if (!validation.valid) {\n throw new ManifestValidationError(\n `Environment '${envObj.name}' recipient at index ${ri}: ${validation.error}`,\n \"environments\",\n );\n }\n const recipientObj: { key: string; label?: string } = { key: validation.key! };\n if (typeof entryObj.label === \"string\") {\n recipientObj.label = entryObj.label;\n }\n parsedRecipients.push(recipientObj);\n } else {\n throw new ManifestValidationError(\n `Environment '${envObj.name}' recipient at index ${ri} must be a string or object.`,\n \"environments\",\n );\n }\n }\n if (parsedRecipients.length > 0) {\n result.recipients = parsedRecipients;\n }\n }\n\n return result;\n });\n\n // Check for duplicate environment names\n const envNames = new Set<string>();\n for (const env of environments) {\n if (envNames.has(env.name)) {\n throw new ManifestValidationError(\n `Duplicate environment name '${env.name}'. Each environment must have a unique name.`,\n \"environments\",\n );\n }\n envNames.add(env.name);\n }\n\n // namespaces\n // Design decision: all namespaces are encrypted. There is no `encrypted: false`\n // option on namespace definitions. This is intentional \u2014 see docs/guide/concepts.md\n // \"Design decision: all namespaces are encrypted\" for the full rationale.\n if (!obj.namespaces) {\n throw new ManifestValidationError(\n \"Missing required field 'namespaces'. Define at least one namespace.\",\n \"namespaces\",\n );\n }\n if (!Array.isArray(obj.namespaces) || obj.namespaces.length === 0) {\n throw new ManifestValidationError(\n \"Field 'namespaces' must be a non-empty array.\",\n \"namespaces\",\n );\n }\n const namespaces = obj.namespaces.map((ns: unknown, i: number) => {\n if (typeof ns !== \"object\" || ns === null) {\n throw new ManifestValidationError(\n `Namespace at index ${i} must be an object with 'name' and 'description'.`,\n \"namespaces\",\n );\n }\n const nsObj = ns as Record<string, unknown>;\n if (!nsObj.name || typeof nsObj.name !== \"string\") {\n throw new ManifestValidationError(\n `Namespace at index ${i} is missing a 'name' string.`,\n \"namespaces\",\n );\n }\n if (!nsObj.description || typeof nsObj.description !== \"string\") {\n throw new ManifestValidationError(\n `Namespace '${nsObj.name}' is missing a 'description' string.`,\n \"namespaces\",\n );\n }\n return {\n name: nsObj.name,\n description: nsObj.description,\n ...(typeof nsObj.schema === \"string\" ? { schema: nsObj.schema } : {}),\n ...(Array.isArray(nsObj.owners) ? { owners: nsObj.owners as string[] } : {}),\n };\n });\n\n // Check for duplicate namespace names\n const nsNames = new Set<string>();\n for (const ns of namespaces) {\n if (nsNames.has(ns.name)) {\n throw new ManifestValidationError(\n `Duplicate namespace name '${ns.name}'. Each namespace must have a unique name.`,\n \"namespaces\",\n );\n }\n nsNames.add(ns.name);\n }\n\n // sops\n if (!obj.sops) {\n throw new ManifestValidationError(\n \"Missing required field 'sops'. Configure at least 'default_backend'.\",\n \"sops\",\n );\n }\n if (typeof obj.sops !== \"object\" || obj.sops === null) {\n throw new ManifestValidationError(\"Field 'sops' must be an object.\", \"sops\");\n }\n const sopsObj = obj.sops as Record<string, unknown>;\n if (!sopsObj.default_backend || typeof sopsObj.default_backend !== \"string\") {\n throw new ManifestValidationError(\n \"Field 'sops.default_backend' is required and must be one of: age, awskms, gcpkms, azurekv, pgp.\",\n \"sops.default_backend\",\n );\n }\n if (!(VALID_BACKENDS as readonly string[]).includes(sopsObj.default_backend)) {\n throw new ManifestValidationError(\n `Invalid sops.default_backend '${sopsObj.default_backend}'. Must be one of: ${VALID_BACKENDS.join(\", \")}.`,\n \"sops.default_backend\",\n );\n }\n\n const sopsConfig = {\n default_backend: sopsObj.default_backend as (typeof VALID_BACKENDS)[number],\n ...(typeof sopsObj.aws_kms_arn === \"string\" ? { aws_kms_arn: sopsObj.aws_kms_arn } : {}),\n ...(typeof sopsObj.gcp_kms_resource_id === \"string\"\n ? { gcp_kms_resource_id: sopsObj.gcp_kms_resource_id }\n : {}),\n ...(typeof sopsObj.azure_kv_url === \"string\" ? { azure_kv_url: sopsObj.azure_kv_url } : {}),\n ...(typeof sopsObj.pgp_fingerprint === \"string\"\n ? { pgp_fingerprint: sopsObj.pgp_fingerprint }\n : {}),\n };\n\n // Post-processing: validate per-env recipients are only used with age backend\n for (const env of environments) {\n if (env.recipients && env.recipients.length > 0) {\n const effectiveBackend = env.sops?.backend ?? sopsConfig.default_backend;\n if (effectiveBackend !== \"age\") {\n throw new ManifestValidationError(\n `Environment '${env.name}' has per-environment recipients but uses '${effectiveBackend}' backend. Per-environment recipients are only supported with the 'age' backend.`,\n \"environments\",\n );\n }\n }\n }\n\n // file_pattern\n if (!obj.file_pattern || typeof obj.file_pattern !== \"string\") {\n throw new ManifestValidationError(\n \"Missing required field 'file_pattern'. Example: '{namespace}/{environment}.enc.yaml'.\",\n \"file_pattern\",\n );\n }\n for (const token of FILE_PATTERN_REQUIRED_TOKENS) {\n if (!obj.file_pattern.includes(token)) {\n throw new ManifestValidationError(\n `file_pattern must contain '${token}'. Got: '${obj.file_pattern}'.`,\n \"file_pattern\",\n );\n }\n }\n\n // service_identities (optional)\n let serviceIdentities: ServiceIdentityDefinition[] | undefined;\n if (obj.service_identities !== undefined) {\n if (!Array.isArray(obj.service_identities)) {\n throw new ManifestValidationError(\n \"Field 'service_identities' must be an array.\",\n \"service_identities\",\n );\n }\n serviceIdentities = obj.service_identities.map((si: unknown, i: number) => {\n if (typeof si !== \"object\" || si === null) {\n throw new ManifestValidationError(\n `Service identity at index ${i} must be an object.`,\n \"service_identities\",\n );\n }\n const siObj = si as Record<string, unknown>;\n\n if (!siObj.name || typeof siObj.name !== \"string\") {\n throw new ManifestValidationError(\n `Service identity at index ${i} is missing a 'name' string.`,\n \"service_identities\",\n );\n }\n const siName = siObj.name;\n\n if (!siObj.description || typeof siObj.description !== \"string\") {\n throw new ManifestValidationError(\n `Service identity '${siName}' is missing a 'description' string.`,\n \"service_identities\",\n );\n }\n\n // namespaces\n if (!Array.isArray(siObj.namespaces) || siObj.namespaces.length === 0) {\n throw new ManifestValidationError(\n `Service identity '${siName}' must have a non-empty 'namespaces' array.`,\n \"service_identities\",\n );\n }\n for (const ns of siObj.namespaces) {\n if (typeof ns !== \"string\") {\n throw new ManifestValidationError(\n `Service identity '${siName}' has a non-string entry in 'namespaces'.`,\n \"service_identities\",\n );\n }\n if (!nsNames.has(ns)) {\n throw new ManifestValidationError(\n `Service identity '${siName}' references unknown namespace '${ns}'.`,\n \"service_identities\",\n );\n }\n }\n\n // environments\n if (\n !siObj.environments ||\n typeof siObj.environments !== \"object\" ||\n Array.isArray(siObj.environments)\n ) {\n throw new ManifestValidationError(\n `Service identity '${siName}' must have an 'environments' object.`,\n \"service_identities\",\n );\n }\n const siEnvs = siObj.environments as Record<string, unknown>;\n const parsedEnvs: Record<string, ServiceIdentityEnvironmentConfig> = {};\n\n // Validate that all declared environments are covered\n for (const env of environments) {\n if (!(env.name in siEnvs)) {\n throw new ManifestValidationError(\n `Service identity '${siName}' is missing environment '${env.name}'. Service identities must cover all declared environments.`,\n \"service_identities\",\n );\n }\n }\n\n for (const [envName, envVal] of Object.entries(siEnvs)) {\n if (!envNames.has(envName)) {\n throw new ManifestValidationError(\n `Service identity '${siName}' references unknown environment '${envName}'.`,\n \"service_identities\",\n );\n }\n if (typeof envVal !== \"object\" || envVal === null) {\n throw new ManifestValidationError(\n `Service identity '${siName}' environment '${envName}' must be an object with 'recipient' or 'kms'.`,\n \"service_identities\",\n );\n }\n const envObj = envVal as Record<string, unknown>;\n const hasRecipient = envObj.recipient !== undefined;\n const hasKms = envObj.kms !== undefined;\n\n if (hasRecipient && hasKms) {\n throw new ManifestValidationError(\n `Service identity '${siName}' environment '${envName}': 'recipient' and 'kms' are mutually exclusive.`,\n \"service_identities\",\n );\n }\n if (!hasRecipient && !hasKms) {\n throw new ManifestValidationError(\n `Service identity '${siName}' environment '${envName}' must have either 'recipient' or 'kms'.`,\n \"service_identities\",\n );\n }\n\n if (hasRecipient) {\n if (typeof envObj.recipient !== \"string\") {\n throw new ManifestValidationError(\n `Service identity '${siName}' environment '${envName}': 'recipient' must be a string.`,\n \"service_identities\",\n );\n }\n const recipientValidation = validateAgePublicKey(envObj.recipient);\n if (!recipientValidation.valid) {\n throw new ManifestValidationError(\n `Service identity '${siName}' environment '${envName}': ${recipientValidation.error}`,\n \"service_identities\",\n );\n }\n parsedEnvs[envName] = { recipient: recipientValidation.key! };\n } else {\n const kmsObj = envObj.kms as Record<string, unknown>;\n if (typeof kmsObj !== \"object\" || kmsObj === null) {\n throw new ManifestValidationError(\n `Service identity '${siName}' environment '${envName}': 'kms' must be an object.`,\n \"service_identities\",\n );\n }\n const validProviders = [\"aws\", \"gcp\", \"azure\"];\n if (!kmsObj.provider || !validProviders.includes(kmsObj.provider as string)) {\n throw new ManifestValidationError(\n `Service identity '${siName}' environment '${envName}': kms.provider must be one of: ${validProviders.join(\", \")}.`,\n \"service_identities\",\n );\n }\n if (!kmsObj.keyId || typeof kmsObj.keyId !== \"string\") {\n throw new ManifestValidationError(\n `Service identity '${siName}' environment '${envName}': kms.keyId must be a non-empty string.`,\n \"service_identities\",\n );\n }\n parsedEnvs[envName] = {\n kms: {\n provider: kmsObj.provider as \"aws\" | \"gcp\" | \"azure\",\n keyId: kmsObj.keyId,\n region: typeof kmsObj.region === \"string\" ? kmsObj.region : undefined,\n },\n };\n }\n }\n\n return {\n name: siName,\n description: siObj.description as string,\n namespaces: siObj.namespaces as string[],\n environments: parsedEnvs,\n };\n });\n\n // Check for duplicate identity names\n const siNames = new Set<string>();\n for (const si of serviceIdentities) {\n if (siNames.has(si.name)) {\n throw new ManifestValidationError(\n `Duplicate service identity name '${si.name}'.`,\n \"service_identities\",\n );\n }\n siNames.add(si.name);\n }\n }\n\n // cloud (optional)\n let cloud: ClefCloudConfig | undefined;\n if (obj.cloud !== undefined) {\n if (typeof obj.cloud !== \"object\" || obj.cloud === null || Array.isArray(obj.cloud)) {\n throw new ManifestValidationError(\"Field 'cloud' must be an object.\", \"cloud\");\n }\n const cloudObj = obj.cloud as Record<string, unknown>;\n if (typeof cloudObj.integrationId !== \"string\" || cloudObj.integrationId.length === 0) {\n throw new ManifestValidationError(\n \"Field 'cloud.integrationId' is required and must be a non-empty string.\",\n \"cloud\",\n );\n }\n cloud = { integrationId: cloudObj.integrationId };\n }\n\n return {\n version: 1,\n environments,\n namespaces,\n sops: sopsConfig,\n file_pattern: obj.file_pattern,\n ...(serviceIdentities ? { service_identities: serviceIdentities } : {}),\n ...(cloud ? { cloud } : {}),\n };\n }\n\n /**\n * Watch a manifest file for changes and invoke a callback on each successful parse.\n *\n * @param filePath - Path to the manifest file to watch.\n * @param onChange - Called with the newly parsed manifest on each valid change.\n * @returns Unsubscribe function \u2014 call it to stop watching.\n */\n watch(filePath: string, onChange: (manifest: ClefManifest) => void): () => void {\n const watcher = fs.watch(filePath, () => {\n try {\n const manifest = this.parse(filePath);\n onChange(manifest);\n } catch {\n // Ignore parse errors during watch \u2014 file may be mid-save\n }\n });\n\n return () => {\n watcher.close();\n };\n }\n}\n", "export interface AgeKeyValidation {\n valid: boolean;\n key?: string;\n error?: string;\n}\n\nconst BECH32_CHARSET = \"qpzry9x8gf2tvdw0s3jn54khce6mua7l\";\nconst AGE_PREFIX = \"age1\";\nconst MIN_LENGTH = 10;\n\n/**\n * Validate that a string is a well-formed age public key (bech32, `age1` prefix).\n *\n * @param input - The string to validate.\n * @returns `{ valid: true, key: trimmedKey }` or `{ valid: false, error: message }`.\n */\nexport function validateAgePublicKey(input: string): AgeKeyValidation {\n const trimmed = input.trim();\n\n if (!trimmed.startsWith(AGE_PREFIX)) {\n return {\n valid: false,\n error: `age public key must start with '${AGE_PREFIX}'. Got: '${trimmed.slice(0, 10)}...'`,\n };\n }\n\n if (trimmed.length < MIN_LENGTH) {\n return {\n valid: false,\n error: `age public key is too short. Expected at least ${MIN_LENGTH} characters, got ${trimmed.length}.`,\n };\n }\n\n const body = trimmed.slice(AGE_PREFIX.length);\n for (const ch of body) {\n if (!BECH32_CHARSET.includes(ch)) {\n return {\n valid: false,\n error: `Invalid character '${ch}' in age public key. Only bech32 characters are allowed after the 'age1' prefix.`,\n };\n }\n }\n\n return { valid: true, key: trimmed };\n}\n\n/**\n * Return a short display preview of an age public key: `age1\u2026last8chars`.\n *\n * @param key - Full age public key string.\n */\nexport function keyPreview(key: string): string {\n const last8 = key.slice(-8);\n return `age1\\u2026${last8}`;\n}\n", "import * as fs from \"fs\";\nimport * as path from \"path\";\nimport type { ClefManifest, SubprocessRunner } from \"../types\";\nimport { matchPatterns, isHighEntropy, shannonEntropy, redactValue, ScanMatch } from \"./patterns\";\nimport { loadIgnoreRules, shouldIgnoreFile, shouldIgnoreMatch } from \"./ignore\";\n\nexport type { ScanMatch } from \"./patterns\";\nexport type { ClefIgnoreRules } from \"./ignore\";\nexport { shannonEntropy, isHighEntropy, matchPatterns, redactValue } from \"./patterns\";\nexport { loadIgnoreRules, shouldIgnoreFile, shouldIgnoreMatch, parseIgnoreContent } from \"./ignore\";\n\nexport interface ScanResult {\n matches: ScanMatch[];\n filesScanned: number;\n filesSkipped: number;\n unencryptedMatrixFiles: string[];\n durationMs: number;\n}\n\nexport interface ScanOptions {\n stagedOnly?: boolean;\n paths?: string[];\n severity?: \"all\" | \"high\";\n}\n\nconst ALWAYS_SKIP_EXTENSIONS = [\".enc.yaml\", \".enc.json\"] as const;\nconst ALWAYS_SKIP_NAMES = [\".clef-meta.yaml\"] as const;\nconst ALWAYS_SKIP_DIRS = [\"node_modules\", \".git\"] as const;\nconst MAX_FILE_SIZE = 1024 * 1024; // 1 MB\n\n/**\n * Scans repository files for plaintext secrets using pattern matching and entropy detection.\n *\n * @example\n * ```ts\n * const scanner = new ScanRunner(runner);\n * const result = await scanner.scan(repoRoot, manifest, { stagedOnly: true });\n * ```\n */\nexport class ScanRunner {\n constructor(private readonly runner: SubprocessRunner) {}\n\n /**\n * Scan tracked (or staged) files for secret-like values and unencrypted matrix files.\n *\n * The scan respects `.clefignore` rules and inline `# clef-ignore` suppressions.\n *\n * @param repoRoot - Absolute path to the repository root.\n * @param manifest - Parsed manifest used to identify matrix file paths.\n * @param options - Optional scan filters.\n */\n async scan(\n repoRoot: string,\n manifest: ClefManifest,\n options: ScanOptions = {},\n ): Promise<ScanResult> {\n const startMs = Date.now();\n const ignoreRules = loadIgnoreRules(repoRoot);\n const matches: ScanMatch[] = [];\n const unencryptedMatrixFiles: string[] = [];\n let filesScanned = 0;\n let filesSkipped = 0;\n\n // \u2500\u2500 Check 1: unencrypted matrix files \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n for (const ns of manifest.namespaces) {\n for (const env of manifest.environments) {\n const relPath = manifest.file_pattern\n .replace(\"{namespace}\", ns.name)\n .replace(\"{environment}\", env.name);\n const absPath = path.join(repoRoot, relPath);\n if (fs.existsSync(absPath)) {\n const content = fs.readFileSync(absPath, \"utf-8\");\n if (!content.includes(\"sops:\") && !content.includes('\"sops\"')) {\n unencryptedMatrixFiles.push(relPath);\n }\n }\n }\n }\n\n // \u2500\u2500 Determine files to scan \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n let filesToScan: string[];\n if (options.stagedOnly) {\n filesToScan = await this.getStagedFiles(repoRoot);\n } else if (options.paths && options.paths.length > 0) {\n filesToScan = await this.getFilesInPaths(repoRoot, options.paths);\n } else {\n filesToScan = await this.getAllTrackedFiles(repoRoot);\n }\n\n // \u2500\u2500 Check 2: secret-looking values \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n for (const relFile of filesToScan) {\n const absFile = path.isAbsolute(relFile) ? relFile : path.join(repoRoot, relFile);\n const relPath = path.relative(repoRoot, absFile).replace(/\\\\/g, \"/\");\n\n if (this.shouldAlwaysSkip(relPath)) {\n filesSkipped++;\n continue;\n }\n\n if (shouldIgnoreFile(relPath, ignoreRules)) {\n filesSkipped++;\n continue;\n }\n\n if (!fs.existsSync(absFile)) {\n filesSkipped++;\n continue;\n }\n\n let stat: fs.Stats;\n try {\n stat = fs.statSync(absFile);\n } catch {\n filesSkipped++;\n continue;\n }\n\n if (stat.size > MAX_FILE_SIZE) {\n filesSkipped++;\n continue;\n }\n\n if (this.isBinary(absFile)) {\n filesSkipped++;\n continue;\n }\n\n filesScanned++;\n const content = fs.readFileSync(absFile, \"utf-8\");\n const lines = content.split(\"\\n\");\n\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i];\n const lineNum = i + 1;\n\n // Inline suppress: # clef-ignore on the same line\n if (line.includes(\"# clef-ignore\")) continue;\n\n // Pattern matching\n const patternHits = matchPatterns(line, lineNum, relPath);\n for (const m of patternHits) {\n if (!shouldIgnoreMatch(m, ignoreRules)) {\n matches.push(m);\n }\n }\n\n // Entropy detection (skip when severity === 'high')\n if (options.severity !== \"high\") {\n const entropyHit = this.detectEntropy(line, lineNum, relPath);\n if (entropyHit && !shouldIgnoreMatch(entropyHit, ignoreRules)) {\n matches.push(entropyHit);\n }\n }\n }\n }\n\n return {\n matches,\n filesScanned,\n filesSkipped,\n unencryptedMatrixFiles,\n durationMs: Date.now() - startMs,\n };\n }\n\n private shouldAlwaysSkip(relPath: string): boolean {\n for (const dir of ALWAYS_SKIP_DIRS) {\n if (relPath === dir || relPath.startsWith(dir + \"/\")) return true;\n }\n for (const ext of ALWAYS_SKIP_EXTENSIONS) {\n if (relPath.endsWith(ext)) return true;\n }\n for (const name of ALWAYS_SKIP_NAMES) {\n if (relPath.endsWith(name)) return true;\n }\n return false;\n }\n\n private isBinary(filePath: string): boolean {\n try {\n const fd = fs.openSync(filePath, \"r\");\n const buf = Buffer.alloc(512);\n const bytesRead = fs.readSync(fd, buf, 0, 512, 0);\n fs.closeSync(fd);\n for (let i = 0; i < bytesRead; i++) {\n if (buf[i] === 0) return true;\n }\n return false;\n } catch {\n return false;\n }\n }\n\n private detectEntropy(line: string, lineNum: number, filePath: string): ScanMatch | null {\n // Look for values appearing after = or : (assignment positions)\n const valuePattern = /(?:=|:\\s*)[\"']?([A-Za-z0-9+/=_-]{20,})[\"']?/;\n const match = valuePattern.exec(line);\n if (!match) return null;\n\n const value = match[1];\n const entropy = shannonEntropy(value);\n\n if (!isHighEntropy(value)) return null;\n\n // Extract variable name for the preview\n const varMatch = /(\\w+)\\s*(?:=|:)/.exec(line);\n const varName = varMatch ? varMatch[1] : \"\";\n const preview = varName\n ? `${varName}=\\u2022\\u2022\\u2022\\u2022\\u2022\\u2022\\u2022\\u2022`\n : redactValue(value);\n\n return {\n file: filePath,\n line: lineNum,\n column: match.index + 1,\n matchType: \"entropy\",\n entropy,\n preview,\n };\n }\n\n private async getStagedFiles(repoRoot: string): Promise<string[]> {\n const result = await this.runner.run(\n \"git\",\n [\"diff\", \"--cached\", \"--name-only\", \"--diff-filter=ACM\"],\n { cwd: repoRoot },\n );\n if (result.exitCode !== 0 || !result.stdout.trim()) return [];\n return result.stdout.trim().split(\"\\n\");\n }\n\n private async getFilesInPaths(repoRoot: string, paths: string[]): Promise<string[]> {\n const files: string[] = [];\n for (const p of paths) {\n const absPath = path.isAbsolute(p) ? p : path.join(repoRoot, p);\n if (!fs.existsSync(absPath)) continue;\n const stat = fs.statSync(absPath);\n if (stat.isDirectory()) {\n files.push(...this.walkDir(absPath, repoRoot));\n } else {\n files.push(path.relative(repoRoot, absPath).replace(/\\\\/g, \"/\"));\n }\n }\n return files;\n }\n\n private async getAllTrackedFiles(repoRoot: string): Promise<string[]> {\n // Use git ls-files to respect .gitignore automatically\n const result = await this.runner.run(\"git\", [\"ls-files\"], { cwd: repoRoot });\n if (result.exitCode !== 0) {\n return this.walkDir(repoRoot, repoRoot);\n }\n return result.stdout.trim() ? result.stdout.trim().split(\"\\n\") : [];\n }\n\n private walkDir(dir: string, repoRoot: string): string[] {\n const files: string[] = [];\n let entries: fs.Dirent[];\n try {\n entries = fs.readdirSync(dir, { withFileTypes: true });\n } catch {\n return files;\n }\n for (const entry of entries) {\n const fullPath = path.join(dir, entry.name);\n const relPath = path.relative(repoRoot, fullPath).replace(/\\\\/g, \"/\");\n if (entry.isDirectory()) {\n if (!(ALWAYS_SKIP_DIRS as readonly string[]).includes(entry.name)) {\n files.push(...this.walkDir(fullPath, repoRoot));\n }\n } else {\n files.push(relPath);\n }\n }\n return files;\n }\n}\n", "/**\n * TIER 1 MODULE \u2014 Security and correctness critical.\n *\n * This module requires exhaustive test coverage. Before\n * adding or modifying code here:\n * 1. Add tests for the happy path\n * 2. Add tests for all documented error paths\n * 3. Add at least one boundary/edge case test\n *\n * Coverage threshold: 95% lines/functions, 90% branches.\n * See docs/contributing/testing.md for the rationale.\n */\nexport interface ScanMatch {\n file: string;\n line: number;\n column: number;\n matchType: \"pattern\" | \"entropy\";\n patternName?: string;\n entropy?: number;\n preview: string;\n}\n\ninterface PatternDef {\n name: string;\n regex: RegExp;\n}\n\nconst PATTERNS: PatternDef[] = [\n { name: \"AWS access key\", regex: /AKIA[0-9A-Z]{16}/ },\n { name: \"Stripe live key\", regex: /sk_live_[0-9a-zA-Z]{24,}/ },\n { name: \"Stripe test key\", regex: /sk_test_[0-9a-zA-Z]{24,}/ },\n { name: \"GitHub personal access token\", regex: /ghp_[0-9a-zA-Z]{36}/ },\n { name: \"GitHub OAuth token\", regex: /gho_[0-9a-zA-Z]{36}/ },\n { name: \"GitHub Actions token\", regex: /ghs_[0-9a-zA-Z]{36}/ },\n { name: \"Slack token\", regex: /xox[baprs]-[0-9a-zA-Z-]{10,}/ },\n {\n name: \"Private key header\",\n regex: /-----BEGIN (?:RSA |EC |DSA |OPENSSH )?PRIVATE KEY-----/,\n },\n {\n name: \"Generic API key\",\n regex: /(?:API_KEY|SECRET_KEY|ACCESS_TOKEN|AUTH_TOKEN)\\s*=\\s*\\S{8,}/,\n },\n { name: \"Database URL\", regex: /(?:postgres|mysql|mongodb|redis):\\/\\/[^:]+:[^@]+@/ },\n];\n\n/**\n * Calculate Shannon entropy (bits per character) of a string.\n */\nexport function shannonEntropy(str: string): number {\n if (str.length === 0) return 0;\n const freq = new Map<string, number>();\n for (const c of str) {\n freq.set(c, (freq.get(c) ?? 0) + 1);\n }\n let entropy = 0;\n for (const count of freq.values()) {\n const p = count / str.length;\n entropy -= p * Math.log2(p);\n }\n return entropy;\n}\n\n/**\n * Returns true if a string has sufficiently high entropy to be considered a potential secret.\n * Threshold: > 4.5 bits/char, minimum 20 characters.\n */\nexport function isHighEntropy(value: string, threshold = 4.5, minLength = 20): boolean {\n return value.length >= minLength && shannonEntropy(value) > threshold;\n}\n\n/**\n * Redact a matched secret value \u2014 show first 4 characters, mask the rest.\n * Never exposes more than 4 characters of any secret.\n */\nexport function redactValue(value: string): string {\n if (value.length <= 4) return \"\\u2022\\u2022\\u2022\\u2022\\u2022\\u2022\\u2022\\u2022\";\n return value.slice(0, 4) + \"\\u2022\\u2022\\u2022\\u2022\\u2022\\u2022\\u2022\\u2022\";\n}\n\n/**\n * Match a line against all known secret patterns.\n * Returns one ScanMatch per matched pattern.\n */\nexport function matchPatterns(line: string, lineNumber: number, filePath: string): ScanMatch[] {\n const matches: ScanMatch[] = [];\n for (const { name, regex } of PATTERNS) {\n const match = regex.exec(line);\n if (match) {\n matches.push({\n file: filePath,\n line: lineNumber,\n column: match.index + 1,\n matchType: \"pattern\",\n patternName: name,\n preview: redactValue(match[0]),\n });\n }\n }\n return matches;\n}\n", "import * as fs from \"fs\";\nimport * as path from \"path\";\nimport type { ScanMatch } from \"./patterns\";\n\nexport interface ClefIgnoreRules {\n files: string[];\n patterns: string[];\n paths: string[];\n}\n\n/**\n * Load .clefignore rules from the repo root.\n * Returns empty rules if the file does not exist.\n */\nexport function loadIgnoreRules(repoRoot: string): ClefIgnoreRules {\n const ignorePath = path.join(repoRoot, \".clefignore\");\n try {\n const content = fs.readFileSync(ignorePath, \"utf-8\");\n return parseIgnoreContent(content);\n } catch {\n return { files: [], patterns: [], paths: [] };\n }\n}\n\n/**\n * Parse raw `.clefignore` content into structured rules.\n * Lines starting with `ignore-pattern:` suppress named patterns; lines ending with `/`\n * suppress entire directory paths; all other lines are treated as file glob patterns.\n *\n * @param content - Raw `.clefignore` file content.\n */\nexport function parseIgnoreContent(content: string): ClefIgnoreRules {\n const files: string[] = [];\n const patterns: string[] = [];\n const paths: string[] = [];\n\n for (const rawLine of content.split(\"\\n\")) {\n const line = rawLine.trim();\n if (!line || line.startsWith(\"#\")) continue;\n\n if (line.startsWith(\"ignore-pattern:\")) {\n const patternName = line.slice(\"ignore-pattern:\".length).trim();\n if (patternName) patterns.push(patternName);\n } else if (line.endsWith(\"/\")) {\n paths.push(line.slice(0, -1));\n } else {\n files.push(line);\n }\n }\n\n return { files, patterns, paths };\n}\n\n/**\n * Returns true if a file path should be ignored per .clefignore rules.\n */\nexport function shouldIgnoreFile(filePath: string, rules: ClefIgnoreRules): boolean {\n const normalized = filePath.replace(/\\\\/g, \"/\");\n\n for (const p of rules.paths) {\n const dir = p.replace(/\\\\/g, \"/\");\n if (normalized === dir || normalized.startsWith(dir + \"/\")) return true;\n }\n\n for (const pattern of rules.files) {\n if (matchesGlob(normalized, pattern)) return true;\n }\n\n return false;\n}\n\n/**\n * Returns true if a scan match should be suppressed per .clefignore rules.\n */\nexport function shouldIgnoreMatch(match: ScanMatch, rules: ClefIgnoreRules): boolean {\n if (match.matchType === \"pattern\" && match.patternName) {\n return rules.patterns.includes(match.patternName);\n }\n return false;\n}\n\nfunction matchesGlob(filePath: string, pattern: string): boolean {\n // Convert glob to regex: support *, **, and ? wildcards.\n // Step 1: stash ** segments, Step 2: escape all regex metacharacters,\n // Step 3: restore wildcards as their regex equivalents.\n const DOUBLE_STAR = \"\\x00DS\\x00\";\n const SINGLE_STAR = \"\\x00SS\\x00\";\n const QUESTION = \"\\x00QM\\x00\";\n\n const escaped = pattern\n .replace(/\\*\\*/g, DOUBLE_STAR)\n .replace(/\\*/g, SINGLE_STAR)\n .replace(/\\?/g, QUESTION)\n .replace(/[.+^${}()|[\\]\\\\]/g, \"\\\\$&\")\n .replace(DOUBLE_STAR, \".*\")\n .replace(SINGLE_STAR, \"[^/]*\")\n .replace(QUESTION, \"[^/]\");\n\n const regex = new RegExp(\"^\" + escaped + \"$\");\n // Also match if the pattern matches a prefix directory\n const prefixRegex = new RegExp(\"^\" + escaped + \"/\");\n return regex.test(filePath) || prefixRegex.test(filePath);\n}\n", "import * as fs from \"fs\";\nimport * as path from \"path\";\nimport * as YAML from \"yaml\";\nimport { ClefManifest, MatrixCell, MatrixIssue, MatrixStatus } from \"../types\";\nimport { EncryptionBackend } from \"../types\";\nimport { getPendingKeys } from \"../pending/metadata\";\n\n/**\n * Resolves and manages the namespace \u00D7 environment matrix of encrypted files.\n *\n * @example\n * ```ts\n * const manager = new MatrixManager();\n * const cells = manager.resolveMatrix(manifest, repoRoot);\n * ```\n */\nexport class MatrixManager {\n /**\n * Build the full grid of {@link MatrixCell} objects from the manifest.\n * Each cell reflects whether its encrypted file exists on disk.\n *\n * @param manifest - Parsed manifest.\n * @param repoRoot - Absolute path to the repository root.\n */\n resolveMatrix(manifest: ClefManifest, repoRoot: string): MatrixCell[] {\n const cells: MatrixCell[] = [];\n\n for (const ns of manifest.namespaces) {\n for (const env of manifest.environments) {\n const relativePath = manifest.file_pattern\n .replace(\"{namespace}\", ns.name)\n .replace(\"{environment}\", env.name);\n const filePath = path.join(repoRoot, relativePath);\n\n cells.push({\n namespace: ns.name,\n environment: env.name,\n filePath,\n exists: fs.existsSync(filePath),\n });\n }\n }\n\n return cells;\n }\n\n /**\n * Return only the cells whose encrypted files do not yet exist on disk.\n *\n * @param manifest - Parsed manifest.\n * @param repoRoot - Absolute path to the repository root.\n */\n detectMissingCells(manifest: ClefManifest, repoRoot: string): MatrixCell[] {\n return this.resolveMatrix(manifest, repoRoot).filter((cell) => !cell.exists);\n }\n\n /**\n * Create an empty encrypted SOPS file for a missing matrix cell.\n *\n * @param cell - The cell to scaffold (must not already exist).\n * @param sopsClient - SOPS client used to write the initial encrypted file.\n * @param manifest - Parsed manifest used to determine the encryption backend.\n */\n async scaffoldCell(\n cell: MatrixCell,\n sopsClient: EncryptionBackend,\n manifest: ClefManifest,\n ): Promise<void> {\n const dir = path.dirname(cell.filePath);\n if (!fs.existsSync(dir)) {\n fs.mkdirSync(dir, { recursive: true });\n }\n\n await sopsClient.encrypt(cell.filePath, {}, manifest, cell.environment);\n }\n\n /**\n * Decrypt each cell and return key counts, pending counts, and cross-environment issues.\n *\n * @param manifest - Parsed manifest.\n * @param repoRoot - Absolute path to the repository root.\n * @param sopsClient - SOPS client used to decrypt each cell.\n */\n async getMatrixStatus(\n manifest: ClefManifest,\n repoRoot: string,\n _sopsClient: EncryptionBackend,\n ): Promise<MatrixStatus[]> {\n const cells = this.resolveMatrix(manifest, repoRoot);\n const statuses: MatrixStatus[] = [];\n\n // First pass: read key names from plaintext YAML (no decryption)\n const cellKeys = new Map<string, string[]>();\n for (const cell of cells) {\n if (cell.exists) {\n cellKeys.set(cell.filePath, this.readKeyNames(cell.filePath));\n }\n }\n\n for (const cell of cells) {\n if (!cell.exists) {\n statuses.push({\n cell,\n keyCount: 0,\n pendingCount: 0,\n lastModified: null,\n issues: [\n {\n type: \"missing_keys\",\n message: `File '${cell.filePath}' does not exist. Run 'clef init' to scaffold missing files.`,\n },\n ],\n });\n continue;\n }\n\n // Read pending count from metadata (plaintext, no decryption needed)\n let pendingCount = 0;\n try {\n const pending = await getPendingKeys(cell.filePath);\n pendingCount = pending.length;\n } catch {\n // Metadata file missing or unreadable \u2014 0 pending\n }\n\n const keys = cellKeys.get(cell.filePath) ?? [];\n const keyCount = keys.length;\n const lastModified = this.readLastModified(cell.filePath);\n const issues: MatrixIssue[] = [];\n\n // Cross-environment key drift (using plaintext key names, no decrypt)\n const siblingCells = cells.filter(\n (c) => c.namespace === cell.namespace && c.environment !== cell.environment && c.exists,\n );\n for (const sibling of siblingCells) {\n const siblingKeys = cellKeys.get(sibling.filePath) ?? [];\n const missingKeys = siblingKeys.filter((k) => !keys.includes(k));\n for (const mk of missingKeys) {\n issues.push({\n type: \"missing_keys\",\n message: `Key '${mk}' exists in ${sibling.environment} but is missing here.`,\n key: mk,\n });\n }\n }\n\n statuses.push({ cell, keyCount, pendingCount, lastModified, issues });\n }\n\n return statuses;\n }\n\n /**\n * Read top-level key names from a SOPS file without decryption.\n * SOPS stores key names in plaintext \u2014 only values are encrypted.\n */\n private readKeyNames(filePath: string): string[] {\n try {\n const raw = fs.readFileSync(filePath, \"utf-8\");\n const parsed = YAML.parse(raw);\n if (!parsed || typeof parsed !== \"object\") return [];\n return Object.keys(parsed as Record<string, unknown>).filter((k) => k !== \"sops\");\n } catch {\n return [];\n }\n }\n\n /**\n * Read the lastModified timestamp from SOPS metadata without decryption.\n */\n private readLastModified(filePath: string): Date | null {\n try {\n const raw = fs.readFileSync(filePath, \"utf-8\");\n const parsed = YAML.parse(raw) as Record<string, unknown>;\n const sops = parsed?.sops as Record<string, unknown> | undefined;\n if (sops?.lastmodified) return new Date(String(sops.lastmodified));\n return null;\n } catch {\n return null;\n }\n }\n\n /**\n * Check whether an environment has the `protected` flag set in the manifest.\n *\n * @param manifest - Parsed manifest.\n * @param environment - Environment name to check.\n */\n isProtectedEnvironment(manifest: ClefManifest, environment: string): boolean {\n const env = manifest.environments.find((e) => e.name === environment);\n return env?.protected === true;\n }\n}\n", "/**\n * TIER 1 MODULE \u2014 Security and correctness critical.\n *\n * This module requires exhaustive test coverage. Before\n * adding or modifying code here:\n * 1. Add tests for the happy path\n * 2. Add tests for all documented error paths\n * 3. Add at least one boundary/edge case test\n *\n * Coverage threshold: 95% lines/functions, 90% branches.\n * See docs/contributing/testing.md for the rationale.\n */\nimport * as fs from \"fs\";\nimport * as path from \"path\";\nimport * as crypto from \"crypto\";\nimport * as YAML from \"yaml\";\n\ninterface PendingKey {\n key: string;\n since: Date;\n setBy: string;\n}\n\ninterface PendingMetadata {\n version: 1;\n pending: PendingKey[];\n}\n\n/**\n * Derive the `.clef-meta.yaml` path from an `.enc.yaml` path.\n * Example: `database/dev.enc.yaml` \u2192 `database/dev.clef-meta.yaml`\n */\nfunction metadataPath(encryptedFilePath: string): string {\n const dir = path.dirname(encryptedFilePath);\n const base = path.basename(encryptedFilePath).replace(/\\.enc\\.(yaml|json)$/, \"\");\n return path.join(dir, `${base}.clef-meta.yaml`);\n}\n\nconst HEADER_COMMENT = \"# Managed by Clef. Do not edit manually.\\n\";\n\n/** Load pending-key metadata for an encrypted file. Returns empty metadata if the file is missing. */\nasync function loadMetadata(filePath: string): Promise<PendingMetadata> {\n const metaPath = metadataPath(filePath);\n try {\n if (!fs.existsSync(metaPath)) {\n return { version: 1, pending: [] };\n }\n const content = fs.readFileSync(metaPath, \"utf-8\");\n const parsed = YAML.parse(content);\n if (!parsed || !Array.isArray(parsed.pending)) {\n return { version: 1, pending: [] };\n }\n return {\n version: 1,\n pending: parsed.pending.map((p: { key: string; since: string; setBy: string }) => ({\n key: p.key,\n since: new Date(p.since),\n setBy: p.setBy,\n })),\n };\n } catch {\n return { version: 1, pending: [] };\n }\n}\n\n/** Write pending-key metadata to disk. Creates parent directories if needed. */\nasync function saveMetadata(filePath: string, metadata: PendingMetadata): Promise<void> {\n const metaPath = metadataPath(filePath);\n const dir = path.dirname(metaPath);\n if (!fs.existsSync(dir)) {\n fs.mkdirSync(dir, { recursive: true });\n }\n const data = {\n version: metadata.version,\n pending: metadata.pending.map((p) => ({\n key: p.key,\n since: p.since.toISOString(),\n setBy: p.setBy,\n })),\n };\n fs.writeFileSync(metaPath, HEADER_COMMENT + YAML.stringify(data), \"utf-8\");\n}\n\n/**\n * Mark one or more keys as pending (placeholder value) for an encrypted file.\n * If a key is already pending, its timestamp and `setBy` are updated.\n *\n * @param filePath - Path to the encrypted file.\n * @param keys - Key names to mark as pending.\n * @param setBy - Identifier of the actor setting these keys (e.g. a username or CI job).\n */\nasync function markPending(filePath: string, keys: string[], setBy: string): Promise<void> {\n const metadata = await loadMetadata(filePath);\n const now = new Date();\n for (const key of keys) {\n const existing = metadata.pending.findIndex((p) => p.key === key);\n if (existing >= 0) {\n // Upsert: update timestamp and setBy on re-randomization\n metadata.pending[existing] = { key, since: now, setBy };\n } else {\n metadata.pending.push({ key, since: now, setBy });\n }\n }\n await saveMetadata(filePath, metadata);\n}\n\n/** Remove keys from the pending list after they have received real values. */\nasync function markResolved(filePath: string, keys: string[]): Promise<void> {\n const metadata = await loadMetadata(filePath);\n metadata.pending = metadata.pending.filter((p) => !keys.includes(p.key));\n await saveMetadata(filePath, metadata);\n}\n\n/** Return the list of key names that are still pending for the given encrypted file. */\nasync function getPendingKeys(filePath: string): Promise<string[]> {\n const metadata = await loadMetadata(filePath);\n return metadata.pending.map((p) => p.key);\n}\n\n/** Check whether a single key is currently pending for the given encrypted file. */\nasync function isPending(filePath: string, key: string): Promise<boolean> {\n const metadata = await loadMetadata(filePath);\n return metadata.pending.some((p) => p.key === key);\n}\n\n/** Generate a cryptographically random 64-character hex string for use as a placeholder value. */\nfunction generateRandomValue(): string {\n return crypto.randomBytes(32).toString(\"hex\");\n}\n\n/**\n * Same as {@link markPending} but retries once after `retryDelayMs` on transient failure.\n *\n * @param filePath - Path to the encrypted file.\n * @param keys - Key names to mark as pending.\n * @param setBy - Identifier of the actor setting these keys.\n * @param retryDelayMs - Delay in milliseconds before the single retry (default: 200).\n */\nasync function markPendingWithRetry(\n filePath: string,\n keys: string[],\n setBy: string,\n retryDelayMs = 200,\n): Promise<void> {\n try {\n await markPending(filePath, keys, setBy);\n } catch {\n // One retry after short delay for transient failures\n await new Promise((r) => setTimeout(r, retryDelayMs));\n await markPending(filePath, keys, setBy);\n }\n}\n\nexport {\n PendingKey,\n PendingMetadata,\n metadataPath,\n loadMetadata,\n saveMetadata,\n markPending,\n markPendingWithRetry,\n markResolved,\n getPendingKeys,\n isPending,\n generateRandomValue,\n};\n", "import * as fs from \"fs\";\nimport * as YAML from \"yaml\";\nimport {\n NamespaceSchema,\n SchemaLoadError,\n ValidationError,\n ValidationResult,\n ValidationWarning,\n} from \"../types\";\n\n/**\n * Loads namespace schemas and validates decrypted key/value maps against them.\n *\n * @example\n * ```ts\n * const validator = new SchemaValidator();\n * const schema = validator.loadSchema(\"schemas/app.yaml\");\n * const result = validator.validate(decrypted.values, schema);\n * ```\n */\nexport class SchemaValidator {\n /**\n * Read and parse a YAML schema file from disk.\n *\n * @param filePath - Path to the schema YAML file.\n * @returns Parsed {@link NamespaceSchema}.\n * @throws {@link SchemaLoadError} If the file cannot be read or contains invalid YAML.\n */\n loadSchema(filePath: string): NamespaceSchema {\n let raw: string;\n try {\n raw = fs.readFileSync(filePath, \"utf-8\");\n } catch {\n throw new SchemaLoadError(`Could not read schema file at '${filePath}'.`, filePath);\n }\n\n let parsed: unknown;\n try {\n parsed = YAML.parse(raw);\n } catch {\n throw new SchemaLoadError(`Schema file '${filePath}' contains invalid YAML.`, filePath);\n }\n\n if (!parsed || typeof parsed !== \"object\") {\n throw new SchemaLoadError(\n `Schema file '${filePath}' must be a YAML object with a 'keys' map.`,\n filePath,\n );\n }\n\n const obj = parsed as Record<string, unknown>;\n if (!obj.keys || typeof obj.keys !== \"object\") {\n throw new SchemaLoadError(\n `Schema file '${filePath}' is missing the required 'keys' map.`,\n filePath,\n );\n }\n\n const keys: NamespaceSchema[\"keys\"] = {};\n const keysObj = obj.keys as Record<string, unknown>;\n\n for (const [keyName, keyDef] of Object.entries(keysObj)) {\n if (!keyDef || typeof keyDef !== \"object\") {\n throw new SchemaLoadError(\n `Schema key '${keyName}' must be an object with at least 'type' and 'required'.`,\n filePath,\n );\n }\n\n const def = keyDef as Record<string, unknown>;\n const type = def.type as string;\n if (![\"string\", \"integer\", \"boolean\"].includes(type)) {\n throw new SchemaLoadError(\n `Schema key '${keyName}' has invalid type '${type}'. Must be 'string', 'integer', or 'boolean'.`,\n filePath,\n );\n }\n\n keys[keyName] = {\n type: type as \"string\" | \"integer\" | \"boolean\",\n required: def.required === true,\n ...(typeof def.pattern === \"string\" ? { pattern: def.pattern } : {}),\n ...(def.default !== undefined ? { default: def.default } : {}),\n ...(typeof def.description === \"string\" ? { description: def.description } : {}),\n ...(typeof def.max === \"number\" ? { max: def.max } : {}),\n };\n }\n\n return { keys };\n }\n\n /**\n * Validate a set of decrypted values against a loaded namespace schema.\n *\n * @param values - Flat key/value map from a decrypted SOPS file.\n * @param schema - Schema loaded via {@link loadSchema}.\n * @returns {@link ValidationResult} with `valid: false` when any errors are present.\n */\n validate(values: Record<string, string>, schema: NamespaceSchema): ValidationResult {\n const errors: ValidationError[] = [];\n const warnings: ValidationWarning[] = [];\n\n // Check required keys and type/pattern validation\n for (const [keyName, keyDef] of Object.entries(schema.keys)) {\n const value = values[keyName];\n\n if (value === undefined || value === null) {\n if (keyDef.required) {\n errors.push({\n key: keyName,\n message: `Required key '${keyName}' is missing.`,\n rule: \"required\",\n });\n }\n continue;\n }\n\n // Type validation\n switch (keyDef.type) {\n case \"integer\": {\n const num = Number(value);\n if (!Number.isInteger(num) || value.trim() === \"\") {\n errors.push({\n key: keyName,\n message: `Key '${keyName}' must be an integer, got '${value}'.`,\n rule: \"type\",\n });\n } else if (keyDef.max !== undefined && num > keyDef.max) {\n warnings.push({\n key: keyName,\n message: `Key '${keyName}' value ${num} exceeds maximum ${keyDef.max}.`,\n rule: \"max_exceeded\",\n });\n }\n break;\n }\n case \"boolean\": {\n const lower = value.toLowerCase();\n if (![\"true\", \"false\"].includes(lower)) {\n errors.push({\n key: keyName,\n message: `Key '${keyName}' must be a boolean ('true' or 'false'), got '${value}'.`,\n rule: \"type\",\n });\n }\n break;\n }\n case \"string\":\n // Strings are always valid type-wise\n break;\n }\n\n // Pattern validation (only for strings)\n if (keyDef.pattern && keyDef.type === \"string\") {\n const regex = new RegExp(keyDef.pattern);\n if (!regex.test(value)) {\n errors.push({\n key: keyName,\n message: `Key '${keyName}' value does not match required pattern '${keyDef.pattern}'.`,\n rule: \"pattern\",\n });\n }\n }\n }\n\n // Check for undeclared keys\n for (const keyName of Object.keys(values)) {\n if (!(keyName in schema.keys)) {\n warnings.push({\n key: keyName,\n message: `Key '${keyName}' is not declared in the schema.`,\n rule: \"undeclared\",\n });\n }\n }\n\n return {\n valid: errors.length === 0,\n errors,\n warnings,\n };\n }\n}\n", "/**\n * TIER 1 MODULE \u2014 Security and correctness critical.\n *\n * This module requires exhaustive test coverage. Before\n * adding or modifying code here:\n * 1. Add tests for the happy path\n * 2. Add tests for all documented error paths\n * 3. Add at least one boundary/edge case test\n *\n * Coverage threshold: 95% lines/functions, 90% branches.\n * See docs/contributing/testing.md for the rationale.\n */\nimport * as path from \"path\";\nimport { ClefManifest, DiffResult, DiffRow, DiffStatus } from \"../types\";\nimport { EncryptionBackend } from \"../types\";\n\n/**\n * Compares decrypted values between two environments or two arbitrary key/value maps.\n *\n * @example\n * ```ts\n * const engine = new DiffEngine();\n * const result = await engine.diffFiles(\"app\", \"staging\", \"production\", manifest, sopsClient, repoRoot);\n * ```\n */\nexport class DiffEngine {\n /**\n * Compare two in-memory value maps and produce a sorted diff result.\n *\n * Rows are sorted with missing and changed keys first, identical keys last.\n *\n * @param valuesA - Decrypted values from environment A.\n * @param valuesB - Decrypted values from environment B.\n * @param envA - Name of environment A.\n * @param envB - Name of environment B.\n * @param namespace - Namespace label included in the result (optional).\n */\n diff(\n valuesA: Record<string, string>,\n valuesB: Record<string, string>,\n envA: string,\n envB: string,\n namespace: string = \"\",\n ): DiffResult {\n const allKeys = new Set([...Object.keys(valuesA), ...Object.keys(valuesB)]);\n const rows: DiffRow[] = [];\n\n for (const key of allKeys) {\n const inA = key in valuesA;\n const inB = key in valuesB;\n\n let status: DiffStatus;\n if (inA && inB) {\n status = valuesA[key] === valuesB[key] ? \"identical\" : \"changed\";\n } else if (inA && !inB) {\n status = \"missing_b\";\n } else {\n status = \"missing_a\";\n }\n\n rows.push({\n key,\n valueA: inA ? valuesA[key] : null,\n valueB: inB ? valuesB[key] : null,\n status,\n });\n }\n\n // Sort: missing and changed first, then identical\n rows.sort((a, b) => {\n const order: Record<DiffStatus, number> = {\n missing_a: 0,\n missing_b: 0,\n changed: 1,\n identical: 2,\n };\n return order[a.status] - order[b.status];\n });\n\n return { namespace, envA, envB, rows };\n }\n\n /**\n * Decrypt two matrix cells and diff their values.\n *\n * @param namespace - Namespace containing both cells.\n * @param envA - Name of environment A.\n * @param envB - Name of environment B.\n * @param manifest - Parsed manifest used to resolve file paths.\n * @param sopsClient - SOPS client used to decrypt both files.\n * @param repoRoot - Absolute path to the repository root.\n * @throws {@link SopsDecryptionError} If either file cannot be decrypted.\n */\n async diffFiles(\n namespace: string,\n envA: string,\n envB: string,\n manifest: ClefManifest,\n sopsClient: EncryptionBackend,\n repoRoot: string,\n ): Promise<DiffResult> {\n const fileA = path.join(\n repoRoot,\n manifest.file_pattern.replace(\"{namespace}\", namespace).replace(\"{environment}\", envA),\n );\n const fileB = path.join(\n repoRoot,\n manifest.file_pattern.replace(\"{namespace}\", namespace).replace(\"{environment}\", envB),\n );\n\n const [decryptedA, decryptedB] = await Promise.all([\n sopsClient.decrypt(fileA),\n sopsClient.decrypt(fileB),\n ]);\n\n return this.diff(decryptedA.values, decryptedB.values, envA, envB, namespace);\n }\n}\n", "import * as path from \"path\";\nimport { ClefManifest, MatrixCell } from \"../types\";\nimport { EncryptionBackend } from \"../types\";\n\n/**\n * Performs bulk set, delete, and copy operations across multiple environments.\n *\n * @example\n * ```ts\n * const bulk = new BulkOps();\n * await bulk.setAcrossEnvironments(\"app\", \"DATABASE_URL\", { staging: \"...\", production: \"...\" }, manifest, sopsClient, repoRoot);\n * ```\n */\nexport class BulkOps {\n /**\n * Set a key to different values in multiple environments at once.\n *\n * @param namespace - Target namespace.\n * @param key - Secret key name to set.\n * @param values - Map of `{ environment: value }` pairs.\n * @param manifest - Parsed manifest.\n * @param sopsClient - SOPS client used to decrypt and re-encrypt each file.\n * @param repoRoot - Absolute path to the repository root.\n * @throws `Error` with details if any environment fails.\n */\n async setAcrossEnvironments(\n namespace: string,\n key: string,\n values: Record<string, string>,\n manifest: ClefManifest,\n sopsClient: EncryptionBackend,\n repoRoot: string,\n ): Promise<void> {\n const errors: Array<{ environment: string; error: Error }> = [];\n\n for (const env of manifest.environments) {\n if (!(env.name in values)) {\n continue;\n }\n\n const filePath = path.join(\n repoRoot,\n manifest.file_pattern.replace(\"{namespace}\", namespace).replace(\"{environment}\", env.name),\n );\n\n try {\n const decrypted = await sopsClient.decrypt(filePath);\n decrypted.values[key] = values[env.name];\n await sopsClient.encrypt(filePath, decrypted.values, manifest, env.name);\n } catch (err) {\n errors.push({ environment: env.name, error: err as Error });\n }\n }\n\n if (errors.length > 0) {\n const details = errors.map((e) => ` - ${e.environment}: ${e.error.message}`).join(\"\\n\");\n throw new Error(\n `Failed to set key '${key}' in ${errors.length} environment(s):\\n${details}\\n` +\n `Successfully updated ${Object.keys(values).length - errors.length} environment(s).`,\n );\n }\n }\n\n /**\n * Delete a key from every environment in a namespace.\n *\n * @param namespace - Target namespace.\n * @param key - Secret key name to delete.\n * @param manifest - Parsed manifest.\n * @param sopsClient - SOPS client.\n * @param repoRoot - Absolute path to the repository root.\n * @throws `Error` with details if any environment fails.\n */\n async deleteAcrossEnvironments(\n namespace: string,\n key: string,\n manifest: ClefManifest,\n sopsClient: EncryptionBackend,\n repoRoot: string,\n ): Promise<void> {\n const errors: Array<{ environment: string; error: Error }> = [];\n\n for (const env of manifest.environments) {\n const filePath = path.join(\n repoRoot,\n manifest.file_pattern.replace(\"{namespace}\", namespace).replace(\"{environment}\", env.name),\n );\n\n try {\n const decrypted = await sopsClient.decrypt(filePath);\n if (key in decrypted.values) {\n delete decrypted.values[key];\n await sopsClient.encrypt(filePath, decrypted.values, manifest, env.name);\n }\n } catch (err) {\n errors.push({ environment: env.name, error: err as Error });\n }\n }\n\n if (errors.length > 0) {\n const details = errors.map((e) => ` - ${e.environment}: ${e.error.message}`).join(\"\\n\");\n throw new Error(\n `Failed to delete key '${key}' in ${errors.length} environment(s):\\n${details}`,\n );\n }\n }\n\n /**\n * Copy a single key's value from one matrix cell to another.\n *\n * @param key - Secret key name to copy.\n * @param fromCell - Source matrix cell.\n * @param toCell - Destination matrix cell.\n * @param sopsClient - SOPS client.\n * @param manifest - Parsed manifest.\n * @throws `Error` if the key does not exist in the source cell.\n */\n async copyValue(\n key: string,\n fromCell: MatrixCell,\n toCell: MatrixCell,\n sopsClient: EncryptionBackend,\n manifest: ClefManifest,\n ): Promise<void> {\n const source = await sopsClient.decrypt(fromCell.filePath);\n\n if (!(key in source.values)) {\n throw new Error(\n `Key '${key}' does not exist in ${fromCell.namespace}/${fromCell.environment}.`,\n );\n }\n\n const dest = await sopsClient.decrypt(toCell.filePath);\n dest.values[key] = source.values[key];\n await sopsClient.encrypt(toCell.filePath, dest.values, manifest, toCell.environment);\n }\n}\n", "import * as fs from \"fs\";\nimport * as path from \"path\";\nimport { GitCommit, GitOperationError, GitStatus, SubprocessRunner } from \"../types\";\n\nconst PRE_COMMIT_HOOK = `#!/bin/sh\n# Clef pre-commit hook \u2014 blocks commits of files missing SOPS encryption metadata\n# and scans staged files for plaintext secrets.\n# Installed by: clef hooks install\n\nSTAGED_FILES=$(git diff --cached --name-only --diff-filter=ACM)\nEXIT_CODE=0\n\nfor FILE in $STAGED_FILES; do\n case \"$FILE\" in\n *.enc.yaml|*.enc.json)\n if ! grep -q '\"sops\":' \"$FILE\" && ! grep -q 'sops:' \"$FILE\"; then\n echo \"ERROR: $FILE appears to be missing SOPS metadata.\"\n echo \" This file may contain unencrypted secrets.\"\n echo \" Encrypt it with 'sops encrypt -i $FILE' before committing.\"\n EXIT_CODE=1\n fi\n ;;\n esac\ndone\n\nif [ $EXIT_CODE -eq 0 ]; then\n # Scan staged files for plaintext secrets\n if command -v clef >/dev/null 2>&1; then\n clef scan --staged\n SCAN_EXIT=$?\n if [ $SCAN_EXIT -ne 0 ]; then\n echo \"\"\n echo \"clef scan found potential secrets in staged files.\"\n echo \"Review the findings above before committing.\"\n echo \"To bypass (use with caution): git commit --no-verify\"\n EXIT_CODE=1\n fi\n fi\nfi\n\nexit $EXIT_CODE\n`;\n\n/**\n * Wraps git operations: staging, committing, log, diff, status, and hook installation.\n *\n * @example\n * ```ts\n * const git = new GitIntegration(runner);\n * await git.stageFiles([\"secrets/app/production.enc.yaml\"], repoRoot);\n * const hash = await git.commit(\"chore(secrets): rotate production keys\", repoRoot);\n * ```\n */\nexport class GitIntegration {\n constructor(private readonly runner: SubprocessRunner) {}\n\n /**\n * Stage one or more file paths with `git add`.\n *\n * @param filePaths - Paths to stage (relative or absolute).\n * @param repoRoot - Working directory for the git command.\n * @throws {@link GitOperationError} On failure.\n */\n async stageFiles(filePaths: string[], repoRoot: string): Promise<void> {\n if (filePaths.length === 0) return;\n\n const result = await this.runner.run(\"git\", [\"add\", ...filePaths], { cwd: repoRoot });\n\n if (result.exitCode !== 0) {\n throw new GitOperationError(\n `Failed to stage files: ${result.stderr.trim()}`,\n \"Check that the files exist and you are inside a git repository.\",\n );\n }\n }\n\n /**\n * Create a commit with the given message.\n *\n * @param message - Commit message.\n * @param repoRoot - Working directory for the git command.\n * @returns The short commit hash, or an empty string if parsing fails.\n * @throws {@link GitOperationError} On failure.\n */\n async commit(message: string, repoRoot: string): Promise<string> {\n const result = await this.runner.run(\"git\", [\"commit\", \"-m\", message], { cwd: repoRoot });\n\n if (result.exitCode !== 0) {\n throw new GitOperationError(\n `Failed to commit: ${result.stderr.trim()}`,\n \"Ensure there are staged changes and your git user is configured.\",\n );\n }\n\n // Extract commit hash from output\n const hashMatch = result.stdout.match(/\\[[\\w/-]+ ([a-f0-9]+)\\]/);\n return hashMatch ? hashMatch[1] : \"\";\n }\n\n /**\n * Retrieve recent commits for a specific file.\n *\n * @param filePath - Path to the file (relative to `repoRoot`).\n * @param repoRoot - Working directory for the git command.\n * @param limit - Maximum number of commits to return (default: 20).\n * @throws {@link GitOperationError} On failure.\n */\n async getLog(filePath: string, repoRoot: string, limit: number = 20): Promise<GitCommit[]> {\n const result = await this.runner.run(\n \"git\",\n [\"log\", `--max-count=${limit}`, \"--format=%H|%an|%aI|%s\", \"--\", filePath],\n { cwd: repoRoot },\n );\n\n if (result.exitCode !== 0) {\n throw new GitOperationError(\n `Failed to get git log for '${filePath}': ${result.stderr.trim()}`,\n );\n }\n\n if (!result.stdout.trim()) return [];\n\n return result.stdout\n .trim()\n .split(\"\\n\")\n .map((line) => {\n const [hash, author, dateStr, ...messageParts] = line.split(\"|\");\n return {\n hash,\n author,\n date: new Date(dateStr),\n message: messageParts.join(\"|\"),\n };\n });\n }\n\n /**\n * Get the staged diff (`git diff --cached`).\n *\n * @param repoRoot - Working directory for the git command.\n * @returns Raw diff output as a string.\n * @throws {@link GitOperationError} On failure.\n */\n async getDiff(repoRoot: string): Promise<string> {\n const result = await this.runner.run(\"git\", [\"diff\", \"--cached\"], { cwd: repoRoot });\n\n if (result.exitCode !== 0) {\n throw new GitOperationError(`Failed to get git diff: ${result.stderr.trim()}`);\n }\n\n return result.stdout;\n }\n\n /**\n * Parse `git status --porcelain` into staged, unstaged, and untracked lists.\n *\n * @param repoRoot - Working directory for the git command.\n * @throws {@link GitOperationError} On failure.\n */\n async getStatus(repoRoot: string): Promise<GitStatus> {\n const result = await this.runner.run(\"git\", [\"status\", \"--porcelain\"], { cwd: repoRoot });\n\n if (result.exitCode !== 0) {\n throw new GitOperationError(`Failed to get git status: ${result.stderr.trim()}`);\n }\n\n const staged: string[] = [];\n const unstaged: string[] = [];\n const untracked: string[] = [];\n\n if (!result.stdout.trim()) {\n return { staged, unstaged, untracked };\n }\n\n for (const line of result.stdout.trim().split(\"\\n\")) {\n const indexStatus = line[0];\n const workTreeStatus = line[1];\n const filePath = line.substring(3);\n\n if (indexStatus === \"?\") {\n untracked.push(filePath);\n } else {\n if (indexStatus !== \" \" && indexStatus !== \"?\") {\n staged.push(filePath);\n }\n if (workTreeStatus !== \" \" && workTreeStatus !== \"?\") {\n unstaged.push(filePath);\n }\n }\n }\n\n return { staged, unstaged, untracked };\n }\n\n /**\n * Configure the SOPS-aware git merge driver so that encrypted files\n * are merged at the plaintext level instead of producing ciphertext conflicts.\n *\n * Sets two things:\n * 1. `.gitattributes` \u2014 tells git which files use the custom driver.\n * 2. `.git/config [merge \"sops\"]` \u2014 tells git what command to run.\n *\n * Both operations are idempotent \u2014 safe to call repeatedly.\n *\n * @param repoRoot - Absolute path to the repository root.\n * @throws {@link GitOperationError} On failure.\n */\n async installMergeDriver(repoRoot: string): Promise<void> {\n // 1. Configure git merge driver in local config\n const configResult = await this.runner.run(\n \"git\",\n [\"config\", \"merge.sops.name\", \"SOPS-aware merge driver\"],\n { cwd: repoRoot },\n );\n if (configResult.exitCode !== 0) {\n throw new GitOperationError(\n `Failed to configure merge driver name: ${configResult.stderr.trim()}`,\n \"Ensure you are inside a git repository.\",\n );\n }\n\n const driverResult = await this.runner.run(\n \"git\",\n [\"config\", \"merge.sops.driver\", \"clef merge-driver %O %A %B\"],\n { cwd: repoRoot },\n );\n if (driverResult.exitCode !== 0) {\n throw new GitOperationError(\n `Failed to configure merge driver command: ${driverResult.stderr.trim()}`,\n \"Ensure you are inside a git repository.\",\n );\n }\n\n // 2. Ensure .gitattributes contains the rule\n await this.ensureGitattributes(repoRoot);\n }\n\n /**\n * Check whether the SOPS merge driver is configured in both\n * `.git/config` and `.gitattributes`.\n *\n * @param repoRoot - Absolute path to the repository root.\n * @returns An object indicating which parts are configured.\n */\n async checkMergeDriver(\n repoRoot: string,\n ): Promise<{ gitConfig: boolean; gitattributes: boolean }> {\n // Check git config\n const configResult = await this.runner.run(\"git\", [\"config\", \"--get\", \"merge.sops.driver\"], {\n cwd: repoRoot,\n });\n const gitConfig = configResult.exitCode === 0 && configResult.stdout.trim().length > 0;\n\n // Check .gitattributes\n const attrFilePath = path.join(repoRoot, \".gitattributes\");\n const attrContent = fs.existsSync(attrFilePath) ? fs.readFileSync(attrFilePath, \"utf-8\") : \"\";\n const gitattributes = attrContent.includes(\"merge=sops\");\n\n return { gitConfig, gitattributes };\n }\n\n private async ensureGitattributes(repoRoot: string): Promise<void> {\n const attrPath = path.join(repoRoot, \".gitattributes\");\n const mergeRule = \"*.enc.yaml merge=sops\\n*.enc.json merge=sops\";\n\n // Read existing content\n const existing = fs.existsSync(attrPath) ? fs.readFileSync(attrPath, \"utf-8\") : \"\";\n\n if (existing.includes(\"merge=sops\")) {\n return; // Already configured\n }\n\n const newContent = existing.trimEnd()\n ? `${existing.trimEnd()}\\n\\n# Clef: SOPS-aware merge driver for encrypted files\\n${mergeRule}\\n`\n : `# Clef: SOPS-aware merge driver for encrypted files\\n${mergeRule}\\n`;\n\n const writeResult = await this.runner.run(\"tee\", [attrPath], {\n stdin: newContent,\n cwd: repoRoot,\n });\n\n if (writeResult.exitCode !== 0) {\n throw new GitOperationError(`Failed to write .gitattributes: ${writeResult.stderr.trim()}`);\n }\n }\n\n /**\n * Write and chmod the Clef pre-commit hook into `.git/hooks/pre-commit`.\n * The hook blocks commits of unencrypted matrix files and scans staged files for secrets.\n *\n * @param repoRoot - Absolute path to the repository root.\n * @throws {@link GitOperationError} On failure.\n */\n async installPreCommitHook(repoRoot: string): Promise<void> {\n const hookPath = path.join(repoRoot, \".git\", \"hooks\", \"pre-commit\");\n\n // Write the hook using the subprocess runner to avoid direct fs writes in the integration\n const result = await this.runner.run(\"tee\", [hookPath], {\n stdin: PRE_COMMIT_HOOK,\n cwd: repoRoot,\n });\n\n if (result.exitCode !== 0) {\n throw new GitOperationError(\n `Failed to install pre-commit hook: ${result.stderr.trim()}`,\n \"Ensure .git/hooks/ directory exists.\",\n );\n }\n\n // Make it executable\n const chmodResult = await this.runner.run(\"chmod\", [\"+x\", hookPath], { cwd: repoRoot });\n\n if (chmodResult.exitCode !== 0) {\n throw new GitOperationError(\n `Failed to make pre-commit hook executable: ${chmodResult.stderr.trim()}`,\n );\n }\n }\n}\n", "/**\n * TIER 1 MODULE \u2014 Security and correctness critical.\n *\n * This module requires exhaustive test coverage. Before\n * adding or modifying code here:\n * 1. Add tests for the happy path\n * 2. Add tests for all documented error paths\n * 3. Add at least one boundary/edge case test\n *\n * Coverage threshold: 95% lines/functions, 90% branches.\n * See docs/contributing/testing.md for the rationale.\n */\nimport * as fs from \"fs\";\nimport * as net from \"net\";\nimport { randomBytes } from \"crypto\";\nimport * as YAML from \"yaml\";\nimport {\n ClefManifest,\n DecryptedFile,\n EncryptionBackend,\n SopsDecryptionError,\n SopsEncryptionError,\n SopsKeyNotFoundError,\n SopsMetadata,\n SubprocessRunner,\n resolveBackendConfig,\n} from \"../types\";\nimport { assertSops } from \"../dependencies/checker\";\nimport { deriveAgePublicKey } from \"../age/keygen\";\nimport { resolveSopsPath } from \"./resolver\";\n\nfunction formatFromPath(filePath: string): \"yaml\" | \"json\" {\n return filePath.endsWith(\".json\") ? \"json\" : \"yaml\";\n}\n\n/**\n * On Windows, /dev/stdin does not exist. Create a named pipe that sops can open\n * as its input file, feed the content through it, and return the pipe path.\n * The returned cleanup function closes the server once sops is done reading.\n *\n * Go's os.Open / CreateFile can open \\\\.\\pipe\\... paths directly, so sops\n * reads from the pipe exactly as it would from a regular file.\n */\nfunction openWindowsInputPipe(content: string): Promise<{ inputArg: string; cleanup: () => void }> {\n const pipeName = `\\\\\\\\.\\\\pipe\\\\clef-sops-${randomBytes(8).toString(\"hex\")}`;\n\n return new Promise((resolve, reject) => {\n const server = net.createServer((socket) => {\n // On Windows, socket.end() does not reliably signal EOF to named pipe\n // clients because libuv's uv_shutdown is a no-op for pipes. Write the\n // content and then force-destroy the socket so the pipe handle is closed,\n // which the Go client (sops) sees as ERROR_BROKEN_PIPE \u2192 io.EOF.\n socket.write(content, () => {\n socket.destroy();\n });\n });\n server.maxConnections = 1;\n server.on(\"error\", reject);\n server.listen(pipeName, () => {\n resolve({\n inputArg: pipeName,\n cleanup: () => server.close(),\n });\n });\n });\n}\n\n/**\n * Wraps the `sops` binary for encryption, decryption, re-encryption, and metadata extraction.\n * All decrypt/encrypt operations are piped via stdin/stdout \u2014 plaintext never touches disk.\n *\n * @example\n * ```ts\n * const client = new SopsClient(runner, \"/home/user/.age/key.txt\");\n * const decrypted = await client.decrypt(\"secrets/production.enc.yaml\");\n * ```\n */\nexport class SopsClient implements EncryptionBackend {\n private readonly sopsCommand: string;\n\n /**\n * @param runner - Subprocess runner used to invoke the `sops` binary.\n * @param ageKeyFile - Optional path to an age private key file. Passed as\n * `SOPS_AGE_KEY_FILE` to the subprocess environment.\n * @param ageKey - Optional inline age private key. Passed as `SOPS_AGE_KEY`\n * to the subprocess environment.\n * @param sopsPath - Optional explicit path to the sops binary. When omitted,\n * resolved automatically via {@link resolveSopsPath}.\n */\n constructor(\n private readonly runner: SubprocessRunner,\n private readonly ageKeyFile?: string,\n private readonly ageKey?: string,\n sopsPath?: string,\n ) {\n this.sopsCommand = sopsPath ?? resolveSopsPath().path;\n }\n\n private buildSopsEnv(): Record<string, string> | undefined {\n const env: Record<string, string> = {};\n if (this.ageKey) {\n env.SOPS_AGE_KEY = this.ageKey;\n }\n if (this.ageKeyFile) {\n env.SOPS_AGE_KEY_FILE = this.ageKeyFile;\n }\n return Object.keys(env).length > 0 ? env : undefined;\n }\n\n /**\n * Decrypt a SOPS-encrypted file and return its values and metadata.\n *\n * @param filePath - Path to the `.enc.yaml` or `.enc.json` file.\n * @returns {@link DecryptedFile} with plaintext values in memory only.\n * @throws {@link SopsKeyNotFoundError} If no matching decryption key is available.\n * @throws {@link SopsDecryptionError} On any other decryption failure.\n */\n async decrypt(filePath: string): Promise<DecryptedFile> {\n await assertSops(this.runner, this.sopsCommand);\n const fmt = formatFromPath(filePath);\n const env = this.buildSopsEnv();\n const result = await this.runner.run(\n this.sopsCommand,\n [\"decrypt\", \"--output-type\", fmt, filePath],\n {\n ...(env ? { env } : {}),\n },\n );\n\n if (result.exitCode !== 0) {\n const errorType = await this.classifyDecryptError(filePath);\n if (errorType === \"key-not-found\") {\n throw new SopsKeyNotFoundError(\n `No decryption key found for '${filePath}'. ${result.stderr.trim()}`,\n );\n }\n throw new SopsDecryptionError(\n `Failed to decrypt '${filePath}': ${result.stderr.trim()}`,\n filePath,\n );\n }\n\n let parsed: Record<string, unknown>;\n try {\n parsed = YAML.parse(result.stdout) ?? {};\n } catch {\n throw new SopsDecryptionError(\n `Decrypted content of '${filePath}' is not valid YAML.`,\n filePath,\n );\n }\n\n const values: Record<string, string> = {};\n for (const [key, value] of Object.entries(parsed)) {\n values[key] = String(value);\n }\n\n const metadata = await this.getMetadata(filePath);\n\n return { values, metadata };\n }\n\n /**\n * Encrypt a key/value map and write it to an encrypted SOPS file.\n *\n * @param filePath - Destination path for the encrypted file.\n * @param values - Flat key/value map to encrypt.\n * @param manifest - Manifest used to determine the encryption backend and key configuration.\n * @param environment - Optional environment name. When provided, per-env backend overrides\n * are resolved from the manifest. When omitted, the global `sops.default_backend` is used.\n * @throws {@link SopsEncryptionError} On encryption or write failure.\n */\n async encrypt(\n filePath: string,\n values: Record<string, string>,\n manifest: ClefManifest,\n environment?: string,\n ): Promise<void> {\n await assertSops(this.runner, this.sopsCommand);\n const fmt = formatFromPath(filePath);\n const content = fmt === \"json\" ? JSON.stringify(values, null, 2) : YAML.stringify(values);\n const args = this.buildEncryptArgs(filePath, manifest, environment);\n const env = this.buildSopsEnv();\n\n // sops requires an explicit input path \u2014 it does not read from stdin implicitly.\n // On Unix we pass /dev/stdin (a special file backed by the process's stdin pipe).\n // On Windows /dev/stdin does not exist, so we create a named pipe, feed content\n // through it, and pass the pipe path as the input file instead.\n let inputArg: string;\n let pipeCleanup: (() => void) | undefined;\n\n if (process.platform === \"win32\") {\n const pipe = await openWindowsInputPipe(content);\n inputArg = pipe.inputArg;\n pipeCleanup = pipe.cleanup;\n } else {\n inputArg = \"/dev/stdin\";\n }\n\n let result;\n try {\n result = await this.runner.run(\n this.sopsCommand,\n [\n \"encrypt\",\n ...args,\n \"--input-type\",\n fmt,\n \"--output-type\",\n fmt,\n \"--filename-override\",\n filePath,\n inputArg,\n ],\n {\n // stdin is still piped on Unix (/dev/stdin reads from it);\n // on Windows the named pipe server feeds content directly.\n ...(process.platform !== \"win32\" ? { stdin: content } : {}),\n ...(env ? { env } : {}),\n },\n );\n } finally {\n pipeCleanup?.();\n }\n\n if (result.exitCode !== 0) {\n throw new SopsEncryptionError(\n `Failed to encrypt '${filePath}': ${result.stderr.trim()}`,\n filePath,\n );\n }\n\n // Write the encrypted output to the file (using fs directly \u2014 tee is not available on Windows)\n try {\n fs.writeFileSync(filePath, result.stdout);\n } catch {\n throw new SopsEncryptionError(`Failed to write encrypted data to '${filePath}'.`, filePath);\n }\n }\n\n /**\n * Rotate encryption by adding a new age recipient key to an existing SOPS file.\n *\n * @param filePath - Path to the encrypted file to re-encrypt.\n * @param newKey - New age public key to add as a recipient.\n * @throws {@link SopsEncryptionError} On failure.\n */\n async reEncrypt(filePath: string, newKey: string): Promise<void> {\n await assertSops(this.runner, this.sopsCommand);\n const env = this.buildSopsEnv();\n const result = await this.runner.run(\n this.sopsCommand,\n [\"rotate\", \"-i\", \"--add-age\", newKey, filePath],\n {\n ...(env ? { env } : {}),\n },\n );\n\n if (result.exitCode !== 0) {\n throw new SopsEncryptionError(\n `Failed to re-encrypt '${filePath}': ${result.stderr.trim()}`,\n filePath,\n );\n }\n }\n\n /**\n * Add an age recipient to an existing SOPS file.\n *\n * @param filePath - Path to the encrypted file.\n * @param key - age public key to add as a recipient.\n * @throws {@link SopsEncryptionError} On failure.\n */\n async addRecipient(filePath: string, key: string): Promise<void> {\n await assertSops(this.runner, this.sopsCommand);\n const env = this.buildSopsEnv();\n const result = await this.runner.run(\n this.sopsCommand,\n [\"rotate\", \"-i\", \"--add-age\", key, filePath],\n {\n ...(env ? { env } : {}),\n },\n );\n\n if (result.exitCode !== 0) {\n throw new SopsEncryptionError(\n `Failed to add recipient to '${filePath}': ${result.stderr.trim()}`,\n filePath,\n );\n }\n }\n\n /**\n * Remove an age recipient from an existing SOPS file.\n *\n * @param filePath - Path to the encrypted file.\n * @param key - age public key to remove.\n * @throws {@link SopsEncryptionError} On failure.\n */\n async removeRecipient(filePath: string, key: string): Promise<void> {\n await assertSops(this.runner, this.sopsCommand);\n const env = this.buildSopsEnv();\n const result = await this.runner.run(\n this.sopsCommand,\n [\"rotate\", \"-i\", \"--rm-age\", key, filePath],\n {\n ...(env ? { env } : {}),\n },\n );\n\n if (result.exitCode !== 0) {\n throw new SopsEncryptionError(\n `Failed to remove recipient from '${filePath}': ${result.stderr.trim()}`,\n filePath,\n );\n }\n }\n\n /**\n * Check whether a file contains valid SOPS encryption metadata.\n *\n * @param filePath - Path to the file to check.\n * @returns `true` if valid SOPS metadata is present; `false` otherwise. Never throws.\n */\n async validateEncryption(filePath: string): Promise<boolean> {\n await assertSops(this.runner, this.sopsCommand);\n try {\n await this.getMetadata(filePath);\n return true;\n } catch {\n return false;\n }\n }\n\n /**\n * Extract SOPS metadata (backend, recipients, last-modified timestamp) from an encrypted file\n * without decrypting its values.\n *\n * @param filePath - Path to the encrypted file.\n * @returns {@link SopsMetadata} parsed from the file's `sops:` block.\n * @throws {@link SopsDecryptionError} If the file cannot be read or parsed.\n */\n async getMetadata(filePath: string): Promise<SopsMetadata> {\n await assertSops(this.runner, this.sopsCommand);\n const env = this.buildSopsEnv();\n const result = await this.runner.run(this.sopsCommand, [\"filestatus\", filePath], {\n ...(env ? { env } : {}),\n });\n\n // filestatus returns JSON with encrypted status; if it fails, try parsing the file directly\n if (result.exitCode !== 0) {\n // Fall back to reading SOPS metadata from the encrypted file\n return this.parseMetadataFromFile(filePath);\n }\n\n return this.parseMetadataFromFile(filePath);\n }\n\n /**\n * Determine whether a decrypt failure is caused by a missing/mismatched key (vs. some other\n * SOPS error) without relying on stderr message text.\n *\n * For age backends: reads the file's recipient list and checks whether any of the configured\n * private keys derive to a matching public key. For non-age backends (pgp, kms) we cannot\n * perform an equivalent check, so those always return \"other\".\n */\n private async classifyDecryptError(filePath: string): Promise<\"key-not-found\" | \"other\"> {\n let metadata: SopsMetadata;\n try {\n metadata = this.parseMetadataFromFile(filePath);\n } catch {\n return \"other\";\n }\n\n if (metadata.backend !== \"age\") return \"other\";\n\n // No age key configured at all\n if (!this.ageKey && !this.ageKeyFile) return \"key-not-found\";\n\n // Obtain the private key material from the constructor params\n let keyContent: string;\n try {\n keyContent = this.ageKey ?? fs.readFileSync(this.ageKeyFile!, \"utf-8\");\n } catch {\n return \"key-not-found\";\n }\n\n // Key files may contain multiple AGE-SECRET-KEY-1... lines (plus comments/blank lines)\n const privateKeys = keyContent\n .split(\"\\n\")\n .map((line) => line.trim())\n .filter((line) => line.startsWith(\"AGE-SECRET-KEY-\"));\n\n if (privateKeys.length === 0) return \"key-not-found\";\n\n try {\n const publicKeys = await Promise.all(privateKeys.map((k) => deriveAgePublicKey(k)));\n const recipients = new Set(metadata.recipients);\n return publicKeys.some((pk) => recipients.has(pk)) ? \"other\" : \"key-not-found\";\n } catch {\n return \"other\";\n }\n }\n\n private parseMetadataFromFile(filePath: string): SopsMetadata {\n let content: string;\n try {\n content = fs.readFileSync(filePath, \"utf-8\");\n } catch {\n throw new SopsDecryptionError(\n `Could not read file '${filePath}' to extract SOPS metadata.`,\n filePath,\n );\n }\n\n let parsed: Record<string, unknown>;\n try {\n parsed = YAML.parse(content);\n } catch {\n throw new SopsDecryptionError(\n `File '${filePath}' is not valid YAML. Cannot extract SOPS metadata.`,\n filePath,\n );\n }\n\n const sops = parsed?.sops as Record<string, unknown> | undefined;\n if (!sops) {\n throw new SopsDecryptionError(\n `File '${filePath}' does not contain SOPS metadata. It may not be encrypted.`,\n filePath,\n );\n }\n\n const backend = this.detectBackend(sops);\n const recipients = this.extractRecipients(sops, backend);\n const lastModified = sops.lastmodified ? new Date(sops.lastmodified as string) : new Date();\n\n return { backend, recipients, lastModified };\n }\n\n private detectBackend(\n sops: Record<string, unknown>,\n ): \"age\" | \"awskms\" | \"gcpkms\" | \"azurekv\" | \"pgp\" {\n if (sops.age && Array.isArray(sops.age) && (sops.age as unknown[]).length > 0) return \"age\";\n if (sops.kms && Array.isArray(sops.kms) && (sops.kms as unknown[]).length > 0) return \"awskms\";\n if (sops.gcp_kms && Array.isArray(sops.gcp_kms) && (sops.gcp_kms as unknown[]).length > 0)\n return \"gcpkms\";\n if (sops.azure_kv && Array.isArray(sops.azure_kv) && (sops.azure_kv as unknown[]).length > 0)\n return \"azurekv\";\n if (sops.pgp && Array.isArray(sops.pgp) && (sops.pgp as unknown[]).length > 0) return \"pgp\";\n return \"age\"; // Interpretation: default to age when metadata is ambiguous\n }\n\n private extractRecipients(\n sops: Record<string, unknown>,\n backend: \"age\" | \"awskms\" | \"gcpkms\" | \"azurekv\" | \"pgp\",\n ): string[] {\n switch (backend) {\n case \"age\": {\n const entries = sops.age as Array<Record<string, unknown>> | undefined;\n return entries?.map((e) => String(e.recipient ?? \"\")) ?? [];\n }\n case \"awskms\": {\n const entries = sops.kms as Array<Record<string, unknown>> | undefined;\n return entries?.map((e) => String(e.arn ?? \"\")) ?? [];\n }\n case \"gcpkms\": {\n const entries = sops.gcp_kms as Array<Record<string, unknown>> | undefined;\n return entries?.map((e) => String(e.resource_id ?? \"\")) ?? [];\n }\n case \"azurekv\": {\n const entries = sops.azure_kv as Array<Record<string, unknown>> | undefined;\n return (\n entries?.map((e) => {\n const vaultUrl = String(e.vaultUrl ?? e.vault_url ?? \"\");\n const name = String(e.name ?? e.key ?? \"\");\n // Return the composite Key Vault key identifier\n return vaultUrl && name ? `${vaultUrl}/keys/${name}` : vaultUrl || name;\n }) ?? []\n );\n }\n case \"pgp\": {\n const entries = sops.pgp as Array<Record<string, unknown>> | undefined;\n return entries?.map((e) => String(e.fp ?? \"\")) ?? [];\n }\n }\n }\n\n private buildEncryptArgs(\n filePath: string,\n manifest: ClefManifest,\n environment?: string,\n ): string[] {\n const args: string[] = [];\n\n const config = environment\n ? resolveBackendConfig(manifest, environment)\n : {\n backend: manifest.sops.default_backend,\n aws_kms_arn: manifest.sops.aws_kms_arn,\n gcp_kms_resource_id: manifest.sops.gcp_kms_resource_id,\n azure_kv_url: manifest.sops.azure_kv_url,\n pgp_fingerprint: manifest.sops.pgp_fingerprint,\n };\n\n switch (config.backend) {\n case \"age\":\n // Key injection is handled via buildSopsEnv() \u2014 no extra args needed here\n break;\n case \"awskms\":\n if (config.aws_kms_arn) {\n args.push(\"--kms\", config.aws_kms_arn);\n }\n break;\n case \"gcpkms\":\n if (config.gcp_kms_resource_id) {\n args.push(\"--gcp-kms\", config.gcp_kms_resource_id);\n }\n break;\n case \"azurekv\":\n if (config.azure_kv_url) {\n args.push(\"--azure-kv\", config.azure_kv_url);\n }\n break;\n case \"pgp\":\n if (config.pgp_fingerprint) {\n args.push(\"--pgp\", config.pgp_fingerprint);\n }\n break;\n }\n\n return args;\n }\n}\n", "/**\n * Resolves the path to the `sops` binary using a three-tier resolution chain:\n *\n * 1. `CLEF_SOPS_PATH` environment variable (explicit user override)\n * 2. Bundled platform-specific package (`@clef-sh/sops-{os}-{arch}`)\n * 3. System PATH fallback (bare `\"sops\"` command name)\n *\n * The result is cached after the first call \u2014 subsequent calls return the\n * same resolution without re-probing the filesystem.\n */\nimport * as fs from \"fs\";\nimport * as path from \"path\";\nimport { tryBundled } from \"./bundled\";\n\nfunction validateSopsPath(candidate: string): void {\n if (!path.isAbsolute(candidate)) {\n throw new Error(`CLEF_SOPS_PATH must be an absolute path, got '${candidate}'.`);\n }\n const segments = candidate.split(/[/\\\\]/);\n if (segments.includes(\"..\")) {\n throw new Error(\n `CLEF_SOPS_PATH contains '..' path segments ('${candidate}'). ` +\n \"Use an absolute path without directory traversal.\",\n );\n }\n}\n\nexport type SopsSource = \"env\" | \"bundled\" | \"system\";\n\nexport interface SopsResolution {\n /** Absolute path to the sops binary, or \"sops\" for system PATH fallback. */\n path: string;\n /** How the binary was located. */\n source: SopsSource;\n}\n\nlet cached: SopsResolution | undefined;\n\n/**\n * Resolve the sops binary path.\n *\n * Resolution order:\n * 1. `CLEF_SOPS_PATH` env var \u2014 explicit override, used as-is\n * 2. Bundled `@clef-sh/sops-{platform}-{arch}` package\n * 3. System PATH fallback \u2014 returns bare `\"sops\"`\n *\n * The result is cached module-wide. Call {@link resetSopsResolution} in tests\n * to clear the cache.\n */\nexport function resolveSopsPath(): SopsResolution {\n if (cached) return cached;\n\n // 1. Explicit environment override\n const envPath = process.env.CLEF_SOPS_PATH?.trim();\n if (envPath) {\n validateSopsPath(envPath);\n if (!fs.existsSync(envPath)) {\n throw new Error(`CLEF_SOPS_PATH points to '${envPath}' but the file does not exist.`);\n }\n cached = { path: envPath, source: \"env\" };\n return cached;\n }\n\n // 2. Bundled platform package\n const bundledPath = tryBundled();\n if (bundledPath) {\n cached = { path: bundledPath, source: \"bundled\" };\n return cached;\n }\n\n // 3. System PATH fallback\n cached = { path: \"sops\", source: \"system\" };\n return cached;\n}\n\n/**\n * Clear the cached resolution. Only intended for use in tests.\n */\nexport function resetSopsResolution(): void {\n cached = undefined;\n}\n", "/**\n * Locates the bundled sops binary from the platform-specific npm package.\n * Extracted into its own module for testability \u2014 the resolver imports this\n * so tests can mock it without overriding require.resolve.\n */\nimport * as fs from \"fs\";\nimport * as path from \"path\";\n\n/**\n * Try to locate the bundled sops binary from the platform-specific npm package.\n * Returns the resolved path or null if the package is not installed.\n */\nexport function tryBundled(): string | null {\n const platform = process.platform;\n const arch = process.arch;\n\n // Map Node.js arch names to our package names\n const archName = arch === \"x64\" ? \"x64\" : arch === \"arm64\" ? \"arm64\" : null;\n if (!archName) return null;\n\n // Map Node.js platform names to our package names\n const platformName =\n platform === \"darwin\"\n ? \"darwin\"\n : platform === \"linux\"\n ? \"linux\"\n : platform === \"win32\"\n ? \"win32\"\n : null;\n if (!platformName) return null;\n\n const packageName = `@clef-sh/sops-${platformName}-${archName}`;\n const binName = platform === \"win32\" ? \"sops.exe\" : \"sops\";\n\n try {\n // Use createRequire to resolve the platform package.\n const packageMain = require.resolve(`${packageName}/package.json`);\n const packageDir = path.dirname(packageMain);\n const binPath = path.join(packageDir, \"bin\", binName);\n return fs.existsSync(binPath) ? binPath : null;\n } catch {\n return null;\n }\n}\n", "import {\n DependencyStatus,\n DependencyVersion,\n SopsMissingError,\n SopsVersionError,\n SubprocessRunner,\n} from \"../types\";\nimport { resolveSopsPath } from \"../sops/resolver\";\n\n// Minimum versions \u2014 update .github/workflows/ci.yml when these change\nexport const REQUIREMENTS = {\n sops: \"3.8.0\",\n git: \"2.28.0\",\n} as const;\n\n/**\n * Parse a version string like \"3.8.1\" into [major, minor, patch].\n * Returns null if the string is not a valid semver-like version.\n */\nfunction parseSemver(version: string): [number, number, number] | null {\n const match = version.match(/^(\\d+)\\.(\\d+)\\.(\\d+)/);\n if (!match) return null;\n return [parseInt(match[1], 10), parseInt(match[2], 10), parseInt(match[3], 10)];\n}\n\n/**\n * Returns true if `installed` >= `required` using semver comparison.\n */\nfunction semverSatisfied(installed: string, required: string): boolean {\n const a = parseSemver(installed);\n const b = parseSemver(required);\n if (!a || !b) return false;\n\n if (a[0] !== b[0]) return a[0] > b[0];\n if (a[1] !== b[1]) return a[1] > b[1];\n return a[2] >= b[2];\n}\n\n/**\n * Extract version from sops output.\n * Format: \"sops 3.8.1 (latest)\" or \"sops 3.9.4\"\n */\nfunction parseSopsVersion(stdout: string): string | null {\n const match = stdout.match(/sops\\s+(\\d+\\.\\d+\\.\\d+)/);\n return match ? match[1] : null;\n}\n\n/**\n * Extract version from git output.\n * Format: \"git version 2.43.0\" or \"git version 2.50.1 (Apple Git-155)\"\n */\nfunction parseGitVersion(stdout: string): string | null {\n const match = stdout.match(/git version\\s+(\\d+\\.\\d+\\.\\d+)/);\n return match ? match[1] : null;\n}\n\n/**\n * Get the platform-appropriate install hint for a binary.\n */\nfunction getInstallHint(name: \"sops\" | \"git\"): string {\n const platform = process.platform;\n\n switch (name) {\n case \"sops\":\n if (platform === \"darwin\") return \"brew install sops\";\n return \"see https://github.com/getsops/sops/releases\";\n case \"git\":\n if (platform === \"darwin\") return \"brew install git\";\n if (platform === \"linux\") return \"apt install git\";\n return \"see https://git-scm.com/downloads\";\n }\n}\n\n/**\n * Check a single dependency. Returns null if the binary is not found.\n * Never throws.\n */\nexport async function checkDependency(\n name: \"sops\" | \"git\",\n runner: SubprocessRunner,\n commandOverride?: string,\n): Promise<DependencyVersion | null> {\n try {\n // For sops, use the resolver to find the binary path (unless overridden)\n const resolution = name === \"sops\" && !commandOverride ? resolveSopsPath() : undefined;\n const command = commandOverride ?? (resolution ? resolution.path : name);\n\n const result = await runner.run(command, [\"--version\"]);\n\n if (result.exitCode !== 0) {\n return null;\n }\n\n const output = result.stdout.trim() || result.stderr.trim();\n let installed: string | null = null;\n\n switch (name) {\n case \"sops\":\n installed = parseSopsVersion(output);\n break;\n case \"git\":\n installed = parseGitVersion(output);\n break;\n }\n\n if (!installed) {\n return null;\n }\n\n const required = REQUIREMENTS[name];\n return {\n installed,\n required,\n satisfied: semverSatisfied(installed, required),\n installHint: getInstallHint(name),\n source: resolution?.source,\n resolvedPath: resolution?.path,\n };\n } catch {\n return null;\n }\n}\n\n/**\n * Check sops and git dependencies in parallel.\n */\nexport async function checkAll(runner: SubprocessRunner): Promise<DependencyStatus> {\n const [sops, git] = await Promise.all([\n checkDependency(\"sops\", runner),\n checkDependency(\"git\", runner),\n ]);\n\n return { sops, git };\n}\n\n/**\n * Assert that sops is installed and meets the minimum version.\n * Throws SopsMissingError or SopsVersionError.\n */\nexport async function assertSops(runner: SubprocessRunner, command?: string): Promise<void> {\n const dep = await checkDependency(\"sops\", runner, command);\n\n if (!dep) {\n throw new SopsMissingError(getInstallHint(\"sops\"));\n }\n\n if (!dep.satisfied) {\n throw new SopsVersionError(dep.installed, dep.required, getInstallHint(\"sops\"));\n }\n}\n\n// Exported for testing\nexport { parseSopsVersion, parseGitVersion, semverSatisfied };\n", "/**\n * age key generation using the age-encryption npm package.\n * Dynamic import() is required: age-encryption is ESM-only; this package compiles to CJS.\n */\n\nexport interface AgeIdentity {\n /** AGE-SECRET-KEY-1... armored private key string */\n privateKey: string;\n /** age1... bech32 public key string */\n publicKey: string;\n}\n\n/**\n * Generate a new age key pair using the `age-encryption` npm package.\n *\n * @returns Private key (`AGE-SECRET-KEY-1...` format) and derived public key (`age1...` bech32 format).\n */\nexport async function generateAgeIdentity(): Promise<AgeIdentity> {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any -- dynamic ESM import of CJS-incompatible package\n const { generateIdentity, identityToRecipient } = await import(\"age-encryption\" as any);\n const privateKey = (await generateIdentity()) as string;\n const publicKey = (await identityToRecipient(privateKey)) as string;\n return { privateKey, publicKey };\n}\n\n/**\n * Derive the age public key (`age1...`) from an existing private key (`AGE-SECRET-KEY-1...`).\n */\nexport async function deriveAgePublicKey(privateKey: string): Promise<string> {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any -- dynamic ESM import of CJS-incompatible package\n const { identityToRecipient } = await import(\"age-encryption\" as any);\n return (await identityToRecipient(privateKey)) as string;\n}\n\n/**\n * Format an age private key and public key into the standard key file format.\n * The output includes a `created` timestamp comment and is ready to write to disk.\n *\n * @param privateKey - `AGE-SECRET-KEY-1...` armored private key string.\n * @param publicKey - `age1...` bech32 public key string.\n */\nexport function formatAgeKeyFile(privateKey: string, publicKey: string): string {\n const now = new Date().toISOString();\n return `# created: ${now}\\n# public key: ${publicKey}\\n${privateKey}\\n`;\n}\n", "import * as path from \"path\";\nimport {\n ClefManifest,\n LintIssue,\n LintResult,\n resolveRecipientsForEnvironment,\n ServiceIdentityDefinition,\n} from \"../types\";\nimport { MatrixManager } from \"../matrix/manager\";\nimport { SchemaValidator } from \"../schema/validator\";\nimport { EncryptionBackend } from \"../types\";\nimport { getPendingKeys } from \"../pending/metadata\";\n\n/**\n * Runs matrix completeness, schema validation, SOPS integrity, and key-drift checks.\n *\n * @example\n * ```ts\n * const runner = new LintRunner(matrixManager, schemaValidator, sopsClient);\n * const result = await runner.run(manifest, repoRoot);\n * ```\n */\nexport class LintRunner {\n constructor(\n private readonly matrixManager: MatrixManager,\n private readonly schemaValidator: SchemaValidator,\n private readonly sopsClient: EncryptionBackend,\n ) {}\n\n /**\n * Lint the entire matrix: check missing files, schema errors, SOPS integrity,\n * single-recipient warnings, and cross-environment key drift.\n *\n * @param manifest - Parsed manifest.\n * @param repoRoot - Absolute path to the repository root.\n */\n async run(manifest: ClefManifest, repoRoot: string): Promise<LintResult> {\n const issues: LintIssue[] = [];\n const cells = this.matrixManager.resolveMatrix(manifest, repoRoot);\n let fileCount = 0;\n let pendingCount = 0;\n\n // Category 1: Matrix completeness\n const missingCells = cells.filter((c) => !c.exists);\n for (const cell of missingCells) {\n issues.push({\n severity: \"error\",\n category: \"matrix\",\n file: cell.filePath,\n message: `Missing encrypted file for ${cell.namespace}/${cell.environment}.`,\n fixCommand: `clef init`,\n });\n }\n\n const existingCells = cells.filter((c) => c.exists);\n fileCount = existingCells.length;\n\n // Build a map of keys per namespace to detect cross-env drift\n const namespaceKeys: Record<string, Record<string, Set<string>>> = {};\n\n for (const cell of existingCells) {\n // Category 3: SOPS integrity\n try {\n const isValid = await this.sopsClient.validateEncryption(cell.filePath);\n if (!isValid) {\n issues.push({\n severity: \"error\",\n category: \"sops\",\n file: cell.filePath,\n message: `File is missing valid SOPS encryption metadata.`,\n fixCommand: `sops encrypt -i ${cell.filePath}`,\n });\n continue;\n }\n } catch {\n issues.push({\n severity: \"error\",\n category: \"sops\",\n file: cell.filePath,\n message: `Could not validate SOPS metadata. The file may be corrupted.`,\n });\n continue;\n }\n\n // Decrypt for schema and key-drift checks\n try {\n const decrypted = await this.sopsClient.decrypt(cell.filePath);\n const keys = Object.keys(decrypted.values);\n\n // Track keys per namespace/environment\n if (!namespaceKeys[cell.namespace]) {\n namespaceKeys[cell.namespace] = {};\n }\n namespaceKeys[cell.namespace][cell.environment] = new Set(keys);\n\n // Check SOPS metadata for single-recipient warning\n if (decrypted.metadata.recipients.length <= 1) {\n issues.push({\n severity: \"info\",\n category: \"sops\",\n file: cell.filePath,\n message: `File is encrypted with only ${decrypted.metadata.recipients.length} recipient(s). Consider adding a backup key.`,\n });\n }\n\n // Per-environment recipient drift check\n const envRecipients = resolveRecipientsForEnvironment(manifest, cell.environment);\n if (envRecipients) {\n const expectedKeys = new Set(\n envRecipients.map((r) => (typeof r === \"string\" ? r : r.key)),\n );\n const actualKeys = new Set(decrypted.metadata.recipients);\n for (const expected of expectedKeys) {\n if (!actualKeys.has(expected)) {\n issues.push({\n severity: \"warning\",\n category: \"sops\",\n file: cell.filePath,\n message: `Expected recipient '${expected.slice(0, 4)}\u2026${expected.slice(-8)}' is missing from encrypted file.`,\n fixCommand: `clef recipients add ${expected} -e ${cell.environment}`,\n });\n }\n }\n for (const actual of actualKeys) {\n if (!expectedKeys.has(actual)) {\n issues.push({\n severity: \"warning\",\n category: \"sops\",\n file: cell.filePath,\n message: `Unexpected recipient '${actual.slice(0, 4)}\u2026${actual.slice(-8)}' found in encrypted file.`,\n fixCommand: `clef recipients remove ${actual} -e ${cell.environment}`,\n });\n }\n }\n }\n\n // Category 2: Schema validation\n const ns = manifest.namespaces.find((n) => n.name === cell.namespace);\n if (ns?.schema) {\n const schemaPath = path.join(repoRoot, ns.schema);\n try {\n const schema = this.schemaValidator.loadSchema(schemaPath);\n const result = this.schemaValidator.validate(decrypted.values, schema);\n\n for (const err of result.errors) {\n issues.push({\n severity: \"error\",\n category: \"schema\",\n file: cell.filePath,\n key: err.key,\n message: err.message,\n fixCommand: `clef set ${cell.namespace}/${cell.environment} ${err.key} <value>`,\n });\n }\n\n for (const warn of result.warnings) {\n issues.push({\n severity: \"warning\",\n category: \"schema\",\n file: cell.filePath,\n key: warn.key,\n message: warn.message,\n });\n }\n } catch {\n issues.push({\n severity: \"warning\",\n category: \"schema\",\n file: cell.filePath,\n message: `Could not load schema '${ns.schema}' for validation.`,\n });\n }\n } else {\n // No schema \u2014 flag keys with no schema as info\n for (const key of keys) {\n issues.push({\n severity: \"info\",\n category: \"schema\",\n file: cell.filePath,\n key,\n message: `Key '${key}' has no schema definition. Consider adding a schema for namespace '${cell.namespace}'.`,\n });\n }\n }\n\n // Check for pending keys\n try {\n const pendingKeys = await getPendingKeys(cell.filePath);\n pendingCount += pendingKeys.length;\n for (const pendingKey of pendingKeys) {\n issues.push({\n severity: \"warning\",\n category: \"schema\",\n file: cell.filePath,\n key: pendingKey,\n message: `Value is a random placeholder \\u2014 replace with the real secret.`,\n fixCommand: `clef set ${cell.namespace}/${cell.environment} ${pendingKey}`,\n });\n }\n } catch {\n // Metadata unreadable \u2014 skip pending check\n }\n } catch {\n issues.push({\n severity: \"error\",\n category: \"sops\",\n file: cell.filePath,\n message: `Failed to decrypt file. Ensure you have the correct decryption key.`,\n });\n }\n }\n\n // Detect cross-environment key drift\n for (const [nsName, envKeys] of Object.entries(namespaceKeys)) {\n const allKeys = new Set<string>();\n for (const keys of Object.values(envKeys)) {\n for (const k of keys) allKeys.add(k);\n }\n\n for (const [envName, keys] of Object.entries(envKeys)) {\n for (const key of allKeys) {\n if (!keys.has(key)) {\n const presentIn = Object.entries(envKeys)\n .filter(([, ks]) => ks.has(key))\n .map(([e]) => e);\n const cell = existingCells.find(\n (c) => c.namespace === nsName && c.environment === envName,\n );\n if (cell) {\n issues.push({\n severity: \"warning\",\n category: \"matrix\",\n file: cell.filePath,\n key,\n message: `Key '${key}' is missing in ${envName} but present in ${presentIn.join(\", \")}.`,\n fixCommand: `clef set ${nsName}/${envName} ${key} <value>`,\n });\n }\n }\n }\n }\n }\n\n // Service identity drift checks\n if (manifest.service_identities && manifest.service_identities.length > 0) {\n const siIssues = await this.lintServiceIdentities(\n manifest.service_identities,\n manifest,\n repoRoot,\n existingCells,\n );\n issues.push(...siIssues);\n }\n\n return { issues, fileCount: fileCount + missingCells.length, pendingCount };\n }\n\n /**\n * Lint service identity configurations for drift issues.\n */\n private async lintServiceIdentities(\n identities: ServiceIdentityDefinition[],\n manifest: ClefManifest,\n repoRoot: string,\n existingCells: { namespace: string; environment: string; filePath: string }[],\n ): Promise<LintIssue[]> {\n const issues: LintIssue[] = [];\n const declaredEnvNames = new Set(manifest.environments.map((e) => e.name));\n const declaredNsNames = new Set(manifest.namespaces.map((ns) => ns.name));\n\n for (const si of identities) {\n // Namespace references\n for (const ns of si.namespaces) {\n if (!declaredNsNames.has(ns)) {\n issues.push({\n severity: \"error\",\n category: \"service-identity\",\n file: \"clef.yaml\",\n message: `Service identity '${si.name}' references non-existent namespace '${ns}'.`,\n });\n }\n }\n\n // Environment coverage\n for (const envName of declaredEnvNames) {\n if (!(envName in si.environments)) {\n issues.push({\n severity: \"error\",\n category: \"service-identity\",\n file: \"clef.yaml\",\n message: `Service identity '${si.name}' is missing environment '${envName}'. Manually add an age key pair for this environment in clef.yaml.`,\n });\n }\n }\n\n // Recipient registration on scoped files\n // (KMS-backed environments skip recipient checks)\n for (const cell of existingCells) {\n const envConfig = si.environments[cell.environment];\n if (!envConfig) continue;\n if (!envConfig.recipient) continue;\n\n if (si.namespaces.includes(cell.namespace)) {\n try {\n const metadata = await this.sopsClient.getMetadata(cell.filePath);\n if (!metadata.recipients.includes(envConfig.recipient)) {\n issues.push({\n severity: \"warning\",\n category: \"service-identity\",\n file: cell.filePath,\n message: `Service identity '${si.name}' recipient is not registered in ${cell.namespace}/${cell.environment}.`,\n fixCommand: `clef service create ${si.name} --namespaces ${si.namespaces.join(\",\")}`,\n });\n }\n } catch {\n // Cannot read metadata \u2014 skip\n }\n } else {\n try {\n const metadata = await this.sopsClient.getMetadata(cell.filePath);\n if (metadata.recipients.includes(envConfig.recipient)) {\n issues.push({\n severity: \"warning\",\n category: \"service-identity\",\n file: cell.filePath,\n message: `Service identity '${si.name}' recipient found in ${cell.namespace}/${cell.environment} but namespace is not in scope.`,\n fixCommand: `clef recipients remove ${envConfig.recipient} -e ${cell.environment}`,\n });\n }\n } catch {\n // Cannot read metadata \u2014 skip\n }\n }\n }\n }\n\n return issues;\n }\n\n /**\n * Auto-fix safe issues (scaffold missing matrix files), then re-run lint.\n *\n * @param manifest - Parsed manifest.\n * @param repoRoot - Absolute path to the repository root.\n */\n async fix(manifest: ClefManifest, repoRoot: string): Promise<LintResult> {\n // Auto-fix safe issues: scaffold missing files\n const missingCells = this.matrixManager.detectMissingCells(manifest, repoRoot);\n\n for (const cell of missingCells) {\n await this.matrixManager.scaffoldCell(cell, this.sopsClient, manifest);\n }\n\n // Re-run lint after fixes\n return this.run(manifest, repoRoot);\n }\n}\n", "import { DecryptedFile, ExecOptions, ExportOptions } from \"../types\";\n\n/**\n * Prepares decrypted secrets for consumption via environment injection or shell export.\n *\n * @example\n * ```ts\n * const client = new ConsumptionClient();\n * const env = client.prepareEnvironment(decrypted, process.env, { prefix: \"APP_\" });\n * ```\n */\nexport class ConsumptionClient {\n /**\n * Merges decrypted values into a base environment, respecting --only, --prefix, and --no-override.\n * Returns a new environment record suitable for child_process.spawn.\n */\n prepareEnvironment(\n decryptedFile: DecryptedFile,\n baseEnv: Record<string, string | undefined>,\n options: ExecOptions = {},\n ): Record<string, string> {\n const result: Record<string, string> = {};\n\n // Copy base environment\n for (const [k, v] of Object.entries(baseEnv)) {\n if (v !== undefined) {\n result[k] = v;\n }\n }\n\n let entries = Object.entries(decryptedFile.values);\n\n // --only: filter to specified keys\n if (options.only && options.only.length > 0) {\n const allowed = new Set(options.only);\n entries = entries.filter(([key]) => allowed.has(key));\n }\n\n // Inject values with optional prefix\n for (const [key, value] of entries) {\n const envKey = options.prefix ? `${options.prefix}${key}` : key;\n\n // --no-override: skip keys that already exist in the base environment\n if (options.noOverride && envKey in result) {\n continue;\n }\n\n result[envKey] = value;\n }\n\n return result;\n }\n\n /**\n * Formats decrypted values for stdout output.\n * Values are single-quoted; embedded single quotes are escaped as '\\''.\n */\n formatExport(\n decryptedFile: DecryptedFile,\n format: ExportOptions[\"format\"],\n noExport: boolean,\n ): string {\n if (format !== \"env\") {\n throw new Error(\n `Unsupported export format '${format}'. Only 'env' is supported.\\n` +\n \"Clef does not support formats that encourage writing plaintext secrets to disk.\\n\" +\n \"Use 'clef exec' to inject secrets directly into a process, or 'clef export --format env' to print shell export statements to stdout.\",\n );\n }\n\n const lines: string[] = [];\n const prefix = noExport ? \"\" : \"export \";\n\n for (const [key, value] of Object.entries(decryptedFile.values)) {\n // Single-quote the value; escape embedded single quotes as '\\''\n const escaped = value.replace(/'/g, \"'\\\\''\");\n lines.push(`${prefix}${key}='${escaped}'`);\n }\n\n return lines.join(\"\\n\") + \"\\n\";\n }\n}\n", "import * as path from \"path\";\nimport { ClefManifest } from \"../types\";\nimport { EncryptionBackend } from \"../types\";\nimport { parse, ImportFormat } from \"./parsers\";\nexport type { ImportFormat, ParsedImport } from \"./parsers\";\n\nexport interface ImportOptions {\n format?: ImportFormat;\n prefix?: string;\n keys?: string[];\n overwrite?: boolean;\n dryRun?: boolean;\n stdin?: boolean;\n}\n\nexport interface ImportResult {\n imported: string[];\n skipped: string[];\n failed: Array<{ key: string; error: string }>;\n warnings: string[];\n dryRun: boolean;\n}\n\n/**\n * Imports secrets from `.env`, JSON, or YAML files into encrypted matrix cells.\n *\n * @example\n * ```ts\n * const runner = new ImportRunner(sopsClient);\n * const result = await runner.import(\"app/staging\", null, envContent, manifest, repoRoot, { format: \"dotenv\" });\n * ```\n */\nexport class ImportRunner {\n constructor(private readonly sopsClient: EncryptionBackend) {}\n\n /**\n * Parse a source file and import its key/value pairs into a target `namespace/environment` cell.\n *\n * @param target - Target cell in `namespace/environment` format.\n * @param sourcePath - Source file path used for format detection (pass `null` when reading from stdin).\n * @param content - Raw file content to import.\n * @param manifest - Parsed manifest.\n * @param repoRoot - Absolute path to the repository root.\n * @param options - Import options (format, prefix, key filter, overwrite, dry-run).\n */\n async import(\n target: string,\n sourcePath: string | null,\n content: string,\n manifest: ClefManifest,\n repoRoot: string,\n options: ImportOptions,\n ): Promise<ImportResult> {\n const [ns, env] = target.split(\"/\");\n const filePath = path.join(\n repoRoot,\n manifest.file_pattern.replace(\"{namespace}\", ns).replace(\"{environment}\", env),\n );\n\n // Parse content\n const parsed = parse(content, options.format ?? \"auto\", sourcePath ?? \"\");\n\n // Build candidate key/value pairs\n let candidates = Object.entries(parsed.pairs);\n\n // Apply prefix filter\n if (options.prefix) {\n const prefix = options.prefix;\n candidates = candidates.filter(([key]) => key.startsWith(prefix));\n }\n\n // Apply keys filter\n if (options.keys && options.keys.length > 0) {\n const keySet = new Set(options.keys);\n candidates = candidates.filter(([key]) => keySet.has(key));\n }\n\n const imported: string[] = [];\n const skipped: string[] = [];\n const failed: Array<{ key: string; error: string }> = [];\n const warnings = [...parsed.warnings];\n\n if (options.dryRun) {\n // Dry run: check existing keys but never call encrypt\n let existingKeys: Set<string>;\n try {\n const decrypted = await this.sopsClient.decrypt(filePath);\n existingKeys = new Set(Object.keys(decrypted.values));\n } catch {\n // File may not exist or be inaccessible \u2014 treat as empty\n existingKeys = new Set<string>();\n }\n\n for (const [key] of candidates) {\n if (existingKeys.has(key) && !options.overwrite) {\n skipped.push(key);\n } else {\n imported.push(key);\n }\n }\n\n return { imported, skipped, failed, warnings, dryRun: true };\n }\n\n // Real import\n const decrypted = await this.sopsClient.decrypt(filePath);\n let currentValues: Record<string, string> = { ...decrypted.values };\n const existingKeys = new Set(Object.keys(decrypted.values));\n\n for (const [key, value] of candidates) {\n if (existingKeys.has(key) && !options.overwrite) {\n skipped.push(key);\n continue;\n }\n\n try {\n const newValues = { ...currentValues, [key]: value };\n await this.sopsClient.encrypt(filePath, newValues, manifest, env);\n currentValues = newValues;\n imported.push(key);\n } catch (err) {\n const message = err instanceof Error ? err.message : \"Encryption failed\";\n failed.push({ key, error: message });\n // Do NOT update currentValues, do NOT rollback previous encrypts. Continue with rest.\n }\n }\n\n return { imported, skipped, failed, warnings, dryRun: false };\n }\n}\n", "import * as path from \"path\";\nimport * as YAML from \"yaml\";\n\nexport type ImportFormat = \"dotenv\" | \"json\" | \"yaml\" | \"auto\";\n\nexport interface ParsedImport {\n pairs: Record<string, string>;\n format: Exclude<ImportFormat, \"auto\">;\n skipped: string[];\n warnings: string[];\n}\n\n/**\n * Auto-detect the format of a file from its extension, basename, and content heuristics.\n *\n * @param filePath - File path used for extension and basename detection.\n * @param content - Raw file content used as a fallback heuristic.\n * @returns Detected format (`\"dotenv\"`, `\"json\"`, or `\"yaml\"`).\n */\nexport function detectFormat(filePath: string, content: string): Exclude<ImportFormat, \"auto\"> {\n const base = path.basename(filePath);\n const ext = path.extname(filePath).toLowerCase();\n\n // basename is \".env\" or starts with \".env.\"\n if (base === \".env\" || base.startsWith(\".env.\")) {\n return \"dotenv\";\n }\n\n // ends with \".env\"\n if (base.endsWith(\".env\")) {\n return \"dotenv\";\n }\n\n // extension-based\n if (ext === \".json\") return \"json\";\n if (ext === \".yaml\" || ext === \".yml\") return \"yaml\";\n\n // content heuristics\n const trimmed = content.trimStart();\n if (trimmed.startsWith(\"{\")) {\n return \"json\";\n }\n\n // try JSON.parse \u2014 if it's a non-array object, it's JSON\n try {\n const parsed = JSON.parse(content);\n if (parsed !== null && typeof parsed === \"object\" && !Array.isArray(parsed)) {\n return \"json\";\n }\n } catch {\n // not JSON\n }\n\n // try YAML.parse \u2014 if it's a non-array object, it's YAML\n try {\n const parsed = YAML.parse(content);\n if (parsed !== null && typeof parsed === \"object\" && !Array.isArray(parsed)) {\n return \"yaml\";\n }\n } catch {\n // not YAML\n }\n\n // fallback\n return \"dotenv\";\n}\n\n/**\n * Parse dotenv-formatted content into flat key/value pairs.\n * Supports `export KEY=VALUE`, inline comments, and both single- and double-quoted values.\n */\nexport function parseDotenv(content: string): ParsedImport {\n const pairs: Record<string, string> = {};\n const skipped: string[] = [];\n const warnings: string[] = [];\n\n const lines = content.split(\"\\n\");\n for (const rawLine of lines) {\n let line = rawLine.trim();\n\n // Skip blank lines and comments\n if (!line || line.startsWith(\"#\")) {\n continue;\n }\n\n // Strip \"export \" prefix\n if (line.startsWith(\"export \")) {\n line = line.slice(7);\n }\n\n // Must have KEY=VALUE format\n const eqIdx = line.indexOf(\"=\");\n if (eqIdx === -1) {\n continue;\n }\n\n const key = line.slice(0, eqIdx).trim();\n if (!key) {\n continue;\n }\n\n let value = line.slice(eqIdx + 1);\n\n // Strip inline comments: everything after \" #\" (space-hash)\n const inlineCommentIdx = value.indexOf(\" #\");\n if (inlineCommentIdx !== -1) {\n value = value.slice(0, inlineCommentIdx);\n }\n\n // Strip matching outer quotes (\" or ')\n if (\n (value.startsWith('\"') && value.endsWith('\"')) ||\n (value.startsWith(\"'\") && value.endsWith(\"'\"))\n ) {\n value = value.slice(1, -1);\n }\n\n pairs[key] = value;\n }\n\n return { pairs, format: \"dotenv\", skipped, warnings };\n}\n\n/**\n * Parse a JSON object into flat string key/value pairs.\n * Non-string values (numbers, booleans, nulls, arrays, objects) are skipped with warnings.\n *\n * @throws `Error` If the content is not valid JSON or the root is not an object.\n */\nexport function parseJson(content: string): ParsedImport {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n let parsed: any;\n try {\n parsed = JSON.parse(content);\n } catch (err) {\n throw new Error(`Invalid JSON: ${(err as Error).message}`);\n }\n\n if (Array.isArray(parsed)) {\n throw new Error(\n \"JSON root must be an object, not an array. Clef keys are flat key/value pairs.\",\n );\n }\n\n if (parsed === null || typeof parsed !== \"object\") {\n throw new Error(\"JSON root must be an object. Clef keys are flat key/value pairs.\");\n }\n\n const pairs: Record<string, string> = {};\n const skipped: string[] = [];\n const warnings: string[] = [];\n\n for (const [key, value] of Object.entries(parsed as Record<string, unknown>)) {\n if (typeof value === \"string\") {\n pairs[key] = value;\n } else if (value === null) {\n skipped.push(key);\n warnings.push(`${key}: skipped \u2014 value is null, not string`);\n } else if (Array.isArray(value)) {\n skipped.push(key);\n warnings.push(`${key}: skipped \u2014 value is array, not string`);\n } else if (typeof value === \"object\") {\n skipped.push(key);\n warnings.push(`${key}: skipped \u2014 value is nested object, not string`);\n } else {\n // number, boolean\n skipped.push(key);\n warnings.push(`${key}: skipped \u2014 value is ${typeof value}, not string`);\n }\n }\n\n return { pairs, format: \"json\", skipped, warnings };\n}\n\n/**\n * Parse a YAML mapping into flat string key/value pairs.\n * Non-string values are skipped with warnings.\n *\n * @throws `Error` If the content is not valid YAML or the root is not a mapping.\n */\nexport function parseYaml(content: string): ParsedImport {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n let parsed: any;\n try {\n parsed = YAML.parse(content);\n } catch (err) {\n throw new Error(`Invalid YAML: ${(err as Error).message}`);\n }\n\n if (Array.isArray(parsed)) {\n throw new Error(\n \"YAML root must be a mapping, not a sequence. Clef keys are flat key/value pairs.\",\n );\n }\n\n if (parsed === null || typeof parsed !== \"object\") {\n throw new Error(\"YAML root must be a mapping. Clef keys are flat key/value pairs.\");\n }\n\n const pairs: Record<string, string> = {};\n const skipped: string[] = [];\n const warnings: string[] = [];\n\n for (const [key, value] of Object.entries(parsed as Record<string, unknown>)) {\n if (typeof value === \"string\") {\n pairs[key] = value;\n } else if (value === null) {\n skipped.push(key);\n warnings.push(`${key}: skipped \u2014 value is null, not string`);\n } else if (Array.isArray(value)) {\n skipped.push(key);\n warnings.push(`${key}: skipped \u2014 value is array, not string`);\n } else if (typeof value === \"object\") {\n skipped.push(key);\n warnings.push(`${key}: skipped \u2014 value is nested object, not string`);\n } else {\n // number, boolean\n skipped.push(key);\n warnings.push(`${key}: skipped \u2014 value is ${typeof value}, not string`);\n }\n }\n\n return { pairs, format: \"yaml\", skipped, warnings };\n}\n\n/**\n * Parse content in the given format (or auto-detect) and return flat key/value pairs.\n *\n * @param content - Raw file content to parse.\n * @param format - Explicit format, or `\"auto\"` to detect from `filePath` and content.\n * @param filePath - File path used for format detection when `format` is `\"auto\"`.\n */\nexport function parse(content: string, format: ImportFormat, filePath?: string): ParsedImport {\n const resolved: Exclude<ImportFormat, \"auto\"> =\n format === \"auto\" ? detectFormat(filePath ?? \"\", content) : format;\n\n switch (resolved) {\n case \"dotenv\":\n return parseDotenv(content);\n case \"json\":\n return parseJson(content);\n case \"yaml\":\n return parseYaml(content);\n }\n}\n", "import * as fs from \"fs\";\nimport * as path from \"path\";\nimport * as YAML from \"yaml\";\nimport { ClefManifest, EncryptionBackend } from \"../types\";\nimport { MatrixManager } from \"../matrix/manager\";\nimport { validateAgePublicKey, keyPreview } from \"./validator\";\nimport { CLEF_MANIFEST_FILENAME } from \"../manifest/parser\";\n\nexport interface Recipient {\n key: string;\n preview: string;\n label?: string;\n}\n\nexport interface RecipientsResult {\n added?: Recipient;\n removed?: Recipient;\n recipients: Recipient[];\n reEncryptedFiles: string[];\n failedFiles: string[];\n warnings: string[];\n}\n\ninterface RawRecipientEntry {\n key: string;\n label?: string;\n}\n\nfunction parseRecipientEntry(entry: unknown): RawRecipientEntry {\n if (typeof entry === \"string\") {\n return { key: entry };\n }\n if (typeof entry === \"object\" && entry !== null) {\n const obj = entry as Record<string, unknown>;\n return {\n key: String(obj.key ?? \"\"),\n ...(typeof obj.label === \"string\" ? { label: obj.label } : {}),\n };\n }\n return { key: \"\" };\n}\n\nfunction toRecipient(entry: RawRecipientEntry): Recipient {\n return {\n key: entry.key,\n preview: keyPreview(entry.key),\n ...(entry.label ? { label: entry.label } : {}),\n };\n}\n\nfunction readManifestYaml(repoRoot: string): Record<string, unknown> {\n const manifestPath = path.join(repoRoot, CLEF_MANIFEST_FILENAME);\n const raw = fs.readFileSync(manifestPath, \"utf-8\");\n return YAML.parse(raw) as Record<string, unknown>;\n}\n\nfunction writeManifestYaml(repoRoot: string, doc: Record<string, unknown>): void {\n const manifestPath = path.join(repoRoot, CLEF_MANIFEST_FILENAME);\n fs.writeFileSync(manifestPath, YAML.stringify(doc), \"utf-8\");\n}\n\nfunction getRecipientsArray(doc: Record<string, unknown>): unknown[] {\n const sops = doc.sops as Record<string, unknown> | undefined;\n if (!sops) return [];\n const age = sops.age as Record<string, unknown> | undefined;\n if (!age) return [];\n const recipients = age.recipients;\n if (!Array.isArray(recipients)) return [];\n return recipients;\n}\n\nfunction ensureRecipientsArray(doc: Record<string, unknown>): unknown[] {\n if (!doc.sops || typeof doc.sops !== \"object\") {\n doc.sops = {};\n }\n const sops = doc.sops as Record<string, unknown>;\n if (!sops.age || typeof sops.age !== \"object\") {\n sops.age = {};\n }\n const age = sops.age as Record<string, unknown>;\n if (!Array.isArray(age.recipients)) {\n age.recipients = [];\n }\n return age.recipients as unknown[];\n}\n\nfunction getEnvironmentRecipientsArray(doc: Record<string, unknown>, envName: string): unknown[] {\n const environments = doc.environments as Record<string, unknown>[] | undefined;\n if (!Array.isArray(environments)) return [];\n const env = environments.find((e) => (e as Record<string, unknown>).name === envName) as\n | Record<string, unknown>\n | undefined;\n if (!env) return [];\n const recipients = env.recipients;\n if (!Array.isArray(recipients)) return [];\n return recipients;\n}\n\nfunction ensureEnvironmentRecipientsArray(\n doc: Record<string, unknown>,\n envName: string,\n): unknown[] {\n const environments = doc.environments as Record<string, unknown>[] | undefined;\n if (!Array.isArray(environments)) {\n throw new Error(`No environments array in manifest.`);\n }\n const env = environments.find((e) => (e as Record<string, unknown>).name === envName) as\n | Record<string, unknown>\n | undefined;\n if (!env) {\n throw new Error(`Environment '${envName}' not found in manifest.`);\n }\n if (!Array.isArray(env.recipients)) {\n env.recipients = [];\n }\n return env.recipients as unknown[];\n}\n\n/**\n * Manages age recipient keys in the manifest and re-encrypts matrix files on add/remove.\n * All add/remove operations are transactional \u2014 a failure triggers a full rollback.\n *\n * @example\n * ```ts\n * const manager = new RecipientManager(runner, matrixManager);\n * const result = await manager.add(\"age1...\", \"Alice\", manifest, repoRoot);\n * ```\n */\nexport class RecipientManager {\n constructor(\n private readonly encryption: EncryptionBackend,\n private readonly matrixManager: MatrixManager,\n ) {}\n\n /**\n * List all age recipients declared in the manifest.\n *\n * @param manifest - Parsed manifest.\n * @param repoRoot - Absolute path to the repository root.\n * @param environment - Optional environment name to list per-env recipients.\n */\n async list(manifest: ClefManifest, repoRoot: string, environment?: string): Promise<Recipient[]> {\n if (environment) {\n const env = manifest.environments.find((e) => e.name === environment);\n if (!env) {\n throw new Error(`Environment '${environment}' not found in manifest.`);\n }\n }\n const doc = readManifestYaml(repoRoot);\n const entries = environment\n ? getEnvironmentRecipientsArray(doc, environment)\n : getRecipientsArray(doc);\n return entries.map((entry) => toRecipient(parseRecipientEntry(entry)));\n }\n\n /**\n * Add a new age recipient and re-encrypt all existing matrix files.\n * Rolls back the manifest and any already-re-encrypted files on failure.\n *\n * @param key - age public key to add (`age1...`).\n * @param label - Optional human-readable label for the recipient.\n * @param manifest - Parsed manifest.\n * @param repoRoot - Absolute path to the repository root.\n * @param environment - Optional environment name to scope the operation.\n * @throws `Error` If the key is invalid or already present.\n */\n async add(\n key: string,\n label: string | undefined,\n manifest: ClefManifest,\n repoRoot: string,\n environment?: string,\n ): Promise<RecipientsResult> {\n const validation = validateAgePublicKey(key);\n if (!validation.valid) {\n throw new Error(validation.error);\n }\n const normalizedKey = validation.key!;\n\n if (environment) {\n const env = manifest.environments.find((e) => e.name === environment);\n if (!env) {\n throw new Error(`Environment '${environment}' not found in manifest.`);\n }\n }\n\n // Read current manifest\n const doc = readManifestYaml(repoRoot);\n const currentEntries = environment\n ? getEnvironmentRecipientsArray(doc, environment)\n : getRecipientsArray(doc);\n const currentKeys = currentEntries.map((e) => parseRecipientEntry(e).key);\n\n if (currentKeys.includes(normalizedKey)) {\n throw new Error(`Recipient '${keyPreview(normalizedKey)}' is already present.`);\n }\n\n // Save backup of manifest for rollback\n const manifestPath = path.join(repoRoot, CLEF_MANIFEST_FILENAME);\n const manifestBackup = fs.readFileSync(manifestPath, \"utf-8\");\n\n // Add new recipient to manifest\n const recipients = environment\n ? ensureEnvironmentRecipientsArray(doc, environment)\n : ensureRecipientsArray(doc);\n if (label) {\n recipients.push({ key: normalizedKey, label });\n } else {\n recipients.push(normalizedKey);\n }\n writeManifestYaml(repoRoot, doc);\n\n // Re-encrypt matching files\n const allCells = this.matrixManager.resolveMatrix(manifest, repoRoot).filter((c) => c.exists);\n const cells = environment ? allCells.filter((c) => c.environment === environment) : allCells;\n const reEncryptedFiles: string[] = [];\n const failedFiles: string[] = [];\n const fileBackups = new Map<string, string>();\n\n for (const cell of cells) {\n try {\n // Save file backup before re-encryption\n fileBackups.set(cell.filePath, fs.readFileSync(cell.filePath, \"utf-8\"));\n\n await this.encryption.addRecipient(cell.filePath, normalizedKey);\n\n reEncryptedFiles.push(cell.filePath);\n } catch {\n failedFiles.push(cell.filePath);\n\n // Rollback: restore manifest\n fs.writeFileSync(manifestPath, manifestBackup, \"utf-8\");\n\n // Rollback: restore previously re-encrypted files\n for (const reEncryptedFile of reEncryptedFiles) {\n const backup = fileBackups.get(reEncryptedFile);\n if (backup) {\n fs.writeFileSync(reEncryptedFile, backup, \"utf-8\");\n }\n }\n\n // Re-read the restored manifest for the result\n const restoredDoc = readManifestYaml(repoRoot);\n const restoredEntries = environment\n ? getEnvironmentRecipientsArray(restoredDoc, environment)\n : getRecipientsArray(restoredDoc);\n const restoredRecipients = restoredEntries.map((e) => toRecipient(parseRecipientEntry(e)));\n\n return {\n added: toRecipient({ key: normalizedKey, label }),\n recipients: restoredRecipients,\n reEncryptedFiles: [],\n failedFiles,\n warnings: [\"Rollback completed: manifest and re-encrypted files have been restored.\"],\n };\n }\n }\n\n // Build final recipient list\n const updatedDoc = readManifestYaml(repoRoot);\n const updatedEntries = environment\n ? getEnvironmentRecipientsArray(updatedDoc, environment)\n : getRecipientsArray(updatedDoc);\n const finalRecipients = updatedEntries.map((e) => toRecipient(parseRecipientEntry(e)));\n\n return {\n added: toRecipient({ key: normalizedKey, label }),\n recipients: finalRecipients,\n reEncryptedFiles,\n failedFiles,\n warnings: [],\n };\n }\n\n /**\n * Remove an age recipient and re-encrypt all existing matrix files.\n * Rolls back on failure. Note: re-encryption removes _future_ access only;\n * rotate secret values to fully revoke access.\n *\n * @param key - age public key to remove.\n * @param manifest - Parsed manifest.\n * @param repoRoot - Absolute path to the repository root.\n * @param environment - Optional environment name to scope the operation.\n * @throws `Error` If the key is not in the manifest.\n */\n async remove(\n key: string,\n manifest: ClefManifest,\n repoRoot: string,\n environment?: string,\n ): Promise<RecipientsResult> {\n const trimmedKey = key.trim();\n\n if (environment) {\n const env = manifest.environments.find((e) => e.name === environment);\n if (!env) {\n throw new Error(`Environment '${environment}' not found in manifest.`);\n }\n }\n\n // Read current manifest\n const doc = readManifestYaml(repoRoot);\n const currentEntries = environment\n ? getEnvironmentRecipientsArray(doc, environment)\n : getRecipientsArray(doc);\n const parsed = currentEntries.map((e) => parseRecipientEntry(e));\n const matchIndex = parsed.findIndex((p) => p.key === trimmedKey);\n\n if (matchIndex === -1) {\n throw new Error(`Recipient '${keyPreview(trimmedKey)}' is not in the manifest.`);\n }\n\n const removedEntry = parsed[matchIndex];\n\n // Save backup of manifest for rollback\n const manifestPath = path.join(repoRoot, CLEF_MANIFEST_FILENAME);\n const manifestBackup = fs.readFileSync(manifestPath, \"utf-8\");\n\n // Remove recipient from manifest\n const recipients = environment\n ? ensureEnvironmentRecipientsArray(doc, environment)\n : ensureRecipientsArray(doc);\n recipients.splice(matchIndex, 1);\n writeManifestYaml(repoRoot, doc);\n\n // Re-encrypt matching files\n const allCells = this.matrixManager.resolveMatrix(manifest, repoRoot).filter((c) => c.exists);\n const cells = environment ? allCells.filter((c) => c.environment === environment) : allCells;\n const reEncryptedFiles: string[] = [];\n const failedFiles: string[] = [];\n const fileBackups = new Map<string, string>();\n\n for (const cell of cells) {\n try {\n // Save file backup before re-encryption\n fileBackups.set(cell.filePath, fs.readFileSync(cell.filePath, \"utf-8\"));\n\n await this.encryption.removeRecipient(cell.filePath, trimmedKey);\n\n reEncryptedFiles.push(cell.filePath);\n } catch {\n failedFiles.push(cell.filePath);\n\n // Rollback: restore manifest\n fs.writeFileSync(manifestPath, manifestBackup, \"utf-8\");\n\n // Rollback: restore previously re-encrypted files\n for (const reEncryptedFile of reEncryptedFiles) {\n const backup = fileBackups.get(reEncryptedFile);\n if (backup) {\n fs.writeFileSync(reEncryptedFile, backup, \"utf-8\");\n }\n }\n\n // Re-read the restored manifest for the result\n const restoredDoc = readManifestYaml(repoRoot);\n const restoredEntries = environment\n ? getEnvironmentRecipientsArray(restoredDoc, environment)\n : getRecipientsArray(restoredDoc);\n const restoredRecipients = restoredEntries.map((e) => toRecipient(parseRecipientEntry(e)));\n\n return {\n removed: toRecipient(removedEntry),\n recipients: restoredRecipients,\n reEncryptedFiles: [],\n failedFiles,\n warnings: [\n \"Rollback completed: manifest and re-encrypted files have been restored.\",\n \"Re-encryption removes future access, not past access. Rotate secret values to complete revocation.\",\n ],\n };\n }\n }\n\n // Build final recipient list\n const updatedDoc = readManifestYaml(repoRoot);\n const updatedEntries = environment\n ? getEnvironmentRecipientsArray(updatedDoc, environment)\n : getRecipientsArray(updatedDoc);\n const finalRecipients = updatedEntries.map((e) => toRecipient(parseRecipientEntry(e)));\n\n return {\n removed: toRecipient(removedEntry),\n recipients: finalRecipients,\n reEncryptedFiles,\n failedFiles,\n warnings: [\n \"Re-encryption removes future access, not past access. Rotate secret values to complete revocation.\",\n ],\n };\n }\n}\n", "import * as fs from \"fs\";\nimport * as path from \"path\";\nimport * as YAML from \"yaml\";\n\nexport const REQUESTS_FILENAME = \".clef-requests.yaml\";\n\nconst HEADER_COMMENT =\n \"# Pending recipient access requests. Approve with: clef recipients approve <label>\\n\";\n\nexport interface RecipientRequest {\n key: string;\n label: string;\n requestedAt: Date;\n environment?: string;\n}\n\ninterface RawRequest {\n key: string;\n label: string;\n requested_at: string;\n environment?: string;\n}\n\nexport function requestsFilePath(repoRoot: string): string {\n return path.join(repoRoot, REQUESTS_FILENAME);\n}\n\n/** Load all pending requests. Returns empty array if file is missing or malformed. */\nexport function loadRequests(repoRoot: string): RecipientRequest[] {\n const filePath = requestsFilePath(repoRoot);\n try {\n if (!fs.existsSync(filePath)) return [];\n const content = fs.readFileSync(filePath, \"utf-8\");\n const parsed = YAML.parse(content);\n if (!parsed || !Array.isArray(parsed.requests)) return [];\n return parsed.requests.map((r: RawRequest) => ({\n key: r.key,\n label: r.label,\n requestedAt: new Date(r.requested_at),\n environment: r.environment,\n }));\n } catch {\n return [];\n }\n}\n\n/** Save requests to disk. Deletes the file if no requests remain. */\nexport function saveRequests(repoRoot: string, requests: RecipientRequest[]): void {\n const filePath = requestsFilePath(repoRoot);\n if (requests.length === 0) {\n try {\n fs.unlinkSync(filePath);\n } catch {\n // File may not exist\n }\n return;\n }\n const data = {\n requests: requests.map((r) => {\n const raw: RawRequest = {\n key: r.key,\n label: r.label,\n requested_at: r.requestedAt.toISOString(),\n };\n if (r.environment) raw.environment = r.environment;\n return raw;\n }),\n };\n fs.writeFileSync(filePath, HEADER_COMMENT + YAML.stringify(data), \"utf-8\");\n}\n\n/**\n * Add or update a request. If a request with the same key already exists,\n * update its label, timestamp, and environment. Returns the upserted request.\n */\nexport function upsertRequest(\n repoRoot: string,\n key: string,\n label: string,\n environment?: string,\n): RecipientRequest {\n const requests = loadRequests(repoRoot);\n const now = new Date();\n const request: RecipientRequest = { key, label, requestedAt: now, environment };\n\n const existingIndex = requests.findIndex((r) => r.key === key);\n if (existingIndex >= 0) {\n requests[existingIndex] = request;\n } else {\n requests.push(request);\n }\n\n saveRequests(repoRoot, requests);\n return request;\n}\n\n/**\n * Remove a request by matching against label (case-insensitive) or key (exact).\n * Returns the removed request, or null if not found.\n */\nexport function removeRequest(repoRoot: string, identifier: string): RecipientRequest | null {\n const requests = loadRequests(repoRoot);\n const match = findInList(requests, identifier);\n if (!match) return null;\n\n const filtered = requests.filter((r) => r.key !== match.key);\n saveRequests(repoRoot, filtered);\n return match;\n}\n\n/**\n * Find a request by label (case-insensitive) or key (exact match).\n * Returns null if not found.\n */\nexport function findRequest(repoRoot: string, identifier: string): RecipientRequest | null {\n const requests = loadRequests(repoRoot);\n return findInList(requests, identifier);\n}\n\nfunction findInList(requests: RecipientRequest[], identifier: string): RecipientRequest | null {\n const lower = identifier.toLowerCase();\n // Match by label (case-insensitive)\n const byLabel = requests.find((r) => r.label.toLowerCase() === lower);\n if (byLabel) return byLabel;\n // Match by key (exact)\n const byKey = requests.find((r) => r.key === identifier);\n return byKey ?? null;\n}\n", "import * as fs from \"fs\";\nimport * as path from \"path\";\nimport * as YAML from \"yaml\";\nimport { ManifestParser, CLEF_MANIFEST_FILENAME } from \"../manifest/parser\";\nimport { MatrixManager } from \"../matrix/manager\";\nimport { DriftIssue, DriftResult } from \"../types\";\n\n/**\n * Compares key sets across two local Clef repositories without decryption.\n *\n * SOPS files store key names in plaintext \u2014 only values are encrypted.\n * This means drift detection works without the `sops` binary or any\n * decryption keys. The detector reads `.enc.yaml` files as plain YAML,\n * strips the `sops` metadata key, and compares the remaining top-level\n * keys across all environments from both repos within each shared namespace.\n */\nexport class DriftDetector {\n private parser = new ManifestParser();\n private matrix = new MatrixManager();\n\n /**\n * Compare key sets between two Clef repos.\n *\n * @param localRoot - Path to the first (local) Clef repository.\n * @param remoteRoot - Path to the second (remote/other) Clef repository.\n * @param namespaceFilter - Optional list of namespace names to scope comparison.\n * @returns Drift result with any issues found.\n */\n detect(localRoot: string, remoteRoot: string, namespaceFilter?: string[]): DriftResult {\n const localManifest = this.parser.parse(path.join(localRoot, CLEF_MANIFEST_FILENAME));\n const remoteManifest = this.parser.parse(path.join(remoteRoot, CLEF_MANIFEST_FILENAME));\n\n const localCells = this.matrix.resolveMatrix(localManifest, localRoot);\n const remoteCells = this.matrix.resolveMatrix(remoteManifest, remoteRoot);\n\n const localEnvNames = localManifest.environments.map((e) => e.name);\n const remoteEnvNames = remoteManifest.environments.map((e) => e.name);\n\n // Find shared namespaces\n const localNsNames = new Set(localManifest.namespaces.map((n) => n.name));\n const remoteNsNames = new Set(remoteManifest.namespaces.map((n) => n.name));\n let sharedNamespaces = [...localNsNames].filter((n) => remoteNsNames.has(n));\n\n if (namespaceFilter && namespaceFilter.length > 0) {\n const filterSet = new Set(namespaceFilter);\n sharedNamespaces = sharedNamespaces.filter((n) => filterSet.has(n));\n }\n\n const issues: DriftIssue[] = [];\n let namespacesClean = 0;\n\n for (const ns of sharedNamespaces) {\n // Collect key \u2192 environment sets across both repos\n const keyEnvs = new Map<string, Set<string>>();\n const allEnvs = new Set<string>();\n\n // Read keys from local cells\n const localNsCells = localCells.filter((c) => c.namespace === ns);\n for (const cell of localNsCells) {\n const keys = this.readKeysFromFile(cell.filePath);\n if (keys === null) continue;\n allEnvs.add(cell.environment);\n for (const key of keys) {\n if (!keyEnvs.has(key)) keyEnvs.set(key, new Set());\n keyEnvs.get(key)!.add(cell.environment);\n }\n }\n\n // Read keys from remote cells\n const remoteNsCells = remoteCells.filter((c) => c.namespace === ns);\n for (const cell of remoteNsCells) {\n const keys = this.readKeysFromFile(cell.filePath);\n if (keys === null) continue;\n allEnvs.add(cell.environment);\n for (const key of keys) {\n if (!keyEnvs.has(key)) keyEnvs.set(key, new Set());\n keyEnvs.get(key)!.add(cell.environment);\n }\n }\n\n // Compare: a key must exist in every environment that has a readable file\n const envList = [...allEnvs];\n let nsClean = true;\n\n for (const [key, envSet] of keyEnvs) {\n const missingFrom = envList.filter((e) => !envSet.has(e));\n if (missingFrom.length > 0) {\n nsClean = false;\n const presentIn = [...envSet].sort();\n issues.push({\n namespace: ns,\n key,\n presentIn,\n missingFrom: missingFrom.sort(),\n message: `Key '${key}' in namespace '${ns}' exists in [${presentIn.join(\", \")}] but is missing from [${missingFrom.sort().join(\", \")}]`,\n });\n }\n }\n\n if (nsClean) namespacesClean++;\n }\n\n return {\n issues,\n namespacesCompared: sharedNamespaces.length,\n namespacesClean,\n localEnvironments: localEnvNames,\n remoteEnvironments: remoteEnvNames,\n };\n }\n\n /**\n * Read top-level key names from an encrypted SOPS YAML file,\n * filtering out the `sops` metadata key.\n *\n * @returns Array of key names, or `null` if the file cannot be read.\n */\n private readKeysFromFile(filePath: string): string[] | null {\n try {\n if (!fs.existsSync(filePath)) return null;\n const raw = fs.readFileSync(filePath, \"utf-8\");\n const parsed = YAML.parse(raw);\n if (parsed === null || parsed === undefined || typeof parsed !== \"object\") return null;\n return Object.keys(parsed).filter((k) => k !== \"sops\");\n } catch {\n return null;\n }\n }\n}\n", "import * as fs from \"fs\";\nimport * as path from \"path\";\nimport * as YAML from \"yaml\";\nimport {\n ClefManifest,\n ClefReport,\n CLEF_REPORT_SCHEMA_VERSION,\n EncryptionBackend,\n MatrixCell,\n ReportCellMetadata,\n ReportManifestStructure,\n ReportMatrixCell,\n ReportPolicy,\n ReportRecipientSummary,\n ReportRepoIdentity,\n SubprocessRunner,\n} from \"../types\";\nimport { ManifestParser } from \"../manifest/parser\";\nimport { MatrixManager } from \"../matrix/manager\";\nimport { SchemaValidator } from \"../schema/validator\";\nimport { LintRunner } from \"../lint/runner\";\nimport { getPendingKeys } from \"../pending/metadata\";\nimport { checkDependency } from \"../dependencies/checker\";\nimport { ReportSanitizer } from \"./sanitizer\";\n\n/**\n * Orchestrates all data-gathering for a `clef report` invocation.\n * Matrix key counts are read from SOPS YAML directly (no decryption).\n * Policy issues are gathered via LintRunner then sanitized.\n */\nexport class ReportGenerator {\n constructor(\n private readonly runner: SubprocessRunner,\n private readonly sopsClient: EncryptionBackend,\n private readonly matrixManager: MatrixManager,\n private readonly schemaValidator: SchemaValidator,\n ) {}\n\n /**\n * Generate a full {@link ClefReport} for the given repository root.\n * Each section gathers data independently \u2014 partial failures return empty\n * values rather than aborting the entire report.\n *\n * @param repoRoot - Absolute path to the repository root.\n * @param clefVersion - The running CLI version string.\n * @param options - Optional namespace/environment filters.\n */\n async generate(\n repoRoot: string,\n clefVersion: string,\n options?: { namespaceFilter?: string[]; environmentFilter?: string[] },\n ): Promise<ClefReport> {\n let manifest: ClefManifest | null = null;\n try {\n const parser = new ManifestParser();\n manifest = parser.parse(path.join(repoRoot, \"clef.yaml\"));\n } catch {\n // Manifest parse failure \u2014 return minimal report\n const emptyManifest: ReportManifestStructure = {\n manifestVersion: 0,\n filePattern: \"\",\n environments: [],\n namespaces: [],\n defaultBackend: \"\",\n };\n return {\n schemaVersion: CLEF_REPORT_SCHEMA_VERSION,\n repoIdentity: await this.buildRepoIdentity(repoRoot, clefVersion),\n manifest: emptyManifest,\n matrix: [],\n policy: { issueCount: { error: 0, warning: 0, info: 0 }, issues: [] },\n recipients: {},\n };\n }\n\n const [repoIdentity, matrixCells, policy] = await Promise.all([\n this.buildRepoIdentity(repoRoot, clefVersion),\n this.buildMatrixCells(manifest, repoRoot, options),\n this.buildPolicy(manifest, repoRoot),\n ]);\n\n return {\n schemaVersion: CLEF_REPORT_SCHEMA_VERSION,\n repoIdentity,\n manifest: this.buildManifestStructure(manifest),\n matrix: matrixCells,\n policy,\n recipients: this.buildRecipients(matrixCells),\n };\n }\n\n private async buildRepoIdentity(\n repoRoot: string,\n clefVersion: string,\n ): Promise<ReportRepoIdentity> {\n let repoOrigin = \"\";\n let commitSha = \"\";\n let branch = \"\";\n let commitTimestamp = \"\";\n let sopsVersion: string | null = null;\n\n try {\n const r = await this.runner.run(\"git\", [\"remote\", \"get-url\", \"origin\"], { cwd: repoRoot });\n if (r.exitCode === 0) repoOrigin = this.normalizeRepoOrigin(r.stdout.trim());\n } catch {\n /* ignore */\n }\n\n try {\n const r = await this.runner.run(\"git\", [\"rev-parse\", \"HEAD\"], { cwd: repoRoot });\n if (r.exitCode === 0) commitSha = r.stdout.trim();\n } catch {\n /* ignore */\n }\n\n try {\n const r = await this.runner.run(\"git\", [\"branch\", \"--show-current\"], { cwd: repoRoot });\n if (r.exitCode === 0) branch = r.stdout.trim();\n } catch {\n /* ignore */\n }\n\n try {\n const r = await this.runner.run(\"git\", [\"log\", \"-1\", \"--format=%cI\"], { cwd: repoRoot });\n if (r.exitCode === 0) commitTimestamp = r.stdout.trim();\n } catch {\n /* ignore */\n }\n\n try {\n const dep = await checkDependency(\"sops\", this.runner);\n sopsVersion = dep?.installed ?? null;\n } catch {\n /* ignore */\n }\n\n return {\n repoOrigin,\n commitSha,\n branch,\n commitTimestamp,\n reportGeneratedAt: new Date().toISOString(),\n clefVersion,\n sopsVersion,\n };\n }\n\n private normalizeRepoOrigin(rawUrl: string): string {\n const sshMatch = rawUrl.match(/^git@([^:]+):(.+?)(?:\\.git)?$/);\n if (sshMatch) return `${sshMatch[1]}/${sshMatch[2]}`;\n const httpsMatch = rawUrl.match(/^https?:\\/\\/([^/]+)\\/(.+?)(?:\\.git)?$/);\n if (httpsMatch) return `${httpsMatch[1]}/${httpsMatch[2]}`;\n return rawUrl.replace(/\\.git$/, \"\");\n }\n\n private buildManifestStructure(manifest: ClefManifest): ReportManifestStructure {\n return {\n manifestVersion: manifest.version,\n filePattern: manifest.file_pattern,\n environments: manifest.environments.map((e) => ({\n name: e.name,\n protected: e.protected ?? false,\n })),\n namespaces: manifest.namespaces.map((ns) => ({\n name: ns.name,\n hasSchema: !!ns.schema,\n owners: ns.owners ?? [],\n })),\n defaultBackend: manifest.sops.default_backend,\n };\n }\n\n private async buildMatrixCells(\n manifest: ClefManifest,\n repoRoot: string,\n options?: { namespaceFilter?: string[]; environmentFilter?: string[] },\n ): Promise<ReportMatrixCell[]> {\n const allCells = this.matrixManager.resolveMatrix(manifest, repoRoot);\n const cells = allCells.filter((cell) => {\n const nsOk =\n !options?.namespaceFilter?.length || options.namespaceFilter.includes(cell.namespace);\n const envOk =\n !options?.environmentFilter?.length || options.environmentFilter.includes(cell.environment);\n return nsOk && envOk;\n });\n\n const result: ReportMatrixCell[] = [];\n\n for (const cell of cells) {\n result.push(await this.buildCell(cell));\n }\n\n return result;\n }\n\n private async buildCell(cell: MatrixCell): Promise<ReportMatrixCell> {\n if (!cell.exists) {\n return {\n namespace: cell.namespace,\n environment: cell.environment,\n filePath: cell.filePath,\n exists: false,\n keyCount: 0,\n pendingCount: 0,\n metadata: null,\n };\n }\n\n const keyCount = this.readKeyCount(cell.filePath);\n\n let pendingCount = 0;\n try {\n const pending = await getPendingKeys(cell.filePath);\n pendingCount = pending.length;\n } catch {\n /* ignore */\n }\n\n let metadata: ReportCellMetadata | null = null;\n try {\n const sopsMetadata = await this.sopsClient.getMetadata(cell.filePath);\n metadata = {\n backend: sopsMetadata.backend,\n recipients: sopsMetadata.recipients,\n lastModified: sopsMetadata.lastModified?.toISOString() ?? null,\n };\n } catch {\n /* ignore */\n }\n\n return {\n namespace: cell.namespace,\n environment: cell.environment,\n filePath: cell.filePath,\n exists: true,\n keyCount,\n pendingCount,\n metadata,\n };\n }\n\n private readKeyCount(filePath: string): number {\n try {\n if (!fs.existsSync(filePath)) return 0;\n const raw = fs.readFileSync(filePath, \"utf-8\");\n const parsed: unknown = YAML.parse(raw);\n if (parsed === null || parsed === undefined || typeof parsed !== \"object\") return 0;\n return Object.keys(parsed as Record<string, unknown>).filter((k) => k !== \"sops\").length;\n } catch {\n return 0;\n }\n }\n\n private async buildPolicy(manifest: ClefManifest, repoRoot: string): Promise<ReportPolicy> {\n try {\n const lintRunner = new LintRunner(this.matrixManager, this.schemaValidator, this.sopsClient);\n const lintResult = await lintRunner.run(manifest, repoRoot);\n return new ReportSanitizer().sanitize(lintResult.issues);\n } catch {\n return { issueCount: { error: 0, warning: 0, info: 0 }, issues: [] };\n }\n }\n\n private buildRecipients(cells: ReportMatrixCell[]): Record<string, ReportRecipientSummary> {\n const recipientMap = new Map<\n string,\n { type: string; environments: Set<string>; fileCount: number }\n >();\n\n for (const cell of cells) {\n if (!cell.metadata) continue;\n for (const recipient of cell.metadata.recipients) {\n const type = this.inferRecipientType(recipient);\n const existing = recipientMap.get(recipient);\n if (existing) {\n existing.environments.add(cell.environment);\n existing.fileCount++;\n } else {\n recipientMap.set(recipient, {\n type,\n environments: new Set([cell.environment]),\n fileCount: 1,\n });\n }\n }\n }\n\n const result: Record<string, ReportRecipientSummary> = {};\n for (const [recipient, data] of recipientMap.entries()) {\n result[recipient] = {\n type: data.type,\n environments: Array.from(data.environments),\n fileCount: data.fileCount,\n };\n }\n return result;\n }\n\n private inferRecipientType(recipient: string): string {\n if (recipient.startsWith(\"age1\")) return \"age\";\n if (recipient.startsWith(\"arn:aws:kms:\")) return \"awskms\";\n if (recipient.includes(\"projects/\") && recipient.includes(\"cryptoKeys/\")) return \"gcpkms\";\n return \"pgp\";\n }\n}\n", "import { LintIssue, ReportIssueCounts, ReportPolicy, ReportPolicyIssue } from \"../types\";\n\n/**\n * Transforms raw `LintIssue[]` from LintRunner into `ReportPolicy` with all\n * key names stripped and similar issues aggregated into counts.\n *\n * This is the trust boundary for Clef Pro: nothing emitted by this class\n * should contain a secret key name.\n */\nexport class ReportSanitizer {\n sanitize(lintIssues: LintIssue[]): ReportPolicy {\n const output: ReportPolicyIssue[] = [];\n\n // \u2500\u2500 Schema issues with a key field \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n const schemaWithKey = lintIssues.filter((i) => i.category === \"schema\" && i.key !== undefined);\n\n // Schema errors \u2192 group by file: \"N keys fail schema validation\"\n const schemaErrors = schemaWithKey.filter((i) => i.severity === \"error\");\n this.groupByFile(schemaErrors).forEach((issues, file) => {\n const n = issues.length;\n output.push({\n severity: \"error\",\n category: \"schema\",\n file,\n count: n,\n message: `${n} key${n !== 1 ? \"s\" : \"\"} fail schema validation`,\n });\n });\n\n // Schema warnings that are pending placeholders \u2192 group by file, reclassify to info/matrix\n const pendingWarnings = schemaWithKey.filter(\n (i) => i.severity === \"warning\" && i.message.includes(\"placeholder\"),\n );\n this.groupByFile(pendingWarnings).forEach((issues, file) => {\n const n = issues.length;\n output.push({\n severity: \"info\",\n category: \"matrix\",\n file,\n count: n,\n message: `${n} pending key${n !== 1 ? \"s\" : \"\"} awaiting values`,\n });\n });\n\n // Schema warnings that are NOT pending \u2192 group by file: \"N keys have schema warnings\"\n const schemaWarnings = schemaWithKey.filter(\n (i) => i.severity === \"warning\" && !i.message.includes(\"placeholder\"),\n );\n this.groupByFile(schemaWarnings).forEach((issues, file) => {\n const n = issues.length;\n output.push({\n severity: \"warning\",\n category: \"schema\",\n file,\n count: n,\n message: n === 1 ? \"1 key has schema warnings\" : `${n} keys have schema warnings`,\n });\n });\n\n // Schema info with key \u2192 DROP entirely (per-key noise that leaks key names)\n\n // \u2500\u2500 Schema issues without a key field \u2014 pass through \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n for (const issue of lintIssues.filter((i) => i.category === \"schema\" && i.key === undefined)) {\n output.push({\n severity: issue.severity,\n category: issue.category,\n file: issue.file,\n message: issue.message,\n });\n }\n\n // \u2500\u2500 Matrix issues \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n const matrixIssues = lintIssues.filter((i) => i.category === \"matrix\");\n\n // Matrix with key = cross-env drift \u2192 group by (namespace, targetEnv, sourceEnvs)\n const driftIssues = matrixIssues.filter((i) => i.key !== undefined);\n const driftGroups = new Map<\n string,\n { namespace: string; targetEnv: string; sourceEnvs: string; count: number }\n >();\n for (const issue of driftIssues) {\n // Use indexOf/lastIndexOf instead of a regex with unbounded quantifiers to\n // avoid ReDoS on uncontrolled `issue.message` input.\n const prefix = \"is missing in \";\n const middle = \" but present in \";\n const pi = issue.message.indexOf(prefix);\n if (pi === -1) continue;\n const afterPrefix = issue.message.indexOf(middle, pi + prefix.length);\n if (afterPrefix === -1) continue;\n const targetEnv = issue.message.slice(pi + prefix.length, afterPrefix);\n if (!targetEnv || /\\s/.test(targetEnv)) continue;\n const rest = issue.message.slice(afterPrefix + middle.length);\n if (!rest.endsWith(\".\")) continue;\n const sourceEnvs = rest.slice(0, -1);\n const namespace = this.extractNamespace(issue.file);\n const groupKey = `${namespace}|${targetEnv}|${sourceEnvs}`;\n const existing = driftGroups.get(groupKey);\n if (existing) {\n existing.count++;\n } else {\n driftGroups.set(groupKey, { namespace, targetEnv, sourceEnvs, count: 1 });\n }\n }\n for (const group of driftGroups.values()) {\n const n = group.count;\n output.push({\n severity: \"warning\",\n category: \"drift\",\n namespace: group.namespace,\n environment: group.targetEnv,\n sourceEnvironment: group.sourceEnvs,\n driftCount: n,\n message: `${n} key${n !== 1 ? \"s\" : \"\"} in [${group.sourceEnvs}] missing from ${group.targetEnv}`,\n });\n }\n\n // Matrix without key (missing file) \u2192 pass through\n for (const issue of matrixIssues.filter((i) => i.key === undefined)) {\n output.push({\n severity: issue.severity,\n category: issue.category,\n file: issue.file,\n message: issue.message,\n });\n }\n\n // \u2500\u2500 SOPS issues \u2014 pass through \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n for (const issue of lintIssues.filter((i) => i.category === \"sops\")) {\n output.push({\n severity: issue.severity,\n category: issue.category,\n file: issue.file,\n message: issue.message,\n });\n }\n\n // \u2500\u2500 Service-identity issues \u2014 pass through \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n for (const issue of lintIssues.filter((i) => i.category === \"service-identity\")) {\n output.push({\n severity: issue.severity,\n category: issue.category,\n file: issue.file,\n message: issue.message,\n });\n }\n\n const issueCount: ReportIssueCounts = {\n error: output.filter((i) => i.severity === \"error\").length,\n warning: output.filter((i) => i.severity === \"warning\").length,\n info: output.filter((i) => i.severity === \"info\").length,\n };\n\n return { issueCount, issues: output };\n }\n\n private groupByFile(issues: LintIssue[]): Map<string, LintIssue[]> {\n const map = new Map<string, LintIssue[]>();\n for (const issue of issues) {\n const arr = map.get(issue.file) ?? [];\n arr.push(issue);\n map.set(issue.file, arr);\n }\n return map;\n }\n\n private extractNamespace(filePath: string): string {\n const normalized = filePath.replace(/\\\\/g, \"/\");\n const parts = normalized.split(\"/\");\n return parts.length >= 2 ? (parts[parts.length - 2] ?? \"\") : (parts[0] ?? \"\");\n }\n}\n", "import {\n ClefReport,\n CloudApiReport,\n CloudCellHealthStatus,\n CloudPolicyResult,\n CloudReportCell,\n CloudReportDrift,\n CloudReportSummary,\n ReportMatrixCell,\n ReportPolicyIssue,\n} from \"../types\";\n\n/**\n * Transforms a local {@link ClefReport} into the {@link CloudApiReport} payload\n * expected by the Clef Pro API. Mapping is deterministic and side-effect-free.\n */\nexport class ReportTransformer {\n transform(report: ClefReport): CloudApiReport {\n const summary = this.buildSummary(report);\n const drift = this.buildDrift(report);\n const policyResults = this.buildPolicyResults(report.policy.issues);\n\n return {\n commitSha: report.repoIdentity.commitSha,\n branch: report.repoIdentity.branch,\n commitTimestamp: new Date(report.repoIdentity.commitTimestamp).getTime(),\n cliVersion: report.repoIdentity.clefVersion,\n summary,\n drift,\n policyResults,\n };\n }\n\n private buildSummary(report: ClefReport): CloudReportSummary {\n const namespaces = [...new Set(report.matrix.map((c) => c.namespace))];\n const environments = [...new Set(report.matrix.map((c) => c.environment))];\n const cells = report.matrix.map((cell) => this.buildCell(cell, report.policy.issues));\n const violations = report.policy.issues.filter((i) => i.severity === \"error\").length;\n\n return {\n filesScanned: report.matrix.length,\n namespaces,\n environments,\n cells,\n violations,\n passed: violations === 0,\n };\n }\n\n private buildCell(cell: ReportMatrixCell, issues: ReportPolicyIssue[]): CloudReportCell {\n const healthStatus = this.computeHealthStatus(cell, issues);\n const description = this.describeCell(cell, healthStatus);\n\n return {\n namespace: cell.namespace,\n environment: cell.environment,\n healthStatus,\n description,\n };\n }\n\n private computeHealthStatus(\n cell: ReportMatrixCell,\n issues: ReportPolicyIssue[],\n ): CloudCellHealthStatus {\n if (!cell.exists) return \"unknown\";\n\n const cellIssues = issues.filter(\n (i) =>\n (i.namespace === cell.namespace && i.environment === cell.environment) ||\n (i.file !== undefined &&\n i.file.includes(cell.namespace) &&\n i.file.includes(cell.environment)),\n );\n\n if (cellIssues.some((i) => i.severity === \"error\")) return \"critical\";\n if (cellIssues.some((i) => i.severity === \"warning\") || cell.pendingCount > 0) return \"warning\";\n return \"healthy\";\n }\n\n private describeCell(cell: ReportMatrixCell, status: CloudCellHealthStatus): string {\n switch (status) {\n case \"unknown\":\n return \"File does not exist\";\n case \"critical\":\n return \"Has error-severity policy issues\";\n case \"warning\":\n return cell.pendingCount > 0\n ? `${cell.pendingCount} pending key(s) awaiting values`\n : \"Has warning-severity policy issues\";\n case \"healthy\":\n return `${cell.keyCount} key(s), no issues`;\n }\n }\n\n private buildDrift(report: ClefReport): CloudReportDrift[] {\n const namespaces = [...new Set(report.matrix.map((c) => c.namespace))];\n const driftIssues = report.policy.issues.filter((i) => i.category === \"drift\");\n\n return namespaces.map((namespace) => {\n const nsIssues = driftIssues.filter((i) => i.namespace === namespace);\n const totalDrift = nsIssues.reduce((sum, i) => sum + (i.driftCount ?? 1), 0);\n return {\n namespace,\n isDrifted: totalDrift > 0,\n driftCount: totalDrift,\n };\n });\n }\n\n private buildPolicyResults(issues: ReportPolicyIssue[]): CloudPolicyResult[] {\n return issues.map((issue) => ({\n ruleId: `${issue.category}/${issue.severity}`,\n ruleName: issue.category,\n passed: issue.severity !== \"error\",\n severity: issue.severity,\n message: issue.message,\n ...(issue.namespace || issue.environment\n ? {\n scope: {\n ...(issue.namespace ? { namespace: issue.namespace } : {}),\n ...(issue.environment ? { environment: issue.environment } : {}),\n },\n }\n : {}),\n }));\n }\n}\n", "import {\n CloudApiError,\n CloudApiReport,\n CloudBatchPayload,\n CloudBatchResponse,\n CloudIntegrationResponse,\n CloudReportResponse,\n} from \"../types\";\n\nconst DEFAULT_RETRY_DELAY_MS = 1000;\n\n/**\n * HTTP client for the Clef Pro API.\n * Uses native `fetch()` (Node 18+). Retries once on 5xx or network errors.\n */\nexport class CloudClient {\n private readonly retryDelayMs: number;\n\n constructor(options?: { retryDelayMs?: number }) {\n this.retryDelayMs = options?.retryDelayMs ?? DEFAULT_RETRY_DELAY_MS;\n }\n async fetchIntegration(\n apiUrl: string,\n apiKey: string,\n integrationId: string,\n ): Promise<CloudIntegrationResponse> {\n const url = `${apiUrl}/api/v1/integrations/${encodeURIComponent(integrationId)}`;\n return this.request<CloudIntegrationResponse>(\"GET\", url, apiKey);\n }\n\n async submitReport(\n apiUrl: string,\n apiKey: string,\n report: CloudApiReport,\n ): Promise<CloudReportResponse> {\n const url = `${apiUrl}/api/v1/reports`;\n return this.request<CloudReportResponse>(\"POST\", url, apiKey, report);\n }\n\n async submitBatchReports(\n apiUrl: string,\n apiKey: string,\n batch: CloudBatchPayload,\n ): Promise<CloudBatchResponse> {\n const url = `${apiUrl}/api/v1/reports/batch`;\n return this.request<CloudBatchResponse>(\"POST\", url, apiKey, batch);\n }\n\n private async request<T>(\n method: string,\n url: string,\n apiKey: string,\n body?: unknown,\n ): Promise<T> {\n const headers: Record<string, string> = {\n Authorization: `Bearer ${apiKey}`,\n \"Content-Type\": \"application/json\",\n };\n\n const init: RequestInit = {\n method,\n headers,\n ...(body !== undefined ? { body: JSON.stringify(body) } : {}),\n };\n\n let response: Response;\n try {\n response = await fetch(url, init);\n } catch {\n // Network error \u2014 retry once\n await this.delay(this.retryDelayMs);\n try {\n response = await fetch(url, init);\n } catch (retryErr) {\n throw new CloudApiError(\n `Network error contacting Clef Pro: ${(retryErr as Error).message}`,\n 0,\n \"Check your network connection and CLEF_API_URL.\",\n );\n }\n }\n\n if (response.ok) {\n return (await response.json()) as T;\n }\n\n // 5xx \u2014 retry once\n if (response.status >= 500 && response.status < 600) {\n await this.delay(this.retryDelayMs);\n const retryResponse = await fetch(url, init);\n if (retryResponse.ok) {\n return (await retryResponse.json()) as T;\n }\n throw this.buildError(retryResponse);\n }\n\n // 4xx \u2014 do not retry\n throw this.buildError(response);\n }\n\n private buildError(response: Response): CloudApiError {\n const hint =\n response.status === 401 || response.status === 403\n ? \"Check your API token (--api-token or CLEF_API_TOKEN).\"\n : response.status === 404\n ? \"Check your cloud.integrationId in clef.yaml.\"\n : undefined;\n\n return new CloudApiError(\n `Clef Pro API returned ${response.status} ${response.statusText}`,\n response.status,\n hint,\n );\n }\n\n private delay(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n }\n}\n", "import { CloudCIContext } from \"../types\";\n\n/**\n * Detects the current CI provider from environment variables and returns\n * a {@link CloudCIContext} with provider, pipeline URL, and trigger info.\n *\n * Returns `undefined` when not running in a CI environment.\n */\nexport function collectCIContext(): CloudCIContext | undefined {\n const env = process.env;\n\n if (env.GITHUB_ACTIONS) {\n const serverUrl = env.GITHUB_SERVER_URL ?? \"https://github.com\";\n const repo = env.GITHUB_REPOSITORY ?? \"\";\n const runId = env.GITHUB_RUN_ID ?? \"\";\n const pipelineUrl = repo && runId ? `${serverUrl}/${repo}/actions/runs/${runId}` : undefined;\n return {\n provider: \"github-actions\",\n pipelineUrl,\n trigger: env.GITHUB_EVENT_NAME,\n };\n }\n\n if (env.GITLAB_CI) {\n return {\n provider: \"gitlab-ci\",\n pipelineUrl: env.CI_PIPELINE_URL,\n trigger: env.CI_PIPELINE_SOURCE,\n };\n }\n\n if (env.CIRCLECI) {\n return {\n provider: \"circleci\",\n pipelineUrl: env.CIRCLE_BUILD_URL,\n };\n }\n\n if (env.CI) {\n return {\n provider: \"unknown\",\n };\n }\n\n return undefined;\n}\n", "import { EncryptionBackend } from \"../types\";\n\n/** Status of a single key in a three-way merge. */\nexport type MergeKeyStatus = \"unchanged\" | \"ours\" | \"theirs\" | \"both_added\" | \"conflict\";\n\n/** One key's resolution in the three-way merge. */\nexport interface MergeKey {\n key: string;\n status: MergeKeyStatus;\n /** Resolved value when status is not \"conflict\". `null` for deletions or unresolvable conflicts. */\n value: string | null;\n /** Base value (common ancestor). `null` if the key did not exist in base. */\n baseValue: string | null;\n /** Value from ours. `null` if the key was deleted or absent in ours. */\n oursValue: string | null;\n /** Value from theirs. `null` if the key was deleted or absent in theirs. */\n theirsValue: string | null;\n}\n\n/** Result of a three-way merge. */\nexport interface MergeResult {\n /** `true` when all keys merged cleanly with no conflicts. */\n clean: boolean;\n /** The merged key/value map. Only complete when `clean` is `true`. */\n merged: Record<string, string>;\n /** Per-key resolution details. */\n keys: MergeKey[];\n /** Keys that could not be auto-resolved. Empty when `clean` is `true`. */\n conflicts: MergeKey[];\n}\n\n/**\n * Three-way merge driver for SOPS-encrypted files.\n *\n * Decrypts the base (common ancestor), ours (current branch), and theirs (incoming branch)\n * versions of a file, performs a three-way merge on the plaintext key/value maps, and\n * returns the merged result for re-encryption.\n *\n * @example\n * ```ts\n * const driver = new SopsMergeDriver(sopsClient);\n * const result = await driver.mergeFiles(basePath, oursPath, theirsPath);\n * if (result.clean) {\n * await sopsClient.encrypt(oursPath, result.merged, manifest, environment);\n * }\n * ```\n */\nexport class SopsMergeDriver {\n constructor(private readonly sopsClient: EncryptionBackend) {}\n\n /**\n * Perform a three-way merge on three in-memory key/value maps.\n *\n * Algorithm: For each key across all three maps, compare ours and theirs against base.\n * - If only one side changed relative to base, take that side's value.\n * - If both sides made the same change, take either (they agree).\n * - If both sides made different changes to the same key, it's a conflict.\n * - If a key was added on both sides with the same value, accept it.\n * - If a key was added on both sides with different values, it's a conflict.\n */\n merge(\n base: Record<string, string>,\n ours: Record<string, string>,\n theirs: Record<string, string>,\n ): MergeResult {\n const allKeys = new Set([...Object.keys(base), ...Object.keys(ours), ...Object.keys(theirs)]);\n\n const merged: Record<string, string> = {};\n const keys: MergeKey[] = [];\n const conflicts: MergeKey[] = [];\n\n for (const key of allKeys) {\n const inBase = key in base;\n const inOurs = key in ours;\n const inTheirs = key in theirs;\n const baseVal = inBase ? base[key] : null;\n const oursVal = inOurs ? ours[key] : null;\n const theirsVal = inTheirs ? theirs[key] : null;\n\n const oursChanged = oursVal !== baseVal;\n const theirsChanged = theirsVal !== baseVal;\n\n let status: MergeKeyStatus;\n let value: string | null;\n\n if (!oursChanged && !theirsChanged) {\n // Neither side changed this key relative to base\n status = \"unchanged\";\n value = baseVal;\n } else if (oursChanged && !theirsChanged) {\n // Only ours changed (including additions and deletions)\n status = \"ours\";\n value = oursVal;\n } else if (!oursChanged && theirsChanged) {\n // Only theirs changed (including additions and deletions)\n status = \"theirs\";\n value = theirsVal;\n } else if (oursVal === theirsVal) {\n // Both changed to the same value (or both deleted)\n status = !inBase && inOurs && inTheirs ? \"both_added\" : \"ours\";\n value = oursVal;\n } else {\n // Both changed to different values \u2014 conflict\n status = \"conflict\";\n value = null;\n }\n\n const mergeKey: MergeKey = {\n key,\n status,\n value,\n baseValue: baseVal,\n oursValue: oursVal,\n theirsValue: theirsVal,\n };\n keys.push(mergeKey);\n\n if (status === \"conflict\") {\n conflicts.push(mergeKey);\n } else if (value !== null) {\n merged[key] = value;\n }\n // value === null && status !== \"conflict\" means the key was deleted \u2014 omit from merged\n }\n\n // Sort keys alphabetically for stable output\n keys.sort((a, b) => a.key.localeCompare(b.key));\n conflicts.sort((a, b) => a.key.localeCompare(b.key));\n\n return { clean: conflicts.length === 0, merged, keys, conflicts };\n }\n\n /**\n * Decrypt three file versions and perform a three-way merge.\n *\n * @param basePath - Path to the common ancestor file (git %O).\n * @param oursPath - Path to the current branch file (git %A).\n * @param theirsPath - Path to the incoming branch file (git %B).\n * @returns The merge result. When `clean` is `true`, `merged` contains the resolved values.\n */\n async mergeFiles(basePath: string, oursPath: string, theirsPath: string): Promise<MergeResult> {\n const [baseDecrypted, oursDecrypted, theirsDecrypted] = await Promise.all([\n this.sopsClient.decrypt(basePath),\n this.sopsClient.decrypt(oursPath),\n this.sopsClient.decrypt(theirsPath),\n ]);\n\n return this.merge(baseDecrypted.values, oursDecrypted.values, theirsDecrypted.values);\n }\n}\n", "import * as fs from \"fs\";\nimport * as os from \"os\";\nimport * as path from \"path\";\nimport * as YAML from \"yaml\";\nimport {\n ClefManifest,\n EncryptionBackend,\n KmsConfig,\n ServiceIdentityDefinition,\n ServiceIdentityDriftIssue,\n ServiceIdentityEnvironmentConfig,\n isKmsEnvelope,\n} from \"../types\";\nimport { generateAgeIdentity } from \"../age/keygen\";\nimport { MatrixManager } from \"../matrix/manager\";\nimport { CLEF_MANIFEST_FILENAME } from \"../manifest/parser\";\n\n/**\n * Thrown when key rotation partially completes before a failure.\n * Contains the private keys for environments that were successfully rotated.\n */\nexport class PartialRotationError extends Error {\n constructor(\n message: string,\n public readonly rotatedKeys: Record<string, string>,\n ) {\n super(message);\n this.name = \"PartialRotationError\";\n }\n}\n\n/**\n * Manages service identities: creation, listing, key rotation, and drift validation.\n *\n * @example\n * ```ts\n * const manager = new ServiceIdentityManager(sopsClient, matrixManager);\n * const result = await manager.create(\"api-gw\", [\"api\"], \"API gateway\", manifest, repoRoot);\n * ```\n */\nexport class ServiceIdentityManager {\n constructor(\n private readonly encryption: EncryptionBackend,\n private readonly matrixManager: MatrixManager,\n ) {}\n\n /**\n * Create a new service identity with per-environment age key pairs or KMS envelope config.\n * For age-only: generates keys, updates the manifest, and registers public keys as SOPS recipients.\n * For KMS: stores KMS config in manifest, no age keys generated.\n *\n * @param kmsEnvConfigs - Optional per-environment KMS config. When provided, those envs use\n * KMS envelope encryption instead of generating age keys.\n * @returns The created identity definition and the per-environment private keys (empty for KMS envs).\n */\n async create(\n name: string,\n namespaces: string[],\n description: string,\n manifest: ClefManifest,\n repoRoot: string,\n kmsEnvConfigs?: Record<string, KmsConfig>,\n ): Promise<{\n identity: ServiceIdentityDefinition;\n privateKeys: Record<string, string>;\n }> {\n // Validate name uniqueness\n if (manifest.service_identities?.some((si) => si.name === name)) {\n throw new Error(`Service identity '${name}' already exists.`);\n }\n\n // Validate namespace references\n const validNamespaces = new Set(manifest.namespaces.map((ns) => ns.name));\n for (const ns of namespaces) {\n if (!validNamespaces.has(ns)) {\n throw new Error(`Namespace '${ns}' not found in manifest.`);\n }\n }\n\n // Generate per-environment config\n const environments: Record<string, ServiceIdentityEnvironmentConfig> = {};\n const privateKeys: Record<string, string> = {};\n\n for (const env of manifest.environments) {\n const kmsConfig = kmsEnvConfigs?.[env.name];\n if (kmsConfig) {\n // KMS envelope path \u2014 no age keys generated\n environments[env.name] = { kms: kmsConfig };\n } else {\n // Age-only path\n const identity = await generateAgeIdentity();\n environments[env.name] = { recipient: identity.publicKey };\n privateKeys[env.name] = identity.privateKey;\n }\n }\n\n const definition: ServiceIdentityDefinition = {\n name,\n description,\n namespaces,\n environments,\n };\n\n // Register public keys as SOPS recipients on scoped files BEFORE writing\n // the manifest, so a registration failure doesn't leave orphaned state.\n // (Only for age-only environments \u2014 KMS envs have no recipient to register.)\n await this.registerRecipients(definition, manifest, repoRoot);\n\n // Update manifest on disk\n const manifestPath = path.join(repoRoot, CLEF_MANIFEST_FILENAME);\n const raw = fs.readFileSync(manifestPath, \"utf-8\");\n const doc = YAML.parse(raw) as Record<string, unknown>;\n\n if (!Array.isArray(doc.service_identities)) {\n doc.service_identities = [];\n }\n (doc.service_identities as unknown[]).push({\n name,\n description,\n namespaces,\n environments,\n });\n const tmpCreate = path.join(os.tmpdir(), `clef-manifest-${process.pid}-${Date.now()}.tmp`);\n try {\n fs.writeFileSync(tmpCreate, YAML.stringify(doc), \"utf-8\");\n fs.renameSync(tmpCreate, manifestPath);\n } finally {\n try {\n fs.unlinkSync(tmpCreate);\n } catch {\n // Already renamed or never written \u2014 ignore\n }\n }\n\n return { identity: definition, privateKeys };\n }\n\n /**\n * List all service identities from the manifest.\n */\n list(manifest: ClefManifest): ServiceIdentityDefinition[] {\n return manifest.service_identities ?? [];\n }\n\n /**\n * Get a single service identity by name.\n */\n get(manifest: ClefManifest, name: string): ServiceIdentityDefinition | undefined {\n return manifest.service_identities?.find((si) => si.name === name);\n }\n\n /**\n * Delete a service identity: remove its recipients from scoped SOPS files\n * and remove it from the manifest.\n */\n async delete(name: string, manifest: ClefManifest, repoRoot: string): Promise<void> {\n const identity = this.get(manifest, name);\n if (!identity) {\n throw new Error(`Service identity '${name}' not found.`);\n }\n\n // Remove age recipients from scoped files\n const cells = this.matrixManager.resolveMatrix(manifest, repoRoot).filter((c) => c.exists);\n for (const cell of cells) {\n if (!identity.namespaces.includes(cell.namespace)) continue;\n const envConfig = identity.environments[cell.environment];\n if (!envConfig?.recipient) continue;\n if (isKmsEnvelope(envConfig)) continue;\n\n try {\n await this.encryption.removeRecipient(cell.filePath, envConfig.recipient);\n } catch {\n // May not be a current recipient\n }\n }\n\n // Remove from manifest on disk\n const manifestPath = path.join(repoRoot, CLEF_MANIFEST_FILENAME);\n const raw = fs.readFileSync(manifestPath, \"utf-8\");\n const doc = YAML.parse(raw) as Record<string, unknown>;\n const identities = doc.service_identities as Record<string, unknown>[];\n if (Array.isArray(identities)) {\n doc.service_identities = identities.filter(\n (si) => (si as Record<string, unknown>).name !== name,\n );\n }\n const tmp = path.join(os.tmpdir(), `clef-manifest-${process.pid}-${Date.now()}.tmp`);\n try {\n fs.writeFileSync(tmp, YAML.stringify(doc), \"utf-8\");\n fs.renameSync(tmp, manifestPath);\n } finally {\n try {\n fs.unlinkSync(tmp);\n } catch {\n // Already renamed or never written \u2014 ignore\n }\n }\n }\n\n /**\n * Update environment backends on an existing service identity.\n * Switches age \u2192 KMS (removes old recipient) or updates KMS config.\n * Returns new private keys for any environments switched from KMS \u2192 age.\n */\n async updateEnvironments(\n name: string,\n kmsEnvConfigs: Record<string, KmsConfig>,\n manifest: ClefManifest,\n repoRoot: string,\n ): Promise<{ privateKeys: Record<string, string> }> {\n const identity = this.get(manifest, name);\n if (!identity) {\n throw new Error(`Service identity '${name}' not found.`);\n }\n\n const manifestPath = path.join(repoRoot, CLEF_MANIFEST_FILENAME);\n const raw = fs.readFileSync(manifestPath, \"utf-8\");\n const doc = YAML.parse(raw) as Record<string, unknown>;\n const identities = doc.service_identities as Record<string, unknown>[];\n const siDoc = identities.find((si) => (si as Record<string, unknown>).name === name) as Record<\n string,\n unknown\n >;\n const envs = siDoc.environments as Record<string, Record<string, unknown>>;\n\n const cells = this.matrixManager.resolveMatrix(manifest, repoRoot).filter((c) => c.exists);\n const privateKeys: Record<string, string> = {};\n\n for (const [envName, kmsConfig] of Object.entries(kmsEnvConfigs)) {\n const oldConfig = identity.environments[envName];\n if (!oldConfig) {\n throw new Error(`Environment '${envName}' not found on identity '${name}'.`);\n }\n\n // If switching from age \u2192 KMS, remove the old age recipient from scoped files\n if (oldConfig.recipient) {\n const scopedCells = cells.filter(\n (c) => identity.namespaces.includes(c.namespace) && c.environment === envName,\n );\n for (const cell of scopedCells) {\n try {\n await this.encryption.removeRecipient(cell.filePath, oldConfig.recipient);\n } catch {\n // May not be a current recipient\n }\n }\n }\n\n // Update manifest entry to KMS\n envs[envName] = { kms: kmsConfig };\n identity.environments[envName] = { kms: kmsConfig };\n }\n\n // Write updated manifest\n const tmp = path.join(os.tmpdir(), `clef-manifest-${process.pid}-${Date.now()}.tmp`);\n try {\n fs.writeFileSync(tmp, YAML.stringify(doc), \"utf-8\");\n fs.renameSync(tmp, manifestPath);\n } finally {\n try {\n fs.unlinkSync(tmp);\n } catch {\n // Already renamed or never written \u2014 ignore\n }\n }\n\n return { privateKeys };\n }\n\n /**\n * Register a service identity's public keys as SOPS recipients on scoped matrix files.\n */\n async registerRecipients(\n identity: ServiceIdentityDefinition,\n manifest: ClefManifest,\n repoRoot: string,\n ): Promise<void> {\n const cells = this.matrixManager.resolveMatrix(manifest, repoRoot).filter((c) => c.exists);\n\n for (const cell of cells) {\n if (!identity.namespaces.includes(cell.namespace)) continue;\n\n const envConfig = identity.environments[cell.environment];\n if (!envConfig) continue;\n\n // KMS-backed environments have no recipient to register\n if (isKmsEnvelope(envConfig)) continue;\n if (!envConfig.recipient) continue;\n\n try {\n await this.encryption.addRecipient(cell.filePath, envConfig.recipient);\n } catch (err) {\n // SOPS exits non-zero for duplicate recipients \u2014 safe to ignore.\n // Re-throw genuine I/O or corruption errors.\n const message = err instanceof Error ? err.message : String(err);\n if (!message.includes(\"already\")) {\n throw err;\n }\n }\n }\n }\n\n /**\n * Rotate the age key for a service identity (all envs or a specific env).\n * Returns the new private keys.\n */\n async rotateKey(\n name: string,\n manifest: ClefManifest,\n repoRoot: string,\n environment?: string,\n ): Promise<Record<string, string>> {\n const identity = this.get(manifest, name);\n if (!identity) {\n throw new Error(`Service identity '${name}' not found.`);\n }\n\n const manifestPath = path.join(repoRoot, CLEF_MANIFEST_FILENAME);\n const raw = fs.readFileSync(manifestPath, \"utf-8\");\n const doc = YAML.parse(raw) as Record<string, unknown>;\n const identities = doc.service_identities as Record<string, unknown>[];\n const siDoc = identities.find((si) => (si as Record<string, unknown>).name === name) as Record<\n string,\n unknown\n >;\n const envs = siDoc.environments as Record<string, Record<string, string>>;\n\n const newPrivateKeys: Record<string, string> = {};\n const envsToRotate = environment ? [environment] : Object.keys(identity.environments);\n\n const cells = this.matrixManager.resolveMatrix(manifest, repoRoot).filter((c) => c.exists);\n\n try {\n for (const envName of envsToRotate) {\n const envConfig = identity.environments[envName];\n if (!envConfig) {\n throw new Error(`Environment '${envName}' not found on identity '${name}'.`);\n }\n // KMS-backed environments don't have age keys to rotate\n if (isKmsEnvelope(envConfig)) continue;\n const oldRecipient = envConfig.recipient;\n if (!oldRecipient) {\n throw new Error(`Environment '${envName}' not found on identity '${name}'.`);\n }\n\n const newIdentity = await generateAgeIdentity();\n newPrivateKeys[envName] = newIdentity.privateKey;\n\n // Update manifest\n envs[envName] = { recipient: newIdentity.publicKey };\n\n // Swap recipients on scoped files\n const scopedCells = cells.filter(\n (c) => identity.namespaces.includes(c.namespace) && c.environment === envName,\n );\n for (const cell of scopedCells) {\n try {\n await this.encryption.removeRecipient(cell.filePath, oldRecipient);\n } catch {\n // May not be a current recipient\n }\n try {\n await this.encryption.addRecipient(cell.filePath, newIdentity.publicKey);\n } catch (addErr) {\n // Attempt rollback: re-add old recipient\n try {\n await this.encryption.addRecipient(cell.filePath, oldRecipient);\n } catch {\n throw new Error(\n `Failed to add new recipient to ${cell.namespace}/${cell.environment} and rollback also failed. ` +\n `File may be in an inconsistent state. ` +\n `Old key: ${oldRecipient.slice(0, 12)}..., New key: ${newIdentity.publicKey.slice(0, 12)}...`,\n );\n }\n throw addErr;\n }\n }\n }\n } catch (err) {\n if (Object.keys(newPrivateKeys).length > 0) {\n const partialErr = new PartialRotationError(\n `Rotation failed after rotating ${Object.keys(newPrivateKeys).join(\", \")}: ${(err as Error).message}`,\n newPrivateKeys,\n );\n throw partialErr;\n }\n throw err;\n }\n\n const tmpRotate = path.join(os.tmpdir(), `clef-manifest-${process.pid}-${Date.now()}.tmp`);\n try {\n fs.writeFileSync(tmpRotate, YAML.stringify(doc), \"utf-8\");\n fs.renameSync(tmpRotate, manifestPath);\n } finally {\n try {\n fs.unlinkSync(tmpRotate);\n } catch {\n // Already renamed or never written \u2014 ignore\n }\n }\n return newPrivateKeys;\n }\n\n /**\n * Validate service identities and return drift issues.\n */\n async validate(manifest: ClefManifest, repoRoot: string): Promise<ServiceIdentityDriftIssue[]> {\n const issues: ServiceIdentityDriftIssue[] = [];\n const identities = manifest.service_identities ?? [];\n\n if (identities.length === 0) return issues;\n\n const declaredEnvNames = new Set(manifest.environments.map((e) => e.name));\n const declaredNsNames = new Set(manifest.namespaces.map((ns) => ns.name));\n const cells = this.matrixManager.resolveMatrix(manifest, repoRoot).filter((c) => c.exists);\n\n for (const si of identities) {\n // Check namespace references\n for (const ns of si.namespaces) {\n if (!declaredNsNames.has(ns)) {\n issues.push({\n identity: si.name,\n namespace: ns,\n type: \"namespace_not_found\",\n message: `Service identity '${si.name}' references non-existent namespace '${ns}'.`,\n });\n }\n }\n\n // Check environment coverage\n for (const envName of declaredEnvNames) {\n if (!(envName in si.environments)) {\n issues.push({\n identity: si.name,\n environment: envName,\n type: \"missing_environment\",\n message: `Service identity '${si.name}' is missing environment '${envName}'. Manually add an age key pair for this environment in clef.yaml.`,\n });\n }\n }\n\n // Check recipient registration on scoped files\n // (KMS-backed environments skip recipient checks \u2014 no recipient to register)\n for (const cell of cells) {\n const envConfig = si.environments[cell.environment];\n if (!envConfig) continue;\n if (isKmsEnvelope(envConfig)) continue;\n if (!envConfig.recipient) continue;\n\n if (si.namespaces.includes(cell.namespace)) {\n // Should be registered\n try {\n const metadata = await this.encryption.getMetadata(cell.filePath);\n if (!metadata.recipients.includes(envConfig.recipient)) {\n issues.push({\n identity: si.name,\n environment: cell.environment,\n namespace: cell.namespace,\n type: \"recipient_not_registered\",\n message: `Service identity '${si.name}' recipient is not registered in ${cell.namespace}/${cell.environment}.`,\n fixCommand: `clef service create ${si.name} --namespaces ${si.namespaces.join(\",\")}`,\n });\n }\n } catch {\n // Cannot read metadata \u2014 skip\n }\n } else {\n // Should NOT be registered (scope mismatch)\n try {\n const metadata = await this.encryption.getMetadata(cell.filePath);\n if (metadata.recipients.includes(envConfig.recipient)) {\n issues.push({\n identity: si.name,\n environment: cell.environment,\n namespace: cell.namespace,\n type: \"scope_mismatch\",\n message: `Service identity '${si.name}' recipient found in ${cell.namespace}/${cell.environment} but namespace '${cell.namespace}' is not in scope.`,\n fixCommand: `clef recipients remove ${envConfig.recipient} -e ${cell.environment}`,\n });\n }\n } catch {\n // Cannot read metadata \u2014 skip\n }\n }\n }\n }\n\n return issues;\n }\n}\n", "import {\n ClefManifest,\n EncryptionBackend,\n ServiceIdentityDefinition,\n ServiceIdentityEnvironmentConfig,\n} from \"../types\";\nimport { MatrixManager } from \"../matrix/manager\";\n\n/** Resolved identity secrets: merged key/value map plus metadata. */\nexport interface ResolvedSecrets {\n /** Flat key/value map (namespace-prefixed if multi-namespace). */\n values: Record<string, string>;\n /** The matched service identity definition. */\n identity: ServiceIdentityDefinition;\n /** Age public key for the target environment (undefined for KMS identities). */\n recipient?: string;\n /** Full environment config (age-only or KMS). */\n envConfig: ServiceIdentityEnvironmentConfig;\n}\n\n/**\n * Decrypt and merge scoped SOPS files for a service identity + environment.\n *\n * Shared by `ArtifactPacker` (and any future consumers) to avoid duplicating\n * the decrypt-merge-collision-check logic.\n */\nexport async function resolveIdentitySecrets(\n identityName: string,\n environment: string,\n manifest: ClefManifest,\n repoRoot: string,\n encryption: EncryptionBackend,\n matrixManager: MatrixManager,\n): Promise<ResolvedSecrets> {\n const identity = manifest.service_identities?.find((si) => si.name === identityName);\n if (!identity) {\n throw new Error(`Service identity '${identityName}' not found in manifest.`);\n }\n\n const envConfig = identity.environments[environment];\n if (!envConfig) {\n throw new Error(\n `Environment '${environment}' not found on service identity '${identityName}'.`,\n );\n }\n\n const allValues: Record<string, string> = {};\n const cells = matrixManager\n .resolveMatrix(manifest, repoRoot)\n .filter(\n (c) => c.exists && identity.namespaces.includes(c.namespace) && c.environment === environment,\n );\n\n const isMultiNamespace = identity.namespaces.length > 1;\n const collisions: string[] = [];\n\n for (const cell of cells) {\n const decrypted = await encryption.decrypt(cell.filePath);\n for (const [key, value] of Object.entries(decrypted.values)) {\n const qualifiedKey = isMultiNamespace ? `${cell.namespace}__${key}` : key;\n if (qualifiedKey in allValues && allValues[qualifiedKey] !== value) {\n collisions.push(qualifiedKey);\n }\n allValues[qualifiedKey] = value;\n }\n }\n\n if (collisions.length > 0) {\n throw new Error(\n `Key collision detected in bundle: ${collisions.join(\", \")}. ` +\n \"Keys with the same name but different values exist across namespaces.\",\n );\n }\n\n return {\n values: allValues,\n identity,\n recipient: envConfig.recipient,\n envConfig,\n };\n}\n", "import * as fs from \"fs\";\nimport * as path from \"path\";\nimport * as crypto from \"crypto\";\nimport { ClefManifest, EncryptionBackend, isKmsEnvelope } from \"../types\";\nimport { KmsProvider } from \"../kms\";\nimport { MatrixManager } from \"../matrix/manager\";\nimport { PackConfig, PackResult, PackedArtifact } from \"./types\";\nimport { resolveIdentitySecrets } from \"./resolve\";\nimport { buildSigningPayload, signEd25519, signKms } from \"./signer\";\n\n/**\n * Packs an encrypted artifact for a service identity + environment.\n *\n * The artifact is a JSON envelope containing age-encrypted secrets that can\n * be fetched by the runtime agent via HTTP or local file.\n */\nexport class ArtifactPacker {\n constructor(\n private readonly encryption: EncryptionBackend,\n private readonly matrixManager: MatrixManager,\n private readonly kms?: KmsProvider,\n ) {}\n\n /**\n * Pack an artifact: decrypt scoped SOPS files, age-encrypt the merged\n * values to the service identity's recipient, and write a JSON envelope.\n */\n async pack(config: PackConfig, manifest: ClefManifest, repoRoot: string): Promise<PackResult> {\n if (config.signingKey && config.signingKmsKeyId) {\n throw new Error(\n \"Cannot specify both signingKey (Ed25519) and signingKmsKeyId (KMS). Choose one.\",\n );\n }\n\n const resolved = await resolveIdentitySecrets(\n config.identity,\n config.environment,\n manifest,\n repoRoot,\n this.encryption,\n this.matrixManager,\n );\n\n const plaintext = JSON.stringify(resolved.values);\n\n let ciphertext: string;\n let artifact: PackedArtifact;\n\n if (isKmsEnvelope(resolved.envConfig)) {\n // KMS envelope path\n if (!this.kms) {\n throw new Error(\"KMS provider required for envelope encryption but none was provided.\");\n }\n\n // Generate ephemeral age key pair\n const { generateIdentity, identityToRecipient, Encrypter } = await import(\n // eslint-disable-next-line @typescript-eslint/no-explicit-any -- dynamic ESM import of CJS-incompatible package\n \"age-encryption\" as any\n );\n const ephemeralPrivateKey = (await generateIdentity()) as string;\n const ephemeralPublicKey = (await identityToRecipient(ephemeralPrivateKey)) as string;\n\n try {\n const e = new Encrypter();\n e.addRecipient(ephemeralPublicKey);\n const encrypted = await e.encrypt(plaintext);\n ciphertext = Buffer.from(encrypted as Uint8Array).toString(\"base64\");\n } catch {\n throw new Error(\"Failed to age-encrypt artifact with ephemeral key.\");\n }\n\n // Wrap the ephemeral private key with KMS\n const kmsConfig = resolved.envConfig.kms;\n const wrapped = await this.kms.wrap(kmsConfig.keyId, Buffer.from(ephemeralPrivateKey));\n\n const revision = `${Date.now()}-${crypto.randomBytes(4).toString(\"hex\")}`;\n const ciphertextHash = crypto.createHash(\"sha256\").update(ciphertext).digest(\"hex\");\n\n artifact = {\n version: 1,\n identity: config.identity,\n environment: config.environment,\n packedAt: new Date().toISOString(),\n revision,\n ciphertextHash,\n ciphertext,\n keys: Object.keys(resolved.values),\n envelope: {\n provider: kmsConfig.provider,\n keyId: kmsConfig.keyId,\n wrappedKey: wrapped.wrappedKey.toString(\"base64\"),\n algorithm: wrapped.algorithm,\n },\n };\n } else {\n // Age-only path (v1, unchanged)\n try {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any -- dynamic ESM import of CJS-incompatible package\n const { Encrypter } = await import(\"age-encryption\" as any);\n const e = new Encrypter();\n e.addRecipient(resolved.recipient!);\n const encrypted = await e.encrypt(plaintext);\n ciphertext = Buffer.from(encrypted as Uint8Array).toString(\"base64\");\n } catch {\n throw new Error(\"Failed to age-encrypt artifact. Check recipient key.\");\n }\n\n const revision = `${Date.now()}-${crypto.randomBytes(4).toString(\"hex\")}`;\n const ciphertextHash = crypto.createHash(\"sha256\").update(ciphertext).digest(\"hex\");\n\n artifact = {\n version: 1,\n identity: config.identity,\n environment: config.environment,\n packedAt: new Date().toISOString(),\n revision,\n ciphertextHash,\n ciphertext,\n keys: Object.keys(resolved.values),\n };\n }\n\n const outputDir = path.dirname(config.outputPath);\n if (!fs.existsSync(outputDir)) {\n fs.mkdirSync(outputDir, { recursive: true });\n }\n\n // Set expiresAt before signing \u2014 the signature covers this field\n if (config.ttl && config.ttl > 0) {\n artifact.expiresAt = new Date(Date.now() + config.ttl * 1000).toISOString();\n }\n\n // Sign the artifact if a signing key is provided\n if (config.signingKey) {\n const payload = buildSigningPayload(artifact);\n artifact.signature = signEd25519(payload, config.signingKey);\n artifact.signatureAlgorithm = \"Ed25519\";\n } else if (config.signingKmsKeyId) {\n if (!this.kms) {\n throw new Error(\"KMS provider required for KMS signing but none was provided.\");\n }\n const payload = buildSigningPayload(artifact);\n artifact.signature = await signKms(payload, this.kms, config.signingKmsKeyId);\n artifact.signatureAlgorithm = \"ECDSA_SHA256\";\n }\n\n const json = JSON.stringify(artifact, null, 2);\n const tmpOutput = `${config.outputPath}.tmp.${process.pid}`;\n fs.writeFileSync(tmpOutput, json, \"utf-8\");\n fs.renameSync(tmpOutput, config.outputPath);\n\n return {\n outputPath: config.outputPath,\n namespaceCount: resolved.identity.namespaces.length,\n keyCount: Object.keys(resolved.values).length,\n artifactSize: Buffer.byteLength(json, \"utf-8\"),\n revision: artifact.revision,\n };\n }\n}\n", "import * as crypto from \"crypto\";\nimport type { PackedArtifact, SignatureAlgorithm } from \"./types\";\nimport type { KmsProvider } from \"../kms\";\n\n/**\n * Build the canonical signing payload from an artifact.\n *\n * The payload is a deterministic newline-separated string of all\n * security-relevant fields. The signature covers everything the\n * runtime acts on \u2014 version, identity, environment, revision, timing,\n * integrity hash, key list, expiry, and envelope fields.\n *\n * `ciphertextHash` transitively covers the ciphertext content, so the\n * (potentially large) ciphertext itself is not included.\n *\n * Keys are sorted to ensure deterministic ordering regardless of\n * insertion order in the source object.\n */\nexport function buildSigningPayload(artifact: PackedArtifact): Buffer {\n const fields = [\n \"clef-sig-v1\",\n String(artifact.version),\n artifact.identity,\n artifact.environment,\n artifact.revision,\n artifact.packedAt,\n artifact.ciphertextHash,\n [...artifact.keys].sort().join(\",\"),\n artifact.expiresAt ?? \"\",\n artifact.envelope?.provider ?? \"\",\n artifact.envelope?.keyId ?? \"\",\n artifact.envelope?.wrappedKey ?? \"\",\n artifact.envelope?.algorithm ?? \"\",\n ];\n return Buffer.from(fields.join(\"\\n\"), \"utf-8\");\n}\n\n/**\n * Generate an Ed25519 signing key pair.\n * Returns base64-encoded DER keys (SPKI for public, PKCS8 for private).\n */\nexport function generateSigningKeyPair(): { publicKey: string; privateKey: string } {\n const pair = crypto.generateKeyPairSync(\"ed25519\");\n return {\n publicKey: (pair.publicKey.export({ type: \"spki\", format: \"der\" }) as Buffer).toString(\n \"base64\",\n ),\n privateKey: (pair.privateKey.export({ type: \"pkcs8\", format: \"der\" }) as Buffer).toString(\n \"base64\",\n ),\n };\n}\n\n/**\n * Sign an artifact payload with an Ed25519 private key.\n *\n * @param payload - Canonical signing payload from {@link buildSigningPayload}\n * @param privateKeyBase64 - Base64-encoded DER PKCS8 private key\n * @returns Base64-encoded Ed25519 signature\n */\nexport function signEd25519(payload: Buffer, privateKeyBase64: string): string {\n const keyObj = crypto.createPrivateKey({\n key: Buffer.from(privateKeyBase64, \"base64\"),\n format: \"der\",\n type: \"pkcs8\",\n });\n const signature = crypto.sign(null, payload, keyObj);\n return signature.toString(\"base64\");\n}\n\n/**\n * Sign an artifact payload with a KMS asymmetric signing key (ECDSA_SHA_256).\n *\n * The KMS `sign` method receives a SHA-256 digest (not the raw payload),\n * matching AWS KMS `MessageType: \"DIGEST\"` semantics.\n *\n * @param payload - Canonical signing payload from {@link buildSigningPayload}\n * @param kms - KMS provider with `sign` method\n * @param signingKeyId - ARN or ID of the KMS asymmetric signing key\n * @returns Base64-encoded ECDSA signature\n */\nexport async function signKms(\n payload: Buffer,\n kms: KmsProvider,\n signingKeyId: string,\n): Promise<string> {\n if (!kms.sign) {\n throw new Error(\n \"KMS provider does not support signing. Ensure the provider implements the sign() method.\",\n );\n }\n const digest = crypto.createHash(\"sha256\").update(payload).digest();\n const signature = await kms.sign(signingKeyId, digest);\n return signature.toString(\"base64\");\n}\n\n/**\n * Verify a signature against a public key.\n *\n * The algorithm is derived from the key's type (Ed25519 or EC), not from\n * the artifact's claimed `signatureAlgorithm` field. This prevents an\n * attacker from downgrading the verification algorithm.\n *\n * @param payload - Canonical signing payload from {@link buildSigningPayload}\n * @param signatureBase64 - Base64-encoded signature to verify\n * @param publicKeyBase64 - Base64-encoded DER SPKI public key\n * @returns true if the signature is valid\n */\nexport function verifySignature(\n payload: Buffer,\n signatureBase64: string,\n publicKeyBase64: string,\n): boolean {\n const keyObj = crypto.createPublicKey({\n key: Buffer.from(publicKeyBase64, \"base64\"),\n format: \"der\",\n type: \"spki\",\n });\n const signature = Buffer.from(signatureBase64, \"base64\");\n\n const keyType = keyObj.asymmetricKeyType;\n if (keyType === \"ed25519\") {\n return crypto.verify(null, payload, keyObj, signature);\n }\n if (keyType === \"ec\") {\n return crypto.verify(\"sha256\", payload, keyObj, signature);\n }\n throw new Error(`Unsupported key type for signature verification: ${keyType}`);\n}\n\n/**\n * Detect the signature algorithm from a DER SPKI public key.\n *\n * @param publicKeyBase64 - Base64-encoded DER SPKI public key\n * @returns The corresponding SignatureAlgorithm\n */\nexport function detectAlgorithm(publicKeyBase64: string): SignatureAlgorithm {\n const keyObj = crypto.createPublicKey({\n key: Buffer.from(publicKeyBase64, \"base64\"),\n format: \"der\",\n type: \"spki\",\n });\n const keyType = keyObj.asymmetricKeyType;\n if (keyType === \"ed25519\") return \"Ed25519\";\n if (keyType === \"ec\") return \"ECDSA_SHA256\";\n throw new Error(`Unsupported key type: ${keyType}`);\n}\n"],
|
|
5
|
+
"mappings": ";;;;;;;;AACO,IAAM,4BAA4B,CAAC,aAAa,WAAW;AAyE3D,SAAS,qBACd,UACA,aACyB;AACzB,QAAM,MAAM,SAAS,aAAa,KAAK,CAAC,MAAM,EAAE,SAAS,WAAW;AACpE,MAAI,KAAK,KAAM,QAAO,IAAI;AAC1B,SAAO;AAAA,IACL,SAAS,SAAS,KAAK;AAAA,IACvB,aAAa,SAAS,KAAK;AAAA,IAC3B,qBAAqB,SAAS,KAAK;AAAA,IACnC,cAAc,SAAS,KAAK;AAAA,IAC5B,iBAAiB,SAAS,KAAK;AAAA,EACjC;AACF;AAOO,SAAS,gCACd,UACA,aAC0D;AAC1D,QAAM,MAAM,SAAS,aAAa,KAAK,CAAC,MAAM,EAAE,SAAS,WAAW;AACpE,MAAI,KAAK,cAAc,IAAI,WAAW,SAAS,EAAG,QAAO,IAAI;AAC7D,SAAO;AACT;AAgQO,IAAM,YAAN,cAAwB,MAAM;AAAA,EACnC,YACE,SACgB,KAChB;AACA,UAAM,OAAO;AAFG;AAGhB,SAAK,OAAO;AAAA,EACd;AACF;AAGO,IAAM,0BAAN,cAAsC,UAAU;AAAA,EACrD,YACE,SACgB,OAChB;AACA,UAAM,SAAS,QAAQ,cAAc,KAAK,yBAAyB,MAAS;AAF5D;AAGhB,SAAK,OAAO;AAAA,EACd;AACF;AAGO,IAAM,sBAAN,cAAkC,UAAU;AAAA,EACjD,YACE,SACgB,UAChB;AACA;AAAA,MACE;AAAA,MACA,WACI,0DAA0D,QAAQ,MAClE;AAAA,IACN;AAPgB;AAQhB,SAAK,OAAO;AAAA,EACd;AACF;AAGO,IAAM,sBAAN,cAAkC,UAAU;AAAA,EACjD,YACE,SACgB,UAChB;AACA;AAAA,MACE;AAAA,MACA,WACI,qDAAqD,QAAQ,MAC7D;AAAA,IACN;AAPgB;AAQhB,SAAK,OAAO;AAAA,EACd;AACF;AAGO,IAAM,uBAAN,cAAmC,UAAU;AAAA,EAClD,YAAY,SAAiB;AAC3B,UAAM,SAAS,wEAAwE;AACvF,SAAK,OAAO;AAAA,EACd;AACF;AAGO,IAAM,oBAAN,cAAgC,UAAU;AAAA,EAC/C,YAAY,SAAiB,KAAc;AACzC,UAAM,SAAS,OAAO,wCAAwC;AAC9D,SAAK,OAAO;AAAA,EACd;AACF;AAGO,IAAM,kBAAN,cAA8B,UAAU;AAAA,EAC7C,YACE,SACgB,UAChB;AACA;AAAA,MACE;AAAA,MACA,WAAW,6BAA6B,QAAQ,MAAM;AAAA,IACxD;AALgB;AAMhB,SAAK,OAAO;AAAA,EACd;AACF;AAKO,IAAM,mBAAN,cAA+B,UAAU;AAAA,EAC9C,YAA4B,aAAqB;AAC/C;AAAA,MACE;AAAA,MACA,oBAAoB,WAAW;AAAA;AAAA,IACjC;AAJ0B;AAK1B,SAAK,OAAO;AAAA,EACd;AACF;AAGO,IAAM,mBAAN,cAA+B,UAAU;AAAA,EAC9C,YACkB,WACA,UACA,aAChB;AACA;AAAA,MACE,SAAS,SAAS,oCAAoC,QAAQ;AAAA,MAC9D,iBAAiB,WAAW;AAAA;AAAA,IAC9B;AAPgB;AACA;AACA;AAMhB,SAAK,OAAO;AAAA,EACd;AACF;AAyBO,SAAS,cACd,KAC8D;AAC9D,SAAO,IAAI,QAAQ;AACrB;AA4EO,IAAM,6BAA6B;AAyKnC,IAAM,gBAAN,cAA4B,UAAU;AAAA,EAC3C,YACE,SACgB,YAChB,KACA;AACA,UAAM,SAAS,GAAG;AAHF;AAIhB,SAAK,OAAO;AAAA,EACd;AACF;;;ACjuBA,YAAY,QAAQ;AACpB,YAAY,UAAU;;;ACPtB,IAAM,iBAAiB;AACvB,IAAM,aAAa;AACnB,IAAM,aAAa;AAQZ,SAAS,qBAAqB,OAAiC;AACpE,QAAM,UAAU,MAAM,KAAK;AAE3B,MAAI,CAAC,QAAQ,WAAW,UAAU,GAAG;AACnC,WAAO;AAAA,MACL,OAAO;AAAA,MACP,OAAO,mCAAmC,UAAU,YAAY,QAAQ,MAAM,GAAG,EAAE,CAAC;AAAA,IACtF;AAAA,EACF;AAEA,MAAI,QAAQ,SAAS,YAAY;AAC/B,WAAO;AAAA,MACL,OAAO;AAAA,MACP,OAAO,kDAAkD,UAAU,oBAAoB,QAAQ,MAAM;AAAA,IACvG;AAAA,EACF;AAEA,QAAM,OAAO,QAAQ,MAAM,WAAW,MAAM;AAC5C,aAAW,MAAM,MAAM;AACrB,QAAI,CAAC,eAAe,SAAS,EAAE,GAAG;AAChC,aAAO;AAAA,QACL,OAAO;AAAA,QACP,OAAO,sBAAsB,EAAE;AAAA,MACjC;AAAA,IACF;AAAA,EACF;AAEA,SAAO,EAAE,OAAO,MAAM,KAAK,QAAQ;AACrC;AAOO,SAAS,WAAW,KAAqB;AAC9C,QAAM,QAAQ,IAAI,MAAM,EAAE;AAC1B,SAAO,aAAa,KAAK;AAC3B;;;AD1BO,IAAM,yBAAyB;AAEtC,IAAM,iBAAiB,CAAC,OAAO,UAAU,UAAU,WAAW,KAAK;AACnE,IAAM,uBAAuB;AAAA,EAC3B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AACA,IAAM,mBAAmB;AACzB,IAAM,+BAA+B,CAAC,eAAe,eAAe;AAW7D,IAAM,iBAAN,MAAqB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAS1B,MAAM,UAAgC;AACpC,QAAI;AACJ,QAAI;AACF,YAAS,gBAAa,UAAU,OAAO;AAAA,IACzC,QAAQ;AACN,YAAM,IAAI;AAAA,QACR,oCAAoC,QAAQ;AAAA,MAC9C;AAAA,IACF;AAEA,QAAI;AACJ,QAAI;AACF,eAAc,WAAM,GAAG;AAAA,IACzB,QAAQ;AACN,YAAM,IAAI;AAAA,QACR;AAAA,MACF;AAAA,IACF;AAEA,WAAO,KAAK,SAAS,MAAM;AAAA,EAC7B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,SAAS,OAA8B;AACrC,QAAI,UAAU,QAAQ,UAAU,UAAa,OAAO,UAAU,UAAU;AACtE,YAAM,IAAI;AAAA,QACR;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAEA,UAAM,MAAM;AAGZ,eAAW,OAAO,OAAO,KAAK,GAAG,GAAG;AAClC,UAAI,CAAC,qBAAqB,SAAS,GAAG,GAAG;AACvC,cAAM,IAAI;AAAA,UACR,0BAA0B,GAAG,kCAAkC,qBAAqB,KAAK,IAAI,CAAC;AAAA,UAC9F;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAGA,QAAI,IAAI,YAAY,QAAW;AAC7B,YAAM,IAAI,wBAAwB,qCAAqC,SAAS;AAAA,IAClF;AACA,QAAI,OAAO,IAAI,YAAY,YAAY,IAAI,YAAY,GAAG;AACxD,YAAM,IAAI;AAAA,QACR;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAGA,QAAI,CAAC,IAAI,cAAc;AACrB,YAAM,IAAI;AAAA,QACR;AAAA,QACA;AAAA,MACF;AAAA,IACF;AACA,QAAI,CAAC,MAAM,QAAQ,IAAI,YAAY,KAAK,IAAI,aAAa,WAAW,GAAG;AACrE,YAAM,IAAI;AAAA,QACR;AAAA,QACA;AAAA,MACF;AAAA,IACF;AACA,UAAM,eAAkC,IAAI,aAAa,IAAI,CAAC,KAAc,MAAc;AACxF,UAAI,OAAO,QAAQ,YAAY,QAAQ,MAAM;AAC3C,cAAM,IAAI;AAAA,UACR,wBAAwB,CAAC;AAAA,UACzB;AAAA,QACF;AAAA,MACF;AACA,YAAM,SAAS;AACf,UAAI,CAAC,OAAO,QAAQ,OAAO,OAAO,SAAS,UAAU;AACnD,cAAM,IAAI;AAAA,UACR,wBAAwB,CAAC;AAAA,UACzB;AAAA,QACF;AAAA,MACF;AACA,UAAI,CAAC,iBAAiB,KAAK,OAAO,IAAI,GAAG;AACvC,cAAM,IAAI;AAAA,UACR,qBAAqB,OAAO,IAAI;AAAA,UAChC;AAAA,QACF;AAAA,MACF;AACA,UAAI,CAAC,OAAO,eAAe,OAAO,OAAO,gBAAgB,UAAU;AACjE,cAAM,IAAI;AAAA,UACR,gBAAgB,OAAO,IAAI;AAAA,UAC3B;AAAA,QACF;AAAA,MACF;AAEA,YAAM,SAA0B;AAAA,QAC9B,MAAM,OAAO;AAAA,QACb,aAAa,OAAO;AAAA,QACpB,GAAI,OAAO,OAAO,cAAc,YAAY,EAAE,WAAW,OAAO,UAAU,IAAI,CAAC;AAAA,MACjF;AAGA,UAAI,OAAO,SAAS,QAAW;AAC7B,YAAI,OAAO,OAAO,SAAS,YAAY,OAAO,SAAS,MAAM;AAC3D,gBAAM,IAAI;AAAA,YACR,gBAAgB,OAAO,IAAI;AAAA,YAC3B;AAAA,UACF;AAAA,QACF;AACA,cAAM,eAAe,OAAO;AAC5B,YAAI,CAAC,aAAa,WAAW,OAAO,aAAa,YAAY,UAAU;AACrE,gBAAM,IAAI;AAAA,YACR,gBAAgB,OAAO,IAAI,yDAAyD,eAAe,KAAK,IAAI,CAAC;AAAA,YAC7G;AAAA,UACF;AAAA,QACF;AACA,YAAI,CAAE,eAAqC,SAAS,aAAa,OAAO,GAAG;AACzE,gBAAM,IAAI;AAAA,YACR,gBAAgB,OAAO,IAAI,+BAA+B,aAAa,OAAO,sBAAsB,eAAe,KAAK,IAAI,CAAC;AAAA,YAC7H;AAAA,UACF;AAAA,QACF;AACA,cAAM,UAAU,aAAa;AAG7B,YAAI,YAAY,YAAY,OAAO,aAAa,gBAAgB,UAAU;AACxE,gBAAM,IAAI;AAAA,YACR,gBAAgB,OAAO,IAAI;AAAA,YAC3B;AAAA,UACF;AAAA,QACF;AACA,YAAI,YAAY,YAAY,OAAO,aAAa,wBAAwB,UAAU;AAChF,gBAAM,IAAI;AAAA,YACR,gBAAgB,OAAO,IAAI;AAAA,YAC3B;AAAA,UACF;AAAA,QACF;AACA,YAAI,YAAY,aAAa,OAAO,aAAa,iBAAiB,UAAU;AAC1E,gBAAM,IAAI;AAAA,YACR,gBAAgB,OAAO,IAAI;AAAA,YAC3B;AAAA,UACF;AAAA,QACF;AACA,YAAI,YAAY,SAAS,OAAO,aAAa,oBAAoB,UAAU;AACzE,gBAAM,IAAI;AAAA,YACR,gBAAgB,OAAO,IAAI;AAAA,YAC3B;AAAA,UACF;AAAA,QACF;AAEA,eAAO,OAAO;AAAA,UACZ;AAAA,UACA,GAAI,OAAO,aAAa,gBAAgB,WACpC,EAAE,aAAa,aAAa,YAAY,IACxC,CAAC;AAAA,UACL,GAAI,OAAO,aAAa,wBAAwB,WAC5C,EAAE,qBAAqB,aAAa,oBAAoB,IACxD,CAAC;AAAA,UACL,GAAI,OAAO,aAAa,iBAAiB,WACrC,EAAE,cAAc,aAAa,aAAa,IAC1C,CAAC;AAAA,UACL,GAAI,OAAO,aAAa,oBAAoB,WACxC,EAAE,iBAAiB,aAAa,gBAAgB,IAChD,CAAC;AAAA,QACP;AAAA,MACF;AAGA,UAAI,OAAO,eAAe,QAAW;AACnC,YAAI,CAAC,MAAM,QAAQ,OAAO,UAAU,GAAG;AACrC,gBAAM,IAAI;AAAA,YACR,gBAAgB,OAAO,IAAI;AAAA,YAC3B;AAAA,UACF;AAAA,QACF;AACA,cAAM,mBAAiE,CAAC;AACxE,iBAAS,KAAK,GAAG,KAAK,OAAO,WAAW,QAAQ,MAAM;AACpD,gBAAM,QAAQ,OAAO,WAAW,EAAE;AAClC,cAAI,OAAO,UAAU,UAAU;AAC7B,kBAAM,aAAa,qBAAqB,KAAK;AAC7C,gBAAI,CAAC,WAAW,OAAO;AACrB,oBAAM,IAAI;AAAA,gBACR,gBAAgB,OAAO,IAAI,wBAAwB,EAAE,KAAK,WAAW,KAAK;AAAA,gBAC1E;AAAA,cACF;AAAA,YACF;AACA,6BAAiB,KAAK,WAAW,GAAI;AAAA,UACvC,WAAW,OAAO,UAAU,YAAY,UAAU,MAAM;AACtD,kBAAM,WAAW;AACjB,gBAAI,OAAO,SAAS,QAAQ,UAAU;AACpC,oBAAM,IAAI;AAAA,gBACR,gBAAgB,OAAO,IAAI,wBAAwB,EAAE;AAAA,gBACrD;AAAA,cACF;AAAA,YACF;AACA,kBAAM,aAAa,qBAAqB,SAAS,GAAG;AACpD,gBAAI,CAAC,WAAW,OAAO;AACrB,oBAAM,IAAI;AAAA,gBACR,gBAAgB,OAAO,IAAI,wBAAwB,EAAE,KAAK,WAAW,KAAK;AAAA,gBAC1E;AAAA,cACF;AAAA,YACF;AACA,kBAAM,eAAgD,EAAE,KAAK,WAAW,IAAK;AAC7E,gBAAI,OAAO,SAAS,UAAU,UAAU;AACtC,2BAAa,QAAQ,SAAS;AAAA,YAChC;AACA,6BAAiB,KAAK,YAAY;AAAA,UACpC,OAAO;AACL,kBAAM,IAAI;AAAA,cACR,gBAAgB,OAAO,IAAI,wBAAwB,EAAE;AAAA,cACrD;AAAA,YACF;AAAA,UACF;AAAA,QACF;AACA,YAAI,iBAAiB,SAAS,GAAG;AAC/B,iBAAO,aAAa;AAAA,QACtB;AAAA,MACF;AAEA,aAAO;AAAA,IACT,CAAC;AAGD,UAAM,WAAW,oBAAI,IAAY;AACjC,eAAW,OAAO,cAAc;AAC9B,UAAI,SAAS,IAAI,IAAI,IAAI,GAAG;AAC1B,cAAM,IAAI;AAAA,UACR,+BAA+B,IAAI,IAAI;AAAA,UACvC;AAAA,QACF;AAAA,MACF;AACA,eAAS,IAAI,IAAI,IAAI;AAAA,IACvB;AAMA,QAAI,CAAC,IAAI,YAAY;AACnB,YAAM,IAAI;AAAA,QACR;AAAA,QACA;AAAA,MACF;AAAA,IACF;AACA,QAAI,CAAC,MAAM,QAAQ,IAAI,UAAU,KAAK,IAAI,WAAW,WAAW,GAAG;AACjE,YAAM,IAAI;AAAA,QACR;AAAA,QACA;AAAA,MACF;AAAA,IACF;AACA,UAAM,aAAa,IAAI,WAAW,IAAI,CAAC,IAAa,MAAc;AAChE,UAAI,OAAO,OAAO,YAAY,OAAO,MAAM;AACzC,cAAM,IAAI;AAAA,UACR,sBAAsB,CAAC;AAAA,UACvB;AAAA,QACF;AAAA,MACF;AACA,YAAM,QAAQ;AACd,UAAI,CAAC,MAAM,QAAQ,OAAO,MAAM,SAAS,UAAU;AACjD,cAAM,IAAI;AAAA,UACR,sBAAsB,CAAC;AAAA,UACvB;AAAA,QACF;AAAA,MACF;AACA,UAAI,CAAC,MAAM,eAAe,OAAO,MAAM,gBAAgB,UAAU;AAC/D,cAAM,IAAI;AAAA,UACR,cAAc,MAAM,IAAI;AAAA,UACxB;AAAA,QACF;AAAA,MACF;AACA,aAAO;AAAA,QACL,MAAM,MAAM;AAAA,QACZ,aAAa,MAAM;AAAA,QACnB,GAAI,OAAO,MAAM,WAAW,WAAW,EAAE,QAAQ,MAAM,OAAO,IAAI,CAAC;AAAA,QACnE,GAAI,MAAM,QAAQ,MAAM,MAAM,IAAI,EAAE,QAAQ,MAAM,OAAmB,IAAI,CAAC;AAAA,MAC5E;AAAA,IACF,CAAC;AAGD,UAAM,UAAU,oBAAI,IAAY;AAChC,eAAW,MAAM,YAAY;AAC3B,UAAI,QAAQ,IAAI,GAAG,IAAI,GAAG;AACxB,cAAM,IAAI;AAAA,UACR,6BAA6B,GAAG,IAAI;AAAA,UACpC;AAAA,QACF;AAAA,MACF;AACA,cAAQ,IAAI,GAAG,IAAI;AAAA,IACrB;AAGA,QAAI,CAAC,IAAI,MAAM;AACb,YAAM,IAAI;AAAA,QACR;AAAA,QACA;AAAA,MACF;AAAA,IACF;AACA,QAAI,OAAO,IAAI,SAAS,YAAY,IAAI,SAAS,MAAM;AACrD,YAAM,IAAI,wBAAwB,mCAAmC,MAAM;AAAA,IAC7E;AACA,UAAM,UAAU,IAAI;AACpB,QAAI,CAAC,QAAQ,mBAAmB,OAAO,QAAQ,oBAAoB,UAAU;AAC3E,YAAM,IAAI;AAAA,QACR;AAAA,QACA;AAAA,MACF;AAAA,IACF;AACA,QAAI,CAAE,eAAqC,SAAS,QAAQ,eAAe,GAAG;AAC5E,YAAM,IAAI;AAAA,QACR,iCAAiC,QAAQ,eAAe,sBAAsB,eAAe,KAAK,IAAI,CAAC;AAAA,QACvG;AAAA,MACF;AAAA,IACF;AAEA,UAAM,aAAa;AAAA,MACjB,iBAAiB,QAAQ;AAAA,MACzB,GAAI,OAAO,QAAQ,gBAAgB,WAAW,EAAE,aAAa,QAAQ,YAAY,IAAI,CAAC;AAAA,MACtF,GAAI,OAAO,QAAQ,wBAAwB,WACvC,EAAE,qBAAqB,QAAQ,oBAAoB,IACnD,CAAC;AAAA,MACL,GAAI,OAAO,QAAQ,iBAAiB,WAAW,EAAE,cAAc,QAAQ,aAAa,IAAI,CAAC;AAAA,MACzF,GAAI,OAAO,QAAQ,oBAAoB,WACnC,EAAE,iBAAiB,QAAQ,gBAAgB,IAC3C,CAAC;AAAA,IACP;AAGA,eAAW,OAAO,cAAc;AAC9B,UAAI,IAAI,cAAc,IAAI,WAAW,SAAS,GAAG;AAC/C,cAAM,mBAAmB,IAAI,MAAM,WAAW,WAAW;AACzD,YAAI,qBAAqB,OAAO;AAC9B,gBAAM,IAAI;AAAA,YACR,gBAAgB,IAAI,IAAI,8CAA8C,gBAAgB;AAAA,YACtF;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAGA,QAAI,CAAC,IAAI,gBAAgB,OAAO,IAAI,iBAAiB,UAAU;AAC7D,YAAM,IAAI;AAAA,QACR;AAAA,QACA;AAAA,MACF;AAAA,IACF;AACA,eAAW,SAAS,8BAA8B;AAChD,UAAI,CAAC,IAAI,aAAa,SAAS,KAAK,GAAG;AACrC,cAAM,IAAI;AAAA,UACR,8BAA8B,KAAK,YAAY,IAAI,YAAY;AAAA,UAC/D;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAGA,QAAI;AACJ,QAAI,IAAI,uBAAuB,QAAW;AACxC,UAAI,CAAC,MAAM,QAAQ,IAAI,kBAAkB,GAAG;AAC1C,cAAM,IAAI;AAAA,UACR;AAAA,UACA;AAAA,QACF;AAAA,MACF;AACA,0BAAoB,IAAI,mBAAmB,IAAI,CAAC,IAAa,MAAc;AACzE,YAAI,OAAO,OAAO,YAAY,OAAO,MAAM;AACzC,gBAAM,IAAI;AAAA,YACR,6BAA6B,CAAC;AAAA,YAC9B;AAAA,UACF;AAAA,QACF;AACA,cAAM,QAAQ;AAEd,YAAI,CAAC,MAAM,QAAQ,OAAO,MAAM,SAAS,UAAU;AACjD,gBAAM,IAAI;AAAA,YACR,6BAA6B,CAAC;AAAA,YAC9B;AAAA,UACF;AAAA,QACF;AACA,cAAM,SAAS,MAAM;AAErB,YAAI,CAAC,MAAM,eAAe,OAAO,MAAM,gBAAgB,UAAU;AAC/D,gBAAM,IAAI;AAAA,YACR,qBAAqB,MAAM;AAAA,YAC3B;AAAA,UACF;AAAA,QACF;AAGA,YAAI,CAAC,MAAM,QAAQ,MAAM,UAAU,KAAK,MAAM,WAAW,WAAW,GAAG;AACrE,gBAAM,IAAI;AAAA,YACR,qBAAqB,MAAM;AAAA,YAC3B;AAAA,UACF;AAAA,QACF;AACA,mBAAW,MAAM,MAAM,YAAY;AACjC,cAAI,OAAO,OAAO,UAAU;AAC1B,kBAAM,IAAI;AAAA,cACR,qBAAqB,MAAM;AAAA,cAC3B;AAAA,YACF;AAAA,UACF;AACA,cAAI,CAAC,QAAQ,IAAI,EAAE,GAAG;AACpB,kBAAM,IAAI;AAAA,cACR,qBAAqB,MAAM,mCAAmC,EAAE;AAAA,cAChE;AAAA,YACF;AAAA,UACF;AAAA,QACF;AAGA,YACE,CAAC,MAAM,gBACP,OAAO,MAAM,iBAAiB,YAC9B,MAAM,QAAQ,MAAM,YAAY,GAChC;AACA,gBAAM,IAAI;AAAA,YACR,qBAAqB,MAAM;AAAA,YAC3B;AAAA,UACF;AAAA,QACF;AACA,cAAM,SAAS,MAAM;AACrB,cAAM,aAA+D,CAAC;AAGtE,mBAAW,OAAO,cAAc;AAC9B,cAAI,EAAE,IAAI,QAAQ,SAAS;AACzB,kBAAM,IAAI;AAAA,cACR,qBAAqB,MAAM,6BAA6B,IAAI,IAAI;AAAA,cAChE;AAAA,YACF;AAAA,UACF;AAAA,QACF;AAEA,mBAAW,CAAC,SAAS,MAAM,KAAK,OAAO,QAAQ,MAAM,GAAG;AACtD,cAAI,CAAC,SAAS,IAAI,OAAO,GAAG;AAC1B,kBAAM,IAAI;AAAA,cACR,qBAAqB,MAAM,qCAAqC,OAAO;AAAA,cACvE;AAAA,YACF;AAAA,UACF;AACA,cAAI,OAAO,WAAW,YAAY,WAAW,MAAM;AACjD,kBAAM,IAAI;AAAA,cACR,qBAAqB,MAAM,kBAAkB,OAAO;AAAA,cACpD;AAAA,YACF;AAAA,UACF;AACA,gBAAM,SAAS;AACf,gBAAM,eAAe,OAAO,cAAc;AAC1C,gBAAM,SAAS,OAAO,QAAQ;AAE9B,cAAI,gBAAgB,QAAQ;AAC1B,kBAAM,IAAI;AAAA,cACR,qBAAqB,MAAM,kBAAkB,OAAO;AAAA,cACpD;AAAA,YACF;AAAA,UACF;AACA,cAAI,CAAC,gBAAgB,CAAC,QAAQ;AAC5B,kBAAM,IAAI;AAAA,cACR,qBAAqB,MAAM,kBAAkB,OAAO;AAAA,cACpD;AAAA,YACF;AAAA,UACF;AAEA,cAAI,cAAc;AAChB,gBAAI,OAAO,OAAO,cAAc,UAAU;AACxC,oBAAM,IAAI;AAAA,gBACR,qBAAqB,MAAM,kBAAkB,OAAO;AAAA,gBACpD;AAAA,cACF;AAAA,YACF;AACA,kBAAM,sBAAsB,qBAAqB,OAAO,SAAS;AACjE,gBAAI,CAAC,oBAAoB,OAAO;AAC9B,oBAAM,IAAI;AAAA,gBACR,qBAAqB,MAAM,kBAAkB,OAAO,MAAM,oBAAoB,KAAK;AAAA,gBACnF;AAAA,cACF;AAAA,YACF;AACA,uBAAW,OAAO,IAAI,EAAE,WAAW,oBAAoB,IAAK;AAAA,UAC9D,OAAO;AACL,kBAAM,SAAS,OAAO;AACtB,gBAAI,OAAO,WAAW,YAAY,WAAW,MAAM;AACjD,oBAAM,IAAI;AAAA,gBACR,qBAAqB,MAAM,kBAAkB,OAAO;AAAA,gBACpD;AAAA,cACF;AAAA,YACF;AACA,kBAAM,iBAAiB,CAAC,OAAO,OAAO,OAAO;AAC7C,gBAAI,CAAC,OAAO,YAAY,CAAC,eAAe,SAAS,OAAO,QAAkB,GAAG;AAC3E,oBAAM,IAAI;AAAA,gBACR,qBAAqB,MAAM,kBAAkB,OAAO,mCAAmC,eAAe,KAAK,IAAI,CAAC;AAAA,gBAChH;AAAA,cACF;AAAA,YACF;AACA,gBAAI,CAAC,OAAO,SAAS,OAAO,OAAO,UAAU,UAAU;AACrD,oBAAM,IAAI;AAAA,gBACR,qBAAqB,MAAM,kBAAkB,OAAO;AAAA,gBACpD;AAAA,cACF;AAAA,YACF;AACA,uBAAW,OAAO,IAAI;AAAA,cACpB,KAAK;AAAA,gBACH,UAAU,OAAO;AAAA,gBACjB,OAAO,OAAO;AAAA,gBACd,QAAQ,OAAO,OAAO,WAAW,WAAW,OAAO,SAAS;AAAA,cAC9D;AAAA,YACF;AAAA,UACF;AAAA,QACF;AAEA,eAAO;AAAA,UACL,MAAM;AAAA,UACN,aAAa,MAAM;AAAA,UACnB,YAAY,MAAM;AAAA,UAClB,cAAc;AAAA,QAChB;AAAA,MACF,CAAC;AAGD,YAAM,UAAU,oBAAI,IAAY;AAChC,iBAAW,MAAM,mBAAmB;AAClC,YAAI,QAAQ,IAAI,GAAG,IAAI,GAAG;AACxB,gBAAM,IAAI;AAAA,YACR,oCAAoC,GAAG,IAAI;AAAA,YAC3C;AAAA,UACF;AAAA,QACF;AACA,gBAAQ,IAAI,GAAG,IAAI;AAAA,MACrB;AAAA,IACF;AAGA,QAAI;AACJ,QAAI,IAAI,UAAU,QAAW;AAC3B,UAAI,OAAO,IAAI,UAAU,YAAY,IAAI,UAAU,QAAQ,MAAM,QAAQ,IAAI,KAAK,GAAG;AACnF,cAAM,IAAI,wBAAwB,oCAAoC,OAAO;AAAA,MAC/E;AACA,YAAM,WAAW,IAAI;AACrB,UAAI,OAAO,SAAS,kBAAkB,YAAY,SAAS,cAAc,WAAW,GAAG;AACrF,cAAM,IAAI;AAAA,UACR;AAAA,UACA;AAAA,QACF;AAAA,MACF;AACA,cAAQ,EAAE,eAAe,SAAS,cAAc;AAAA,IAClD;AAEA,WAAO;AAAA,MACL,SAAS;AAAA,MACT;AAAA,MACA;AAAA,MACA,MAAM;AAAA,MACN,cAAc,IAAI;AAAA,MAClB,GAAI,oBAAoB,EAAE,oBAAoB,kBAAkB,IAAI,CAAC;AAAA,MACrE,GAAI,QAAQ,EAAE,MAAM,IAAI,CAAC;AAAA,IAC3B;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,MAAM,UAAkB,UAAwD;AAC9E,UAAM,UAAa,SAAM,UAAU,MAAM;AACvC,UAAI;AACF,cAAM,WAAW,KAAK,MAAM,QAAQ;AACpC,iBAAS,QAAQ;AAAA,MACnB,QAAQ;AAAA,MAER;AAAA,IACF,CAAC;AAED,WAAO,MAAM;AACX,cAAQ,MAAM;AAAA,IAChB;AAAA,EACF;AACF;;;AEroBA,YAAYA,SAAQ;AACpB,YAAYC,WAAU;;;AC0BtB,IAAM,WAAyB;AAAA,EAC7B,EAAE,MAAM,kBAAkB,OAAO,mBAAmB;AAAA,EACpD,EAAE,MAAM,mBAAmB,OAAO,2BAA2B;AAAA,EAC7D,EAAE,MAAM,mBAAmB,OAAO,2BAA2B;AAAA,EAC7D,EAAE,MAAM,gCAAgC,OAAO,sBAAsB;AAAA,EACrE,EAAE,MAAM,sBAAsB,OAAO,sBAAsB;AAAA,EAC3D,EAAE,MAAM,wBAAwB,OAAO,sBAAsB;AAAA,EAC7D,EAAE,MAAM,eAAe,OAAO,+BAA+B;AAAA,EAC7D;AAAA,IACE,MAAM;AAAA,IACN,OAAO;AAAA,EACT;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,OAAO;AAAA,EACT;AAAA,EACA,EAAE,MAAM,gBAAgB,OAAO,oDAAoD;AACrF;AAKO,SAAS,eAAe,KAAqB;AAClD,MAAI,IAAI,WAAW,EAAG,QAAO;AAC7B,QAAM,OAAO,oBAAI,IAAoB;AACrC,aAAW,KAAK,KAAK;AACnB,SAAK,IAAI,IAAI,KAAK,IAAI,CAAC,KAAK,KAAK,CAAC;AAAA,EACpC;AACA,MAAI,UAAU;AACd,aAAW,SAAS,KAAK,OAAO,GAAG;AACjC,UAAM,IAAI,QAAQ,IAAI;AACtB,eAAW,IAAI,KAAK,KAAK,CAAC;AAAA,EAC5B;AACA,SAAO;AACT;AAMO,SAAS,cAAc,OAAe,YAAY,KAAK,YAAY,IAAa;AACrF,SAAO,MAAM,UAAU,aAAa,eAAe,KAAK,IAAI;AAC9D;AAMO,SAAS,YAAY,OAAuB;AACjD,MAAI,MAAM,UAAU,EAAG,QAAO;AAC9B,SAAO,MAAM,MAAM,GAAG,CAAC,IAAI;AAC7B;AAMO,SAAS,cAAc,MAAc,YAAoB,UAA+B;AAC7F,QAAM,UAAuB,CAAC;AAC9B,aAAW,EAAE,MAAM,MAAM,KAAK,UAAU;AACtC,UAAM,QAAQ,MAAM,KAAK,IAAI;AAC7B,QAAI,OAAO;AACT,cAAQ,KAAK;AAAA,QACX,MAAM;AAAA,QACN,MAAM;AAAA,QACN,QAAQ,MAAM,QAAQ;AAAA,QACtB,WAAW;AAAA,QACX,aAAa;AAAA,QACb,SAAS,YAAY,MAAM,CAAC,CAAC;AAAA,MAC/B,CAAC;AAAA,IACH;AAAA,EACF;AACA,SAAO;AACT;;;ACpGA,YAAYC,SAAQ;AACpB,YAAY,UAAU;AAaf,SAAS,gBAAgB,UAAmC;AACjE,QAAM,aAAkB,UAAK,UAAU,aAAa;AACpD,MAAI;AACF,UAAM,UAAa,iBAAa,YAAY,OAAO;AACnD,WAAO,mBAAmB,OAAO;AAAA,EACnC,QAAQ;AACN,WAAO,EAAE,OAAO,CAAC,GAAG,UAAU,CAAC,GAAG,OAAO,CAAC,EAAE;AAAA,EAC9C;AACF;AASO,SAAS,mBAAmB,SAAkC;AACnE,QAAM,QAAkB,CAAC;AACzB,QAAM,WAAqB,CAAC;AAC5B,QAAM,QAAkB,CAAC;AAEzB,aAAW,WAAW,QAAQ,MAAM,IAAI,GAAG;AACzC,UAAM,OAAO,QAAQ,KAAK;AAC1B,QAAI,CAAC,QAAQ,KAAK,WAAW,GAAG,EAAG;AAEnC,QAAI,KAAK,WAAW,iBAAiB,GAAG;AACtC,YAAM,cAAc,KAAK,MAAM,kBAAkB,MAAM,EAAE,KAAK;AAC9D,UAAI,YAAa,UAAS,KAAK,WAAW;AAAA,IAC5C,WAAW,KAAK,SAAS,GAAG,GAAG;AAC7B,YAAM,KAAK,KAAK,MAAM,GAAG,EAAE,CAAC;AAAA,IAC9B,OAAO;AACL,YAAM,KAAK,IAAI;AAAA,IACjB;AAAA,EACF;AAEA,SAAO,EAAE,OAAO,UAAU,MAAM;AAClC;AAKO,SAAS,iBAAiB,UAAkB,OAAiC;AAClF,QAAM,aAAa,SAAS,QAAQ,OAAO,GAAG;AAE9C,aAAW,KAAK,MAAM,OAAO;AAC3B,UAAM,MAAM,EAAE,QAAQ,OAAO,GAAG;AAChC,QAAI,eAAe,OAAO,WAAW,WAAW,MAAM,GAAG,EAAG,QAAO;AAAA,EACrE;AAEA,aAAW,WAAW,MAAM,OAAO;AACjC,QAAI,YAAY,YAAY,OAAO,EAAG,QAAO;AAAA,EAC/C;AAEA,SAAO;AACT;AAKO,SAAS,kBAAkB,OAAkB,OAAiC;AACnF,MAAI,MAAM,cAAc,aAAa,MAAM,aAAa;AACtD,WAAO,MAAM,SAAS,SAAS,MAAM,WAAW;AAAA,EAClD;AACA,SAAO;AACT;AAEA,SAAS,YAAY,UAAkB,SAA0B;AAI/D,QAAM,cAAc;AACpB,QAAM,cAAc;AACpB,QAAM,WAAW;AAEjB,QAAM,UAAU,QACb,QAAQ,SAAS,WAAW,EAC5B,QAAQ,OAAO,WAAW,EAC1B,QAAQ,OAAO,QAAQ,EACvB,QAAQ,qBAAqB,MAAM,EACnC,QAAQ,aAAa,IAAI,EACzB,QAAQ,aAAa,OAAO,EAC5B,QAAQ,UAAU,MAAM;AAE3B,QAAM,QAAQ,IAAI,OAAO,MAAM,UAAU,GAAG;AAE5C,QAAM,cAAc,IAAI,OAAO,MAAM,UAAU,GAAG;AAClD,SAAO,MAAM,KAAK,QAAQ,KAAK,YAAY,KAAK,QAAQ;AAC1D;;;AF7EA,IAAM,yBAAyB,CAAC,aAAa,WAAW;AACxD,IAAM,oBAAoB,CAAC,iBAAiB;AAC5C,IAAM,mBAAmB,CAAC,gBAAgB,MAAM;AAChD,IAAM,gBAAgB,OAAO;AAWtB,IAAM,aAAN,MAAiB;AAAA,EACtB,YAA6B,QAA0B;AAA1B;AAAA,EAA2B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWxD,MAAM,KACJ,UACA,UACA,UAAuB,CAAC,GACH;AACrB,UAAM,UAAU,KAAK,IAAI;AACzB,UAAM,cAAc,gBAAgB,QAAQ;AAC5C,UAAM,UAAuB,CAAC;AAC9B,UAAM,yBAAmC,CAAC;AAC1C,QAAI,eAAe;AACnB,QAAI,eAAe;AAGnB,eAAW,MAAM,SAAS,YAAY;AACpC,iBAAW,OAAO,SAAS,cAAc;AACvC,cAAM,UAAU,SAAS,aACtB,QAAQ,eAAe,GAAG,IAAI,EAC9B,QAAQ,iBAAiB,IAAI,IAAI;AACpC,cAAM,UAAe,WAAK,UAAU,OAAO;AAC3C,YAAO,eAAW,OAAO,GAAG;AAC1B,gBAAM,UAAa,iBAAa,SAAS,OAAO;AAChD,cAAI,CAAC,QAAQ,SAAS,OAAO,KAAK,CAAC,QAAQ,SAAS,QAAQ,GAAG;AAC7D,mCAAuB,KAAK,OAAO;AAAA,UACrC;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAGA,QAAI;AACJ,QAAI,QAAQ,YAAY;AACtB,oBAAc,MAAM,KAAK,eAAe,QAAQ;AAAA,IAClD,WAAW,QAAQ,SAAS,QAAQ,MAAM,SAAS,GAAG;AACpD,oBAAc,MAAM,KAAK,gBAAgB,UAAU,QAAQ,KAAK;AAAA,IAClE,OAAO;AACL,oBAAc,MAAM,KAAK,mBAAmB,QAAQ;AAAA,IACtD;AAGA,eAAW,WAAW,aAAa;AACjC,YAAM,UAAe,iBAAW,OAAO,IAAI,UAAe,WAAK,UAAU,OAAO;AAChF,YAAM,UAAe,eAAS,UAAU,OAAO,EAAE,QAAQ,OAAO,GAAG;AAEnE,UAAI,KAAK,iBAAiB,OAAO,GAAG;AAClC;AACA;AAAA,MACF;AAEA,UAAI,iBAAiB,SAAS,WAAW,GAAG;AAC1C;AACA;AAAA,MACF;AAEA,UAAI,CAAI,eAAW,OAAO,GAAG;AAC3B;AACA;AAAA,MACF;AAEA,UAAI;AACJ,UAAI;AACF,eAAU,aAAS,OAAO;AAAA,MAC5B,QAAQ;AACN;AACA;AAAA,MACF;AAEA,UAAI,KAAK,OAAO,eAAe;AAC7B;AACA;AAAA,MACF;AAEA,UAAI,KAAK,SAAS,OAAO,GAAG;AAC1B;AACA;AAAA,MACF;AAEA;AACA,YAAM,UAAa,iBAAa,SAAS,OAAO;AAChD,YAAM,QAAQ,QAAQ,MAAM,IAAI;AAEhC,eAAS,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;AACrC,cAAM,OAAO,MAAM,CAAC;AACpB,cAAM,UAAU,IAAI;AAGpB,YAAI,KAAK,SAAS,eAAe,EAAG;AAGpC,cAAM,cAAc,cAAc,MAAM,SAAS,OAAO;AACxD,mBAAW,KAAK,aAAa;AAC3B,cAAI,CAAC,kBAAkB,GAAG,WAAW,GAAG;AACtC,oBAAQ,KAAK,CAAC;AAAA,UAChB;AAAA,QACF;AAGA,YAAI,QAAQ,aAAa,QAAQ;AAC/B,gBAAM,aAAa,KAAK,cAAc,MAAM,SAAS,OAAO;AAC5D,cAAI,cAAc,CAAC,kBAAkB,YAAY,WAAW,GAAG;AAC7D,oBAAQ,KAAK,UAAU;AAAA,UACzB;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAEA,WAAO;AAAA,MACL;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,YAAY,KAAK,IAAI,IAAI;AAAA,IAC3B;AAAA,EACF;AAAA,EAEQ,iBAAiB,SAA0B;AACjD,eAAW,OAAO,kBAAkB;AAClC,UAAI,YAAY,OAAO,QAAQ,WAAW,MAAM,GAAG,EAAG,QAAO;AAAA,IAC/D;AACA,eAAW,OAAO,wBAAwB;AACxC,UAAI,QAAQ,SAAS,GAAG,EAAG,QAAO;AAAA,IACpC;AACA,eAAW,QAAQ,mBAAmB;AACpC,UAAI,QAAQ,SAAS,IAAI,EAAG,QAAO;AAAA,IACrC;AACA,WAAO;AAAA,EACT;AAAA,EAEQ,SAAS,UAA2B;AAC1C,QAAI;AACF,YAAM,KAAQ,aAAS,UAAU,GAAG;AACpC,YAAM,MAAM,OAAO,MAAM,GAAG;AAC5B,YAAM,YAAe,aAAS,IAAI,KAAK,GAAG,KAAK,CAAC;AAChD,MAAG,cAAU,EAAE;AACf,eAAS,IAAI,GAAG,IAAI,WAAW,KAAK;AAClC,YAAI,IAAI,CAAC,MAAM,EAAG,QAAO;AAAA,MAC3B;AACA,aAAO;AAAA,IACT,QAAQ;AACN,aAAO;AAAA,IACT;AAAA,EACF;AAAA,EAEQ,cAAc,MAAc,SAAiB,UAAoC;AAEvF,UAAM,eAAe;AACrB,UAAM,QAAQ,aAAa,KAAK,IAAI;AACpC,QAAI,CAAC,MAAO,QAAO;AAEnB,UAAM,QAAQ,MAAM,CAAC;AACrB,UAAM,UAAU,eAAe,KAAK;AAEpC,QAAI,CAAC,cAAc,KAAK,EAAG,QAAO;AAGlC,UAAM,WAAW,kBAAkB,KAAK,IAAI;AAC5C,UAAM,UAAU,WAAW,SAAS,CAAC,IAAI;AACzC,UAAM,UAAU,UACZ,GAAG,OAAO,sDACV,YAAY,KAAK;AAErB,WAAO;AAAA,MACL,MAAM;AAAA,MACN,MAAM;AAAA,MACN,QAAQ,MAAM,QAAQ;AAAA,MACtB,WAAW;AAAA,MACX;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAAA,EAEA,MAAc,eAAe,UAAqC;AAChE,UAAM,SAAS,MAAM,KAAK,OAAO;AAAA,MAC/B;AAAA,MACA,CAAC,QAAQ,YAAY,eAAe,mBAAmB;AAAA,MACvD,EAAE,KAAK,SAAS;AAAA,IAClB;AACA,QAAI,OAAO,aAAa,KAAK,CAAC,OAAO,OAAO,KAAK,EAAG,QAAO,CAAC;AAC5D,WAAO,OAAO,OAAO,KAAK,EAAE,MAAM,IAAI;AAAA,EACxC;AAAA,EAEA,MAAc,gBAAgB,UAAkB,OAAoC;AAClF,UAAM,QAAkB,CAAC;AACzB,eAAW,KAAK,OAAO;AACrB,YAAM,UAAe,iBAAW,CAAC,IAAI,IAAS,WAAK,UAAU,CAAC;AAC9D,UAAI,CAAI,eAAW,OAAO,EAAG;AAC7B,YAAM,OAAU,aAAS,OAAO;AAChC,UAAI,KAAK,YAAY,GAAG;AACtB,cAAM,KAAK,GAAG,KAAK,QAAQ,SAAS,QAAQ,CAAC;AAAA,MAC/C,OAAO;AACL,cAAM,KAAU,eAAS,UAAU,OAAO,EAAE,QAAQ,OAAO,GAAG,CAAC;AAAA,MACjE;AAAA,IACF;AACA,WAAO;AAAA,EACT;AAAA,EAEA,MAAc,mBAAmB,UAAqC;AAEpE,UAAM,SAAS,MAAM,KAAK,OAAO,IAAI,OAAO,CAAC,UAAU,GAAG,EAAE,KAAK,SAAS,CAAC;AAC3E,QAAI,OAAO,aAAa,GAAG;AACzB,aAAO,KAAK,QAAQ,UAAU,QAAQ;AAAA,IACxC;AACA,WAAO,OAAO,OAAO,KAAK,IAAI,OAAO,OAAO,KAAK,EAAE,MAAM,IAAI,IAAI,CAAC;AAAA,EACpE;AAAA,EAEQ,QAAQ,KAAa,UAA4B;AACvD,UAAM,QAAkB,CAAC;AACzB,QAAI;AACJ,QAAI;AACF,gBAAa,gBAAY,KAAK,EAAE,eAAe,KAAK,CAAC;AAAA,IACvD,QAAQ;AACN,aAAO;AAAA,IACT;AACA,eAAW,SAAS,SAAS;AAC3B,YAAM,WAAgB,WAAK,KAAK,MAAM,IAAI;AAC1C,YAAM,UAAe,eAAS,UAAU,QAAQ,EAAE,QAAQ,OAAO,GAAG;AACpE,UAAI,MAAM,YAAY,GAAG;AACvB,YAAI,CAAE,iBAAuC,SAAS,MAAM,IAAI,GAAG;AACjE,gBAAM,KAAK,GAAG,KAAK,QAAQ,UAAU,QAAQ,CAAC;AAAA,QAChD;AAAA,MACF,OAAO;AACL,cAAM,KAAK,OAAO;AAAA,MACpB;AAAA,IACF;AACA,WAAO;AAAA,EACT;AACF;;;AGpRA,YAAYC,SAAQ;AACpB,YAAYC,WAAU;AACtB,YAAYC,WAAU;;;ACUtB,YAAYC,SAAQ;AACpB,YAAYC,WAAU;AACtB,YAAY,YAAY;AACxB,YAAYC,WAAU;AAiBtB,SAAS,aAAa,mBAAmC;AACvD,QAAM,MAAW,cAAQ,iBAAiB;AAC1C,QAAM,OAAY,eAAS,iBAAiB,EAAE,QAAQ,uBAAuB,EAAE;AAC/E,SAAY,WAAK,KAAK,GAAG,IAAI,iBAAiB;AAChD;AAEA,IAAM,iBAAiB;AAGvB,eAAe,aAAa,UAA4C;AACtE,QAAM,WAAW,aAAa,QAAQ;AACtC,MAAI;AACF,QAAI,CAAI,eAAW,QAAQ,GAAG;AAC5B,aAAO,EAAE,SAAS,GAAG,SAAS,CAAC,EAAE;AAAA,IACnC;AACA,UAAM,UAAa,iBAAa,UAAU,OAAO;AACjD,UAAM,SAAc,YAAM,OAAO;AACjC,QAAI,CAAC,UAAU,CAAC,MAAM,QAAQ,OAAO,OAAO,GAAG;AAC7C,aAAO,EAAE,SAAS,GAAG,SAAS,CAAC,EAAE;AAAA,IACnC;AACA,WAAO;AAAA,MACL,SAAS;AAAA,MACT,SAAS,OAAO,QAAQ,IAAI,CAAC,OAAsD;AAAA,QACjF,KAAK,EAAE;AAAA,QACP,OAAO,IAAI,KAAK,EAAE,KAAK;AAAA,QACvB,OAAO,EAAE;AAAA,MACX,EAAE;AAAA,IACJ;AAAA,EACF,QAAQ;AACN,WAAO,EAAE,SAAS,GAAG,SAAS,CAAC,EAAE;AAAA,EACnC;AACF;AAGA,eAAe,aAAa,UAAkB,UAA0C;AACtF,QAAM,WAAW,aAAa,QAAQ;AACtC,QAAM,MAAW,cAAQ,QAAQ;AACjC,MAAI,CAAI,eAAW,GAAG,GAAG;AACvB,IAAG,cAAU,KAAK,EAAE,WAAW,KAAK,CAAC;AAAA,EACvC;AACA,QAAM,OAAO;AAAA,IACX,SAAS,SAAS;AAAA,IAClB,SAAS,SAAS,QAAQ,IAAI,CAAC,OAAO;AAAA,MACpC,KAAK,EAAE;AAAA,MACP,OAAO,EAAE,MAAM,YAAY;AAAA,MAC3B,OAAO,EAAE;AAAA,IACX,EAAE;AAAA,EACJ;AACA,EAAG,kBAAc,UAAU,iBAAsB,gBAAU,IAAI,GAAG,OAAO;AAC3E;AAUA,eAAe,YAAY,UAAkB,MAAgB,OAA8B;AACzF,QAAM,WAAW,MAAM,aAAa,QAAQ;AAC5C,QAAM,MAAM,oBAAI,KAAK;AACrB,aAAW,OAAO,MAAM;AACtB,UAAM,WAAW,SAAS,QAAQ,UAAU,CAAC,MAAM,EAAE,QAAQ,GAAG;AAChE,QAAI,YAAY,GAAG;AAEjB,eAAS,QAAQ,QAAQ,IAAI,EAAE,KAAK,OAAO,KAAK,MAAM;AAAA,IACxD,OAAO;AACL,eAAS,QAAQ,KAAK,EAAE,KAAK,OAAO,KAAK,MAAM,CAAC;AAAA,IAClD;AAAA,EACF;AACA,QAAM,aAAa,UAAU,QAAQ;AACvC;AAGA,eAAe,aAAa,UAAkB,MAA+B;AAC3E,QAAM,WAAW,MAAM,aAAa,QAAQ;AAC5C,WAAS,UAAU,SAAS,QAAQ,OAAO,CAAC,MAAM,CAAC,KAAK,SAAS,EAAE,GAAG,CAAC;AACvE,QAAM,aAAa,UAAU,QAAQ;AACvC;AAGA,eAAe,eAAe,UAAqC;AACjE,QAAM,WAAW,MAAM,aAAa,QAAQ;AAC5C,SAAO,SAAS,QAAQ,IAAI,CAAC,MAAM,EAAE,GAAG;AAC1C;AAGA,eAAe,UAAU,UAAkB,KAA+B;AACxE,QAAM,WAAW,MAAM,aAAa,QAAQ;AAC5C,SAAO,SAAS,QAAQ,KAAK,CAAC,MAAM,EAAE,QAAQ,GAAG;AACnD;AAGA,SAAS,sBAA8B;AACrC,SAAc,mBAAY,EAAE,EAAE,SAAS,KAAK;AAC9C;AAUA,eAAe,qBACb,UACA,MACA,OACA,eAAe,KACA;AACf,MAAI;AACF,UAAM,YAAY,UAAU,MAAM,KAAK;AAAA,EACzC,QAAQ;AAEN,UAAM,IAAI,QAAQ,CAAC,MAAM,WAAW,GAAG,YAAY,CAAC;AACpD,UAAM,YAAY,UAAU,MAAM,KAAK;AAAA,EACzC;AACF;;;ADvIO,IAAM,gBAAN,MAAoB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQzB,cAAc,UAAwB,UAAgC;AACpE,UAAM,QAAsB,CAAC;AAE7B,eAAW,MAAM,SAAS,YAAY;AACpC,iBAAW,OAAO,SAAS,cAAc;AACvC,cAAM,eAAe,SAAS,aAC3B,QAAQ,eAAe,GAAG,IAAI,EAC9B,QAAQ,iBAAiB,IAAI,IAAI;AACpC,cAAM,WAAgB,WAAK,UAAU,YAAY;AAEjD,cAAM,KAAK;AAAA,UACT,WAAW,GAAG;AAAA,UACd,aAAa,IAAI;AAAA,UACjB;AAAA,UACA,QAAW,eAAW,QAAQ;AAAA,QAChC,CAAC;AAAA,MACH;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,mBAAmB,UAAwB,UAAgC;AACzE,WAAO,KAAK,cAAc,UAAU,QAAQ,EAAE,OAAO,CAAC,SAAS,CAAC,KAAK,MAAM;AAAA,EAC7E;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,MAAM,aACJ,MACA,YACA,UACe;AACf,UAAM,MAAW,cAAQ,KAAK,QAAQ;AACtC,QAAI,CAAI,eAAW,GAAG,GAAG;AACvB,MAAG,cAAU,KAAK,EAAE,WAAW,KAAK,CAAC;AAAA,IACvC;AAEA,UAAM,WAAW,QAAQ,KAAK,UAAU,CAAC,GAAG,UAAU,KAAK,WAAW;AAAA,EACxE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,MAAM,gBACJ,UACA,UACA,aACyB;AACzB,UAAM,QAAQ,KAAK,cAAc,UAAU,QAAQ;AACnD,UAAM,WAA2B,CAAC;AAGlC,UAAM,WAAW,oBAAI,IAAsB;AAC3C,eAAW,QAAQ,OAAO;AACxB,UAAI,KAAK,QAAQ;AACf,iBAAS,IAAI,KAAK,UAAU,KAAK,aAAa,KAAK,QAAQ,CAAC;AAAA,MAC9D;AAAA,IACF;AAEA,eAAW,QAAQ,OAAO;AACxB,UAAI,CAAC,KAAK,QAAQ;AAChB,iBAAS,KAAK;AAAA,UACZ;AAAA,UACA,UAAU;AAAA,UACV,cAAc;AAAA,UACd,cAAc;AAAA,UACd,QAAQ;AAAA,YACN;AAAA,cACE,MAAM;AAAA,cACN,SAAS,SAAS,KAAK,QAAQ;AAAA,YACjC;AAAA,UACF;AAAA,QACF,CAAC;AACD;AAAA,MACF;AAGA,UAAI,eAAe;AACnB,UAAI;AACF,cAAM,UAAU,MAAM,eAAe,KAAK,QAAQ;AAClD,uBAAe,QAAQ;AAAA,MACzB,QAAQ;AAAA,MAER;AAEA,YAAM,OAAO,SAAS,IAAI,KAAK,QAAQ,KAAK,CAAC;AAC7C,YAAM,WAAW,KAAK;AACtB,YAAM,eAAe,KAAK,iBAAiB,KAAK,QAAQ;AACxD,YAAM,SAAwB,CAAC;AAG/B,YAAM,eAAe,MAAM;AAAA,QACzB,CAAC,MAAM,EAAE,cAAc,KAAK,aAAa,EAAE,gBAAgB,KAAK,eAAe,EAAE;AAAA,MACnF;AACA,iBAAW,WAAW,cAAc;AAClC,cAAM,cAAc,SAAS,IAAI,QAAQ,QAAQ,KAAK,CAAC;AACvD,cAAM,cAAc,YAAY,OAAO,CAAC,MAAM,CAAC,KAAK,SAAS,CAAC,CAAC;AAC/D,mBAAW,MAAM,aAAa;AAC5B,iBAAO,KAAK;AAAA,YACV,MAAM;AAAA,YACN,SAAS,QAAQ,EAAE,eAAe,QAAQ,WAAW;AAAA,YACrD,KAAK;AAAA,UACP,CAAC;AAAA,QACH;AAAA,MACF;AAEA,eAAS,KAAK,EAAE,MAAM,UAAU,cAAc,cAAc,OAAO,CAAC;AAAA,IACtE;AAEA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA,EAMQ,aAAa,UAA4B;AAC/C,QAAI;AACF,YAAM,MAAS,iBAAa,UAAU,OAAO;AAC7C,YAAM,SAAc,YAAM,GAAG;AAC7B,UAAI,CAAC,UAAU,OAAO,WAAW,SAAU,QAAO,CAAC;AACnD,aAAO,OAAO,KAAK,MAAiC,EAAE,OAAO,CAAC,MAAM,MAAM,MAAM;AAAA,IAClF,QAAQ;AACN,aAAO,CAAC;AAAA,IACV;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKQ,iBAAiB,UAA+B;AACtD,QAAI;AACF,YAAM,MAAS,iBAAa,UAAU,OAAO;AAC7C,YAAM,SAAc,YAAM,GAAG;AAC7B,YAAM,OAAO,QAAQ;AACrB,UAAI,MAAM,aAAc,QAAO,IAAI,KAAK,OAAO,KAAK,YAAY,CAAC;AACjE,aAAO;AAAA,IACT,QAAQ;AACN,aAAO;AAAA,IACT;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,uBAAuB,UAAwB,aAA8B;AAC3E,UAAM,MAAM,SAAS,aAAa,KAAK,CAAC,MAAM,EAAE,SAAS,WAAW;AACpE,WAAO,KAAK,cAAc;AAAA,EAC5B;AACF;;;AEhMA,YAAYC,SAAQ;AACpB,YAAYC,WAAU;AAmBf,IAAM,kBAAN,MAAsB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQ3B,WAAW,UAAmC;AAC5C,QAAI;AACJ,QAAI;AACF,YAAS,iBAAa,UAAU,OAAO;AAAA,IACzC,QAAQ;AACN,YAAM,IAAI,gBAAgB,kCAAkC,QAAQ,MAAM,QAAQ;AAAA,IACpF;AAEA,QAAI;AACJ,QAAI;AACF,eAAc,YAAM,GAAG;AAAA,IACzB,QAAQ;AACN,YAAM,IAAI,gBAAgB,gBAAgB,QAAQ,4BAA4B,QAAQ;AAAA,IACxF;AAEA,QAAI,CAAC,UAAU,OAAO,WAAW,UAAU;AACzC,YAAM,IAAI;AAAA,QACR,gBAAgB,QAAQ;AAAA,QACxB;AAAA,MACF;AAAA,IACF;AAEA,UAAM,MAAM;AACZ,QAAI,CAAC,IAAI,QAAQ,OAAO,IAAI,SAAS,UAAU;AAC7C,YAAM,IAAI;AAAA,QACR,gBAAgB,QAAQ;AAAA,QACxB;AAAA,MACF;AAAA,IACF;AAEA,UAAM,OAAgC,CAAC;AACvC,UAAM,UAAU,IAAI;AAEpB,eAAW,CAAC,SAAS,MAAM,KAAK,OAAO,QAAQ,OAAO,GAAG;AACvD,UAAI,CAAC,UAAU,OAAO,WAAW,UAAU;AACzC,cAAM,IAAI;AAAA,UACR,eAAe,OAAO;AAAA,UACtB;AAAA,QACF;AAAA,MACF;AAEA,YAAM,MAAM;AACZ,YAAM,OAAO,IAAI;AACjB,UAAI,CAAC,CAAC,UAAU,WAAW,SAAS,EAAE,SAAS,IAAI,GAAG;AACpD,cAAM,IAAI;AAAA,UACR,eAAe,OAAO,uBAAuB,IAAI;AAAA,UACjD;AAAA,QACF;AAAA,MACF;AAEA,WAAK,OAAO,IAAI;AAAA,QACd;AAAA,QACA,UAAU,IAAI,aAAa;AAAA,QAC3B,GAAI,OAAO,IAAI,YAAY,WAAW,EAAE,SAAS,IAAI,QAAQ,IAAI,CAAC;AAAA,QAClE,GAAI,IAAI,YAAY,SAAY,EAAE,SAAS,IAAI,QAAQ,IAAI,CAAC;AAAA,QAC5D,GAAI,OAAO,IAAI,gBAAgB,WAAW,EAAE,aAAa,IAAI,YAAY,IAAI,CAAC;AAAA,QAC9E,GAAI,OAAO,IAAI,QAAQ,WAAW,EAAE,KAAK,IAAI,IAAI,IAAI,CAAC;AAAA,MACxD;AAAA,IACF;AAEA,WAAO,EAAE,KAAK;AAAA,EAChB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,SAAS,QAAgC,QAA2C;AAClF,UAAM,SAA4B,CAAC;AACnC,UAAM,WAAgC,CAAC;AAGvC,eAAW,CAAC,SAAS,MAAM,KAAK,OAAO,QAAQ,OAAO,IAAI,GAAG;AAC3D,YAAM,QAAQ,OAAO,OAAO;AAE5B,UAAI,UAAU,UAAa,UAAU,MAAM;AACzC,YAAI,OAAO,UAAU;AACnB,iBAAO,KAAK;AAAA,YACV,KAAK;AAAA,YACL,SAAS,iBAAiB,OAAO;AAAA,YACjC,MAAM;AAAA,UACR,CAAC;AAAA,QACH;AACA;AAAA,MACF;AAGA,cAAQ,OAAO,MAAM;AAAA,QACnB,KAAK,WAAW;AACd,gBAAM,MAAM,OAAO,KAAK;AACxB,cAAI,CAAC,OAAO,UAAU,GAAG,KAAK,MAAM,KAAK,MAAM,IAAI;AACjD,mBAAO,KAAK;AAAA,cACV,KAAK;AAAA,cACL,SAAS,QAAQ,OAAO,8BAA8B,KAAK;AAAA,cAC3D,MAAM;AAAA,YACR,CAAC;AAAA,UACH,WAAW,OAAO,QAAQ,UAAa,MAAM,OAAO,KAAK;AACvD,qBAAS,KAAK;AAAA,cACZ,KAAK;AAAA,cACL,SAAS,QAAQ,OAAO,WAAW,GAAG,oBAAoB,OAAO,GAAG;AAAA,cACpE,MAAM;AAAA,YACR,CAAC;AAAA,UACH;AACA;AAAA,QACF;AAAA,QACA,KAAK,WAAW;AACd,gBAAM,QAAQ,MAAM,YAAY;AAChC,cAAI,CAAC,CAAC,QAAQ,OAAO,EAAE,SAAS,KAAK,GAAG;AACtC,mBAAO,KAAK;AAAA,cACV,KAAK;AAAA,cACL,SAAS,QAAQ,OAAO,iDAAiD,KAAK;AAAA,cAC9E,MAAM;AAAA,YACR,CAAC;AAAA,UACH;AACA;AAAA,QACF;AAAA,QACA,KAAK;AAEH;AAAA,MACJ;AAGA,UAAI,OAAO,WAAW,OAAO,SAAS,UAAU;AAC9C,cAAM,QAAQ,IAAI,OAAO,OAAO,OAAO;AACvC,YAAI,CAAC,MAAM,KAAK,KAAK,GAAG;AACtB,iBAAO,KAAK;AAAA,YACV,KAAK;AAAA,YACL,SAAS,QAAQ,OAAO,4CAA4C,OAAO,OAAO;AAAA,YAClF,MAAM;AAAA,UACR,CAAC;AAAA,QACH;AAAA,MACF;AAAA,IACF;AAGA,eAAW,WAAW,OAAO,KAAK,MAAM,GAAG;AACzC,UAAI,EAAE,WAAW,OAAO,OAAO;AAC7B,iBAAS,KAAK;AAAA,UACZ,KAAK;AAAA,UACL,SAAS,QAAQ,OAAO;AAAA,UACxB,MAAM;AAAA,QACR,CAAC;AAAA,MACH;AAAA,IACF;AAEA,WAAO;AAAA,MACL,OAAO,OAAO,WAAW;AAAA,MACzB;AAAA,MACA;AAAA,IACF;AAAA,EACF;AACF;;;AC1KA,YAAYC,WAAU;AAaf,IAAM,aAAN,MAAiB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAYtB,KACE,SACA,SACA,MACA,MACA,YAAoB,IACR;AACZ,UAAM,UAAU,oBAAI,IAAI,CAAC,GAAG,OAAO,KAAK,OAAO,GAAG,GAAG,OAAO,KAAK,OAAO,CAAC,CAAC;AAC1E,UAAM,OAAkB,CAAC;AAEzB,eAAW,OAAO,SAAS;AACzB,YAAM,MAAM,OAAO;AACnB,YAAM,MAAM,OAAO;AAEnB,UAAI;AACJ,UAAI,OAAO,KAAK;AACd,iBAAS,QAAQ,GAAG,MAAM,QAAQ,GAAG,IAAI,cAAc;AAAA,MACzD,WAAW,OAAO,CAAC,KAAK;AACtB,iBAAS;AAAA,MACX,OAAO;AACL,iBAAS;AAAA,MACX;AAEA,WAAK,KAAK;AAAA,QACR;AAAA,QACA,QAAQ,MAAM,QAAQ,GAAG,IAAI;AAAA,QAC7B,QAAQ,MAAM,QAAQ,GAAG,IAAI;AAAA,QAC7B;AAAA,MACF,CAAC;AAAA,IACH;AAGA,SAAK,KAAK,CAAC,GAAG,MAAM;AAClB,YAAM,QAAoC;AAAA,QACxC,WAAW;AAAA,QACX,WAAW;AAAA,QACX,SAAS;AAAA,QACT,WAAW;AAAA,MACb;AACA,aAAO,MAAM,EAAE,MAAM,IAAI,MAAM,EAAE,MAAM;AAAA,IACzC,CAAC;AAED,WAAO,EAAE,WAAW,MAAM,MAAM,KAAK;AAAA,EACvC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAaA,MAAM,UACJ,WACA,MACA,MACA,UACA,YACA,UACqB;AACrB,UAAM,QAAa;AAAA,MACjB;AAAA,MACA,SAAS,aAAa,QAAQ,eAAe,SAAS,EAAE,QAAQ,iBAAiB,IAAI;AAAA,IACvF;AACA,UAAM,QAAa;AAAA,MACjB;AAAA,MACA,SAAS,aAAa,QAAQ,eAAe,SAAS,EAAE,QAAQ,iBAAiB,IAAI;AAAA,IACvF;AAEA,UAAM,CAAC,YAAY,UAAU,IAAI,MAAM,QAAQ,IAAI;AAAA,MACjD,WAAW,QAAQ,KAAK;AAAA,MACxB,WAAW,QAAQ,KAAK;AAAA,IAC1B,CAAC;AAED,WAAO,KAAK,KAAK,WAAW,QAAQ,WAAW,QAAQ,MAAM,MAAM,SAAS;AAAA,EAC9E;AACF;;;ACrHA,YAAYC,WAAU;AAaf,IAAM,UAAN,MAAc;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAYnB,MAAM,sBACJ,WACA,KACA,QACA,UACA,YACA,UACe;AACf,UAAM,SAAuD,CAAC;AAE9D,eAAW,OAAO,SAAS,cAAc;AACvC,UAAI,EAAE,IAAI,QAAQ,SAAS;AACzB;AAAA,MACF;AAEA,YAAM,WAAgB;AAAA,QACpB;AAAA,QACA,SAAS,aAAa,QAAQ,eAAe,SAAS,EAAE,QAAQ,iBAAiB,IAAI,IAAI;AAAA,MAC3F;AAEA,UAAI;AACF,cAAM,YAAY,MAAM,WAAW,QAAQ,QAAQ;AACnD,kBAAU,OAAO,GAAG,IAAI,OAAO,IAAI,IAAI;AACvC,cAAM,WAAW,QAAQ,UAAU,UAAU,QAAQ,UAAU,IAAI,IAAI;AAAA,MACzE,SAAS,KAAK;AACZ,eAAO,KAAK,EAAE,aAAa,IAAI,MAAM,OAAO,IAAa,CAAC;AAAA,MAC5D;AAAA,IACF;AAEA,QAAI,OAAO,SAAS,GAAG;AACrB,YAAM,UAAU,OAAO,IAAI,CAAC,MAAM,OAAO,EAAE,WAAW,KAAK,EAAE,MAAM,OAAO,EAAE,EAAE,KAAK,IAAI;AACvF,YAAM,IAAI;AAAA,QACR,sBAAsB,GAAG,QAAQ,OAAO,MAAM;AAAA,EAAqB,OAAO;AAAA,uBAChD,OAAO,KAAK,MAAM,EAAE,SAAS,OAAO,MAAM;AAAA,MACtE;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAYA,MAAM,yBACJ,WACA,KACA,UACA,YACA,UACe;AACf,UAAM,SAAuD,CAAC;AAE9D,eAAW,OAAO,SAAS,cAAc;AACvC,YAAM,WAAgB;AAAA,QACpB;AAAA,QACA,SAAS,aAAa,QAAQ,eAAe,SAAS,EAAE,QAAQ,iBAAiB,IAAI,IAAI;AAAA,MAC3F;AAEA,UAAI;AACF,cAAM,YAAY,MAAM,WAAW,QAAQ,QAAQ;AACnD,YAAI,OAAO,UAAU,QAAQ;AAC3B,iBAAO,UAAU,OAAO,GAAG;AAC3B,gBAAM,WAAW,QAAQ,UAAU,UAAU,QAAQ,UAAU,IAAI,IAAI;AAAA,QACzE;AAAA,MACF,SAAS,KAAK;AACZ,eAAO,KAAK,EAAE,aAAa,IAAI,MAAM,OAAO,IAAa,CAAC;AAAA,MAC5D;AAAA,IACF;AAEA,QAAI,OAAO,SAAS,GAAG;AACrB,YAAM,UAAU,OAAO,IAAI,CAAC,MAAM,OAAO,EAAE,WAAW,KAAK,EAAE,MAAM,OAAO,EAAE,EAAE,KAAK,IAAI;AACvF,YAAM,IAAI;AAAA,QACR,yBAAyB,GAAG,QAAQ,OAAO,MAAM;AAAA,EAAqB,OAAO;AAAA,MAC/E;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAYA,MAAM,UACJ,KACA,UACA,QACA,YACA,UACe;AACf,UAAM,SAAS,MAAM,WAAW,QAAQ,SAAS,QAAQ;AAEzD,QAAI,EAAE,OAAO,OAAO,SAAS;AAC3B,YAAM,IAAI;AAAA,QACR,QAAQ,GAAG,uBAAuB,SAAS,SAAS,IAAI,SAAS,WAAW;AAAA,MAC9E;AAAA,IACF;AAEA,UAAM,OAAO,MAAM,WAAW,QAAQ,OAAO,QAAQ;AACrD,SAAK,OAAO,GAAG,IAAI,OAAO,OAAO,GAAG;AACpC,UAAM,WAAW,QAAQ,OAAO,UAAU,KAAK,QAAQ,UAAU,OAAO,WAAW;AAAA,EACrF;AACF;;;ACxIA,YAAYC,SAAQ;AACpB,YAAYC,WAAU;AAGtB,IAAM,kBAAkB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAiDjB,IAAM,iBAAN,MAAqB;AAAA,EAC1B,YAA6B,QAA0B;AAA1B;AAAA,EAA2B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASxD,MAAM,WAAW,WAAqB,UAAiC;AACrE,QAAI,UAAU,WAAW,EAAG;AAE5B,UAAM,SAAS,MAAM,KAAK,OAAO,IAAI,OAAO,CAAC,OAAO,GAAG,SAAS,GAAG,EAAE,KAAK,SAAS,CAAC;AAEpF,QAAI,OAAO,aAAa,GAAG;AACzB,YAAM,IAAI;AAAA,QACR,0BAA0B,OAAO,OAAO,KAAK,CAAC;AAAA,QAC9C;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUA,MAAM,OAAO,SAAiB,UAAmC;AAC/D,UAAM,SAAS,MAAM,KAAK,OAAO,IAAI,OAAO,CAAC,UAAU,MAAM,OAAO,GAAG,EAAE,KAAK,SAAS,CAAC;AAExF,QAAI,OAAO,aAAa,GAAG;AACzB,YAAM,IAAI;AAAA,QACR,qBAAqB,OAAO,OAAO,KAAK,CAAC;AAAA,QACzC;AAAA,MACF;AAAA,IACF;AAGA,UAAM,YAAY,OAAO,OAAO,MAAM,yBAAyB;AAC/D,WAAO,YAAY,UAAU,CAAC,IAAI;AAAA,EACpC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUA,MAAM,OAAO,UAAkB,UAAkB,QAAgB,IAA0B;AACzF,UAAM,SAAS,MAAM,KAAK,OAAO;AAAA,MAC/B;AAAA,MACA,CAAC,OAAO,eAAe,KAAK,IAAI,0BAA0B,MAAM,QAAQ;AAAA,MACxE,EAAE,KAAK,SAAS;AAAA,IAClB;AAEA,QAAI,OAAO,aAAa,GAAG;AACzB,YAAM,IAAI;AAAA,QACR,8BAA8B,QAAQ,MAAM,OAAO,OAAO,KAAK,CAAC;AAAA,MAClE;AAAA,IACF;AAEA,QAAI,CAAC,OAAO,OAAO,KAAK,EAAG,QAAO,CAAC;AAEnC,WAAO,OAAO,OACX,KAAK,EACL,MAAM,IAAI,EACV,IAAI,CAAC,SAAS;AACb,YAAM,CAAC,MAAM,QAAQ,SAAS,GAAG,YAAY,IAAI,KAAK,MAAM,GAAG;AAC/D,aAAO;AAAA,QACL;AAAA,QACA;AAAA,QACA,MAAM,IAAI,KAAK,OAAO;AAAA,QACtB,SAAS,aAAa,KAAK,GAAG;AAAA,MAChC;AAAA,IACF,CAAC;AAAA,EACL;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,MAAM,QAAQ,UAAmC;AAC/C,UAAM,SAAS,MAAM,KAAK,OAAO,IAAI,OAAO,CAAC,QAAQ,UAAU,GAAG,EAAE,KAAK,SAAS,CAAC;AAEnF,QAAI,OAAO,aAAa,GAAG;AACzB,YAAM,IAAI,kBAAkB,2BAA2B,OAAO,OAAO,KAAK,CAAC,EAAE;AAAA,IAC/E;AAEA,WAAO,OAAO;AAAA,EAChB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAM,UAAU,UAAsC;AACpD,UAAM,SAAS,MAAM,KAAK,OAAO,IAAI,OAAO,CAAC,UAAU,aAAa,GAAG,EAAE,KAAK,SAAS,CAAC;AAExF,QAAI,OAAO,aAAa,GAAG;AACzB,YAAM,IAAI,kBAAkB,6BAA6B,OAAO,OAAO,KAAK,CAAC,EAAE;AAAA,IACjF;AAEA,UAAM,SAAmB,CAAC;AAC1B,UAAM,WAAqB,CAAC;AAC5B,UAAM,YAAsB,CAAC;AAE7B,QAAI,CAAC,OAAO,OAAO,KAAK,GAAG;AACzB,aAAO,EAAE,QAAQ,UAAU,UAAU;AAAA,IACvC;AAEA,eAAW,QAAQ,OAAO,OAAO,KAAK,EAAE,MAAM,IAAI,GAAG;AACnD,YAAM,cAAc,KAAK,CAAC;AAC1B,YAAM,iBAAiB,KAAK,CAAC;AAC7B,YAAM,WAAW,KAAK,UAAU,CAAC;AAEjC,UAAI,gBAAgB,KAAK;AACvB,kBAAU,KAAK,QAAQ;AAAA,MACzB,OAAO;AACL,YAAI,gBAAgB,OAAO,gBAAgB,KAAK;AAC9C,iBAAO,KAAK,QAAQ;AAAA,QACtB;AACA,YAAI,mBAAmB,OAAO,mBAAmB,KAAK;AACpD,mBAAS,KAAK,QAAQ;AAAA,QACxB;AAAA,MACF;AAAA,IACF;AAEA,WAAO,EAAE,QAAQ,UAAU,UAAU;AAAA,EACvC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAeA,MAAM,mBAAmB,UAAiC;AAExD,UAAM,eAAe,MAAM,KAAK,OAAO;AAAA,MACrC;AAAA,MACA,CAAC,UAAU,mBAAmB,yBAAyB;AAAA,MACvD,EAAE,KAAK,SAAS;AAAA,IAClB;AACA,QAAI,aAAa,aAAa,GAAG;AAC/B,YAAM,IAAI;AAAA,QACR,0CAA0C,aAAa,OAAO,KAAK,CAAC;AAAA,QACpE;AAAA,MACF;AAAA,IACF;AAEA,UAAM,eAAe,MAAM,KAAK,OAAO;AAAA,MACrC;AAAA,MACA,CAAC,UAAU,qBAAqB,4BAA4B;AAAA,MAC5D,EAAE,KAAK,SAAS;AAAA,IAClB;AACA,QAAI,aAAa,aAAa,GAAG;AAC/B,YAAM,IAAI;AAAA,QACR,6CAA6C,aAAa,OAAO,KAAK,CAAC;AAAA,QACvE;AAAA,MACF;AAAA,IACF;AAGA,UAAM,KAAK,oBAAoB,QAAQ;AAAA,EACzC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,MAAM,iBACJ,UACyD;AAEzD,UAAM,eAAe,MAAM,KAAK,OAAO,IAAI,OAAO,CAAC,UAAU,SAAS,mBAAmB,GAAG;AAAA,MAC1F,KAAK;AAAA,IACP,CAAC;AACD,UAAM,YAAY,aAAa,aAAa,KAAK,aAAa,OAAO,KAAK,EAAE,SAAS;AAGrF,UAAM,eAAoB,WAAK,UAAU,gBAAgB;AACzD,UAAM,cAAiB,eAAW,YAAY,IAAO,iBAAa,cAAc,OAAO,IAAI;AAC3F,UAAM,gBAAgB,YAAY,SAAS,YAAY;AAEvD,WAAO,EAAE,WAAW,cAAc;AAAA,EACpC;AAAA,EAEA,MAAc,oBAAoB,UAAiC;AACjE,UAAM,WAAgB,WAAK,UAAU,gBAAgB;AACrD,UAAM,YAAY;AAGlB,UAAM,WAAc,eAAW,QAAQ,IAAO,iBAAa,UAAU,OAAO,IAAI;AAEhF,QAAI,SAAS,SAAS,YAAY,GAAG;AACnC;AAAA,IACF;AAEA,UAAM,aAAa,SAAS,QAAQ,IAChC,GAAG,SAAS,QAAQ,CAAC;AAAA;AAAA;AAAA,EAA4D,SAAS;AAAA,IAC1F;AAAA,EAAwD,SAAS;AAAA;AAErE,UAAM,cAAc,MAAM,KAAK,OAAO,IAAI,OAAO,CAAC,QAAQ,GAAG;AAAA,MAC3D,OAAO;AAAA,MACP,KAAK;AAAA,IACP,CAAC;AAED,QAAI,YAAY,aAAa,GAAG;AAC9B,YAAM,IAAI,kBAAkB,mCAAmC,YAAY,OAAO,KAAK,CAAC,EAAE;AAAA,IAC5F;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,MAAM,qBAAqB,UAAiC;AAC1D,UAAM,WAAgB,WAAK,UAAU,QAAQ,SAAS,YAAY;AAGlE,UAAM,SAAS,MAAM,KAAK,OAAO,IAAI,OAAO,CAAC,QAAQ,GAAG;AAAA,MACtD,OAAO;AAAA,MACP,KAAK;AAAA,IACP,CAAC;AAED,QAAI,OAAO,aAAa,GAAG;AACzB,YAAM,IAAI;AAAA,QACR,sCAAsC,OAAO,OAAO,KAAK,CAAC;AAAA,QAC1D;AAAA,MACF;AAAA,IACF;AAGA,UAAM,cAAc,MAAM,KAAK,OAAO,IAAI,SAAS,CAAC,MAAM,QAAQ,GAAG,EAAE,KAAK,SAAS,CAAC;AAEtF,QAAI,YAAY,aAAa,GAAG;AAC9B,YAAM,IAAI;AAAA,QACR,8CAA8C,YAAY,OAAO,KAAK,CAAC;AAAA,MACzE;AAAA,IACF;AAAA,EACF;AACF;;;AClTA,YAAYC,UAAQ;AACpB,YAAY,SAAS;AACrB,SAAS,eAAAC,oBAAmB;AAC5B,YAAYC,WAAU;;;ACLtB,YAAYC,SAAQ;AACpB,YAAYC,WAAU;;;ACNtB,YAAYC,SAAQ;AACpB,YAAYC,WAAU;AAMf,SAAS,aAA4B;AAC1C,QAAM,WAAW,QAAQ;AACzB,QAAM,OAAO,QAAQ;AAGrB,QAAM,WAAW,SAAS,QAAQ,QAAQ,SAAS,UAAU,UAAU;AACvE,MAAI,CAAC,SAAU,QAAO;AAGtB,QAAM,eACJ,aAAa,WACT,WACA,aAAa,UACX,UACA,aAAa,UACX,UACA;AACV,MAAI,CAAC,aAAc,QAAO;AAE1B,QAAM,cAAc,iBAAiB,YAAY,IAAI,QAAQ;AAC7D,QAAM,UAAU,aAAa,UAAU,aAAa;AAEpD,MAAI;AAEF,UAAM,cAAc,UAAQ,QAAQ,GAAG,WAAW,eAAe;AACjE,UAAM,aAAkB,cAAQ,WAAW;AAC3C,UAAM,UAAe,WAAK,YAAY,OAAO,OAAO;AACpD,WAAU,eAAW,OAAO,IAAI,UAAU;AAAA,EAC5C,QAAQ;AACN,WAAO;AAAA,EACT;AACF;;;AD7BA,SAAS,iBAAiB,WAAyB;AACjD,MAAI,CAAM,iBAAW,SAAS,GAAG;AAC/B,UAAM,IAAI,MAAM,iDAAiD,SAAS,IAAI;AAAA,EAChF;AACA,QAAM,WAAW,UAAU,MAAM,OAAO;AACxC,MAAI,SAAS,SAAS,IAAI,GAAG;AAC3B,UAAM,IAAI;AAAA,MACR,gDAAgD,SAAS;AAAA,IAE3D;AAAA,EACF;AACF;AAWA,IAAI;AAaG,SAAS,kBAAkC;AAChD,MAAI,OAAQ,QAAO;AAGnB,QAAM,UAAU,QAAQ,IAAI,gBAAgB,KAAK;AACjD,MAAI,SAAS;AACX,qBAAiB,OAAO;AACxB,QAAI,CAAI,eAAW,OAAO,GAAG;AAC3B,YAAM,IAAI,MAAM,6BAA6B,OAAO,gCAAgC;AAAA,IACtF;AACA,aAAS,EAAE,MAAM,SAAS,QAAQ,MAAM;AACxC,WAAO;AAAA,EACT;AAGA,QAAM,cAAc,WAAW;AAC/B,MAAI,aAAa;AACf,aAAS,EAAE,MAAM,aAAa,QAAQ,UAAU;AAChD,WAAO;AAAA,EACT;AAGA,WAAS,EAAE,MAAM,QAAQ,QAAQ,SAAS;AAC1C,SAAO;AACT;AAKO,SAAS,sBAA4B;AAC1C,WAAS;AACX;;;AEtEO,IAAM,eAAe;AAAA,EAC1B,MAAM;AAAA,EACN,KAAK;AACP;AAMA,SAAS,YAAY,SAAkD;AACrE,QAAM,QAAQ,QAAQ,MAAM,sBAAsB;AAClD,MAAI,CAAC,MAAO,QAAO;AACnB,SAAO,CAAC,SAAS,MAAM,CAAC,GAAG,EAAE,GAAG,SAAS,MAAM,CAAC,GAAG,EAAE,GAAG,SAAS,MAAM,CAAC,GAAG,EAAE,CAAC;AAChF;AAKA,SAAS,gBAAgB,WAAmB,UAA2B;AACrE,QAAM,IAAI,YAAY,SAAS;AAC/B,QAAM,IAAI,YAAY,QAAQ;AAC9B,MAAI,CAAC,KAAK,CAAC,EAAG,QAAO;AAErB,MAAI,EAAE,CAAC,MAAM,EAAE,CAAC,EAAG,QAAO,EAAE,CAAC,IAAI,EAAE,CAAC;AACpC,MAAI,EAAE,CAAC,MAAM,EAAE,CAAC,EAAG,QAAO,EAAE,CAAC,IAAI,EAAE,CAAC;AACpC,SAAO,EAAE,CAAC,KAAK,EAAE,CAAC;AACpB;AAMA,SAAS,iBAAiB,QAA+B;AACvD,QAAM,QAAQ,OAAO,MAAM,wBAAwB;AACnD,SAAO,QAAQ,MAAM,CAAC,IAAI;AAC5B;AAMA,SAAS,gBAAgB,QAA+B;AACtD,QAAM,QAAQ,OAAO,MAAM,+BAA+B;AAC1D,SAAO,QAAQ,MAAM,CAAC,IAAI;AAC5B;AAKA,SAAS,eAAe,MAA8B;AACpD,QAAM,WAAW,QAAQ;AAEzB,UAAQ,MAAM;AAAA,IACZ,KAAK;AACH,UAAI,aAAa,SAAU,QAAO;AAClC,aAAO;AAAA,IACT,KAAK;AACH,UAAI,aAAa,SAAU,QAAO;AAClC,UAAI,aAAa,QAAS,QAAO;AACjC,aAAO;AAAA,EACX;AACF;AAMA,eAAsB,gBACpB,MACA,QACA,iBACmC;AACnC,MAAI;AAEF,UAAM,aAAa,SAAS,UAAU,CAAC,kBAAkB,gBAAgB,IAAI;AAC7E,UAAM,UAAU,oBAAoB,aAAa,WAAW,OAAO;AAEnE,UAAM,SAAS,MAAM,OAAO,IAAI,SAAS,CAAC,WAAW,CAAC;AAEtD,QAAI,OAAO,aAAa,GAAG;AACzB,aAAO;AAAA,IACT;AAEA,UAAM,SAAS,OAAO,OAAO,KAAK,KAAK,OAAO,OAAO,KAAK;AAC1D,QAAI,YAA2B;AAE/B,YAAQ,MAAM;AAAA,MACZ,KAAK;AACH,oBAAY,iBAAiB,MAAM;AACnC;AAAA,MACF,KAAK;AACH,oBAAY,gBAAgB,MAAM;AAClC;AAAA,IACJ;AAEA,QAAI,CAAC,WAAW;AACd,aAAO;AAAA,IACT;AAEA,UAAM,WAAW,aAAa,IAAI;AAClC,WAAO;AAAA,MACL;AAAA,MACA;AAAA,MACA,WAAW,gBAAgB,WAAW,QAAQ;AAAA,MAC9C,aAAa,eAAe,IAAI;AAAA,MAChC,QAAQ,YAAY;AAAA,MACpB,cAAc,YAAY;AAAA,IAC5B;AAAA,EACF,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAKA,eAAsB,SAAS,QAAqD;AAClF,QAAM,CAAC,MAAM,GAAG,IAAI,MAAM,QAAQ,IAAI;AAAA,IACpC,gBAAgB,QAAQ,MAAM;AAAA,IAC9B,gBAAgB,OAAO,MAAM;AAAA,EAC/B,CAAC;AAED,SAAO,EAAE,MAAM,IAAI;AACrB;AAMA,eAAsB,WAAW,QAA0B,SAAiC;AAC1F,QAAM,MAAM,MAAM,gBAAgB,QAAQ,QAAQ,OAAO;AAEzD,MAAI,CAAC,KAAK;AACR,UAAM,IAAI,iBAAiB,eAAe,MAAM,CAAC;AAAA,EACnD;AAEA,MAAI,CAAC,IAAI,WAAW;AAClB,UAAM,IAAI,iBAAiB,IAAI,WAAW,IAAI,UAAU,eAAe,MAAM,CAAC;AAAA,EAChF;AACF;;;ACpIA,eAAsB,sBAA4C;AAEhE,QAAM,EAAE,kBAAkB,oBAAoB,IAAI,MAAM,OAAO,gBAAuB;AACtF,QAAM,aAAc,MAAM,iBAAiB;AAC3C,QAAM,YAAa,MAAM,oBAAoB,UAAU;AACvD,SAAO,EAAE,YAAY,UAAU;AACjC;AAKA,eAAsB,mBAAmB,YAAqC;AAE5E,QAAM,EAAE,oBAAoB,IAAI,MAAM,OAAO,gBAAuB;AACpE,SAAQ,MAAM,oBAAoB,UAAU;AAC9C;AASO,SAAS,iBAAiB,YAAoB,WAA2B;AAC9E,QAAM,OAAM,oBAAI,KAAK,GAAE,YAAY;AACnC,SAAO,cAAc,GAAG;AAAA,gBAAmB,SAAS;AAAA,EAAK,UAAU;AAAA;AACrE;;;AJbA,SAAS,eAAe,UAAmC;AACzD,SAAO,SAAS,SAAS,OAAO,IAAI,SAAS;AAC/C;AAUA,SAAS,qBAAqB,SAAqE;AACjG,QAAM,WAAW,0BAA0BC,aAAY,CAAC,EAAE,SAAS,KAAK,CAAC;AAEzE,SAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,UAAM,SAAa,iBAAa,CAAC,WAAW;AAK1C,aAAO,MAAM,SAAS,MAAM;AAC1B,eAAO,QAAQ;AAAA,MACjB,CAAC;AAAA,IACH,CAAC;AACD,WAAO,iBAAiB;AACxB,WAAO,GAAG,SAAS,MAAM;AACzB,WAAO,OAAO,UAAU,MAAM;AAC5B,cAAQ;AAAA,QACN,UAAU;AAAA,QACV,SAAS,MAAM,OAAO,MAAM;AAAA,MAC9B,CAAC;AAAA,IACH,CAAC;AAAA,EACH,CAAC;AACH;AAYO,IAAM,aAAN,MAA8C;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAYnD,YACmB,QACA,YACA,QACjB,UACA;AAJiB;AACA;AACA;AAGjB,SAAK,cAAc,YAAY,gBAAgB,EAAE;AAAA,EACnD;AAAA,EAlBiB;AAAA,EAoBT,eAAmD;AACzD,UAAM,MAA8B,CAAC;AACrC,QAAI,KAAK,QAAQ;AACf,UAAI,eAAe,KAAK;AAAA,IAC1B;AACA,QAAI,KAAK,YAAY;AACnB,UAAI,oBAAoB,KAAK;AAAA,IAC/B;AACA,WAAO,OAAO,KAAK,GAAG,EAAE,SAAS,IAAI,MAAM;AAAA,EAC7C;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUA,MAAM,QAAQ,UAA0C;AACtD,UAAM,WAAW,KAAK,QAAQ,KAAK,WAAW;AAC9C,UAAM,MAAM,eAAe,QAAQ;AACnC,UAAM,MAAM,KAAK,aAAa;AAC9B,UAAM,SAAS,MAAM,KAAK,OAAO;AAAA,MAC/B,KAAK;AAAA,MACL,CAAC,WAAW,iBAAiB,KAAK,QAAQ;AAAA,MAC1C;AAAA,QACE,GAAI,MAAM,EAAE,IAAI,IAAI,CAAC;AAAA,MACvB;AAAA,IACF;AAEA,QAAI,OAAO,aAAa,GAAG;AACzB,YAAM,YAAY,MAAM,KAAK,qBAAqB,QAAQ;AAC1D,UAAI,cAAc,iBAAiB;AACjC,cAAM,IAAI;AAAA,UACR,gCAAgC,QAAQ,MAAM,OAAO,OAAO,KAAK,CAAC;AAAA,QACpE;AAAA,MACF;AACA,YAAM,IAAI;AAAA,QACR,sBAAsB,QAAQ,MAAM,OAAO,OAAO,KAAK,CAAC;AAAA,QACxD;AAAA,MACF;AAAA,IACF;AAEA,QAAI;AACJ,QAAI;AACF,eAAc,YAAM,OAAO,MAAM,KAAK,CAAC;AAAA,IACzC,QAAQ;AACN,YAAM,IAAI;AAAA,QACR,yBAAyB,QAAQ;AAAA,QACjC;AAAA,MACF;AAAA,IACF;AAEA,UAAM,SAAiC,CAAC;AACxC,eAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,MAAM,GAAG;AACjD,aAAO,GAAG,IAAI,OAAO,KAAK;AAAA,IAC5B;AAEA,UAAM,WAAW,MAAM,KAAK,YAAY,QAAQ;AAEhD,WAAO,EAAE,QAAQ,SAAS;AAAA,EAC5B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAYA,MAAM,QACJ,UACA,QACA,UACA,aACe;AACf,UAAM,WAAW,KAAK,QAAQ,KAAK,WAAW;AAC9C,UAAM,MAAM,eAAe,QAAQ;AACnC,UAAM,UAAU,QAAQ,SAAS,KAAK,UAAU,QAAQ,MAAM,CAAC,IAAS,gBAAU,MAAM;AACxF,UAAM,OAAO,KAAK,iBAAiB,UAAU,UAAU,WAAW;AAClE,UAAM,MAAM,KAAK,aAAa;AAM9B,QAAI;AACJ,QAAI;AAEJ,QAAI,QAAQ,aAAa,SAAS;AAChC,YAAM,OAAO,MAAM,qBAAqB,OAAO;AAC/C,iBAAW,KAAK;AAChB,oBAAc,KAAK;AAAA,IACrB,OAAO;AACL,iBAAW;AAAA,IACb;AAEA,QAAI;AACJ,QAAI;AACF,eAAS,MAAM,KAAK,OAAO;AAAA,QACzB,KAAK;AAAA,QACL;AAAA,UACE;AAAA,UACA,GAAG;AAAA,UACH;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,QACF;AAAA,QACA;AAAA;AAAA;AAAA,UAGE,GAAI,QAAQ,aAAa,UAAU,EAAE,OAAO,QAAQ,IAAI,CAAC;AAAA,UACzD,GAAI,MAAM,EAAE,IAAI,IAAI,CAAC;AAAA,QACvB;AAAA,MACF;AAAA,IACF,UAAE;AACA,oBAAc;AAAA,IAChB;AAEA,QAAI,OAAO,aAAa,GAAG;AACzB,YAAM,IAAI;AAAA,QACR,sBAAsB,QAAQ,MAAM,OAAO,OAAO,KAAK,CAAC;AAAA,QACxD;AAAA,MACF;AAAA,IACF;AAGA,QAAI;AACF,MAAG,mBAAc,UAAU,OAAO,MAAM;AAAA,IAC1C,QAAQ;AACN,YAAM,IAAI,oBAAoB,sCAAsC,QAAQ,MAAM,QAAQ;AAAA,IAC5F;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,MAAM,UAAU,UAAkB,QAA+B;AAC/D,UAAM,WAAW,KAAK,QAAQ,KAAK,WAAW;AAC9C,UAAM,MAAM,KAAK,aAAa;AAC9B,UAAM,SAAS,MAAM,KAAK,OAAO;AAAA,MAC/B,KAAK;AAAA,MACL,CAAC,UAAU,MAAM,aAAa,QAAQ,QAAQ;AAAA,MAC9C;AAAA,QACE,GAAI,MAAM,EAAE,IAAI,IAAI,CAAC;AAAA,MACvB;AAAA,IACF;AAEA,QAAI,OAAO,aAAa,GAAG;AACzB,YAAM,IAAI;AAAA,QACR,yBAAyB,QAAQ,MAAM,OAAO,OAAO,KAAK,CAAC;AAAA,QAC3D;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,MAAM,aAAa,UAAkB,KAA4B;AAC/D,UAAM,WAAW,KAAK,QAAQ,KAAK,WAAW;AAC9C,UAAM,MAAM,KAAK,aAAa;AAC9B,UAAM,SAAS,MAAM,KAAK,OAAO;AAAA,MAC/B,KAAK;AAAA,MACL,CAAC,UAAU,MAAM,aAAa,KAAK,QAAQ;AAAA,MAC3C;AAAA,QACE,GAAI,MAAM,EAAE,IAAI,IAAI,CAAC;AAAA,MACvB;AAAA,IACF;AAEA,QAAI,OAAO,aAAa,GAAG;AACzB,YAAM,IAAI;AAAA,QACR,+BAA+B,QAAQ,MAAM,OAAO,OAAO,KAAK,CAAC;AAAA,QACjE;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,MAAM,gBAAgB,UAAkB,KAA4B;AAClE,UAAM,WAAW,KAAK,QAAQ,KAAK,WAAW;AAC9C,UAAM,MAAM,KAAK,aAAa;AAC9B,UAAM,SAAS,MAAM,KAAK,OAAO;AAAA,MAC/B,KAAK;AAAA,MACL,CAAC,UAAU,MAAM,YAAY,KAAK,QAAQ;AAAA,MAC1C;AAAA,QACE,GAAI,MAAM,EAAE,IAAI,IAAI,CAAC;AAAA,MACvB;AAAA,IACF;AAEA,QAAI,OAAO,aAAa,GAAG;AACzB,YAAM,IAAI;AAAA,QACR,oCAAoC,QAAQ,MAAM,OAAO,OAAO,KAAK,CAAC;AAAA,QACtE;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAM,mBAAmB,UAAoC;AAC3D,UAAM,WAAW,KAAK,QAAQ,KAAK,WAAW;AAC9C,QAAI;AACF,YAAM,KAAK,YAAY,QAAQ;AAC/B,aAAO;AAAA,IACT,QAAQ;AACN,aAAO;AAAA,IACT;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUA,MAAM,YAAY,UAAyC;AACzD,UAAM,WAAW,KAAK,QAAQ,KAAK,WAAW;AAC9C,UAAM,MAAM,KAAK,aAAa;AAC9B,UAAM,SAAS,MAAM,KAAK,OAAO,IAAI,KAAK,aAAa,CAAC,cAAc,QAAQ,GAAG;AAAA,MAC/E,GAAI,MAAM,EAAE,IAAI,IAAI,CAAC;AAAA,IACvB,CAAC;AAGD,QAAI,OAAO,aAAa,GAAG;AAEzB,aAAO,KAAK,sBAAsB,QAAQ;AAAA,IAC5C;AAEA,WAAO,KAAK,sBAAsB,QAAQ;AAAA,EAC5C;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUA,MAAc,qBAAqB,UAAsD;AACvF,QAAI;AACJ,QAAI;AACF,iBAAW,KAAK,sBAAsB,QAAQ;AAAA,IAChD,QAAQ;AACN,aAAO;AAAA,IACT;AAEA,QAAI,SAAS,YAAY,MAAO,QAAO;AAGvC,QAAI,CAAC,KAAK,UAAU,CAAC,KAAK,WAAY,QAAO;AAG7C,QAAI;AACJ,QAAI;AACF,mBAAa,KAAK,UAAa,kBAAa,KAAK,YAAa,OAAO;AAAA,IACvE,QAAQ;AACN,aAAO;AAAA,IACT;AAGA,UAAM,cAAc,WACjB,MAAM,IAAI,EACV,IAAI,CAAC,SAAS,KAAK,KAAK,CAAC,EACzB,OAAO,CAAC,SAAS,KAAK,WAAW,iBAAiB,CAAC;AAEtD,QAAI,YAAY,WAAW,EAAG,QAAO;AAErC,QAAI;AACF,YAAM,aAAa,MAAM,QAAQ,IAAI,YAAY,IAAI,CAAC,MAAM,mBAAmB,CAAC,CAAC,CAAC;AAClF,YAAM,aAAa,IAAI,IAAI,SAAS,UAAU;AAC9C,aAAO,WAAW,KAAK,CAAC,OAAO,WAAW,IAAI,EAAE,CAAC,IAAI,UAAU;AAAA,IACjE,QAAQ;AACN,aAAO;AAAA,IACT;AAAA,EACF;AAAA,EAEQ,sBAAsB,UAAgC;AAC5D,QAAI;AACJ,QAAI;AACF,gBAAa,kBAAa,UAAU,OAAO;AAAA,IAC7C,QAAQ;AACN,YAAM,IAAI;AAAA,QACR,wBAAwB,QAAQ;AAAA,QAChC;AAAA,MACF;AAAA,IACF;AAEA,QAAI;AACJ,QAAI;AACF,eAAc,YAAM,OAAO;AAAA,IAC7B,QAAQ;AACN,YAAM,IAAI;AAAA,QACR,SAAS,QAAQ;AAAA,QACjB;AAAA,MACF;AAAA,IACF;AAEA,UAAM,OAAO,QAAQ;AACrB,QAAI,CAAC,MAAM;AACT,YAAM,IAAI;AAAA,QACR,SAAS,QAAQ;AAAA,QACjB;AAAA,MACF;AAAA,IACF;AAEA,UAAM,UAAU,KAAK,cAAc,IAAI;AACvC,UAAM,aAAa,KAAK,kBAAkB,MAAM,OAAO;AACvD,UAAM,eAAe,KAAK,eAAe,IAAI,KAAK,KAAK,YAAsB,IAAI,oBAAI,KAAK;AAE1F,WAAO,EAAE,SAAS,YAAY,aAAa;AAAA,EAC7C;AAAA,EAEQ,cACN,MACiD;AACjD,QAAI,KAAK,OAAO,MAAM,QAAQ,KAAK,GAAG,KAAM,KAAK,IAAkB,SAAS,EAAG,QAAO;AACtF,QAAI,KAAK,OAAO,MAAM,QAAQ,KAAK,GAAG,KAAM,KAAK,IAAkB,SAAS,EAAG,QAAO;AACtF,QAAI,KAAK,WAAW,MAAM,QAAQ,KAAK,OAAO,KAAM,KAAK,QAAsB,SAAS;AACtF,aAAO;AACT,QAAI,KAAK,YAAY,MAAM,QAAQ,KAAK,QAAQ,KAAM,KAAK,SAAuB,SAAS;AACzF,aAAO;AACT,QAAI,KAAK,OAAO,MAAM,QAAQ,KAAK,GAAG,KAAM,KAAK,IAAkB,SAAS,EAAG,QAAO;AACtF,WAAO;AAAA,EACT;AAAA,EAEQ,kBACN,MACA,SACU;AACV,YAAQ,SAAS;AAAA,MACf,KAAK,OAAO;AACV,cAAM,UAAU,KAAK;AACrB,eAAO,SAAS,IAAI,CAAC,MAAM,OAAO,EAAE,aAAa,EAAE,CAAC,KAAK,CAAC;AAAA,MAC5D;AAAA,MACA,KAAK,UAAU;AACb,cAAM,UAAU,KAAK;AACrB,eAAO,SAAS,IAAI,CAAC,MAAM,OAAO,EAAE,OAAO,EAAE,CAAC,KAAK,CAAC;AAAA,MACtD;AAAA,MACA,KAAK,UAAU;AACb,cAAM,UAAU,KAAK;AACrB,eAAO,SAAS,IAAI,CAAC,MAAM,OAAO,EAAE,eAAe,EAAE,CAAC,KAAK,CAAC;AAAA,MAC9D;AAAA,MACA,KAAK,WAAW;AACd,cAAM,UAAU,KAAK;AACrB,eACE,SAAS,IAAI,CAAC,MAAM;AAClB,gBAAM,WAAW,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE;AACvD,gBAAM,OAAO,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE;AAEzC,iBAAO,YAAY,OAAO,GAAG,QAAQ,SAAS,IAAI,KAAK,YAAY;AAAA,QACrE,CAAC,KAAK,CAAC;AAAA,MAEX;AAAA,MACA,KAAK,OAAO;AACV,cAAM,UAAU,KAAK;AACrB,eAAO,SAAS,IAAI,CAAC,MAAM,OAAO,EAAE,MAAM,EAAE,CAAC,KAAK,CAAC;AAAA,MACrD;AAAA,IACF;AAAA,EACF;AAAA,EAEQ,iBACN,UACA,UACA,aACU;AACV,UAAM,OAAiB,CAAC;AAExB,UAAM,SAAS,cACX,qBAAqB,UAAU,WAAW,IAC1C;AAAA,MACE,SAAS,SAAS,KAAK;AAAA,MACvB,aAAa,SAAS,KAAK;AAAA,MAC3B,qBAAqB,SAAS,KAAK;AAAA,MACnC,cAAc,SAAS,KAAK;AAAA,MAC5B,iBAAiB,SAAS,KAAK;AAAA,IACjC;AAEJ,YAAQ,OAAO,SAAS;AAAA,MACtB,KAAK;AAEH;AAAA,MACF,KAAK;AACH,YAAI,OAAO,aAAa;AACtB,eAAK,KAAK,SAAS,OAAO,WAAW;AAAA,QACvC;AACA;AAAA,MACF,KAAK;AACH,YAAI,OAAO,qBAAqB;AAC9B,eAAK,KAAK,aAAa,OAAO,mBAAmB;AAAA,QACnD;AACA;AAAA,MACF,KAAK;AACH,YAAI,OAAO,cAAc;AACvB,eAAK,KAAK,cAAc,OAAO,YAAY;AAAA,QAC7C;AACA;AAAA,MACF,KAAK;AACH,YAAI,OAAO,iBAAiB;AAC1B,eAAK,KAAK,SAAS,OAAO,eAAe;AAAA,QAC3C;AACA;AAAA,IACJ;AAEA,WAAO;AAAA,EACT;AACF;;;AKrhBA,YAAYC,YAAU;AAsBf,IAAM,aAAN,MAAiB;AAAA,EACtB,YACmB,eACA,iBACA,YACjB;AAHiB;AACA;AACA;AAAA,EAChB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASH,MAAM,IAAI,UAAwB,UAAuC;AACvE,UAAM,SAAsB,CAAC;AAC7B,UAAM,QAAQ,KAAK,cAAc,cAAc,UAAU,QAAQ;AACjE,QAAI,YAAY;AAChB,QAAI,eAAe;AAGnB,UAAM,eAAe,MAAM,OAAO,CAAC,MAAM,CAAC,EAAE,MAAM;AAClD,eAAW,QAAQ,cAAc;AAC/B,aAAO,KAAK;AAAA,QACV,UAAU;AAAA,QACV,UAAU;AAAA,QACV,MAAM,KAAK;AAAA,QACX,SAAS,8BAA8B,KAAK,SAAS,IAAI,KAAK,WAAW;AAAA,QACzE,YAAY;AAAA,MACd,CAAC;AAAA,IACH;AAEA,UAAM,gBAAgB,MAAM,OAAO,CAAC,MAAM,EAAE,MAAM;AAClD,gBAAY,cAAc;AAG1B,UAAM,gBAA6D,CAAC;AAEpE,eAAW,QAAQ,eAAe;AAEhC,UAAI;AACF,cAAM,UAAU,MAAM,KAAK,WAAW,mBAAmB,KAAK,QAAQ;AACtE,YAAI,CAAC,SAAS;AACZ,iBAAO,KAAK;AAAA,YACV,UAAU;AAAA,YACV,UAAU;AAAA,YACV,MAAM,KAAK;AAAA,YACX,SAAS;AAAA,YACT,YAAY,mBAAmB,KAAK,QAAQ;AAAA,UAC9C,CAAC;AACD;AAAA,QACF;AAAA,MACF,QAAQ;AACN,eAAO,KAAK;AAAA,UACV,UAAU;AAAA,UACV,UAAU;AAAA,UACV,MAAM,KAAK;AAAA,UACX,SAAS;AAAA,QACX,CAAC;AACD;AAAA,MACF;AAGA,UAAI;AACF,cAAM,YAAY,MAAM,KAAK,WAAW,QAAQ,KAAK,QAAQ;AAC7D,cAAM,OAAO,OAAO,KAAK,UAAU,MAAM;AAGzC,YAAI,CAAC,cAAc,KAAK,SAAS,GAAG;AAClC,wBAAc,KAAK,SAAS,IAAI,CAAC;AAAA,QACnC;AACA,sBAAc,KAAK,SAAS,EAAE,KAAK,WAAW,IAAI,IAAI,IAAI,IAAI;AAG9D,YAAI,UAAU,SAAS,WAAW,UAAU,GAAG;AAC7C,iBAAO,KAAK;AAAA,YACV,UAAU;AAAA,YACV,UAAU;AAAA,YACV,MAAM,KAAK;AAAA,YACX,SAAS,+BAA+B,UAAU,SAAS,WAAW,MAAM;AAAA,UAC9E,CAAC;AAAA,QACH;AAGA,cAAM,gBAAgB,gCAAgC,UAAU,KAAK,WAAW;AAChF,YAAI,eAAe;AACjB,gBAAM,eAAe,IAAI;AAAA,YACvB,cAAc,IAAI,CAAC,MAAO,OAAO,MAAM,WAAW,IAAI,EAAE,GAAI;AAAA,UAC9D;AACA,gBAAM,aAAa,IAAI,IAAI,UAAU,SAAS,UAAU;AACxD,qBAAW,YAAY,cAAc;AACnC,gBAAI,CAAC,WAAW,IAAI,QAAQ,GAAG;AAC7B,qBAAO,KAAK;AAAA,gBACV,UAAU;AAAA,gBACV,UAAU;AAAA,gBACV,MAAM,KAAK;AAAA,gBACX,SAAS,uBAAuB,SAAS,MAAM,GAAG,CAAC,CAAC,SAAI,SAAS,MAAM,EAAE,CAAC;AAAA,gBAC1E,YAAY,uBAAuB,QAAQ,OAAO,KAAK,WAAW;AAAA,cACpE,CAAC;AAAA,YACH;AAAA,UACF;AACA,qBAAW,UAAU,YAAY;AAC/B,gBAAI,CAAC,aAAa,IAAI,MAAM,GAAG;AAC7B,qBAAO,KAAK;AAAA,gBACV,UAAU;AAAA,gBACV,UAAU;AAAA,gBACV,MAAM,KAAK;AAAA,gBACX,SAAS,yBAAyB,OAAO,MAAM,GAAG,CAAC,CAAC,SAAI,OAAO,MAAM,EAAE,CAAC;AAAA,gBACxE,YAAY,0BAA0B,MAAM,OAAO,KAAK,WAAW;AAAA,cACrE,CAAC;AAAA,YACH;AAAA,UACF;AAAA,QACF;AAGA,cAAM,KAAK,SAAS,WAAW,KAAK,CAAC,MAAM,EAAE,SAAS,KAAK,SAAS;AACpE,YAAI,IAAI,QAAQ;AACd,gBAAM,aAAkB,YAAK,UAAU,GAAG,MAAM;AAChD,cAAI;AACF,kBAAM,SAAS,KAAK,gBAAgB,WAAW,UAAU;AACzD,kBAAM,SAAS,KAAK,gBAAgB,SAAS,UAAU,QAAQ,MAAM;AAErE,uBAAW,OAAO,OAAO,QAAQ;AAC/B,qBAAO,KAAK;AAAA,gBACV,UAAU;AAAA,gBACV,UAAU;AAAA,gBACV,MAAM,KAAK;AAAA,gBACX,KAAK,IAAI;AAAA,gBACT,SAAS,IAAI;AAAA,gBACb,YAAY,YAAY,KAAK,SAAS,IAAI,KAAK,WAAW,IAAI,IAAI,GAAG;AAAA,cACvE,CAAC;AAAA,YACH;AAEA,uBAAW,QAAQ,OAAO,UAAU;AAClC,qBAAO,KAAK;AAAA,gBACV,UAAU;AAAA,gBACV,UAAU;AAAA,gBACV,MAAM,KAAK;AAAA,gBACX,KAAK,KAAK;AAAA,gBACV,SAAS,KAAK;AAAA,cAChB,CAAC;AAAA,YACH;AAAA,UACF,QAAQ;AACN,mBAAO,KAAK;AAAA,cACV,UAAU;AAAA,cACV,UAAU;AAAA,cACV,MAAM,KAAK;AAAA,cACX,SAAS,0BAA0B,GAAG,MAAM;AAAA,YAC9C,CAAC;AAAA,UACH;AAAA,QACF,OAAO;AAEL,qBAAW,OAAO,MAAM;AACtB,mBAAO,KAAK;AAAA,cACV,UAAU;AAAA,cACV,UAAU;AAAA,cACV,MAAM,KAAK;AAAA,cACX;AAAA,cACA,SAAS,QAAQ,GAAG,uEAAuE,KAAK,SAAS;AAAA,YAC3G,CAAC;AAAA,UACH;AAAA,QACF;AAGA,YAAI;AACF,gBAAM,cAAc,MAAM,eAAe,KAAK,QAAQ;AACtD,0BAAgB,YAAY;AAC5B,qBAAW,cAAc,aAAa;AACpC,mBAAO,KAAK;AAAA,cACV,UAAU;AAAA,cACV,UAAU;AAAA,cACV,MAAM,KAAK;AAAA,cACX,KAAK;AAAA,cACL,SAAS;AAAA,cACT,YAAY,YAAY,KAAK,SAAS,IAAI,KAAK,WAAW,IAAI,UAAU;AAAA,YAC1E,CAAC;AAAA,UACH;AAAA,QACF,QAAQ;AAAA,QAER;AAAA,MACF,QAAQ;AACN,eAAO,KAAK;AAAA,UACV,UAAU;AAAA,UACV,UAAU;AAAA,UACV,MAAM,KAAK;AAAA,UACX,SAAS;AAAA,QACX,CAAC;AAAA,MACH;AAAA,IACF;AAGA,eAAW,CAAC,QAAQ,OAAO,KAAK,OAAO,QAAQ,aAAa,GAAG;AAC7D,YAAM,UAAU,oBAAI,IAAY;AAChC,iBAAW,QAAQ,OAAO,OAAO,OAAO,GAAG;AACzC,mBAAW,KAAK,KAAM,SAAQ,IAAI,CAAC;AAAA,MACrC;AAEA,iBAAW,CAAC,SAAS,IAAI,KAAK,OAAO,QAAQ,OAAO,GAAG;AACrD,mBAAW,OAAO,SAAS;AACzB,cAAI,CAAC,KAAK,IAAI,GAAG,GAAG;AAClB,kBAAM,YAAY,OAAO,QAAQ,OAAO,EACrC,OAAO,CAAC,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,GAAG,CAAC,EAC9B,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC;AACjB,kBAAM,OAAO,cAAc;AAAA,cACzB,CAAC,MAAM,EAAE,cAAc,UAAU,EAAE,gBAAgB;AAAA,YACrD;AACA,gBAAI,MAAM;AACR,qBAAO,KAAK;AAAA,gBACV,UAAU;AAAA,gBACV,UAAU;AAAA,gBACV,MAAM,KAAK;AAAA,gBACX;AAAA,gBACA,SAAS,QAAQ,GAAG,mBAAmB,OAAO,mBAAmB,UAAU,KAAK,IAAI,CAAC;AAAA,gBACrF,YAAY,YAAY,MAAM,IAAI,OAAO,IAAI,GAAG;AAAA,cAClD,CAAC;AAAA,YACH;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAGA,QAAI,SAAS,sBAAsB,SAAS,mBAAmB,SAAS,GAAG;AACzE,YAAM,WAAW,MAAM,KAAK;AAAA,QAC1B,SAAS;AAAA,QACT;AAAA,QACA;AAAA,QACA;AAAA,MACF;AACA,aAAO,KAAK,GAAG,QAAQ;AAAA,IACzB;AAEA,WAAO,EAAE,QAAQ,WAAW,YAAY,aAAa,QAAQ,aAAa;AAAA,EAC5E;AAAA;AAAA;AAAA;AAAA,EAKA,MAAc,sBACZ,YACA,UACA,UACA,eACsB;AACtB,UAAM,SAAsB,CAAC;AAC7B,UAAM,mBAAmB,IAAI,IAAI,SAAS,aAAa,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC;AACzE,UAAM,kBAAkB,IAAI,IAAI,SAAS,WAAW,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC;AAExE,eAAW,MAAM,YAAY;AAE3B,iBAAW,MAAM,GAAG,YAAY;AAC9B,YAAI,CAAC,gBAAgB,IAAI,EAAE,GAAG;AAC5B,iBAAO,KAAK;AAAA,YACV,UAAU;AAAA,YACV,UAAU;AAAA,YACV,MAAM;AAAA,YACN,SAAS,qBAAqB,GAAG,IAAI,wCAAwC,EAAE;AAAA,UACjF,CAAC;AAAA,QACH;AAAA,MACF;AAGA,iBAAW,WAAW,kBAAkB;AACtC,YAAI,EAAE,WAAW,GAAG,eAAe;AACjC,iBAAO,KAAK;AAAA,YACV,UAAU;AAAA,YACV,UAAU;AAAA,YACV,MAAM;AAAA,YACN,SAAS,qBAAqB,GAAG,IAAI,6BAA6B,OAAO;AAAA,UAC3E,CAAC;AAAA,QACH;AAAA,MACF;AAIA,iBAAW,QAAQ,eAAe;AAChC,cAAM,YAAY,GAAG,aAAa,KAAK,WAAW;AAClD,YAAI,CAAC,UAAW;AAChB,YAAI,CAAC,UAAU,UAAW;AAE1B,YAAI,GAAG,WAAW,SAAS,KAAK,SAAS,GAAG;AAC1C,cAAI;AACF,kBAAM,WAAW,MAAM,KAAK,WAAW,YAAY,KAAK,QAAQ;AAChE,gBAAI,CAAC,SAAS,WAAW,SAAS,UAAU,SAAS,GAAG;AACtD,qBAAO,KAAK;AAAA,gBACV,UAAU;AAAA,gBACV,UAAU;AAAA,gBACV,MAAM,KAAK;AAAA,gBACX,SAAS,qBAAqB,GAAG,IAAI,oCAAoC,KAAK,SAAS,IAAI,KAAK,WAAW;AAAA,gBAC3G,YAAY,uBAAuB,GAAG,IAAI,iBAAiB,GAAG,WAAW,KAAK,GAAG,CAAC;AAAA,cACpF,CAAC;AAAA,YACH;AAAA,UACF,QAAQ;AAAA,UAER;AAAA,QACF,OAAO;AACL,cAAI;AACF,kBAAM,WAAW,MAAM,KAAK,WAAW,YAAY,KAAK,QAAQ;AAChE,gBAAI,SAAS,WAAW,SAAS,UAAU,SAAS,GAAG;AACrD,qBAAO,KAAK;AAAA,gBACV,UAAU;AAAA,gBACV,UAAU;AAAA,gBACV,MAAM,KAAK;AAAA,gBACX,SAAS,qBAAqB,GAAG,IAAI,wBAAwB,KAAK,SAAS,IAAI,KAAK,WAAW;AAAA,gBAC/F,YAAY,0BAA0B,UAAU,SAAS,OAAO,KAAK,WAAW;AAAA,cAClF,CAAC;AAAA,YACH;AAAA,UACF,QAAQ;AAAA,UAER;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAM,IAAI,UAAwB,UAAuC;AAEvE,UAAM,eAAe,KAAK,cAAc,mBAAmB,UAAU,QAAQ;AAE7E,eAAW,QAAQ,cAAc;AAC/B,YAAM,KAAK,cAAc,aAAa,MAAM,KAAK,YAAY,QAAQ;AAAA,IACvE;AAGA,WAAO,KAAK,IAAI,UAAU,QAAQ;AAAA,EACpC;AACF;;;ACzVO,IAAM,oBAAN,MAAwB;AAAA;AAAA;AAAA;AAAA;AAAA,EAK7B,mBACE,eACA,SACA,UAAuB,CAAC,GACA;AACxB,UAAM,SAAiC,CAAC;AAGxC,eAAW,CAAC,GAAG,CAAC,KAAK,OAAO,QAAQ,OAAO,GAAG;AAC5C,UAAI,MAAM,QAAW;AACnB,eAAO,CAAC,IAAI;AAAA,MACd;AAAA,IACF;AAEA,QAAI,UAAU,OAAO,QAAQ,cAAc,MAAM;AAGjD,QAAI,QAAQ,QAAQ,QAAQ,KAAK,SAAS,GAAG;AAC3C,YAAM,UAAU,IAAI,IAAI,QAAQ,IAAI;AACpC,gBAAU,QAAQ,OAAO,CAAC,CAAC,GAAG,MAAM,QAAQ,IAAI,GAAG,CAAC;AAAA,IACtD;AAGA,eAAW,CAAC,KAAK,KAAK,KAAK,SAAS;AAClC,YAAM,SAAS,QAAQ,SAAS,GAAG,QAAQ,MAAM,GAAG,GAAG,KAAK;AAG5D,UAAI,QAAQ,cAAc,UAAU,QAAQ;AAC1C;AAAA,MACF;AAEA,aAAO,MAAM,IAAI;AAAA,IACnB;AAEA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,aACE,eACA,QACA,UACQ;AACR,QAAI,WAAW,OAAO;AACpB,YAAM,IAAI;AAAA,QACR,8BAA8B,MAAM;AAAA;AAAA;AAAA,MAGtC;AAAA,IACF;AAEA,UAAM,QAAkB,CAAC;AACzB,UAAM,SAAS,WAAW,KAAK;AAE/B,eAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,cAAc,MAAM,GAAG;AAE/D,YAAM,UAAU,MAAM,QAAQ,MAAM,OAAO;AAC3C,YAAM,KAAK,GAAG,MAAM,GAAG,GAAG,KAAK,OAAO,GAAG;AAAA,IAC3C;AAEA,WAAO,MAAM,KAAK,IAAI,IAAI;AAAA,EAC5B;AACF;;;ACjFA,YAAYC,YAAU;;;ACAtB,YAAYC,YAAU;AACtB,YAAYC,WAAU;AAkBf,SAAS,aAAa,UAAkB,SAAgD;AAC7F,QAAM,OAAY,gBAAS,QAAQ;AACnC,QAAM,MAAW,eAAQ,QAAQ,EAAE,YAAY;AAG/C,MAAI,SAAS,UAAU,KAAK,WAAW,OAAO,GAAG;AAC/C,WAAO;AAAA,EACT;AAGA,MAAI,KAAK,SAAS,MAAM,GAAG;AACzB,WAAO;AAAA,EACT;AAGA,MAAI,QAAQ,QAAS,QAAO;AAC5B,MAAI,QAAQ,WAAW,QAAQ,OAAQ,QAAO;AAG9C,QAAM,UAAU,QAAQ,UAAU;AAClC,MAAI,QAAQ,WAAW,GAAG,GAAG;AAC3B,WAAO;AAAA,EACT;AAGA,MAAI;AACF,UAAM,SAAS,KAAK,MAAM,OAAO;AACjC,QAAI,WAAW,QAAQ,OAAO,WAAW,YAAY,CAAC,MAAM,QAAQ,MAAM,GAAG;AAC3E,aAAO;AAAA,IACT;AAAA,EACF,QAAQ;AAAA,EAER;AAGA,MAAI;AACF,UAAM,SAAc,YAAM,OAAO;AACjC,QAAI,WAAW,QAAQ,OAAO,WAAW,YAAY,CAAC,MAAM,QAAQ,MAAM,GAAG;AAC3E,aAAO;AAAA,IACT;AAAA,EACF,QAAQ;AAAA,EAER;AAGA,SAAO;AACT;AAMO,SAAS,YAAY,SAA+B;AACzD,QAAM,QAAgC,CAAC;AACvC,QAAM,UAAoB,CAAC;AAC3B,QAAM,WAAqB,CAAC;AAE5B,QAAM,QAAQ,QAAQ,MAAM,IAAI;AAChC,aAAW,WAAW,OAAO;AAC3B,QAAI,OAAO,QAAQ,KAAK;AAGxB,QAAI,CAAC,QAAQ,KAAK,WAAW,GAAG,GAAG;AACjC;AAAA,IACF;AAGA,QAAI,KAAK,WAAW,SAAS,GAAG;AAC9B,aAAO,KAAK,MAAM,CAAC;AAAA,IACrB;AAGA,UAAM,QAAQ,KAAK,QAAQ,GAAG;AAC9B,QAAI,UAAU,IAAI;AAChB;AAAA,IACF;AAEA,UAAM,MAAM,KAAK,MAAM,GAAG,KAAK,EAAE,KAAK;AACtC,QAAI,CAAC,KAAK;AACR;AAAA,IACF;AAEA,QAAI,QAAQ,KAAK,MAAM,QAAQ,CAAC;AAGhC,UAAM,mBAAmB,MAAM,QAAQ,IAAI;AAC3C,QAAI,qBAAqB,IAAI;AAC3B,cAAQ,MAAM,MAAM,GAAG,gBAAgB;AAAA,IACzC;AAGA,QACG,MAAM,WAAW,GAAG,KAAK,MAAM,SAAS,GAAG,KAC3C,MAAM,WAAW,GAAG,KAAK,MAAM,SAAS,GAAG,GAC5C;AACA,cAAQ,MAAM,MAAM,GAAG,EAAE;AAAA,IAC3B;AAEA,UAAM,GAAG,IAAI;AAAA,EACf;AAEA,SAAO,EAAE,OAAO,QAAQ,UAAU,SAAS,SAAS;AACtD;AAQO,SAAS,UAAU,SAA+B;AAEvD,MAAI;AACJ,MAAI;AACF,aAAS,KAAK,MAAM,OAAO;AAAA,EAC7B,SAAS,KAAK;AACZ,UAAM,IAAI,MAAM,iBAAkB,IAAc,OAAO,EAAE;AAAA,EAC3D;AAEA,MAAI,MAAM,QAAQ,MAAM,GAAG;AACzB,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AAEA,MAAI,WAAW,QAAQ,OAAO,WAAW,UAAU;AACjD,UAAM,IAAI,MAAM,kEAAkE;AAAA,EACpF;AAEA,QAAM,QAAgC,CAAC;AACvC,QAAM,UAAoB,CAAC;AAC3B,QAAM,WAAqB,CAAC;AAE5B,aAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,MAAiC,GAAG;AAC5E,QAAI,OAAO,UAAU,UAAU;AAC7B,YAAM,GAAG,IAAI;AAAA,IACf,WAAW,UAAU,MAAM;AACzB,cAAQ,KAAK,GAAG;AAChB,eAAS,KAAK,GAAG,GAAG,4CAAuC;AAAA,IAC7D,WAAW,MAAM,QAAQ,KAAK,GAAG;AAC/B,cAAQ,KAAK,GAAG;AAChB,eAAS,KAAK,GAAG,GAAG,6CAAwC;AAAA,IAC9D,WAAW,OAAO,UAAU,UAAU;AACpC,cAAQ,KAAK,GAAG;AAChB,eAAS,KAAK,GAAG,GAAG,qDAAgD;AAAA,IACtE,OAAO;AAEL,cAAQ,KAAK,GAAG;AAChB,eAAS,KAAK,GAAG,GAAG,6BAAwB,OAAO,KAAK,cAAc;AAAA,IACxE;AAAA,EACF;AAEA,SAAO,EAAE,OAAO,QAAQ,QAAQ,SAAS,SAAS;AACpD;AAQO,SAAS,UAAU,SAA+B;AAEvD,MAAI;AACJ,MAAI;AACF,aAAc,YAAM,OAAO;AAAA,EAC7B,SAAS,KAAK;AACZ,UAAM,IAAI,MAAM,iBAAkB,IAAc,OAAO,EAAE;AAAA,EAC3D;AAEA,MAAI,MAAM,QAAQ,MAAM,GAAG;AACzB,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AAEA,MAAI,WAAW,QAAQ,OAAO,WAAW,UAAU;AACjD,UAAM,IAAI,MAAM,kEAAkE;AAAA,EACpF;AAEA,QAAM,QAAgC,CAAC;AACvC,QAAM,UAAoB,CAAC;AAC3B,QAAM,WAAqB,CAAC;AAE5B,aAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,MAAiC,GAAG;AAC5E,QAAI,OAAO,UAAU,UAAU;AAC7B,YAAM,GAAG,IAAI;AAAA,IACf,WAAW,UAAU,MAAM;AACzB,cAAQ,KAAK,GAAG;AAChB,eAAS,KAAK,GAAG,GAAG,4CAAuC;AAAA,IAC7D,WAAW,MAAM,QAAQ,KAAK,GAAG;AAC/B,cAAQ,KAAK,GAAG;AAChB,eAAS,KAAK,GAAG,GAAG,6CAAwC;AAAA,IAC9D,WAAW,OAAO,UAAU,UAAU;AACpC,cAAQ,KAAK,GAAG;AAChB,eAAS,KAAK,GAAG,GAAG,qDAAgD;AAAA,IACtE,OAAO;AAEL,cAAQ,KAAK,GAAG;AAChB,eAAS,KAAK,GAAG,GAAG,6BAAwB,OAAO,KAAK,cAAc;AAAA,IACxE;AAAA,EACF;AAEA,SAAO,EAAE,OAAO,QAAQ,QAAQ,SAAS,SAAS;AACpD;AASO,SAASC,OAAM,SAAiB,QAAsB,UAAiC;AAC5F,QAAM,WACJ,WAAW,SAAS,aAAa,YAAY,IAAI,OAAO,IAAI;AAE9D,UAAQ,UAAU;AAAA,IAChB,KAAK;AACH,aAAO,YAAY,OAAO;AAAA,IAC5B,KAAK;AACH,aAAO,UAAU,OAAO;AAAA,IAC1B,KAAK;AACH,aAAO,UAAU,OAAO;AAAA,EAC5B;AACF;;;ADpNO,IAAM,eAAN,MAAmB;AAAA,EACxB,YAA6B,YAA+B;AAA/B;AAAA,EAAgC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAY7D,MAAM,OACJ,QACA,YACA,SACA,UACA,UACA,SACuB;AACvB,UAAM,CAAC,IAAI,GAAG,IAAI,OAAO,MAAM,GAAG;AAClC,UAAM,WAAgB;AAAA,MACpB;AAAA,MACA,SAAS,aAAa,QAAQ,eAAe,EAAE,EAAE,QAAQ,iBAAiB,GAAG;AAAA,IAC/E;AAGA,UAAM,SAASC,OAAM,SAAS,QAAQ,UAAU,QAAQ,cAAc,EAAE;AAGxE,QAAI,aAAa,OAAO,QAAQ,OAAO,KAAK;AAG5C,QAAI,QAAQ,QAAQ;AAClB,YAAM,SAAS,QAAQ;AACvB,mBAAa,WAAW,OAAO,CAAC,CAAC,GAAG,MAAM,IAAI,WAAW,MAAM,CAAC;AAAA,IAClE;AAGA,QAAI,QAAQ,QAAQ,QAAQ,KAAK,SAAS,GAAG;AAC3C,YAAM,SAAS,IAAI,IAAI,QAAQ,IAAI;AACnC,mBAAa,WAAW,OAAO,CAAC,CAAC,GAAG,MAAM,OAAO,IAAI,GAAG,CAAC;AAAA,IAC3D;AAEA,UAAM,WAAqB,CAAC;AAC5B,UAAM,UAAoB,CAAC;AAC3B,UAAM,SAAgD,CAAC;AACvD,UAAM,WAAW,CAAC,GAAG,OAAO,QAAQ;AAEpC,QAAI,QAAQ,QAAQ;AAElB,UAAIC;AACJ,UAAI;AACF,cAAMC,aAAY,MAAM,KAAK,WAAW,QAAQ,QAAQ;AACxD,QAAAD,gBAAe,IAAI,IAAI,OAAO,KAAKC,WAAU,MAAM,CAAC;AAAA,MACtD,QAAQ;AAEN,QAAAD,gBAAe,oBAAI,IAAY;AAAA,MACjC;AAEA,iBAAW,CAAC,GAAG,KAAK,YAAY;AAC9B,YAAIA,cAAa,IAAI,GAAG,KAAK,CAAC,QAAQ,WAAW;AAC/C,kBAAQ,KAAK,GAAG;AAAA,QAClB,OAAO;AACL,mBAAS,KAAK,GAAG;AAAA,QACnB;AAAA,MACF;AAEA,aAAO,EAAE,UAAU,SAAS,QAAQ,UAAU,QAAQ,KAAK;AAAA,IAC7D;AAGA,UAAM,YAAY,MAAM,KAAK,WAAW,QAAQ,QAAQ;AACxD,QAAI,gBAAwC,EAAE,GAAG,UAAU,OAAO;AAClE,UAAM,eAAe,IAAI,IAAI,OAAO,KAAK,UAAU,MAAM,CAAC;AAE1D,eAAW,CAAC,KAAK,KAAK,KAAK,YAAY;AACrC,UAAI,aAAa,IAAI,GAAG,KAAK,CAAC,QAAQ,WAAW;AAC/C,gBAAQ,KAAK,GAAG;AAChB;AAAA,MACF;AAEA,UAAI;AACF,cAAM,YAAY,EAAE,GAAG,eAAe,CAAC,GAAG,GAAG,MAAM;AACnD,cAAM,KAAK,WAAW,QAAQ,UAAU,WAAW,UAAU,GAAG;AAChE,wBAAgB;AAChB,iBAAS,KAAK,GAAG;AAAA,MACnB,SAAS,KAAK;AACZ,cAAM,UAAU,eAAe,QAAQ,IAAI,UAAU;AACrD,eAAO,KAAK,EAAE,KAAK,OAAO,QAAQ,CAAC;AAAA,MAErC;AAAA,IACF;AAEA,WAAO,EAAE,UAAU,SAAS,QAAQ,UAAU,QAAQ,MAAM;AAAA,EAC9D;AACF;;;AEjIA,YAAYE,UAAQ;AACpB,YAAYC,YAAU;AACtB,YAAYC,WAAU;AA0BtB,SAAS,oBAAoB,OAAmC;AAC9D,MAAI,OAAO,UAAU,UAAU;AAC7B,WAAO,EAAE,KAAK,MAAM;AAAA,EACtB;AACA,MAAI,OAAO,UAAU,YAAY,UAAU,MAAM;AAC/C,UAAM,MAAM;AACZ,WAAO;AAAA,MACL,KAAK,OAAO,IAAI,OAAO,EAAE;AAAA,MACzB,GAAI,OAAO,IAAI,UAAU,WAAW,EAAE,OAAO,IAAI,MAAM,IAAI,CAAC;AAAA,IAC9D;AAAA,EACF;AACA,SAAO,EAAE,KAAK,GAAG;AACnB;AAEA,SAAS,YAAY,OAAqC;AACxD,SAAO;AAAA,IACL,KAAK,MAAM;AAAA,IACX,SAAS,WAAW,MAAM,GAAG;AAAA,IAC7B,GAAI,MAAM,QAAQ,EAAE,OAAO,MAAM,MAAM,IAAI,CAAC;AAAA,EAC9C;AACF;AAEA,SAAS,iBAAiB,UAA2C;AACnE,QAAM,eAAoB,YAAK,UAAU,sBAAsB;AAC/D,QAAM,MAAS,kBAAa,cAAc,OAAO;AACjD,SAAY,YAAM,GAAG;AACvB;AAEA,SAAS,kBAAkB,UAAkB,KAAoC;AAC/E,QAAM,eAAoB,YAAK,UAAU,sBAAsB;AAC/D,EAAG,mBAAc,cAAmB,gBAAU,GAAG,GAAG,OAAO;AAC7D;AAEA,SAAS,mBAAmB,KAAyC;AACnE,QAAM,OAAO,IAAI;AACjB,MAAI,CAAC,KAAM,QAAO,CAAC;AACnB,QAAM,MAAM,KAAK;AACjB,MAAI,CAAC,IAAK,QAAO,CAAC;AAClB,QAAM,aAAa,IAAI;AACvB,MAAI,CAAC,MAAM,QAAQ,UAAU,EAAG,QAAO,CAAC;AACxC,SAAO;AACT;AAEA,SAAS,sBAAsB,KAAyC;AACtE,MAAI,CAAC,IAAI,QAAQ,OAAO,IAAI,SAAS,UAAU;AAC7C,QAAI,OAAO,CAAC;AAAA,EACd;AACA,QAAM,OAAO,IAAI;AACjB,MAAI,CAAC,KAAK,OAAO,OAAO,KAAK,QAAQ,UAAU;AAC7C,SAAK,MAAM,CAAC;AAAA,EACd;AACA,QAAM,MAAM,KAAK;AACjB,MAAI,CAAC,MAAM,QAAQ,IAAI,UAAU,GAAG;AAClC,QAAI,aAAa,CAAC;AAAA,EACpB;AACA,SAAO,IAAI;AACb;AAEA,SAAS,8BAA8B,KAA8B,SAA4B;AAC/F,QAAM,eAAe,IAAI;AACzB,MAAI,CAAC,MAAM,QAAQ,YAAY,EAAG,QAAO,CAAC;AAC1C,QAAM,MAAM,aAAa,KAAK,CAAC,MAAO,EAA8B,SAAS,OAAO;AAGpF,MAAI,CAAC,IAAK,QAAO,CAAC;AAClB,QAAM,aAAa,IAAI;AACvB,MAAI,CAAC,MAAM,QAAQ,UAAU,EAAG,QAAO,CAAC;AACxC,SAAO;AACT;AAEA,SAAS,iCACP,KACA,SACW;AACX,QAAM,eAAe,IAAI;AACzB,MAAI,CAAC,MAAM,QAAQ,YAAY,GAAG;AAChC,UAAM,IAAI,MAAM,oCAAoC;AAAA,EACtD;AACA,QAAM,MAAM,aAAa,KAAK,CAAC,MAAO,EAA8B,SAAS,OAAO;AAGpF,MAAI,CAAC,KAAK;AACR,UAAM,IAAI,MAAM,gBAAgB,OAAO,0BAA0B;AAAA,EACnE;AACA,MAAI,CAAC,MAAM,QAAQ,IAAI,UAAU,GAAG;AAClC,QAAI,aAAa,CAAC;AAAA,EACpB;AACA,SAAO,IAAI;AACb;AAYO,IAAM,mBAAN,MAAuB;AAAA,EAC5B,YACmB,YACA,eACjB;AAFiB;AACA;AAAA,EAChB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASH,MAAM,KAAK,UAAwB,UAAkB,aAA4C;AAC/F,QAAI,aAAa;AACf,YAAM,MAAM,SAAS,aAAa,KAAK,CAAC,MAAM,EAAE,SAAS,WAAW;AACpE,UAAI,CAAC,KAAK;AACR,cAAM,IAAI,MAAM,gBAAgB,WAAW,0BAA0B;AAAA,MACvE;AAAA,IACF;AACA,UAAM,MAAM,iBAAiB,QAAQ;AACrC,UAAM,UAAU,cACZ,8BAA8B,KAAK,WAAW,IAC9C,mBAAmB,GAAG;AAC1B,WAAO,QAAQ,IAAI,CAAC,UAAU,YAAY,oBAAoB,KAAK,CAAC,CAAC;AAAA,EACvE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAaA,MAAM,IACJ,KACA,OACA,UACA,UACA,aAC2B;AAC3B,UAAM,aAAa,qBAAqB,GAAG;AAC3C,QAAI,CAAC,WAAW,OAAO;AACrB,YAAM,IAAI,MAAM,WAAW,KAAK;AAAA,IAClC;AACA,UAAM,gBAAgB,WAAW;AAEjC,QAAI,aAAa;AACf,YAAM,MAAM,SAAS,aAAa,KAAK,CAAC,MAAM,EAAE,SAAS,WAAW;AACpE,UAAI,CAAC,KAAK;AACR,cAAM,IAAI,MAAM,gBAAgB,WAAW,0BAA0B;AAAA,MACvE;AAAA,IACF;AAGA,UAAM,MAAM,iBAAiB,QAAQ;AACrC,UAAM,iBAAiB,cACnB,8BAA8B,KAAK,WAAW,IAC9C,mBAAmB,GAAG;AAC1B,UAAM,cAAc,eAAe,IAAI,CAAC,MAAM,oBAAoB,CAAC,EAAE,GAAG;AAExE,QAAI,YAAY,SAAS,aAAa,GAAG;AACvC,YAAM,IAAI,MAAM,cAAc,WAAW,aAAa,CAAC,uBAAuB;AAAA,IAChF;AAGA,UAAM,eAAoB,YAAK,UAAU,sBAAsB;AAC/D,UAAM,iBAAoB,kBAAa,cAAc,OAAO;AAG5D,UAAM,aAAa,cACf,iCAAiC,KAAK,WAAW,IACjD,sBAAsB,GAAG;AAC7B,QAAI,OAAO;AACT,iBAAW,KAAK,EAAE,KAAK,eAAe,MAAM,CAAC;AAAA,IAC/C,OAAO;AACL,iBAAW,KAAK,aAAa;AAAA,IAC/B;AACA,sBAAkB,UAAU,GAAG;AAG/B,UAAM,WAAW,KAAK,cAAc,cAAc,UAAU,QAAQ,EAAE,OAAO,CAAC,MAAM,EAAE,MAAM;AAC5F,UAAM,QAAQ,cAAc,SAAS,OAAO,CAAC,MAAM,EAAE,gBAAgB,WAAW,IAAI;AACpF,UAAM,mBAA6B,CAAC;AACpC,UAAM,cAAwB,CAAC;AAC/B,UAAM,cAAc,oBAAI,IAAoB;AAE5C,eAAW,QAAQ,OAAO;AACxB,UAAI;AAEF,oBAAY,IAAI,KAAK,UAAa,kBAAa,KAAK,UAAU,OAAO,CAAC;AAEtE,cAAM,KAAK,WAAW,aAAa,KAAK,UAAU,aAAa;AAE/D,yBAAiB,KAAK,KAAK,QAAQ;AAAA,MACrC,QAAQ;AACN,oBAAY,KAAK,KAAK,QAAQ;AAG9B,QAAG,mBAAc,cAAc,gBAAgB,OAAO;AAGtD,mBAAW,mBAAmB,kBAAkB;AAC9C,gBAAM,SAAS,YAAY,IAAI,eAAe;AAC9C,cAAI,QAAQ;AACV,YAAG,mBAAc,iBAAiB,QAAQ,OAAO;AAAA,UACnD;AAAA,QACF;AAGA,cAAM,cAAc,iBAAiB,QAAQ;AAC7C,cAAM,kBAAkB,cACpB,8BAA8B,aAAa,WAAW,IACtD,mBAAmB,WAAW;AAClC,cAAM,qBAAqB,gBAAgB,IAAI,CAAC,MAAM,YAAY,oBAAoB,CAAC,CAAC,CAAC;AAEzF,eAAO;AAAA,UACL,OAAO,YAAY,EAAE,KAAK,eAAe,MAAM,CAAC;AAAA,UAChD,YAAY;AAAA,UACZ,kBAAkB,CAAC;AAAA,UACnB;AAAA,UACA,UAAU,CAAC,yEAAyE;AAAA,QACtF;AAAA,MACF;AAAA,IACF;AAGA,UAAM,aAAa,iBAAiB,QAAQ;AAC5C,UAAM,iBAAiB,cACnB,8BAA8B,YAAY,WAAW,IACrD,mBAAmB,UAAU;AACjC,UAAM,kBAAkB,eAAe,IAAI,CAAC,MAAM,YAAY,oBAAoB,CAAC,CAAC,CAAC;AAErF,WAAO;AAAA,MACL,OAAO,YAAY,EAAE,KAAK,eAAe,MAAM,CAAC;AAAA,MAChD,YAAY;AAAA,MACZ;AAAA,MACA;AAAA,MACA,UAAU,CAAC;AAAA,IACb;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAaA,MAAM,OACJ,KACA,UACA,UACA,aAC2B;AAC3B,UAAM,aAAa,IAAI,KAAK;AAE5B,QAAI,aAAa;AACf,YAAM,MAAM,SAAS,aAAa,KAAK,CAAC,MAAM,EAAE,SAAS,WAAW;AACpE,UAAI,CAAC,KAAK;AACR,cAAM,IAAI,MAAM,gBAAgB,WAAW,0BAA0B;AAAA,MACvE;AAAA,IACF;AAGA,UAAM,MAAM,iBAAiB,QAAQ;AACrC,UAAM,iBAAiB,cACnB,8BAA8B,KAAK,WAAW,IAC9C,mBAAmB,GAAG;AAC1B,UAAM,SAAS,eAAe,IAAI,CAAC,MAAM,oBAAoB,CAAC,CAAC;AAC/D,UAAM,aAAa,OAAO,UAAU,CAAC,MAAM,EAAE,QAAQ,UAAU;AAE/D,QAAI,eAAe,IAAI;AACrB,YAAM,IAAI,MAAM,cAAc,WAAW,UAAU,CAAC,2BAA2B;AAAA,IACjF;AAEA,UAAM,eAAe,OAAO,UAAU;AAGtC,UAAM,eAAoB,YAAK,UAAU,sBAAsB;AAC/D,UAAM,iBAAoB,kBAAa,cAAc,OAAO;AAG5D,UAAM,aAAa,cACf,iCAAiC,KAAK,WAAW,IACjD,sBAAsB,GAAG;AAC7B,eAAW,OAAO,YAAY,CAAC;AAC/B,sBAAkB,UAAU,GAAG;AAG/B,UAAM,WAAW,KAAK,cAAc,cAAc,UAAU,QAAQ,EAAE,OAAO,CAAC,MAAM,EAAE,MAAM;AAC5F,UAAM,QAAQ,cAAc,SAAS,OAAO,CAAC,MAAM,EAAE,gBAAgB,WAAW,IAAI;AACpF,UAAM,mBAA6B,CAAC;AACpC,UAAM,cAAwB,CAAC;AAC/B,UAAM,cAAc,oBAAI,IAAoB;AAE5C,eAAW,QAAQ,OAAO;AACxB,UAAI;AAEF,oBAAY,IAAI,KAAK,UAAa,kBAAa,KAAK,UAAU,OAAO,CAAC;AAEtE,cAAM,KAAK,WAAW,gBAAgB,KAAK,UAAU,UAAU;AAE/D,yBAAiB,KAAK,KAAK,QAAQ;AAAA,MACrC,QAAQ;AACN,oBAAY,KAAK,KAAK,QAAQ;AAG9B,QAAG,mBAAc,cAAc,gBAAgB,OAAO;AAGtD,mBAAW,mBAAmB,kBAAkB;AAC9C,gBAAM,SAAS,YAAY,IAAI,eAAe;AAC9C,cAAI,QAAQ;AACV,YAAG,mBAAc,iBAAiB,QAAQ,OAAO;AAAA,UACnD;AAAA,QACF;AAGA,cAAM,cAAc,iBAAiB,QAAQ;AAC7C,cAAM,kBAAkB,cACpB,8BAA8B,aAAa,WAAW,IACtD,mBAAmB,WAAW;AAClC,cAAM,qBAAqB,gBAAgB,IAAI,CAAC,MAAM,YAAY,oBAAoB,CAAC,CAAC,CAAC;AAEzF,eAAO;AAAA,UACL,SAAS,YAAY,YAAY;AAAA,UACjC,YAAY;AAAA,UACZ,kBAAkB,CAAC;AAAA,UACnB;AAAA,UACA,UAAU;AAAA,YACR;AAAA,YACA;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAGA,UAAM,aAAa,iBAAiB,QAAQ;AAC5C,UAAM,iBAAiB,cACnB,8BAA8B,YAAY,WAAW,IACrD,mBAAmB,UAAU;AACjC,UAAM,kBAAkB,eAAe,IAAI,CAAC,MAAM,YAAY,oBAAoB,CAAC,CAAC,CAAC;AAErF,WAAO;AAAA,MACL,SAAS,YAAY,YAAY;AAAA,MACjC,YAAY;AAAA,MACZ;AAAA,MACA;AAAA,MACA,UAAU;AAAA,QACR;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;;;ACvYA,YAAYC,UAAQ;AACpB,YAAYC,YAAU;AACtB,YAAYC,WAAU;AAEf,IAAM,oBAAoB;AAEjC,IAAMC,kBACJ;AAgBK,SAAS,iBAAiB,UAA0B;AACzD,SAAY,YAAK,UAAU,iBAAiB;AAC9C;AAGO,SAAS,aAAa,UAAsC;AACjE,QAAM,WAAW,iBAAiB,QAAQ;AAC1C,MAAI;AACF,QAAI,CAAI,gBAAW,QAAQ,EAAG,QAAO,CAAC;AACtC,UAAM,UAAa,kBAAa,UAAU,OAAO;AACjD,UAAM,SAAc,YAAM,OAAO;AACjC,QAAI,CAAC,UAAU,CAAC,MAAM,QAAQ,OAAO,QAAQ,EAAG,QAAO,CAAC;AACxD,WAAO,OAAO,SAAS,IAAI,CAAC,OAAmB;AAAA,MAC7C,KAAK,EAAE;AAAA,MACP,OAAO,EAAE;AAAA,MACT,aAAa,IAAI,KAAK,EAAE,YAAY;AAAA,MACpC,aAAa,EAAE;AAAA,IACjB,EAAE;AAAA,EACJ,QAAQ;AACN,WAAO,CAAC;AAAA,EACV;AACF;AAGO,SAAS,aAAa,UAAkB,UAAoC;AACjF,QAAM,WAAW,iBAAiB,QAAQ;AAC1C,MAAI,SAAS,WAAW,GAAG;AACzB,QAAI;AACF,MAAG,gBAAW,QAAQ;AAAA,IACxB,QAAQ;AAAA,IAER;AACA;AAAA,EACF;AACA,QAAM,OAAO;AAAA,IACX,UAAU,SAAS,IAAI,CAAC,MAAM;AAC5B,YAAM,MAAkB;AAAA,QACtB,KAAK,EAAE;AAAA,QACP,OAAO,EAAE;AAAA,QACT,cAAc,EAAE,YAAY,YAAY;AAAA,MAC1C;AACA,UAAI,EAAE,YAAa,KAAI,cAAc,EAAE;AACvC,aAAO;AAAA,IACT,CAAC;AAAA,EACH;AACA,EAAG,mBAAc,UAAUA,kBAAsB,gBAAU,IAAI,GAAG,OAAO;AAC3E;AAMO,SAAS,cACd,UACA,KACA,OACA,aACkB;AAClB,QAAM,WAAW,aAAa,QAAQ;AACtC,QAAM,MAAM,oBAAI,KAAK;AACrB,QAAM,UAA4B,EAAE,KAAK,OAAO,aAAa,KAAK,YAAY;AAE9E,QAAM,gBAAgB,SAAS,UAAU,CAAC,MAAM,EAAE,QAAQ,GAAG;AAC7D,MAAI,iBAAiB,GAAG;AACtB,aAAS,aAAa,IAAI;AAAA,EAC5B,OAAO;AACL,aAAS,KAAK,OAAO;AAAA,EACvB;AAEA,eAAa,UAAU,QAAQ;AAC/B,SAAO;AACT;AAMO,SAAS,cAAc,UAAkB,YAA6C;AAC3F,QAAM,WAAW,aAAa,QAAQ;AACtC,QAAM,QAAQ,WAAW,UAAU,UAAU;AAC7C,MAAI,CAAC,MAAO,QAAO;AAEnB,QAAM,WAAW,SAAS,OAAO,CAAC,MAAM,EAAE,QAAQ,MAAM,GAAG;AAC3D,eAAa,UAAU,QAAQ;AAC/B,SAAO;AACT;AAMO,SAAS,YAAY,UAAkB,YAA6C;AACzF,QAAM,WAAW,aAAa,QAAQ;AACtC,SAAO,WAAW,UAAU,UAAU;AACxC;AAEA,SAAS,WAAW,UAA8B,YAA6C;AAC7F,QAAM,QAAQ,WAAW,YAAY;AAErC,QAAM,UAAU,SAAS,KAAK,CAAC,MAAM,EAAE,MAAM,YAAY,MAAM,KAAK;AACpE,MAAI,QAAS,QAAO;AAEpB,QAAM,QAAQ,SAAS,KAAK,CAAC,MAAM,EAAE,QAAQ,UAAU;AACvD,SAAO,SAAS;AAClB;;;AC/HA,YAAYC,UAAQ;AACpB,YAAYC,YAAU;AACtB,YAAYC,WAAU;AAcf,IAAM,gBAAN,MAAoB;AAAA,EACjB,SAAS,IAAI,eAAe;AAAA,EAC5B,SAAS,IAAI,cAAc;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUnC,OAAO,WAAmB,YAAoB,iBAAyC;AACrF,UAAM,gBAAgB,KAAK,OAAO,MAAW,YAAK,WAAW,sBAAsB,CAAC;AACpF,UAAM,iBAAiB,KAAK,OAAO,MAAW,YAAK,YAAY,sBAAsB,CAAC;AAEtF,UAAM,aAAa,KAAK,OAAO,cAAc,eAAe,SAAS;AACrE,UAAM,cAAc,KAAK,OAAO,cAAc,gBAAgB,UAAU;AAExE,UAAM,gBAAgB,cAAc,aAAa,IAAI,CAAC,MAAM,EAAE,IAAI;AAClE,UAAM,iBAAiB,eAAe,aAAa,IAAI,CAAC,MAAM,EAAE,IAAI;AAGpE,UAAM,eAAe,IAAI,IAAI,cAAc,WAAW,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC;AACxE,UAAM,gBAAgB,IAAI,IAAI,eAAe,WAAW,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC;AAC1E,QAAI,mBAAmB,CAAC,GAAG,YAAY,EAAE,OAAO,CAAC,MAAM,cAAc,IAAI,CAAC,CAAC;AAE3E,QAAI,mBAAmB,gBAAgB,SAAS,GAAG;AACjD,YAAM,YAAY,IAAI,IAAI,eAAe;AACzC,yBAAmB,iBAAiB,OAAO,CAAC,MAAM,UAAU,IAAI,CAAC,CAAC;AAAA,IACpE;AAEA,UAAM,SAAuB,CAAC;AAC9B,QAAI,kBAAkB;AAEtB,eAAW,MAAM,kBAAkB;AAEjC,YAAM,UAAU,oBAAI,IAAyB;AAC7C,YAAM,UAAU,oBAAI,IAAY;AAGhC,YAAM,eAAe,WAAW,OAAO,CAAC,MAAM,EAAE,cAAc,EAAE;AAChE,iBAAW,QAAQ,cAAc;AAC/B,cAAM,OAAO,KAAK,iBAAiB,KAAK,QAAQ;AAChD,YAAI,SAAS,KAAM;AACnB,gBAAQ,IAAI,KAAK,WAAW;AAC5B,mBAAW,OAAO,MAAM;AACtB,cAAI,CAAC,QAAQ,IAAI,GAAG,EAAG,SAAQ,IAAI,KAAK,oBAAI,IAAI,CAAC;AACjD,kBAAQ,IAAI,GAAG,EAAG,IAAI,KAAK,WAAW;AAAA,QACxC;AAAA,MACF;AAGA,YAAM,gBAAgB,YAAY,OAAO,CAAC,MAAM,EAAE,cAAc,EAAE;AAClE,iBAAW,QAAQ,eAAe;AAChC,cAAM,OAAO,KAAK,iBAAiB,KAAK,QAAQ;AAChD,YAAI,SAAS,KAAM;AACnB,gBAAQ,IAAI,KAAK,WAAW;AAC5B,mBAAW,OAAO,MAAM;AACtB,cAAI,CAAC,QAAQ,IAAI,GAAG,EAAG,SAAQ,IAAI,KAAK,oBAAI,IAAI,CAAC;AACjD,kBAAQ,IAAI,GAAG,EAAG,IAAI,KAAK,WAAW;AAAA,QACxC;AAAA,MACF;AAGA,YAAM,UAAU,CAAC,GAAG,OAAO;AAC3B,UAAI,UAAU;AAEd,iBAAW,CAAC,KAAK,MAAM,KAAK,SAAS;AACnC,cAAM,cAAc,QAAQ,OAAO,CAAC,MAAM,CAAC,OAAO,IAAI,CAAC,CAAC;AACxD,YAAI,YAAY,SAAS,GAAG;AAC1B,oBAAU;AACV,gBAAM,YAAY,CAAC,GAAG,MAAM,EAAE,KAAK;AACnC,iBAAO,KAAK;AAAA,YACV,WAAW;AAAA,YACX;AAAA,YACA;AAAA,YACA,aAAa,YAAY,KAAK;AAAA,YAC9B,SAAS,QAAQ,GAAG,mBAAmB,EAAE,gBAAgB,UAAU,KAAK,IAAI,CAAC,0BAA0B,YAAY,KAAK,EAAE,KAAK,IAAI,CAAC;AAAA,UACtI,CAAC;AAAA,QACH;AAAA,MACF;AAEA,UAAI,QAAS;AAAA,IACf;AAEA,WAAO;AAAA,MACL;AAAA,MACA,oBAAoB,iBAAiB;AAAA,MACrC;AAAA,MACA,mBAAmB;AAAA,MACnB,oBAAoB;AAAA,IACtB;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQQ,iBAAiB,UAAmC;AAC1D,QAAI;AACF,UAAI,CAAI,gBAAW,QAAQ,EAAG,QAAO;AACrC,YAAM,MAAS,kBAAa,UAAU,OAAO;AAC7C,YAAM,SAAc,YAAM,GAAG;AAC7B,UAAI,WAAW,QAAQ,WAAW,UAAa,OAAO,WAAW,SAAU,QAAO;AAClF,aAAO,OAAO,KAAK,MAAM,EAAE,OAAO,CAAC,MAAM,MAAM,MAAM;AAAA,IACvD,QAAQ;AACN,aAAO;AAAA,IACT;AAAA,EACF;AACF;;;AChIA,YAAYC,UAAQ;AACpB,YAAYC,YAAU;AACtB,YAAYC,YAAU;;;ACOf,IAAM,kBAAN,MAAsB;AAAA,EAC3B,SAAS,YAAuC;AAC9C,UAAM,SAA8B,CAAC;AAIrC,UAAM,gBAAgB,WAAW,OAAO,CAAC,MAAM,EAAE,aAAa,YAAY,EAAE,QAAQ,MAAS;AAG7F,UAAM,eAAe,cAAc,OAAO,CAAC,MAAM,EAAE,aAAa,OAAO;AACvE,SAAK,YAAY,YAAY,EAAE,QAAQ,CAAC,QAAQ,SAAS;AACvD,YAAM,IAAI,OAAO;AACjB,aAAO,KAAK;AAAA,QACV,UAAU;AAAA,QACV,UAAU;AAAA,QACV;AAAA,QACA,OAAO;AAAA,QACP,SAAS,GAAG,CAAC,OAAO,MAAM,IAAI,MAAM,EAAE;AAAA,MACxC,CAAC;AAAA,IACH,CAAC;AAGD,UAAM,kBAAkB,cAAc;AAAA,MACpC,CAAC,MAAM,EAAE,aAAa,aAAa,EAAE,QAAQ,SAAS,aAAa;AAAA,IACrE;AACA,SAAK,YAAY,eAAe,EAAE,QAAQ,CAAC,QAAQ,SAAS;AAC1D,YAAM,IAAI,OAAO;AACjB,aAAO,KAAK;AAAA,QACV,UAAU;AAAA,QACV,UAAU;AAAA,QACV;AAAA,QACA,OAAO;AAAA,QACP,SAAS,GAAG,CAAC,eAAe,MAAM,IAAI,MAAM,EAAE;AAAA,MAChD,CAAC;AAAA,IACH,CAAC;AAGD,UAAM,iBAAiB,cAAc;AAAA,MACnC,CAAC,MAAM,EAAE,aAAa,aAAa,CAAC,EAAE,QAAQ,SAAS,aAAa;AAAA,IACtE;AACA,SAAK,YAAY,cAAc,EAAE,QAAQ,CAAC,QAAQ,SAAS;AACzD,YAAM,IAAI,OAAO;AACjB,aAAO,KAAK;AAAA,QACV,UAAU;AAAA,QACV,UAAU;AAAA,QACV;AAAA,QACA,OAAO;AAAA,QACP,SAAS,MAAM,IAAI,8BAA8B,GAAG,CAAC;AAAA,MACvD,CAAC;AAAA,IACH,CAAC;AAMD,eAAW,SAAS,WAAW,OAAO,CAAC,MAAM,EAAE,aAAa,YAAY,EAAE,QAAQ,MAAS,GAAG;AAC5F,aAAO,KAAK;AAAA,QACV,UAAU,MAAM;AAAA,QAChB,UAAU,MAAM;AAAA,QAChB,MAAM,MAAM;AAAA,QACZ,SAAS,MAAM;AAAA,MACjB,CAAC;AAAA,IACH;AAIA,UAAM,eAAe,WAAW,OAAO,CAAC,MAAM,EAAE,aAAa,QAAQ;AAGrE,UAAM,cAAc,aAAa,OAAO,CAAC,MAAM,EAAE,QAAQ,MAAS;AAClE,UAAM,cAAc,oBAAI,IAGtB;AACF,eAAW,SAAS,aAAa;AAG/B,YAAM,SAAS;AACf,YAAM,SAAS;AACf,YAAM,KAAK,MAAM,QAAQ,QAAQ,MAAM;AACvC,UAAI,OAAO,GAAI;AACf,YAAM,cAAc,MAAM,QAAQ,QAAQ,QAAQ,KAAK,OAAO,MAAM;AACpE,UAAI,gBAAgB,GAAI;AACxB,YAAM,YAAY,MAAM,QAAQ,MAAM,KAAK,OAAO,QAAQ,WAAW;AACrE,UAAI,CAAC,aAAa,KAAK,KAAK,SAAS,EAAG;AACxC,YAAM,OAAO,MAAM,QAAQ,MAAM,cAAc,OAAO,MAAM;AAC5D,UAAI,CAAC,KAAK,SAAS,GAAG,EAAG;AACzB,YAAM,aAAa,KAAK,MAAM,GAAG,EAAE;AACnC,YAAM,YAAY,KAAK,iBAAiB,MAAM,IAAI;AAClD,YAAM,WAAW,GAAG,SAAS,IAAI,SAAS,IAAI,UAAU;AACxD,YAAM,WAAW,YAAY,IAAI,QAAQ;AACzC,UAAI,UAAU;AACZ,iBAAS;AAAA,MACX,OAAO;AACL,oBAAY,IAAI,UAAU,EAAE,WAAW,WAAW,YAAY,OAAO,EAAE,CAAC;AAAA,MAC1E;AAAA,IACF;AACA,eAAW,SAAS,YAAY,OAAO,GAAG;AACxC,YAAM,IAAI,MAAM;AAChB,aAAO,KAAK;AAAA,QACV,UAAU;AAAA,QACV,UAAU;AAAA,QACV,WAAW,MAAM;AAAA,QACjB,aAAa,MAAM;AAAA,QACnB,mBAAmB,MAAM;AAAA,QACzB,YAAY;AAAA,QACZ,SAAS,GAAG,CAAC,OAAO,MAAM,IAAI,MAAM,EAAE,QAAQ,MAAM,UAAU,kBAAkB,MAAM,SAAS;AAAA,MACjG,CAAC;AAAA,IACH;AAGA,eAAW,SAAS,aAAa,OAAO,CAAC,MAAM,EAAE,QAAQ,MAAS,GAAG;AACnE,aAAO,KAAK;AAAA,QACV,UAAU,MAAM;AAAA,QAChB,UAAU,MAAM;AAAA,QAChB,MAAM,MAAM;AAAA,QACZ,SAAS,MAAM;AAAA,MACjB,CAAC;AAAA,IACH;AAIA,eAAW,SAAS,WAAW,OAAO,CAAC,MAAM,EAAE,aAAa,MAAM,GAAG;AACnE,aAAO,KAAK;AAAA,QACV,UAAU,MAAM;AAAA,QAChB,UAAU,MAAM;AAAA,QAChB,MAAM,MAAM;AAAA,QACZ,SAAS,MAAM;AAAA,MACjB,CAAC;AAAA,IACH;AAIA,eAAW,SAAS,WAAW,OAAO,CAAC,MAAM,EAAE,aAAa,kBAAkB,GAAG;AAC/E,aAAO,KAAK;AAAA,QACV,UAAU,MAAM;AAAA,QAChB,UAAU,MAAM;AAAA,QAChB,MAAM,MAAM;AAAA,QACZ,SAAS,MAAM;AAAA,MACjB,CAAC;AAAA,IACH;AAEA,UAAM,aAAgC;AAAA,MACpC,OAAO,OAAO,OAAO,CAAC,MAAM,EAAE,aAAa,OAAO,EAAE;AAAA,MACpD,SAAS,OAAO,OAAO,CAAC,MAAM,EAAE,aAAa,SAAS,EAAE;AAAA,MACxD,MAAM,OAAO,OAAO,CAAC,MAAM,EAAE,aAAa,MAAM,EAAE;AAAA,IACpD;AAEA,WAAO,EAAE,YAAY,QAAQ,OAAO;AAAA,EACtC;AAAA,EAEQ,YAAY,QAA+C;AACjE,UAAM,MAAM,oBAAI,IAAyB;AACzC,eAAW,SAAS,QAAQ;AAC1B,YAAM,MAAM,IAAI,IAAI,MAAM,IAAI,KAAK,CAAC;AACpC,UAAI,KAAK,KAAK;AACd,UAAI,IAAI,MAAM,MAAM,GAAG;AAAA,IACzB;AACA,WAAO;AAAA,EACT;AAAA,EAEQ,iBAAiB,UAA0B;AACjD,UAAM,aAAa,SAAS,QAAQ,OAAO,GAAG;AAC9C,UAAM,QAAQ,WAAW,MAAM,GAAG;AAClC,WAAO,MAAM,UAAU,IAAK,MAAM,MAAM,SAAS,CAAC,KAAK,KAAO,MAAM,CAAC,KAAK;AAAA,EAC5E;AACF;;;ADjJO,IAAM,kBAAN,MAAsB;AAAA,EAC3B,YACmB,QACA,YACA,eACA,iBACjB;AAJiB;AACA;AACA;AACA;AAAA,EAChB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWH,MAAM,SACJ,UACA,aACA,SACqB;AACrB,QAAI,WAAgC;AACpC,QAAI;AACF,YAAM,SAAS,IAAI,eAAe;AAClC,iBAAW,OAAO,MAAW,YAAK,UAAU,WAAW,CAAC;AAAA,IAC1D,QAAQ;AAEN,YAAM,gBAAyC;AAAA,QAC7C,iBAAiB;AAAA,QACjB,aAAa;AAAA,QACb,cAAc,CAAC;AAAA,QACf,YAAY,CAAC;AAAA,QACb,gBAAgB;AAAA,MAClB;AACA,aAAO;AAAA,QACL,eAAe;AAAA,QACf,cAAc,MAAM,KAAK,kBAAkB,UAAU,WAAW;AAAA,QAChE,UAAU;AAAA,QACV,QAAQ,CAAC;AAAA,QACT,QAAQ,EAAE,YAAY,EAAE,OAAO,GAAG,SAAS,GAAG,MAAM,EAAE,GAAG,QAAQ,CAAC,EAAE;AAAA,QACpE,YAAY,CAAC;AAAA,MACf;AAAA,IACF;AAEA,UAAM,CAAC,cAAc,aAAa,MAAM,IAAI,MAAM,QAAQ,IAAI;AAAA,MAC5D,KAAK,kBAAkB,UAAU,WAAW;AAAA,MAC5C,KAAK,iBAAiB,UAAU,UAAU,OAAO;AAAA,MACjD,KAAK,YAAY,UAAU,QAAQ;AAAA,IACrC,CAAC;AAED,WAAO;AAAA,MACL,eAAe;AAAA,MACf;AAAA,MACA,UAAU,KAAK,uBAAuB,QAAQ;AAAA,MAC9C,QAAQ;AAAA,MACR;AAAA,MACA,YAAY,KAAK,gBAAgB,WAAW;AAAA,IAC9C;AAAA,EACF;AAAA,EAEA,MAAc,kBACZ,UACA,aAC6B;AAC7B,QAAI,aAAa;AACjB,QAAI,YAAY;AAChB,QAAI,SAAS;AACb,QAAI,kBAAkB;AACtB,QAAI,cAA6B;AAEjC,QAAI;AACF,YAAM,IAAI,MAAM,KAAK,OAAO,IAAI,OAAO,CAAC,UAAU,WAAW,QAAQ,GAAG,EAAE,KAAK,SAAS,CAAC;AACzF,UAAI,EAAE,aAAa,EAAG,cAAa,KAAK,oBAAoB,EAAE,OAAO,KAAK,CAAC;AAAA,IAC7E,QAAQ;AAAA,IAER;AAEA,QAAI;AACF,YAAM,IAAI,MAAM,KAAK,OAAO,IAAI,OAAO,CAAC,aAAa,MAAM,GAAG,EAAE,KAAK,SAAS,CAAC;AAC/E,UAAI,EAAE,aAAa,EAAG,aAAY,EAAE,OAAO,KAAK;AAAA,IAClD,QAAQ;AAAA,IAER;AAEA,QAAI;AACF,YAAM,IAAI,MAAM,KAAK,OAAO,IAAI,OAAO,CAAC,UAAU,gBAAgB,GAAG,EAAE,KAAK,SAAS,CAAC;AACtF,UAAI,EAAE,aAAa,EAAG,UAAS,EAAE,OAAO,KAAK;AAAA,IAC/C,QAAQ;AAAA,IAER;AAEA,QAAI;AACF,YAAM,IAAI,MAAM,KAAK,OAAO,IAAI,OAAO,CAAC,OAAO,MAAM,cAAc,GAAG,EAAE,KAAK,SAAS,CAAC;AACvF,UAAI,EAAE,aAAa,EAAG,mBAAkB,EAAE,OAAO,KAAK;AAAA,IACxD,QAAQ;AAAA,IAER;AAEA,QAAI;AACF,YAAM,MAAM,MAAM,gBAAgB,QAAQ,KAAK,MAAM;AACrD,oBAAc,KAAK,aAAa;AAAA,IAClC,QAAQ;AAAA,IAER;AAEA,WAAO;AAAA,MACL;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,oBAAmB,oBAAI,KAAK,GAAE,YAAY;AAAA,MAC1C;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAAA,EAEQ,oBAAoB,QAAwB;AAClD,UAAM,WAAW,OAAO,MAAM,+BAA+B;AAC7D,QAAI,SAAU,QAAO,GAAG,SAAS,CAAC,CAAC,IAAI,SAAS,CAAC,CAAC;AAClD,UAAM,aAAa,OAAO,MAAM,uCAAuC;AACvE,QAAI,WAAY,QAAO,GAAG,WAAW,CAAC,CAAC,IAAI,WAAW,CAAC,CAAC;AACxD,WAAO,OAAO,QAAQ,UAAU,EAAE;AAAA,EACpC;AAAA,EAEQ,uBAAuB,UAAiD;AAC9E,WAAO;AAAA,MACL,iBAAiB,SAAS;AAAA,MAC1B,aAAa,SAAS;AAAA,MACtB,cAAc,SAAS,aAAa,IAAI,CAAC,OAAO;AAAA,QAC9C,MAAM,EAAE;AAAA,QACR,WAAW,EAAE,aAAa;AAAA,MAC5B,EAAE;AAAA,MACF,YAAY,SAAS,WAAW,IAAI,CAAC,QAAQ;AAAA,QAC3C,MAAM,GAAG;AAAA,QACT,WAAW,CAAC,CAAC,GAAG;AAAA,QAChB,QAAQ,GAAG,UAAU,CAAC;AAAA,MACxB,EAAE;AAAA,MACF,gBAAgB,SAAS,KAAK;AAAA,IAChC;AAAA,EACF;AAAA,EAEA,MAAc,iBACZ,UACA,UACA,SAC6B;AAC7B,UAAM,WAAW,KAAK,cAAc,cAAc,UAAU,QAAQ;AACpE,UAAM,QAAQ,SAAS,OAAO,CAAC,SAAS;AACtC,YAAM,OACJ,CAAC,SAAS,iBAAiB,UAAU,QAAQ,gBAAgB,SAAS,KAAK,SAAS;AACtF,YAAM,QACJ,CAAC,SAAS,mBAAmB,UAAU,QAAQ,kBAAkB,SAAS,KAAK,WAAW;AAC5F,aAAO,QAAQ;AAAA,IACjB,CAAC;AAED,UAAM,SAA6B,CAAC;AAEpC,eAAW,QAAQ,OAAO;AACxB,aAAO,KAAK,MAAM,KAAK,UAAU,IAAI,CAAC;AAAA,IACxC;AAEA,WAAO;AAAA,EACT;AAAA,EAEA,MAAc,UAAU,MAA6C;AACnE,QAAI,CAAC,KAAK,QAAQ;AAChB,aAAO;AAAA,QACL,WAAW,KAAK;AAAA,QAChB,aAAa,KAAK;AAAA,QAClB,UAAU,KAAK;AAAA,QACf,QAAQ;AAAA,QACR,UAAU;AAAA,QACV,cAAc;AAAA,QACd,UAAU;AAAA,MACZ;AAAA,IACF;AAEA,UAAM,WAAW,KAAK,aAAa,KAAK,QAAQ;AAEhD,QAAI,eAAe;AACnB,QAAI;AACF,YAAM,UAAU,MAAM,eAAe,KAAK,QAAQ;AAClD,qBAAe,QAAQ;AAAA,IACzB,QAAQ;AAAA,IAER;AAEA,QAAI,WAAsC;AAC1C,QAAI;AACF,YAAM,eAAe,MAAM,KAAK,WAAW,YAAY,KAAK,QAAQ;AACpE,iBAAW;AAAA,QACT,SAAS,aAAa;AAAA,QACtB,YAAY,aAAa;AAAA,QACzB,cAAc,aAAa,cAAc,YAAY,KAAK;AAAA,MAC5D;AAAA,IACF,QAAQ;AAAA,IAER;AAEA,WAAO;AAAA,MACL,WAAW,KAAK;AAAA,MAChB,aAAa,KAAK;AAAA,MAClB,UAAU,KAAK;AAAA,MACf,QAAQ;AAAA,MACR;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAAA,EAEQ,aAAa,UAA0B;AAC7C,QAAI;AACF,UAAI,CAAI,gBAAW,QAAQ,EAAG,QAAO;AACrC,YAAM,MAAS,kBAAa,UAAU,OAAO;AAC7C,YAAM,SAAuB,aAAM,GAAG;AACtC,UAAI,WAAW,QAAQ,WAAW,UAAa,OAAO,WAAW,SAAU,QAAO;AAClF,aAAO,OAAO,KAAK,MAAiC,EAAE,OAAO,CAAC,MAAM,MAAM,MAAM,EAAE;AAAA,IACpF,QAAQ;AACN,aAAO;AAAA,IACT;AAAA,EACF;AAAA,EAEA,MAAc,YAAY,UAAwB,UAAyC;AACzF,QAAI;AACF,YAAM,aAAa,IAAI,WAAW,KAAK,eAAe,KAAK,iBAAiB,KAAK,UAAU;AAC3F,YAAM,aAAa,MAAM,WAAW,IAAI,UAAU,QAAQ;AAC1D,aAAO,IAAI,gBAAgB,EAAE,SAAS,WAAW,MAAM;AAAA,IACzD,QAAQ;AACN,aAAO,EAAE,YAAY,EAAE,OAAO,GAAG,SAAS,GAAG,MAAM,EAAE,GAAG,QAAQ,CAAC,EAAE;AAAA,IACrE;AAAA,EACF;AAAA,EAEQ,gBAAgB,OAAmE;AACzF,UAAM,eAAe,oBAAI,IAGvB;AAEF,eAAW,QAAQ,OAAO;AACxB,UAAI,CAAC,KAAK,SAAU;AACpB,iBAAW,aAAa,KAAK,SAAS,YAAY;AAChD,cAAM,OAAO,KAAK,mBAAmB,SAAS;AAC9C,cAAM,WAAW,aAAa,IAAI,SAAS;AAC3C,YAAI,UAAU;AACZ,mBAAS,aAAa,IAAI,KAAK,WAAW;AAC1C,mBAAS;AAAA,QACX,OAAO;AACL,uBAAa,IAAI,WAAW;AAAA,YAC1B;AAAA,YACA,cAAc,oBAAI,IAAI,CAAC,KAAK,WAAW,CAAC;AAAA,YACxC,WAAW;AAAA,UACb,CAAC;AAAA,QACH;AAAA,MACF;AAAA,IACF;AAEA,UAAM,SAAiD,CAAC;AACxD,eAAW,CAAC,WAAW,IAAI,KAAK,aAAa,QAAQ,GAAG;AACtD,aAAO,SAAS,IAAI;AAAA,QAClB,MAAM,KAAK;AAAA,QACX,cAAc,MAAM,KAAK,KAAK,YAAY;AAAA,QAC1C,WAAW,KAAK;AAAA,MAClB;AAAA,IACF;AACA,WAAO;AAAA,EACT;AAAA,EAEQ,mBAAmB,WAA2B;AACpD,QAAI,UAAU,WAAW,MAAM,EAAG,QAAO;AACzC,QAAI,UAAU,WAAW,cAAc,EAAG,QAAO;AACjD,QAAI,UAAU,SAAS,WAAW,KAAK,UAAU,SAAS,aAAa,EAAG,QAAO;AACjF,WAAO;AAAA,EACT;AACF;;;AEhSO,IAAM,oBAAN,MAAwB;AAAA,EAC7B,UAAU,QAAoC;AAC5C,UAAM,UAAU,KAAK,aAAa,MAAM;AACxC,UAAM,QAAQ,KAAK,WAAW,MAAM;AACpC,UAAM,gBAAgB,KAAK,mBAAmB,OAAO,OAAO,MAAM;AAElE,WAAO;AAAA,MACL,WAAW,OAAO,aAAa;AAAA,MAC/B,QAAQ,OAAO,aAAa;AAAA,MAC5B,iBAAiB,IAAI,KAAK,OAAO,aAAa,eAAe,EAAE,QAAQ;AAAA,MACvE,YAAY,OAAO,aAAa;AAAA,MAChC;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAAA,EAEQ,aAAa,QAAwC;AAC3D,UAAM,aAAa,CAAC,GAAG,IAAI,IAAI,OAAO,OAAO,IAAI,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;AACrE,UAAM,eAAe,CAAC,GAAG,IAAI,IAAI,OAAO,OAAO,IAAI,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;AACzE,UAAM,QAAQ,OAAO,OAAO,IAAI,CAAC,SAAS,KAAK,UAAU,MAAM,OAAO,OAAO,MAAM,CAAC;AACpF,UAAM,aAAa,OAAO,OAAO,OAAO,OAAO,CAAC,MAAM,EAAE,aAAa,OAAO,EAAE;AAE9E,WAAO;AAAA,MACL,cAAc,OAAO,OAAO;AAAA,MAC5B;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,QAAQ,eAAe;AAAA,IACzB;AAAA,EACF;AAAA,EAEQ,UAAU,MAAwB,QAA8C;AACtF,UAAM,eAAe,KAAK,oBAAoB,MAAM,MAAM;AAC1D,UAAM,cAAc,KAAK,aAAa,MAAM,YAAY;AAExD,WAAO;AAAA,MACL,WAAW,KAAK;AAAA,MAChB,aAAa,KAAK;AAAA,MAClB;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAAA,EAEQ,oBACN,MACA,QACuB;AACvB,QAAI,CAAC,KAAK,OAAQ,QAAO;AAEzB,UAAM,aAAa,OAAO;AAAA,MACxB,CAAC,MACE,EAAE,cAAc,KAAK,aAAa,EAAE,gBAAgB,KAAK,eACzD,EAAE,SAAS,UACV,EAAE,KAAK,SAAS,KAAK,SAAS,KAC9B,EAAE,KAAK,SAAS,KAAK,WAAW;AAAA,IACtC;AAEA,QAAI,WAAW,KAAK,CAAC,MAAM,EAAE,aAAa,OAAO,EAAG,QAAO;AAC3D,QAAI,WAAW,KAAK,CAAC,MAAM,EAAE,aAAa,SAAS,KAAK,KAAK,eAAe,EAAG,QAAO;AACtF,WAAO;AAAA,EACT;AAAA,EAEQ,aAAa,MAAwB,QAAuC;AAClF,YAAQ,QAAQ;AAAA,MACd,KAAK;AACH,eAAO;AAAA,MACT,KAAK;AACH,eAAO;AAAA,MACT,KAAK;AACH,eAAO,KAAK,eAAe,IACvB,GAAG,KAAK,YAAY,oCACpB;AAAA,MACN,KAAK;AACH,eAAO,GAAG,KAAK,QAAQ;AAAA,IAC3B;AAAA,EACF;AAAA,EAEQ,WAAW,QAAwC;AACzD,UAAM,aAAa,CAAC,GAAG,IAAI,IAAI,OAAO,OAAO,IAAI,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;AACrE,UAAM,cAAc,OAAO,OAAO,OAAO,OAAO,CAAC,MAAM,EAAE,aAAa,OAAO;AAE7E,WAAO,WAAW,IAAI,CAAC,cAAc;AACnC,YAAM,WAAW,YAAY,OAAO,CAAC,MAAM,EAAE,cAAc,SAAS;AACpE,YAAM,aAAa,SAAS,OAAO,CAAC,KAAK,MAAM,OAAO,EAAE,cAAc,IAAI,CAAC;AAC3E,aAAO;AAAA,QACL;AAAA,QACA,WAAW,aAAa;AAAA,QACxB,YAAY;AAAA,MACd;AAAA,IACF,CAAC;AAAA,EACH;AAAA,EAEQ,mBAAmB,QAAkD;AAC3E,WAAO,OAAO,IAAI,CAAC,WAAW;AAAA,MAC5B,QAAQ,GAAG,MAAM,QAAQ,IAAI,MAAM,QAAQ;AAAA,MAC3C,UAAU,MAAM;AAAA,MAChB,QAAQ,MAAM,aAAa;AAAA,MAC3B,UAAU,MAAM;AAAA,MAChB,SAAS,MAAM;AAAA,MACf,GAAI,MAAM,aAAa,MAAM,cACzB;AAAA,QACE,OAAO;AAAA,UACL,GAAI,MAAM,YAAY,EAAE,WAAW,MAAM,UAAU,IAAI,CAAC;AAAA,UACxD,GAAI,MAAM,cAAc,EAAE,aAAa,MAAM,YAAY,IAAI,CAAC;AAAA,QAChE;AAAA,MACF,IACA,CAAC;AAAA,IACP,EAAE;AAAA,EACJ;AACF;;;ACtHA,IAAM,yBAAyB;AAMxB,IAAM,cAAN,MAAkB;AAAA,EACN;AAAA,EAEjB,YAAY,SAAqC;AAC/C,SAAK,eAAe,SAAS,gBAAgB;AAAA,EAC/C;AAAA,EACA,MAAM,iBACJ,QACA,QACA,eACmC;AACnC,UAAM,MAAM,GAAG,MAAM,wBAAwB,mBAAmB,aAAa,CAAC;AAC9E,WAAO,KAAK,QAAkC,OAAO,KAAK,MAAM;AAAA,EAClE;AAAA,EAEA,MAAM,aACJ,QACA,QACA,QAC8B;AAC9B,UAAM,MAAM,GAAG,MAAM;AACrB,WAAO,KAAK,QAA6B,QAAQ,KAAK,QAAQ,MAAM;AAAA,EACtE;AAAA,EAEA,MAAM,mBACJ,QACA,QACA,OAC6B;AAC7B,UAAM,MAAM,GAAG,MAAM;AACrB,WAAO,KAAK,QAA4B,QAAQ,KAAK,QAAQ,KAAK;AAAA,EACpE;AAAA,EAEA,MAAc,QACZ,QACA,KACA,QACA,MACY;AACZ,UAAM,UAAkC;AAAA,MACtC,eAAe,UAAU,MAAM;AAAA,MAC/B,gBAAgB;AAAA,IAClB;AAEA,UAAM,OAAoB;AAAA,MACxB;AAAA,MACA;AAAA,MACA,GAAI,SAAS,SAAY,EAAE,MAAM,KAAK,UAAU,IAAI,EAAE,IAAI,CAAC;AAAA,IAC7D;AAEA,QAAI;AACJ,QAAI;AACF,iBAAW,MAAM,MAAM,KAAK,IAAI;AAAA,IAClC,QAAQ;AAEN,YAAM,KAAK,MAAM,KAAK,YAAY;AAClC,UAAI;AACF,mBAAW,MAAM,MAAM,KAAK,IAAI;AAAA,MAClC,SAAS,UAAU;AACjB,cAAM,IAAI;AAAA,UACR,sCAAuC,SAAmB,OAAO;AAAA,UACjE;AAAA,UACA;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAEA,QAAI,SAAS,IAAI;AACf,aAAQ,MAAM,SAAS,KAAK;AAAA,IAC9B;AAGA,QAAI,SAAS,UAAU,OAAO,SAAS,SAAS,KAAK;AACnD,YAAM,KAAK,MAAM,KAAK,YAAY;AAClC,YAAM,gBAAgB,MAAM,MAAM,KAAK,IAAI;AAC3C,UAAI,cAAc,IAAI;AACpB,eAAQ,MAAM,cAAc,KAAK;AAAA,MACnC;AACA,YAAM,KAAK,WAAW,aAAa;AAAA,IACrC;AAGA,UAAM,KAAK,WAAW,QAAQ;AAAA,EAChC;AAAA,EAEQ,WAAW,UAAmC;AACpD,UAAM,OACJ,SAAS,WAAW,OAAO,SAAS,WAAW,MAC3C,0DACA,SAAS,WAAW,MAClB,iDACA;AAER,WAAO,IAAI;AAAA,MACT,yBAAyB,SAAS,MAAM,IAAI,SAAS,UAAU;AAAA,MAC/D,SAAS;AAAA,MACT;AAAA,IACF;AAAA,EACF;AAAA,EAEQ,MAAM,IAA2B;AACvC,WAAO,IAAI,QAAQ,CAAC,YAAY,WAAW,SAAS,EAAE,CAAC;AAAA,EACzD;AACF;;;AC9GO,SAAS,mBAA+C;AAC7D,QAAM,MAAM,QAAQ;AAEpB,MAAI,IAAI,gBAAgB;AACtB,UAAM,YAAY,IAAI,qBAAqB;AAC3C,UAAM,OAAO,IAAI,qBAAqB;AACtC,UAAM,QAAQ,IAAI,iBAAiB;AACnC,UAAM,cAAc,QAAQ,QAAQ,GAAG,SAAS,IAAI,IAAI,iBAAiB,KAAK,KAAK;AACnF,WAAO;AAAA,MACL,UAAU;AAAA,MACV;AAAA,MACA,SAAS,IAAI;AAAA,IACf;AAAA,EACF;AAEA,MAAI,IAAI,WAAW;AACjB,WAAO;AAAA,MACL,UAAU;AAAA,MACV,aAAa,IAAI;AAAA,MACjB,SAAS,IAAI;AAAA,IACf;AAAA,EACF;AAEA,MAAI,IAAI,UAAU;AAChB,WAAO;AAAA,MACL,UAAU;AAAA,MACV,aAAa,IAAI;AAAA,IACnB;AAAA,EACF;AAEA,MAAI,IAAI,IAAI;AACV,WAAO;AAAA,MACL,UAAU;AAAA,IACZ;AAAA,EACF;AAEA,SAAO;AACT;;;ACEO,IAAM,kBAAN,MAAsB;AAAA,EAC3B,YAA6B,YAA+B;AAA/B;AAAA,EAAgC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAY7D,MACE,MACA,MACA,QACa;AACb,UAAM,UAAU,oBAAI,IAAI,CAAC,GAAG,OAAO,KAAK,IAAI,GAAG,GAAG,OAAO,KAAK,IAAI,GAAG,GAAG,OAAO,KAAK,MAAM,CAAC,CAAC;AAE5F,UAAM,SAAiC,CAAC;AACxC,UAAM,OAAmB,CAAC;AAC1B,UAAM,YAAwB,CAAC;AAE/B,eAAW,OAAO,SAAS;AACzB,YAAM,SAAS,OAAO;AACtB,YAAM,SAAS,OAAO;AACtB,YAAM,WAAW,OAAO;AACxB,YAAM,UAAU,SAAS,KAAK,GAAG,IAAI;AACrC,YAAM,UAAU,SAAS,KAAK,GAAG,IAAI;AACrC,YAAM,YAAY,WAAW,OAAO,GAAG,IAAI;AAE3C,YAAM,cAAc,YAAY;AAChC,YAAM,gBAAgB,cAAc;AAEpC,UAAI;AACJ,UAAI;AAEJ,UAAI,CAAC,eAAe,CAAC,eAAe;AAElC,iBAAS;AACT,gBAAQ;AAAA,MACV,WAAW,eAAe,CAAC,eAAe;AAExC,iBAAS;AACT,gBAAQ;AAAA,MACV,WAAW,CAAC,eAAe,eAAe;AAExC,iBAAS;AACT,gBAAQ;AAAA,MACV,WAAW,YAAY,WAAW;AAEhC,iBAAS,CAAC,UAAU,UAAU,WAAW,eAAe;AACxD,gBAAQ;AAAA,MACV,OAAO;AAEL,iBAAS;AACT,gBAAQ;AAAA,MACV;AAEA,YAAM,WAAqB;AAAA,QACzB;AAAA,QACA;AAAA,QACA;AAAA,QACA,WAAW;AAAA,QACX,WAAW;AAAA,QACX,aAAa;AAAA,MACf;AACA,WAAK,KAAK,QAAQ;AAElB,UAAI,WAAW,YAAY;AACzB,kBAAU,KAAK,QAAQ;AAAA,MACzB,WAAW,UAAU,MAAM;AACzB,eAAO,GAAG,IAAI;AAAA,MAChB;AAAA,IAEF;AAGA,SAAK,KAAK,CAAC,GAAG,MAAM,EAAE,IAAI,cAAc,EAAE,GAAG,CAAC;AAC9C,cAAU,KAAK,CAAC,GAAG,MAAM,EAAE,IAAI,cAAc,EAAE,GAAG,CAAC;AAEnD,WAAO,EAAE,OAAO,UAAU,WAAW,GAAG,QAAQ,MAAM,UAAU;AAAA,EAClE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUA,MAAM,WAAW,UAAkB,UAAkB,YAA0C;AAC7F,UAAM,CAAC,eAAe,eAAe,eAAe,IAAI,MAAM,QAAQ,IAAI;AAAA,MACxE,KAAK,WAAW,QAAQ,QAAQ;AAAA,MAChC,KAAK,WAAW,QAAQ,QAAQ;AAAA,MAChC,KAAK,WAAW,QAAQ,UAAU;AAAA,IACpC,CAAC;AAED,WAAO,KAAK,MAAM,cAAc,QAAQ,cAAc,QAAQ,gBAAgB,MAAM;AAAA,EACtF;AACF;;;ACrJA,YAAYC,UAAQ;AACpB,YAAY,QAAQ;AACpB,YAAYC,YAAU;AACtB,YAAYC,YAAU;AAkBf,IAAM,uBAAN,cAAmC,MAAM;AAAA,EAC9C,YACE,SACgB,aAChB;AACA,UAAM,OAAO;AAFG;AAGhB,SAAK,OAAO;AAAA,EACd;AACF;AAWO,IAAM,yBAAN,MAA6B;AAAA,EAClC,YACmB,YACA,eACjB;AAFiB;AACA;AAAA,EAChB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWH,MAAM,OACJ,MACA,YACA,aACA,UACA,UACA,eAIC;AAED,QAAI,SAAS,oBAAoB,KAAK,CAAC,OAAO,GAAG,SAAS,IAAI,GAAG;AAC/D,YAAM,IAAI,MAAM,qBAAqB,IAAI,mBAAmB;AAAA,IAC9D;AAGA,UAAM,kBAAkB,IAAI,IAAI,SAAS,WAAW,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC;AACxE,eAAW,MAAM,YAAY;AAC3B,UAAI,CAAC,gBAAgB,IAAI,EAAE,GAAG;AAC5B,cAAM,IAAI,MAAM,cAAc,EAAE,0BAA0B;AAAA,MAC5D;AAAA,IACF;AAGA,UAAM,eAAiE,CAAC;AACxE,UAAM,cAAsC,CAAC;AAE7C,eAAW,OAAO,SAAS,cAAc;AACvC,YAAM,YAAY,gBAAgB,IAAI,IAAI;AAC1C,UAAI,WAAW;AAEb,qBAAa,IAAI,IAAI,IAAI,EAAE,KAAK,UAAU;AAAA,MAC5C,OAAO;AAEL,cAAM,WAAW,MAAM,oBAAoB;AAC3C,qBAAa,IAAI,IAAI,IAAI,EAAE,WAAW,SAAS,UAAU;AACzD,oBAAY,IAAI,IAAI,IAAI,SAAS;AAAA,MACnC;AAAA,IACF;AAEA,UAAM,aAAwC;AAAA,MAC5C;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAKA,UAAM,KAAK,mBAAmB,YAAY,UAAU,QAAQ;AAG5D,UAAM,eAAoB,YAAK,UAAU,sBAAsB;AAC/D,UAAM,MAAS,kBAAa,cAAc,OAAO;AACjD,UAAM,MAAW,aAAM,GAAG;AAE1B,QAAI,CAAC,MAAM,QAAQ,IAAI,kBAAkB,GAAG;AAC1C,UAAI,qBAAqB,CAAC;AAAA,IAC5B;AACA,IAAC,IAAI,mBAAiC,KAAK;AAAA,MACzC;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF,CAAC;AACD,UAAM,YAAiB,YAAQ,UAAO,GAAG,iBAAiB,QAAQ,GAAG,IAAI,KAAK,IAAI,CAAC,MAAM;AACzF,QAAI;AACF,MAAG,mBAAc,WAAgB,iBAAU,GAAG,GAAG,OAAO;AACxD,MAAG,gBAAW,WAAW,YAAY;AAAA,IACvC,UAAE;AACA,UAAI;AACF,QAAG,gBAAW,SAAS;AAAA,MACzB,QAAQ;AAAA,MAER;AAAA,IACF;AAEA,WAAO,EAAE,UAAU,YAAY,YAAY;AAAA,EAC7C;AAAA;AAAA;AAAA;AAAA,EAKA,KAAK,UAAqD;AACxD,WAAO,SAAS,sBAAsB,CAAC;AAAA,EACzC;AAAA;AAAA;AAAA;AAAA,EAKA,IAAI,UAAwB,MAAqD;AAC/E,WAAO,SAAS,oBAAoB,KAAK,CAAC,OAAO,GAAG,SAAS,IAAI;AAAA,EACnE;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,OAAO,MAAc,UAAwB,UAAiC;AAClF,UAAM,WAAW,KAAK,IAAI,UAAU,IAAI;AACxC,QAAI,CAAC,UAAU;AACb,YAAM,IAAI,MAAM,qBAAqB,IAAI,cAAc;AAAA,IACzD;AAGA,UAAM,QAAQ,KAAK,cAAc,cAAc,UAAU,QAAQ,EAAE,OAAO,CAAC,MAAM,EAAE,MAAM;AACzF,eAAW,QAAQ,OAAO;AACxB,UAAI,CAAC,SAAS,WAAW,SAAS,KAAK,SAAS,EAAG;AACnD,YAAM,YAAY,SAAS,aAAa,KAAK,WAAW;AACxD,UAAI,CAAC,WAAW,UAAW;AAC3B,UAAI,cAAc,SAAS,EAAG;AAE9B,UAAI;AACF,cAAM,KAAK,WAAW,gBAAgB,KAAK,UAAU,UAAU,SAAS;AAAA,MAC1E,QAAQ;AAAA,MAER;AAAA,IACF;AAGA,UAAM,eAAoB,YAAK,UAAU,sBAAsB;AAC/D,UAAM,MAAS,kBAAa,cAAc,OAAO;AACjD,UAAM,MAAW,aAAM,GAAG;AAC1B,UAAM,aAAa,IAAI;AACvB,QAAI,MAAM,QAAQ,UAAU,GAAG;AAC7B,UAAI,qBAAqB,WAAW;AAAA,QAClC,CAAC,OAAQ,GAA+B,SAAS;AAAA,MACnD;AAAA,IACF;AACA,UAAM,MAAW,YAAQ,UAAO,GAAG,iBAAiB,QAAQ,GAAG,IAAI,KAAK,IAAI,CAAC,MAAM;AACnF,QAAI;AACF,MAAG,mBAAc,KAAU,iBAAU,GAAG,GAAG,OAAO;AAClD,MAAG,gBAAW,KAAK,YAAY;AAAA,IACjC,UAAE;AACA,UAAI;AACF,QAAG,gBAAW,GAAG;AAAA,MACnB,QAAQ;AAAA,MAER;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,mBACJ,MACA,eACA,UACA,UACkD;AAClD,UAAM,WAAW,KAAK,IAAI,UAAU,IAAI;AACxC,QAAI,CAAC,UAAU;AACb,YAAM,IAAI,MAAM,qBAAqB,IAAI,cAAc;AAAA,IACzD;AAEA,UAAM,eAAoB,YAAK,UAAU,sBAAsB;AAC/D,UAAM,MAAS,kBAAa,cAAc,OAAO;AACjD,UAAM,MAAW,aAAM,GAAG;AAC1B,UAAM,aAAa,IAAI;AACvB,UAAM,QAAQ,WAAW,KAAK,CAAC,OAAQ,GAA+B,SAAS,IAAI;AAInF,UAAM,OAAO,MAAM;AAEnB,UAAM,QAAQ,KAAK,cAAc,cAAc,UAAU,QAAQ,EAAE,OAAO,CAAC,MAAM,EAAE,MAAM;AACzF,UAAM,cAAsC,CAAC;AAE7C,eAAW,CAAC,SAAS,SAAS,KAAK,OAAO,QAAQ,aAAa,GAAG;AAChE,YAAM,YAAY,SAAS,aAAa,OAAO;AAC/C,UAAI,CAAC,WAAW;AACd,cAAM,IAAI,MAAM,gBAAgB,OAAO,4BAA4B,IAAI,IAAI;AAAA,MAC7E;AAGA,UAAI,UAAU,WAAW;AACvB,cAAM,cAAc,MAAM;AAAA,UACxB,CAAC,MAAM,SAAS,WAAW,SAAS,EAAE,SAAS,KAAK,EAAE,gBAAgB;AAAA,QACxE;AACA,mBAAW,QAAQ,aAAa;AAC9B,cAAI;AACF,kBAAM,KAAK,WAAW,gBAAgB,KAAK,UAAU,UAAU,SAAS;AAAA,UAC1E,QAAQ;AAAA,UAER;AAAA,QACF;AAAA,MACF;AAGA,WAAK,OAAO,IAAI,EAAE,KAAK,UAAU;AACjC,eAAS,aAAa,OAAO,IAAI,EAAE,KAAK,UAAU;AAAA,IACpD;AAGA,UAAM,MAAW,YAAQ,UAAO,GAAG,iBAAiB,QAAQ,GAAG,IAAI,KAAK,IAAI,CAAC,MAAM;AACnF,QAAI;AACF,MAAG,mBAAc,KAAU,iBAAU,GAAG,GAAG,OAAO;AAClD,MAAG,gBAAW,KAAK,YAAY;AAAA,IACjC,UAAE;AACA,UAAI;AACF,QAAG,gBAAW,GAAG;AAAA,MACnB,QAAQ;AAAA,MAER;AAAA,IACF;AAEA,WAAO,EAAE,YAAY;AAAA,EACvB;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,mBACJ,UACA,UACA,UACe;AACf,UAAM,QAAQ,KAAK,cAAc,cAAc,UAAU,QAAQ,EAAE,OAAO,CAAC,MAAM,EAAE,MAAM;AAEzF,eAAW,QAAQ,OAAO;AACxB,UAAI,CAAC,SAAS,WAAW,SAAS,KAAK,SAAS,EAAG;AAEnD,YAAM,YAAY,SAAS,aAAa,KAAK,WAAW;AACxD,UAAI,CAAC,UAAW;AAGhB,UAAI,cAAc,SAAS,EAAG;AAC9B,UAAI,CAAC,UAAU,UAAW;AAE1B,UAAI;AACF,cAAM,KAAK,WAAW,aAAa,KAAK,UAAU,UAAU,SAAS;AAAA,MACvE,SAAS,KAAK;AAGZ,cAAM,UAAU,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC/D,YAAI,CAAC,QAAQ,SAAS,SAAS,GAAG;AAChC,gBAAM;AAAA,QACR;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,UACJ,MACA,UACA,UACA,aACiC;AACjC,UAAM,WAAW,KAAK,IAAI,UAAU,IAAI;AACxC,QAAI,CAAC,UAAU;AACb,YAAM,IAAI,MAAM,qBAAqB,IAAI,cAAc;AAAA,IACzD;AAEA,UAAM,eAAoB,YAAK,UAAU,sBAAsB;AAC/D,UAAM,MAAS,kBAAa,cAAc,OAAO;AACjD,UAAM,MAAW,aAAM,GAAG;AAC1B,UAAM,aAAa,IAAI;AACvB,UAAM,QAAQ,WAAW,KAAK,CAAC,OAAQ,GAA+B,SAAS,IAAI;AAInF,UAAM,OAAO,MAAM;AAEnB,UAAM,iBAAyC,CAAC;AAChD,UAAM,eAAe,cAAc,CAAC,WAAW,IAAI,OAAO,KAAK,SAAS,YAAY;AAEpF,UAAM,QAAQ,KAAK,cAAc,cAAc,UAAU,QAAQ,EAAE,OAAO,CAAC,MAAM,EAAE,MAAM;AAEzF,QAAI;AACF,iBAAW,WAAW,cAAc;AAClC,cAAM,YAAY,SAAS,aAAa,OAAO;AAC/C,YAAI,CAAC,WAAW;AACd,gBAAM,IAAI,MAAM,gBAAgB,OAAO,4BAA4B,IAAI,IAAI;AAAA,QAC7E;AAEA,YAAI,cAAc,SAAS,EAAG;AAC9B,cAAM,eAAe,UAAU;AAC/B,YAAI,CAAC,cAAc;AACjB,gBAAM,IAAI,MAAM,gBAAgB,OAAO,4BAA4B,IAAI,IAAI;AAAA,QAC7E;AAEA,cAAM,cAAc,MAAM,oBAAoB;AAC9C,uBAAe,OAAO,IAAI,YAAY;AAGtC,aAAK,OAAO,IAAI,EAAE,WAAW,YAAY,UAAU;AAGnD,cAAM,cAAc,MAAM;AAAA,UACxB,CAAC,MAAM,SAAS,WAAW,SAAS,EAAE,SAAS,KAAK,EAAE,gBAAgB;AAAA,QACxE;AACA,mBAAW,QAAQ,aAAa;AAC9B,cAAI;AACF,kBAAM,KAAK,WAAW,gBAAgB,KAAK,UAAU,YAAY;AAAA,UACnE,QAAQ;AAAA,UAER;AACA,cAAI;AACF,kBAAM,KAAK,WAAW,aAAa,KAAK,UAAU,YAAY,SAAS;AAAA,UACzE,SAAS,QAAQ;AAEf,gBAAI;AACF,oBAAM,KAAK,WAAW,aAAa,KAAK,UAAU,YAAY;AAAA,YAChE,QAAQ;AACN,oBAAM,IAAI;AAAA,gBACR,kCAAkC,KAAK,SAAS,IAAI,KAAK,WAAW,6EAEtD,aAAa,MAAM,GAAG,EAAE,CAAC,iBAAiB,YAAY,UAAU,MAAM,GAAG,EAAE,CAAC;AAAA,cAC5F;AAAA,YACF;AACA,kBAAM;AAAA,UACR;AAAA,QACF;AAAA,MACF;AAAA,IACF,SAAS,KAAK;AACZ,UAAI,OAAO,KAAK,cAAc,EAAE,SAAS,GAAG;AAC1C,cAAM,aAAa,IAAI;AAAA,UACrB,kCAAkC,OAAO,KAAK,cAAc,EAAE,KAAK,IAAI,CAAC,KAAM,IAAc,OAAO;AAAA,UACnG;AAAA,QACF;AACA,cAAM;AAAA,MACR;AACA,YAAM;AAAA,IACR;AAEA,UAAM,YAAiB,YAAQ,UAAO,GAAG,iBAAiB,QAAQ,GAAG,IAAI,KAAK,IAAI,CAAC,MAAM;AACzF,QAAI;AACF,MAAG,mBAAc,WAAgB,iBAAU,GAAG,GAAG,OAAO;AACxD,MAAG,gBAAW,WAAW,YAAY;AAAA,IACvC,UAAE;AACA,UAAI;AACF,QAAG,gBAAW,SAAS;AAAA,MACzB,QAAQ;AAAA,MAER;AAAA,IACF;AACA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,SAAS,UAAwB,UAAwD;AAC7F,UAAM,SAAsC,CAAC;AAC7C,UAAM,aAAa,SAAS,sBAAsB,CAAC;AAEnD,QAAI,WAAW,WAAW,EAAG,QAAO;AAEpC,UAAM,mBAAmB,IAAI,IAAI,SAAS,aAAa,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC;AACzE,UAAM,kBAAkB,IAAI,IAAI,SAAS,WAAW,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC;AACxE,UAAM,QAAQ,KAAK,cAAc,cAAc,UAAU,QAAQ,EAAE,OAAO,CAAC,MAAM,EAAE,MAAM;AAEzF,eAAW,MAAM,YAAY;AAE3B,iBAAW,MAAM,GAAG,YAAY;AAC9B,YAAI,CAAC,gBAAgB,IAAI,EAAE,GAAG;AAC5B,iBAAO,KAAK;AAAA,YACV,UAAU,GAAG;AAAA,YACb,WAAW;AAAA,YACX,MAAM;AAAA,YACN,SAAS,qBAAqB,GAAG,IAAI,wCAAwC,EAAE;AAAA,UACjF,CAAC;AAAA,QACH;AAAA,MACF;AAGA,iBAAW,WAAW,kBAAkB;AACtC,YAAI,EAAE,WAAW,GAAG,eAAe;AACjC,iBAAO,KAAK;AAAA,YACV,UAAU,GAAG;AAAA,YACb,aAAa;AAAA,YACb,MAAM;AAAA,YACN,SAAS,qBAAqB,GAAG,IAAI,6BAA6B,OAAO;AAAA,UAC3E,CAAC;AAAA,QACH;AAAA,MACF;AAIA,iBAAW,QAAQ,OAAO;AACxB,cAAM,YAAY,GAAG,aAAa,KAAK,WAAW;AAClD,YAAI,CAAC,UAAW;AAChB,YAAI,cAAc,SAAS,EAAG;AAC9B,YAAI,CAAC,UAAU,UAAW;AAE1B,YAAI,GAAG,WAAW,SAAS,KAAK,SAAS,GAAG;AAE1C,cAAI;AACF,kBAAM,WAAW,MAAM,KAAK,WAAW,YAAY,KAAK,QAAQ;AAChE,gBAAI,CAAC,SAAS,WAAW,SAAS,UAAU,SAAS,GAAG;AACtD,qBAAO,KAAK;AAAA,gBACV,UAAU,GAAG;AAAA,gBACb,aAAa,KAAK;AAAA,gBAClB,WAAW,KAAK;AAAA,gBAChB,MAAM;AAAA,gBACN,SAAS,qBAAqB,GAAG,IAAI,oCAAoC,KAAK,SAAS,IAAI,KAAK,WAAW;AAAA,gBAC3G,YAAY,uBAAuB,GAAG,IAAI,iBAAiB,GAAG,WAAW,KAAK,GAAG,CAAC;AAAA,cACpF,CAAC;AAAA,YACH;AAAA,UACF,QAAQ;AAAA,UAER;AAAA,QACF,OAAO;AAEL,cAAI;AACF,kBAAM,WAAW,MAAM,KAAK,WAAW,YAAY,KAAK,QAAQ;AAChE,gBAAI,SAAS,WAAW,SAAS,UAAU,SAAS,GAAG;AACrD,qBAAO,KAAK;AAAA,gBACV,UAAU,GAAG;AAAA,gBACb,aAAa,KAAK;AAAA,gBAClB,WAAW,KAAK;AAAA,gBAChB,MAAM;AAAA,gBACN,SAAS,qBAAqB,GAAG,IAAI,wBAAwB,KAAK,SAAS,IAAI,KAAK,WAAW,mBAAmB,KAAK,SAAS;AAAA,gBAChI,YAAY,0BAA0B,UAAU,SAAS,OAAO,KAAK,WAAW;AAAA,cAClF,CAAC;AAAA,YACH;AAAA,UACF,QAAQ;AAAA,UAER;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AACF;;;AC/cA,eAAsB,uBACpB,cACA,aACA,UACA,UACA,YACA,eAC0B;AAC1B,QAAM,WAAW,SAAS,oBAAoB,KAAK,CAAC,OAAO,GAAG,SAAS,YAAY;AACnF,MAAI,CAAC,UAAU;AACb,UAAM,IAAI,MAAM,qBAAqB,YAAY,0BAA0B;AAAA,EAC7E;AAEA,QAAM,YAAY,SAAS,aAAa,WAAW;AACnD,MAAI,CAAC,WAAW;AACd,UAAM,IAAI;AAAA,MACR,gBAAgB,WAAW,oCAAoC,YAAY;AAAA,IAC7E;AAAA,EACF;AAEA,QAAM,YAAoC,CAAC;AAC3C,QAAM,QAAQ,cACX,cAAc,UAAU,QAAQ,EAChC;AAAA,IACC,CAAC,MAAM,EAAE,UAAU,SAAS,WAAW,SAAS,EAAE,SAAS,KAAK,EAAE,gBAAgB;AAAA,EACpF;AAEF,QAAM,mBAAmB,SAAS,WAAW,SAAS;AACtD,QAAM,aAAuB,CAAC;AAE9B,aAAW,QAAQ,OAAO;AACxB,UAAM,YAAY,MAAM,WAAW,QAAQ,KAAK,QAAQ;AACxD,eAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,UAAU,MAAM,GAAG;AAC3D,YAAM,eAAe,mBAAmB,GAAG,KAAK,SAAS,KAAK,GAAG,KAAK;AACtE,UAAI,gBAAgB,aAAa,UAAU,YAAY,MAAM,OAAO;AAClE,mBAAW,KAAK,YAAY;AAAA,MAC9B;AACA,gBAAU,YAAY,IAAI;AAAA,IAC5B;AAAA,EACF;AAEA,MAAI,WAAW,SAAS,GAAG;AACzB,UAAM,IAAI;AAAA,MACR,qCAAqC,WAAW,KAAK,IAAI,CAAC;AAAA,IAE5D;AAAA,EACF;AAEA,SAAO;AAAA,IACL,QAAQ;AAAA,IACR;AAAA,IACA,WAAW,UAAU;AAAA,IACrB;AAAA,EACF;AACF;;;AChFA,YAAYC,UAAQ;AACpB,YAAYC,YAAU;AACtB,YAAYC,aAAY;;;ACFxB,YAAYC,aAAY;AAkBjB,SAAS,oBAAoB,UAAkC;AACpE,QAAM,SAAS;AAAA,IACb;AAAA,IACA,OAAO,SAAS,OAAO;AAAA,IACvB,SAAS;AAAA,IACT,SAAS;AAAA,IACT,SAAS;AAAA,IACT,SAAS;AAAA,IACT,SAAS;AAAA,IACT,CAAC,GAAG,SAAS,IAAI,EAAE,KAAK,EAAE,KAAK,GAAG;AAAA,IAClC,SAAS,aAAa;AAAA,IACtB,SAAS,UAAU,YAAY;AAAA,IAC/B,SAAS,UAAU,SAAS;AAAA,IAC5B,SAAS,UAAU,cAAc;AAAA,IACjC,SAAS,UAAU,aAAa;AAAA,EAClC;AACA,SAAO,OAAO,KAAK,OAAO,KAAK,IAAI,GAAG,OAAO;AAC/C;AAMO,SAAS,yBAAoE;AAClF,QAAM,OAAc,4BAAoB,SAAS;AACjD,SAAO;AAAA,IACL,WAAY,KAAK,UAAU,OAAO,EAAE,MAAM,QAAQ,QAAQ,MAAM,CAAC,EAAa;AAAA,MAC5E;AAAA,IACF;AAAA,IACA,YAAa,KAAK,WAAW,OAAO,EAAE,MAAM,SAAS,QAAQ,MAAM,CAAC,EAAa;AAAA,MAC/E;AAAA,IACF;AAAA,EACF;AACF;AASO,SAAS,YAAY,SAAiB,kBAAkC;AAC7E,QAAM,SAAgB,yBAAiB;AAAA,IACrC,KAAK,OAAO,KAAK,kBAAkB,QAAQ;AAAA,IAC3C,QAAQ;AAAA,IACR,MAAM;AAAA,EACR,CAAC;AACD,QAAM,YAAmB,aAAK,MAAM,SAAS,MAAM;AACnD,SAAO,UAAU,SAAS,QAAQ;AACpC;AAaA,eAAsB,QACpB,SACA,KACA,cACiB;AACjB,MAAI,CAAC,IAAI,MAAM;AACb,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AACA,QAAM,SAAgB,mBAAW,QAAQ,EAAE,OAAO,OAAO,EAAE,OAAO;AAClE,QAAM,YAAY,MAAM,IAAI,KAAK,cAAc,MAAM;AACrD,SAAO,UAAU,SAAS,QAAQ;AACpC;AAcO,SAAS,gBACd,SACA,iBACA,iBACS;AACT,QAAM,SAAgB,wBAAgB;AAAA,IACpC,KAAK,OAAO,KAAK,iBAAiB,QAAQ;AAAA,IAC1C,QAAQ;AAAA,IACR,MAAM;AAAA,EACR,CAAC;AACD,QAAM,YAAY,OAAO,KAAK,iBAAiB,QAAQ;AAEvD,QAAM,UAAU,OAAO;AACvB,MAAI,YAAY,WAAW;AACzB,WAAc,eAAO,MAAM,SAAS,QAAQ,SAAS;AAAA,EACvD;AACA,MAAI,YAAY,MAAM;AACpB,WAAc,eAAO,UAAU,SAAS,QAAQ,SAAS;AAAA,EAC3D;AACA,QAAM,IAAI,MAAM,oDAAoD,OAAO,EAAE;AAC/E;AAQO,SAAS,gBAAgB,iBAA6C;AAC3E,QAAM,SAAgB,wBAAgB;AAAA,IACpC,KAAK,OAAO,KAAK,iBAAiB,QAAQ;AAAA,IAC1C,QAAQ;AAAA,IACR,MAAM;AAAA,EACR,CAAC;AACD,QAAM,UAAU,OAAO;AACvB,MAAI,YAAY,UAAW,QAAO;AAClC,MAAI,YAAY,KAAM,QAAO;AAC7B,QAAM,IAAI,MAAM,yBAAyB,OAAO,EAAE;AACpD;;;ADlIO,IAAM,iBAAN,MAAqB;AAAA,EAC1B,YACmB,YACA,eACA,KACjB;AAHiB;AACA;AACA;AAAA,EAChB;AAAA;AAAA;AAAA;AAAA;AAAA,EAMH,MAAM,KAAK,QAAoB,UAAwB,UAAuC;AAC5F,QAAI,OAAO,cAAc,OAAO,iBAAiB;AAC/C,YAAM,IAAI;AAAA,QACR;AAAA,MACF;AAAA,IACF;AAEA,UAAM,WAAW,MAAM;AAAA,MACrB,OAAO;AAAA,MACP,OAAO;AAAA,MACP;AAAA,MACA;AAAA,MACA,KAAK;AAAA,MACL,KAAK;AAAA,IACP;AAEA,UAAM,YAAY,KAAK,UAAU,SAAS,MAAM;AAEhD,QAAI;AACJ,QAAI;AAEJ,QAAI,cAAc,SAAS,SAAS,GAAG;AAErC,UAAI,CAAC,KAAK,KAAK;AACb,cAAM,IAAI,MAAM,sEAAsE;AAAA,MACxF;AAGA,YAAM,EAAE,kBAAkB,qBAAqB,UAAU,IAAI,MAAM;AAAA;AAAA,QAEjE;AAAA,MACF;AACA,YAAM,sBAAuB,MAAM,iBAAiB;AACpD,YAAM,qBAAsB,MAAM,oBAAoB,mBAAmB;AAEzE,UAAI;AACF,cAAM,IAAI,IAAI,UAAU;AACxB,UAAE,aAAa,kBAAkB;AACjC,cAAM,YAAY,MAAM,EAAE,QAAQ,SAAS;AAC3C,qBAAa,OAAO,KAAK,SAAuB,EAAE,SAAS,QAAQ;AAAA,MACrE,QAAQ;AACN,cAAM,IAAI,MAAM,oDAAoD;AAAA,MACtE;AAGA,YAAM,YAAY,SAAS,UAAU;AACrC,YAAM,UAAU,MAAM,KAAK,IAAI,KAAK,UAAU,OAAO,OAAO,KAAK,mBAAmB,CAAC;AAErF,YAAM,WAAW,GAAG,KAAK,IAAI,CAAC,IAAW,oBAAY,CAAC,EAAE,SAAS,KAAK,CAAC;AACvE,YAAM,iBAAwB,mBAAW,QAAQ,EAAE,OAAO,UAAU,EAAE,OAAO,KAAK;AAElF,iBAAW;AAAA,QACT,SAAS;AAAA,QACT,UAAU,OAAO;AAAA,QACjB,aAAa,OAAO;AAAA,QACpB,WAAU,oBAAI,KAAK,GAAE,YAAY;AAAA,QACjC;AAAA,QACA;AAAA,QACA;AAAA,QACA,MAAM,OAAO,KAAK,SAAS,MAAM;AAAA,QACjC,UAAU;AAAA,UACR,UAAU,UAAU;AAAA,UACpB,OAAO,UAAU;AAAA,UACjB,YAAY,QAAQ,WAAW,SAAS,QAAQ;AAAA,UAChD,WAAW,QAAQ;AAAA,QACrB;AAAA,MACF;AAAA,IACF,OAAO;AAEL,UAAI;AAEF,cAAM,EAAE,UAAU,IAAI,MAAM,OAAO,gBAAuB;AAC1D,cAAM,IAAI,IAAI,UAAU;AACxB,UAAE,aAAa,SAAS,SAAU;AAClC,cAAM,YAAY,MAAM,EAAE,QAAQ,SAAS;AAC3C,qBAAa,OAAO,KAAK,SAAuB,EAAE,SAAS,QAAQ;AAAA,MACrE,QAAQ;AACN,cAAM,IAAI,MAAM,sDAAsD;AAAA,MACxE;AAEA,YAAM,WAAW,GAAG,KAAK,IAAI,CAAC,IAAW,oBAAY,CAAC,EAAE,SAAS,KAAK,CAAC;AACvE,YAAM,iBAAwB,mBAAW,QAAQ,EAAE,OAAO,UAAU,EAAE,OAAO,KAAK;AAElF,iBAAW;AAAA,QACT,SAAS;AAAA,QACT,UAAU,OAAO;AAAA,QACjB,aAAa,OAAO;AAAA,QACpB,WAAU,oBAAI,KAAK,GAAE,YAAY;AAAA,QACjC;AAAA,QACA;AAAA,QACA;AAAA,QACA,MAAM,OAAO,KAAK,SAAS,MAAM;AAAA,MACnC;AAAA,IACF;AAEA,UAAM,YAAiB,eAAQ,OAAO,UAAU;AAChD,QAAI,CAAI,gBAAW,SAAS,GAAG;AAC7B,MAAG,eAAU,WAAW,EAAE,WAAW,KAAK,CAAC;AAAA,IAC7C;AAGA,QAAI,OAAO,OAAO,OAAO,MAAM,GAAG;AAChC,eAAS,YAAY,IAAI,KAAK,KAAK,IAAI,IAAI,OAAO,MAAM,GAAI,EAAE,YAAY;AAAA,IAC5E;AAGA,QAAI,OAAO,YAAY;AACrB,YAAM,UAAU,oBAAoB,QAAQ;AAC5C,eAAS,YAAY,YAAY,SAAS,OAAO,UAAU;AAC3D,eAAS,qBAAqB;AAAA,IAChC,WAAW,OAAO,iBAAiB;AACjC,UAAI,CAAC,KAAK,KAAK;AACb,cAAM,IAAI,MAAM,8DAA8D;AAAA,MAChF;AACA,YAAM,UAAU,oBAAoB,QAAQ;AAC5C,eAAS,YAAY,MAAM,QAAQ,SAAS,KAAK,KAAK,OAAO,eAAe;AAC5E,eAAS,qBAAqB;AAAA,IAChC;AAEA,UAAM,OAAO,KAAK,UAAU,UAAU,MAAM,CAAC;AAC7C,UAAM,YAAY,GAAG,OAAO,UAAU,QAAQ,QAAQ,GAAG;AACzD,IAAG,mBAAc,WAAW,MAAM,OAAO;AACzC,IAAG,gBAAW,WAAW,OAAO,UAAU;AAE1C,WAAO;AAAA,MACL,YAAY,OAAO;AAAA,MACnB,gBAAgB,SAAS,SAAS,WAAW;AAAA,MAC7C,UAAU,OAAO,KAAK,SAAS,MAAM,EAAE;AAAA,MACvC,cAAc,OAAO,WAAW,MAAM,OAAO;AAAA,MAC7C,UAAU,SAAS;AAAA,IACrB;AAAA,EACF;AACF;",
|
|
6
|
+
"names": ["fs", "path", "fs", "fs", "path", "YAML", "fs", "path", "YAML", "fs", "YAML", "path", "path", "fs", "path", "fs", "randomBytes", "YAML", "fs", "path", "fs", "path", "randomBytes", "path", "path", "path", "YAML", "parse", "parse", "existingKeys", "decrypted", "fs", "path", "YAML", "fs", "path", "YAML", "HEADER_COMMENT", "fs", "path", "YAML", "fs", "path", "YAML", "fs", "path", "YAML", "fs", "path", "crypto", "crypto"]
|
|
7
7
|
}
|