@standards-kit/conform 0.3.1 → 0.4.0
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/README.md +0 -9
- package/dist/{chunk-DXIYZR62.js → chunk-G5AS4QBP.js} +1 -1
- package/dist/{chunk-YKKWXHYS.js → chunk-HLF56NMK.js} +2 -5
- package/dist/chunk-HLF56NMK.js.map +1 -0
- package/dist/{chunk-RHM53NLG.js → chunk-RR4OEIAZ.js} +2 -2
- package/dist/{chunk-RHM53NLG.js.map → chunk-RR4OEIAZ.js.map} +1 -1
- package/dist/{chunk-M7G73Q6P.js → chunk-T2PWT2B5.js} +1 -1
- package/dist/chunk-T2PWT2B5.js.map +1 -0
- package/dist/{chunk-J5S6GRGW.js → chunk-XZERDHOH.js} +2 -2
- package/dist/{chunk-FJZMUGYW.js → chunk-ZKFWS3GU.js} +45 -13
- package/dist/chunk-ZKFWS3GU.js.map +1 -0
- package/dist/cli/index.d.ts +1 -1
- package/dist/cli/utils.d.ts +2 -4
- package/dist/cli.js +55 -23
- package/dist/cli.js.map +1 -1
- package/dist/code/tools/base.d.ts +4 -2
- package/dist/constants.d.ts +0 -9
- package/dist/core/index.d.ts +0 -1
- package/dist/core/schema.d.ts +1 -0
- package/dist/{core-LFX2BFLG.js → core-HQ7WZCSW.js} +6 -16
- package/dist/{generate-D4MFMOHP.js → generate-4EY6VETG.js} +3 -3
- package/dist/{iam-YXMHK2MV.js → iam-363WGRLI.js} +2 -2
- package/dist/index.d.ts +1 -1
- package/dist/index.js +47 -16
- package/dist/index.js.map +1 -1
- package/dist/infra/checkers/index.d.ts +0 -4
- package/dist/infra/schemas.d.ts +1 -1
- package/dist/{infra-RFEWGWPW.js → infra-CHHXGSWU.js} +10 -10
- package/dist/infra-CHHXGSWU.js.map +1 -0
- package/dist/{manifest-7AIL2FK2.js → manifest-CL4QMZT6.js} +2 -2
- package/dist/mcp/index.d.ts +1 -1
- package/dist/mcp/standards/matcher.d.ts +0 -9
- package/dist/mcp/standards/parser.d.ts +0 -4
- package/dist/{mcp-DYQG6JEQ.js → mcp-RPYXEVCR.js} +5 -5
- package/dist/process/index.d.ts +1 -1
- package/dist/process/scan/remote-fetcher.d.ts +0 -2
- package/dist/process/tools/hooks.d.ts +3 -0
- package/dist/process/tools/index.d.ts +0 -1
- package/dist/projects/templates.d.ts +0 -4
- package/dist/{registry-J2LVW3M2.js → registry-7CTKYGKA.js} +3 -3
- package/dist/{s3-S4GXNR7H.js → s3-7GRZRXLA.js} +2 -2
- package/dist/{s3-53UELUWT.js → s3-MFBDXYQ5.js} +2 -2
- package/dist/{scan-BZH5IR3Z.js → scan-S5VYRCPC.js} +5 -5
- package/dist/scan-S5VYRCPC.js.map +1 -0
- package/dist/{standards-ALMA4VIU.js → standards-CASOIZJV.js} +3 -9
- package/dist/{sync-EGJ2CSYK.js → sync-PT6KT46K.js} +4 -4
- package/dist/validate/guidelines.d.ts +0 -14
- package/dist/validate/index.d.ts +1 -2
- package/dist/{validate-X4K2SHYT.js → validate-WLVATJEZ.js} +5 -6
- package/dist/validate-WLVATJEZ.js.map +1 -0
- package/package.json +2 -2
- package/dist/chunk-FJZMUGYW.js.map +0 -1
- package/dist/chunk-M7G73Q6P.js.map +0 -1
- package/dist/chunk-YKKWXHYS.js.map +0 -1
- package/dist/infra-RFEWGWPW.js.map +0 -1
- package/dist/scan-BZH5IR3Z.js.map +0 -1
- package/dist/validate-X4K2SHYT.js.map +0 -1
- /package/dist/{chunk-DXIYZR62.js.map → chunk-G5AS4QBP.js.map} +0 -0
- /package/dist/{chunk-J5S6GRGW.js.map → chunk-XZERDHOH.js.map} +0 -0
- /package/dist/{core-LFX2BFLG.js.map → core-HQ7WZCSW.js.map} +0 -0
- /package/dist/{generate-D4MFMOHP.js.map → generate-4EY6VETG.js.map} +0 -0
- /package/dist/{iam-YXMHK2MV.js.map → iam-363WGRLI.js.map} +0 -0
- /package/dist/{manifest-7AIL2FK2.js.map → manifest-CL4QMZT6.js.map} +0 -0
- /package/dist/{mcp-DYQG6JEQ.js.map → mcp-RPYXEVCR.js.map} +0 -0
- /package/dist/{registry-J2LVW3M2.js.map → registry-7CTKYGKA.js.map} +0 -0
- /package/dist/{s3-53UELUWT.js.map → s3-7GRZRXLA.js.map} +0 -0
- /package/dist/{s3-S4GXNR7H.js.map → s3-MFBDXYQ5.js.map} +0 -0
- /package/dist/{standards-ALMA4VIU.js.map → standards-CASOIZJV.js.map} +0 -0
- /package/dist/{sync-EGJ2CSYK.js.map → sync-PT6KT46K.js.map} +0 -0
package/README.md
CHANGED
|
@@ -129,15 +129,6 @@ conform mcp
|
|
|
129
129
|
| 2 | Configuration error |
|
|
130
130
|
| 3 | Runtime error |
|
|
131
131
|
|
|
132
|
-
## Migration from check-my-toolkit
|
|
133
|
-
|
|
134
|
-
See the [Migration Guide](../../docs/migration.md) for details on migrating from `check-my-toolkit`.
|
|
135
|
-
|
|
136
|
-
Key changes:
|
|
137
|
-
- Config file: `check.toml` -> `standards.toml`
|
|
138
|
-
- CLI command: `cm` -> `conform`
|
|
139
|
-
- Package name: `check-my-toolkit` -> `@standards-kit/conform`
|
|
140
|
-
|
|
141
132
|
## License
|
|
142
133
|
|
|
143
134
|
MIT
|
|
@@ -2,7 +2,7 @@ import {
|
|
|
2
2
|
CACHE,
|
|
3
3
|
STANDARDS_REPO,
|
|
4
4
|
TIMEOUTS
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-RR4OEIAZ.js";
|
|
6
6
|
|
|
7
7
|
// src/mcp/standards/fetcher.ts
|
|
8
8
|
import * as fs from "fs";
|
|
@@ -269,15 +269,12 @@ export {
|
|
|
269
269
|
getGuidelinesDir,
|
|
270
270
|
getRulesetsDir,
|
|
271
271
|
frontmatterSchema,
|
|
272
|
-
parseGuideline,
|
|
273
272
|
loadAllGuidelines,
|
|
274
273
|
loadGuideline,
|
|
275
274
|
toListItems,
|
|
276
275
|
loadRuleset,
|
|
277
276
|
listRulesets,
|
|
278
|
-
parseContext,
|
|
279
|
-
scoreGuideline,
|
|
280
277
|
matchGuidelines,
|
|
281
278
|
composeGuidelines
|
|
282
279
|
};
|
|
283
|
-
//# sourceMappingURL=chunk-
|
|
280
|
+
//# sourceMappingURL=chunk-HLF56NMK.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/mcp/standards/fetcher.ts","../src/mcp/standards/parser.ts","../src/mcp/standards/matcher.ts"],"sourcesContent":["/**\n * Fetches the standards repository from GitHub or local filesystem\n */\nimport * as fs from \"node:fs\";\nimport * as os from \"node:os\";\nimport * as path from \"node:path\";\n\nimport { execa } from \"execa\";\n\nimport { CACHE, STANDARDS_REPO, TIMEOUTS } from \"../../constants.js\";\n\nconst CACHE_DIR = path.join(os.tmpdir(), CACHE.standardsCacheDir);\n\n/** Parsed GitHub source */\ninterface GitHubSource {\n type: \"github\";\n owner: string;\n repo: string;\n ref?: string;\n}\n\n/** Parsed local source */\ninterface LocalSource {\n type: \"local\";\n path: string;\n}\n\n/** Parsed source type */\ntype ParsedSource = GitHubSource | LocalSource;\n\n/** Parse github:owner/repo[@ref] format */\nfunction parseGitHubSource(source: string): GitHubSource {\n const remainder = source.slice(7); // Remove \"github:\"\n const atIndex = remainder.indexOf(\"@\");\n const ownerRepo = atIndex !== -1 ? remainder.slice(0, atIndex) : remainder;\n const ref = atIndex !== -1 ? remainder.slice(atIndex + 1) : undefined;\n const slashIndex = ownerRepo.indexOf(\"/\");\n\n if (slashIndex === -1) {\n throw new StandardsError(`Invalid GitHub source format: ${source}. Expected github:owner/repo`);\n }\n\n const owner = ownerRepo.slice(0, slashIndex);\n const repo = ownerRepo.slice(slashIndex + 1);\n\n if (!owner || !repo) {\n throw new StandardsError(`Invalid GitHub source format: ${source}. Expected github:owner/repo`);\n }\n\n return { type: \"github\", owner, repo, ref };\n}\n\n/**\n * Parse a source string into owner/repo/ref or local path.\n * Formats:\n * - \"github:owner/repo\" - GitHub repository\n * - \"github:owner/repo@ref\" - GitHub with branch/tag\n * - Local filesystem path (absolute or relative)\n */\nfunction parseSource(source: string): ParsedSource {\n if (source.startsWith(\"github:\")) {\n return parseGitHubSource(source);\n }\n return { type: \"local\", path: source };\n}\n\n/** Error class for standards fetching failures */\nexport class StandardsError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"StandardsError\";\n }\n}\n\n/** Authentication method for GitHub */\ntype AuthMethod = \"token\" | \"ssh\" | \"none\";\n\n/**\n * Detect authentication method based on environment variables.\n * Priority: CM_REGISTRY_TOKEN > GITHUB_TOKEN > SSH key detection > none\n */\nfunction detectAuthMethod(): AuthMethod {\n if (process.env.CM_REGISTRY_TOKEN || process.env.GITHUB_TOKEN) {\n return \"token\";\n }\n if (process.env.SSH_AUTH_SOCK) {\n return \"ssh\";\n }\n return \"none\";\n}\n\n/**\n * Get the authentication token from environment variables.\n */\nfunction getAuthToken(): string | undefined {\n return process.env.CM_REGISTRY_TOKEN ?? process.env.GITHUB_TOKEN;\n}\n\n/**\n * Build the git URL for a repository based on auth method.\n */\nfunction buildGitHubUrl(auth: AuthMethod, owner: string, repo: string): string {\n switch (auth) {\n case \"ssh\":\n return `git@github.com:${owner}/${repo}.git`;\n case \"token\": {\n const token = getAuthToken();\n if (token) {\n return `https://x-access-token:${token}@github.com/${owner}/${repo}.git`;\n }\n return `https://github.com/${owner}/${repo}.git`;\n }\n case \"none\":\n default:\n return `https://github.com/${owner}/${repo}.git`;\n }\n}\n\n/**\n * Update an existing cloned repository.\n */\nasync function updateExistingRepo(repoDir: string): Promise<boolean> {\n try {\n await execa(\"git\", [\"pull\", \"--ff-only\"], { cwd: repoDir, timeout: TIMEOUTS.git });\n return true;\n } catch {\n // If update fails, remove the directory so it will be re-cloned\n fs.rmSync(repoDir, { recursive: true, force: true });\n return false;\n }\n}\n\n/**\n * Clone a repository from GitHub.\n */\nasync function cloneRepo(repoDir: string, owner: string, repo: string, ref?: string): Promise<void> {\n fs.mkdirSync(CACHE_DIR, { recursive: true });\n\n const auth = detectAuthMethod();\n const url = buildGitHubUrl(auth, owner, repo);\n\n try {\n const args = [\"clone\", \"--depth\", \"1\"];\n if (ref) {\n args.push(\"--branch\", ref);\n }\n args.push(url, repoDir);\n\n await execa(\"git\", args, {\n timeout: TIMEOUTS.git,\n });\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n if (message.includes(\"timed out\")) {\n throw new StandardsError(`Standards repo clone timed out after ${TIMEOUTS.git / 1000} seconds`);\n }\n throw new StandardsError(`Failed to clone standards repo: ${message}`);\n }\n}\n\n/**\n * Fetch a GitHub repository, caching it locally.\n * Returns the path to the cached repository.\n */\nasync function fetchGitHubRepo(owner: string, repo: string, ref?: string): Promise<string> {\n const cacheKey = ref ? `${owner}-${repo}-${ref}` : `${owner}-${repo}`;\n const repoDir = path.join(CACHE_DIR, cacheKey);\n\n // If repo exists, try to update it\n if (fs.existsSync(repoDir)) {\n await updateExistingRepo(repoDir);\n }\n\n // Clone if it doesn't exist (either first time or after failed update)\n if (!fs.existsSync(repoDir)) {\n await cloneRepo(repoDir, owner, repo, ref);\n }\n\n return repoDir;\n}\n\n/**\n * Resolve a local source path to an absolute path.\n */\nfunction resolveLocalPath(localPath: string, basePath?: string): string {\n if (path.isAbsolute(localPath)) {\n return localPath;\n }\n const base = basePath ?? process.cwd();\n return path.resolve(base, localPath);\n}\n\n/**\n * Fetch the standards repository from a source string.\n * Supports:\n * - \"github:owner/repo\" - GitHub repository\n * - \"github:owner/repo@ref\" - GitHub with branch/tag\n * - Local filesystem path (absolute or relative)\n *\n * @param source - Source string to fetch from\n * @param basePath - Base path for resolving relative local paths (defaults to cwd)\n * @returns Path to the standards repository\n */\nexport async function fetchStandardsRepoFromSource(\n source: string,\n basePath?: string\n): Promise<string> {\n const parsed = parseSource(source);\n\n if (parsed.type === \"local\") {\n const resolvedPath = resolveLocalPath(parsed.path, basePath);\n if (!fs.existsSync(resolvedPath)) {\n throw new StandardsError(`Local standards path does not exist: ${resolvedPath}`);\n }\n return resolvedPath;\n }\n\n return fetchGitHubRepo(parsed.owner, parsed.repo, parsed.ref);\n}\n\n/**\n * Fetch the default standards repository, caching it locally.\n * Returns the path to the cached repository.\n */\nexport async function fetchStandardsRepo(): Promise<string> {\n return fetchGitHubRepo(STANDARDS_REPO.owner, STANDARDS_REPO.repo);\n}\n\n/**\n * Get the path to the guidelines directory.\n */\nexport function getGuidelinesDir(repoPath: string): string {\n return path.join(repoPath, \"guidelines\");\n}\n\n/**\n * Get the path to the rulesets directory.\n */\nexport function getRulesetsDir(repoPath: string): string {\n return path.join(repoPath, \"rulesets\");\n}\n","/**\n * Parser for guideline markdown files with YAML frontmatter\n */\nimport * as fs from \"node:fs\";\nimport * as path from \"node:path\";\n\nimport matter from \"gray-matter\";\nimport { z } from \"zod\";\n\nimport { type Guideline, type GuidelineListItem, type Ruleset } from \"./types.js\";\nimport { StandardsError } from \"./fetcher.js\";\n\n/** Zod schema for validating guideline frontmatter */\nexport const frontmatterSchema = z.object({\n id: z.string(),\n title: z.string(),\n category: z.string(),\n priority: z.number(),\n tags: z.array(z.string()),\n});\n\n/**\n * Parse a guideline markdown file content into a Guideline object.\n */\nfunction parseGuideline(fileContent: string, filename: string): Guideline {\n const { data, content } = matter(fileContent);\n\n const result = frontmatterSchema.safeParse(data);\n if (!result.success) {\n const errors = result.error.issues.map((e) => `${e.path.join(\".\")}: ${e.message}`).join(\", \");\n throw new StandardsError(`Invalid frontmatter in ${filename}: ${errors}`);\n }\n\n return {\n ...result.data,\n content: content.trim(),\n };\n}\n\n/**\n * Load all guidelines from a directory.\n */\nexport function loadAllGuidelines(guidelinesDir: string): Guideline[] {\n if (!fs.existsSync(guidelinesDir)) {\n throw new StandardsError(`Guidelines directory not found: ${guidelinesDir}`);\n }\n\n const files = fs.readdirSync(guidelinesDir).filter((f) => f.endsWith(\".md\"));\n const guidelines: Guideline[] = [];\n\n for (const file of files) {\n const filePath = path.join(guidelinesDir, file);\n const content = fs.readFileSync(filePath, \"utf-8\");\n\n try {\n guidelines.push(parseGuideline(content, file));\n } catch (error) {\n // Skip files that fail to parse, log warning\n console.warn(`Warning: Failed to parse guideline ${file}: ${error}`);\n }\n }\n\n return guidelines;\n}\n\n/**\n * Load a single guideline by ID.\n */\nexport function loadGuideline(guidelinesDir: string, id: string): Guideline | null {\n const filePath = path.join(guidelinesDir, `${id}.md`);\n\n if (!fs.existsSync(filePath)) {\n return null;\n }\n\n const content = fs.readFileSync(filePath, \"utf-8\");\n return parseGuideline(content, `${id}.md`);\n}\n\n/**\n * Convert guidelines to list items (summary format).\n */\nexport function toListItems(guidelines: Guideline[]): GuidelineListItem[] {\n return guidelines.map((g) => ({\n id: g.id,\n title: g.title,\n tags: g.tags,\n category: g.category,\n }));\n}\n\n/**\n * Load a ruleset file by ID.\n */\nexport function loadRuleset(rulesetsDir: string, id: string): Ruleset | null {\n const filePath = path.join(rulesetsDir, `${id}.toml`);\n\n if (!fs.existsSync(filePath)) {\n return null;\n }\n\n const content = fs.readFileSync(filePath, \"utf-8\");\n return { id, content };\n}\n\n/**\n * List all available ruleset IDs.\n */\nexport function listRulesets(rulesetsDir: string): string[] {\n if (!fs.existsSync(rulesetsDir)) {\n return [];\n }\n\n return fs\n .readdirSync(rulesetsDir)\n .filter((f) => f.endsWith(\".toml\"))\n .map((f) => f.replace(\".toml\", \"\"));\n}\n","/**\n * Smart keyword matching logic for guidelines\n */\nimport { type Guideline, type MatchedGuideline } from \"./types.js\";\n\n/**\n * Parse a context string into keywords.\n * Extracts words, lowercases them, and removes duplicates.\n */\nfunction parseContext(context: string): string[] {\n const words = context\n .toLowerCase()\n .split(/[\\s,.\\-_/]+/)\n .filter((word) => word.length > 1);\n\n return [...new Set(words)];\n}\n\n/**\n * Score a guideline based on how many keywords match its tags.\n */\nfunction scoreGuideline(guideline: Guideline, keywords: string[]): number {\n const tags = new Set(guideline.tags.map((t) => t.toLowerCase()));\n let score = 0;\n\n for (const keyword of keywords) {\n if (tags.has(keyword)) {\n score++;\n }\n }\n\n // Also check if keyword appears in category or id\n const category = guideline.category.toLowerCase();\n const id = guideline.id.toLowerCase();\n\n for (const keyword of keywords) {\n if (category.includes(keyword) || id.includes(keyword)) {\n score += 0.5; // Partial match bonus\n }\n }\n\n return score;\n}\n\n/**\n * Match guidelines against a context string.\n * Returns guidelines sorted by score (descending) then priority (ascending).\n */\nexport function matchGuidelines(\n guidelines: Guideline[],\n context: string,\n limit?: number\n): MatchedGuideline[] {\n const keywords = parseContext(context);\n\n if (keywords.length === 0) {\n return [];\n }\n\n const scored = guidelines\n .map((guideline) => ({\n guideline,\n score: scoreGuideline(guideline, keywords),\n }))\n .filter((m) => m.score > 0);\n\n // Sort by score descending, then by priority ascending\n scored.sort((a, b) => {\n if (b.score !== a.score) {\n return b.score - a.score;\n }\n return a.guideline.priority - b.guideline.priority;\n });\n\n return limit ? scored.slice(0, limit) : scored;\n}\n\n/**\n * Compose matched guidelines into a single markdown document.\n */\nexport function composeGuidelines(matches: MatchedGuideline[]): string {\n if (matches.length === 0) {\n return \"No matching guidelines found for the given context.\";\n }\n\n const sections = matches.map((m) => {\n const { guideline } = m;\n return `# ${guideline.title}\\n\\n**Category:** ${guideline.category} | **Priority:** ${guideline.priority}\\n**Tags:** ${guideline.tags.join(\", \")}\\n\\n${guideline.content}`;\n });\n\n return sections.join(\"\\n\\n---\\n\\n\");\n}\n"],"mappings":";;;;;;;AAGA,YAAY,QAAQ;AACpB,YAAY,QAAQ;AACpB,YAAY,UAAU;AAEtB,SAAS,aAAa;AAItB,IAAM,YAAiB,UAAQ,UAAO,GAAG,MAAM,iBAAiB;AAoBhE,SAAS,kBAAkB,QAA8B;AACvD,QAAM,YAAY,OAAO,MAAM,CAAC;AAChC,QAAM,UAAU,UAAU,QAAQ,GAAG;AACrC,QAAM,YAAY,YAAY,KAAK,UAAU,MAAM,GAAG,OAAO,IAAI;AACjE,QAAM,MAAM,YAAY,KAAK,UAAU,MAAM,UAAU,CAAC,IAAI;AAC5D,QAAM,aAAa,UAAU,QAAQ,GAAG;AAExC,MAAI,eAAe,IAAI;AACrB,UAAM,IAAI,eAAe,iCAAiC,MAAM,8BAA8B;AAAA,EAChG;AAEA,QAAM,QAAQ,UAAU,MAAM,GAAG,UAAU;AAC3C,QAAM,OAAO,UAAU,MAAM,aAAa,CAAC;AAE3C,MAAI,CAAC,SAAS,CAAC,MAAM;AACnB,UAAM,IAAI,eAAe,iCAAiC,MAAM,8BAA8B;AAAA,EAChG;AAEA,SAAO,EAAE,MAAM,UAAU,OAAO,MAAM,IAAI;AAC5C;AASA,SAAS,YAAY,QAA8B;AACjD,MAAI,OAAO,WAAW,SAAS,GAAG;AAChC,WAAO,kBAAkB,MAAM;AAAA,EACjC;AACA,SAAO,EAAE,MAAM,SAAS,MAAM,OAAO;AACvC;AAGO,IAAM,iBAAN,cAA6B,MAAM;AAAA,EACxC,YAAY,SAAiB;AAC3B,UAAM,OAAO;AACb,SAAK,OAAO;AAAA,EACd;AACF;AASA,SAAS,mBAA+B;AACtC,MAAI,QAAQ,IAAI,qBAAqB,QAAQ,IAAI,cAAc;AAC7D,WAAO;AAAA,EACT;AACA,MAAI,QAAQ,IAAI,eAAe;AAC7B,WAAO;AAAA,EACT;AACA,SAAO;AACT;AAKA,SAAS,eAAmC;AAC1C,SAAO,QAAQ,IAAI,qBAAqB,QAAQ,IAAI;AACtD;AAKA,SAAS,eAAe,MAAkB,OAAe,MAAsB;AAC7E,UAAQ,MAAM;AAAA,IACZ,KAAK;AACH,aAAO,kBAAkB,KAAK,IAAI,IAAI;AAAA,IACxC,KAAK,SAAS;AACZ,YAAM,QAAQ,aAAa;AAC3B,UAAI,OAAO;AACT,eAAO,0BAA0B,KAAK,eAAe,KAAK,IAAI,IAAI;AAAA,MACpE;AACA,aAAO,sBAAsB,KAAK,IAAI,IAAI;AAAA,IAC5C;AAAA,IACA,KAAK;AAAA,IACL;AACE,aAAO,sBAAsB,KAAK,IAAI,IAAI;AAAA,EAC9C;AACF;AAKA,eAAe,mBAAmB,SAAmC;AACnE,MAAI;AACF,UAAM,MAAM,OAAO,CAAC,QAAQ,WAAW,GAAG,EAAE,KAAK,SAAS,SAAS,SAAS,IAAI,CAAC;AACjF,WAAO;AAAA,EACT,QAAQ;AAEN,IAAG,UAAO,SAAS,EAAE,WAAW,MAAM,OAAO,KAAK,CAAC;AACnD,WAAO;AAAA,EACT;AACF;AAKA,eAAe,UAAU,SAAiB,OAAe,MAAc,KAA6B;AAClG,EAAG,aAAU,WAAW,EAAE,WAAW,KAAK,CAAC;AAE3C,QAAM,OAAO,iBAAiB;AAC9B,QAAM,MAAM,eAAe,MAAM,OAAO,IAAI;AAE5C,MAAI;AACF,UAAM,OAAO,CAAC,SAAS,WAAW,GAAG;AACrC,QAAI,KAAK;AACP,WAAK,KAAK,YAAY,GAAG;AAAA,IAC3B;AACA,SAAK,KAAK,KAAK,OAAO;AAEtB,UAAM,MAAM,OAAO,MAAM;AAAA,MACvB,SAAS,SAAS;AAAA,IACpB,CAAC;AAAA,EACH,SAAS,OAAO;AACd,UAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AACrE,QAAI,QAAQ,SAAS,WAAW,GAAG;AACjC,YAAM,IAAI,eAAe,wCAAwC,SAAS,MAAM,GAAI,UAAU;AAAA,IAChG;AACA,UAAM,IAAI,eAAe,mCAAmC,OAAO,EAAE;AAAA,EACvE;AACF;AAMA,eAAe,gBAAgB,OAAe,MAAc,KAA+B;AACzF,QAAM,WAAW,MAAM,GAAG,KAAK,IAAI,IAAI,IAAI,GAAG,KAAK,GAAG,KAAK,IAAI,IAAI;AACnE,QAAM,UAAe,UAAK,WAAW,QAAQ;AAG7C,MAAO,cAAW,OAAO,GAAG;AAC1B,UAAM,mBAAmB,OAAO;AAAA,EAClC;AAGA,MAAI,CAAI,cAAW,OAAO,GAAG;AAC3B,UAAM,UAAU,SAAS,OAAO,MAAM,GAAG;AAAA,EAC3C;AAEA,SAAO;AACT;AAKA,SAAS,iBAAiB,WAAmB,UAA2B;AACtE,MAAS,gBAAW,SAAS,GAAG;AAC9B,WAAO;AAAA,EACT;AACA,QAAM,OAAO,YAAY,QAAQ,IAAI;AACrC,SAAY,aAAQ,MAAM,SAAS;AACrC;AAaA,eAAsB,6BACpB,QACA,UACiB;AACjB,QAAM,SAAS,YAAY,MAAM;AAEjC,MAAI,OAAO,SAAS,SAAS;AAC3B,UAAM,eAAe,iBAAiB,OAAO,MAAM,QAAQ;AAC3D,QAAI,CAAI,cAAW,YAAY,GAAG;AAChC,YAAM,IAAI,eAAe,wCAAwC,YAAY,EAAE;AAAA,IACjF;AACA,WAAO;AAAA,EACT;AAEA,SAAO,gBAAgB,OAAO,OAAO,OAAO,MAAM,OAAO,GAAG;AAC9D;AAMA,eAAsB,qBAAsC;AAC1D,SAAO,gBAAgB,eAAe,OAAO,eAAe,IAAI;AAClE;AAKO,SAAS,iBAAiB,UAA0B;AACzD,SAAY,UAAK,UAAU,YAAY;AACzC;AAKO,SAAS,eAAe,UAA0B;AACvD,SAAY,UAAK,UAAU,UAAU;AACvC;;;AC7OA,YAAYA,SAAQ;AACpB,YAAYC,WAAU;AAEtB,OAAO,YAAY;AACnB,SAAS,SAAS;AAMX,IAAM,oBAAoB,EAAE,OAAO;AAAA,EACxC,IAAI,EAAE,OAAO;AAAA,EACb,OAAO,EAAE,OAAO;AAAA,EAChB,UAAU,EAAE,OAAO;AAAA,EACnB,UAAU,EAAE,OAAO;AAAA,EACnB,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC;AAC1B,CAAC;AAKD,SAAS,eAAe,aAAqB,UAA6B;AACxE,QAAM,EAAE,MAAM,QAAQ,IAAI,OAAO,WAAW;AAE5C,QAAM,SAAS,kBAAkB,UAAU,IAAI;AAC/C,MAAI,CAAC,OAAO,SAAS;AACnB,UAAM,SAAS,OAAO,MAAM,OAAO,IAAI,CAAC,MAAM,GAAG,EAAE,KAAK,KAAK,GAAG,CAAC,KAAK,EAAE,OAAO,EAAE,EAAE,KAAK,IAAI;AAC5F,UAAM,IAAI,eAAe,0BAA0B,QAAQ,KAAK,MAAM,EAAE;AAAA,EAC1E;AAEA,SAAO;AAAA,IACL,GAAG,OAAO;AAAA,IACV,SAAS,QAAQ,KAAK;AAAA,EACxB;AACF;AAKO,SAAS,kBAAkB,eAAoC;AACpE,MAAI,CAAI,eAAW,aAAa,GAAG;AACjC,UAAM,IAAI,eAAe,mCAAmC,aAAa,EAAE;AAAA,EAC7E;AAEA,QAAM,QAAW,gBAAY,aAAa,EAAE,OAAO,CAAC,MAAM,EAAE,SAAS,KAAK,CAAC;AAC3E,QAAM,aAA0B,CAAC;AAEjC,aAAW,QAAQ,OAAO;AACxB,UAAM,WAAgB,WAAK,eAAe,IAAI;AAC9C,UAAM,UAAa,iBAAa,UAAU,OAAO;AAEjD,QAAI;AACF,iBAAW,KAAK,eAAe,SAAS,IAAI,CAAC;AAAA,IAC/C,SAAS,OAAO;AAEd,cAAQ,KAAK,sCAAsC,IAAI,KAAK,KAAK,EAAE;AAAA,IACrE;AAAA,EACF;AAEA,SAAO;AACT;AAKO,SAAS,cAAc,eAAuB,IAA8B;AACjF,QAAM,WAAgB,WAAK,eAAe,GAAG,EAAE,KAAK;AAEpD,MAAI,CAAI,eAAW,QAAQ,GAAG;AAC5B,WAAO;AAAA,EACT;AAEA,QAAM,UAAa,iBAAa,UAAU,OAAO;AACjD,SAAO,eAAe,SAAS,GAAG,EAAE,KAAK;AAC3C;AAKO,SAAS,YAAY,YAA8C;AACxE,SAAO,WAAW,IAAI,CAAC,OAAO;AAAA,IAC5B,IAAI,EAAE;AAAA,IACN,OAAO,EAAE;AAAA,IACT,MAAM,EAAE;AAAA,IACR,UAAU,EAAE;AAAA,EACd,EAAE;AACJ;AAKO,SAAS,YAAY,aAAqB,IAA4B;AAC3E,QAAM,WAAgB,WAAK,aAAa,GAAG,EAAE,OAAO;AAEpD,MAAI,CAAI,eAAW,QAAQ,GAAG;AAC5B,WAAO;AAAA,EACT;AAEA,QAAM,UAAa,iBAAa,UAAU,OAAO;AACjD,SAAO,EAAE,IAAI,QAAQ;AACvB;AAKO,SAAS,aAAa,aAA+B;AAC1D,MAAI,CAAI,eAAW,WAAW,GAAG;AAC/B,WAAO,CAAC;AAAA,EACV;AAEA,SACG,gBAAY,WAAW,EACvB,OAAO,CAAC,MAAM,EAAE,SAAS,OAAO,CAAC,EACjC,IAAI,CAAC,MAAM,EAAE,QAAQ,SAAS,EAAE,CAAC;AACtC;;;AC5GA,SAAS,aAAa,SAA2B;AAC/C,QAAM,QAAQ,QACX,YAAY,EACZ,MAAM,aAAa,EACnB,OAAO,CAAC,SAAS,KAAK,SAAS,CAAC;AAEnC,SAAO,CAAC,GAAG,IAAI,IAAI,KAAK,CAAC;AAC3B;AAKA,SAAS,eAAe,WAAsB,UAA4B;AACxE,QAAM,OAAO,IAAI,IAAI,UAAU,KAAK,IAAI,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;AAC/D,MAAI,QAAQ;AAEZ,aAAW,WAAW,UAAU;AAC9B,QAAI,KAAK,IAAI,OAAO,GAAG;AACrB;AAAA,IACF;AAAA,EACF;AAGA,QAAM,WAAW,UAAU,SAAS,YAAY;AAChD,QAAM,KAAK,UAAU,GAAG,YAAY;AAEpC,aAAW,WAAW,UAAU;AAC9B,QAAI,SAAS,SAAS,OAAO,KAAK,GAAG,SAAS,OAAO,GAAG;AACtD,eAAS;AAAA,IACX;AAAA,EACF;AAEA,SAAO;AACT;AAMO,SAAS,gBACd,YACA,SACA,OACoB;AACpB,QAAM,WAAW,aAAa,OAAO;AAErC,MAAI,SAAS,WAAW,GAAG;AACzB,WAAO,CAAC;AAAA,EACV;AAEA,QAAM,SAAS,WACZ,IAAI,CAAC,eAAe;AAAA,IACnB;AAAA,IACA,OAAO,eAAe,WAAW,QAAQ;AAAA,EAC3C,EAAE,EACD,OAAO,CAAC,MAAM,EAAE,QAAQ,CAAC;AAG5B,SAAO,KAAK,CAAC,GAAG,MAAM;AACpB,QAAI,EAAE,UAAU,EAAE,OAAO;AACvB,aAAO,EAAE,QAAQ,EAAE;AAAA,IACrB;AACA,WAAO,EAAE,UAAU,WAAW,EAAE,UAAU;AAAA,EAC5C,CAAC;AAED,SAAO,QAAQ,OAAO,MAAM,GAAG,KAAK,IAAI;AAC1C;AAKO,SAAS,kBAAkB,SAAqC;AACrE,MAAI,QAAQ,WAAW,GAAG;AACxB,WAAO;AAAA,EACT;AAEA,QAAM,WAAW,QAAQ,IAAI,CAAC,MAAM;AAClC,UAAM,EAAE,UAAU,IAAI;AACtB,WAAO,KAAK,UAAU,KAAK;AAAA;AAAA,gBAAqB,UAAU,QAAQ,oBAAoB,UAAU,QAAQ;AAAA,YAAe,UAAU,KAAK,KAAK,IAAI,CAAC;AAAA;AAAA,EAAO,UAAU,OAAO;AAAA,EAC1K,CAAC;AAED,SAAO,SAAS,KAAK,aAAa;AACpC;","names":["fs","path"]}
|
|
@@ -23,7 +23,7 @@ var GITHUB_API = {
|
|
|
23
23
|
};
|
|
24
24
|
var STANDARDS_REPO = {
|
|
25
25
|
/** Default owner for standards repository (can be overridden via STANDARDS_REPO_OWNER env var) */
|
|
26
|
-
owner: process.env.STANDARDS_REPO_OWNER ?? "
|
|
26
|
+
owner: process.env.STANDARDS_REPO_OWNER ?? "progression-labs",
|
|
27
27
|
/** Default repository name (can be overridden via STANDARDS_REPO_NAME env var) */
|
|
28
28
|
repo: process.env.STANDARDS_REPO_NAME ?? "standards"
|
|
29
29
|
};
|
|
@@ -46,4 +46,4 @@ export {
|
|
|
46
46
|
CACHE,
|
|
47
47
|
CONCURRENCY
|
|
48
48
|
};
|
|
49
|
-
//# sourceMappingURL=chunk-
|
|
49
|
+
//# sourceMappingURL=chunk-RR4OEIAZ.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/constants.ts"],"sourcesContent":["/**\n * Centralized constants for the conform package.\n * Consolidates all hardcoded values for easier maintenance and configuration.\n */\n\n/**\n * Timeout values in milliseconds\n */\nexport const TIMEOUTS = {\n /** Git clone/pull operation timeout (30 seconds) */\n git: 30_000,\n /** Standard code tool execution timeout (5 minutes) */\n codeTool: 5 * 60 * 1000,\n /** Extended timeout for longer operations like coverage runs (10 minutes) */\n codeToolExtended: 10 * 60 * 1000,\n /** Quick operation timeout for version checks, etc (30 seconds) */\n quick: 30_000,\n /** Gitleaks version check timeout (10 seconds) */\n versionCheck: 10_000,\n} as const;\n\n/**\n * AWS configuration defaults\n */\nexport const AWS_DEFAULTS = {\n /** Default region for global AWS services (IAM, S3 global operations) */\n globalRegion: \"us-east-1\",\n} as const;\n\n/**\n * GitHub API configuration\n */\nexport const GITHUB_API = {\n /** Base URL for GitHub API (can be overridden via GITHUB_API_URL env var for GitHub Enterprise) */\n baseUrl: process.env.GITHUB_API_URL ?? \"https://api.github.com\",\n /** Number of items per page for pagination */\n perPage: 100,\n} as const;\n\n/**\n * Standards repository defaults\n */\nexport const STANDARDS_REPO = {\n /** Default owner for standards repository (can be overridden via STANDARDS_REPO_OWNER env var) */\n owner: process.env.STANDARDS_REPO_OWNER ?? \"
|
|
1
|
+
{"version":3,"sources":["../src/constants.ts"],"sourcesContent":["/**\n * Centralized constants for the conform package.\n * Consolidates all hardcoded values for easier maintenance and configuration.\n */\n\n/**\n * Timeout values in milliseconds\n */\nexport const TIMEOUTS = {\n /** Git clone/pull operation timeout (30 seconds) */\n git: 30_000,\n /** Standard code tool execution timeout (5 minutes) */\n codeTool: 5 * 60 * 1000,\n /** Extended timeout for longer operations like coverage runs (10 minutes) */\n codeToolExtended: 10 * 60 * 1000,\n /** Quick operation timeout for version checks, etc (30 seconds) */\n quick: 30_000,\n /** Gitleaks version check timeout (10 seconds) */\n versionCheck: 10_000,\n} as const;\n\n/**\n * AWS configuration defaults\n */\nexport const AWS_DEFAULTS = {\n /** Default region for global AWS services (IAM, S3 global operations) */\n globalRegion: \"us-east-1\",\n} as const;\n\n/**\n * GitHub API configuration\n */\nexport const GITHUB_API = {\n /** Base URL for GitHub API (can be overridden via GITHUB_API_URL env var for GitHub Enterprise) */\n baseUrl: process.env.GITHUB_API_URL ?? \"https://api.github.com\",\n /** Number of items per page for pagination */\n perPage: 100,\n} as const;\n\n/**\n * Standards repository defaults\n */\nexport const STANDARDS_REPO = {\n /** Default owner for standards repository (can be overridden via STANDARDS_REPO_OWNER env var) */\n owner: process.env.STANDARDS_REPO_OWNER ?? \"progression-labs\",\n /** Default repository name (can be overridden via STANDARDS_REPO_NAME env var) */\n repo: process.env.STANDARDS_REPO_NAME ?? \"standards\",\n} as const;\n\n/**\n * Cache directory configuration\n */\nexport const CACHE = {\n /** Base directory name for standards cache (can be overridden via CM_STANDARDS_CACHE_DIR env var) */\n standardsCacheDir: process.env.CM_STANDARDS_CACHE_DIR ?? \"cm-standards-cache\",\n /** Base directory name for registry cache */\n registryCacheDir: \"conform-registry-cache\",\n} as const;\n\n/**\n * Concurrency limits\n */\nexport const CONCURRENCY = {\n /** Default concurrency for infrastructure resource checks */\n infraScan: 10,\n} as const;\n\n/**\n * Default threshold values\n */\nconst _DEFAULTS = {\n /** Default backup max age in hours */\n backupMaxAgeHours: 24,\n /** Default code coverage minimum threshold */\n coverageMinThreshold: 80,\n} as const;\n"],"mappings":";AAQO,IAAM,WAAW;AAAA;AAAA,EAEtB,KAAK;AAAA;AAAA,EAEL,UAAU,IAAI,KAAK;AAAA;AAAA,EAEnB,kBAAkB,KAAK,KAAK;AAAA;AAAA,EAE5B,OAAO;AAAA;AAAA,EAEP,cAAc;AAChB;AAKO,IAAM,eAAe;AAAA;AAAA,EAE1B,cAAc;AAChB;AAKO,IAAM,aAAa;AAAA;AAAA,EAExB,SAAS,QAAQ,IAAI,kBAAkB;AAAA;AAAA,EAEvC,SAAS;AACX;AAKO,IAAM,iBAAiB;AAAA;AAAA,EAE5B,OAAO,QAAQ,IAAI,wBAAwB;AAAA;AAAA,EAE3C,MAAM,QAAQ,IAAI,uBAAuB;AAC3C;AAKO,IAAM,QAAQ;AAAA;AAAA,EAEnB,mBAAmB,QAAQ,IAAI,0BAA0B;AAAA;AAAA,EAEzD,kBAAkB;AACpB;AAKO,IAAM,cAAc;AAAA;AAAA,EAEzB,WAAW;AACb;","names":[]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/infra/manifest.ts","../src/infra/arn.ts","../src/infra/gcp.ts","../src/infra/schemas.ts"],"sourcesContent":["/**\n * Manifest reader for infra scan\n *\n * Supports two formats:\n * 1. JSON: { \"project\": \"...\", \"resources\": [\"arn:...\", \"projects/...\"] }\n * 2. TXT: One resource per line, # for comments\n *\n * Resources can be:\n * - AWS ARNs: arn:aws:s3:::bucket-name\n * - GCP paths: projects/{project}/locations/{location}/services/{service}\n */\n\nimport * as fs from \"node:fs\";\nimport * as path from \"node:path\";\n\nimport { isValidArn } from \"./arn.js\";\nimport { isValidGcpResource } from \"./gcp.js\";\nimport {\n isValidAccountKey,\n isLegacyManifestSchema,\n isMultiAccountManifestSchema,\n validateLegacyManifest,\n validateMultiAccountManifest,\n type AccountId,\n type LegacyManifest,\n type Manifest,\n type ManifestAccount,\n type MultiAccountManifest,\n} from \"./types.js\";\n\n/**\n * Check if a resource identifier is valid (AWS ARN or GCP path)\n */\nfunction isValidResource(resource: string): boolean {\n return isValidArn(resource) || isValidGcpResource(resource);\n}\n\n/**\n * Error thrown when manifest parsing fails\n */\nexport class ManifestError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"ManifestError\";\n }\n}\n\n/**\n * Type guard: check if manifest is multi-account format (v2)\n */\nexport function isMultiAccountManifest(manifest: Manifest): manifest is MultiAccountManifest {\n return \"accounts\" in manifest && typeof manifest.accounts === \"object\";\n}\n\n/**\n * Type guard: check if manifest is legacy format (v1)\n */\nexport function isLegacyManifest(manifest: Manifest): manifest is LegacyManifest {\n return \"resources\" in manifest && Array.isArray(manifest.resources);\n}\n\n/**\n * Parse an account key (e.g., \"aws:111111111111\" or \"gcp:my-project\")\n *\n * @param key - The account key in format \"cloud:id\"\n * @returns Parsed AccountId or null if invalid\n */\nexport function parseAccountKey(key: string): AccountId | null {\n // Use schema validation first\n if (!isValidAccountKey(key)) {\n return null;\n }\n\n // Extract components (we know the format is valid from schema check)\n const colonIndex = key.indexOf(\":\");\n return {\n cloud: key.substring(0, colonIndex) as \"aws\" | \"gcp\",\n id: key.substring(colonIndex + 1),\n };\n}\n\n/**\n * Format an account key from cloud and id\n */\nexport function formatAccountKey(cloud: \"aws\" | \"gcp\", id: string): string {\n return `${cloud}:${id}`;\n}\n\n/**\n * Normalize a legacy manifest to multi-account format\n * This converts v1 manifests to v2 format for unified processing\n */\nexport function normalizeManifest(manifest: Manifest): MultiAccountManifest {\n if (isMultiAccountManifest(manifest)) {\n return manifest;\n }\n\n // Group resources by detected account\n const accounts: Record<string, ManifestAccount> = {};\n\n for (const resource of manifest.resources) {\n const accountKey = detectAccountFromResource(resource);\n if (accountKey in accounts) {\n accounts[accountKey].resources.push(resource);\n } else {\n accounts[accountKey] = { resources: [resource] };\n }\n }\n\n return {\n version: 2,\n project: manifest.project,\n accounts,\n };\n}\n\n/**\n * Detect the account key from a resource identifier\n * Extracts AWS account ID from ARN or GCP project from resource path\n */\nexport function detectAccountFromResource(resource: string): string {\n // Check for AWS ARN: arn:partition:service:region:account:resource\n if (resource.startsWith(\"arn:\")) {\n const parts = resource.split(\":\");\n if (parts.length >= 5) {\n const accountId = parts[4];\n // Some AWS resources (like S3 buckets) don't have account ID in the ARN\n if (accountId) {\n return formatAccountKey(\"aws\", accountId);\n }\n // For S3 buckets without account ID, use a placeholder\n return \"aws:unknown\";\n }\n }\n\n // Check for GCP resource path: projects/{project}/...\n const gcpRegex = /^projects\\/([^/]+)\\//;\n const gcpMatch = gcpRegex.exec(resource);\n if (gcpMatch) {\n return formatAccountKey(\"gcp\", gcpMatch[1]);\n }\n\n return \"unknown:unknown\";\n}\n\n/**\n * Get all resources from a manifest (flattened for v2 manifests)\n */\nexport function getAllResources(manifest: Manifest): string[] {\n if (isMultiAccountManifest(manifest)) {\n return Object.values(manifest.accounts).flatMap((account) => account.resources);\n }\n return manifest.resources;\n}\n\n/**\n * Read and parse a manifest file\n *\n * @param manifestPath - Path to the manifest file\n * @returns Parsed manifest with project name and resource ARNs\n */\nexport function readManifest(manifestPath: string): Manifest {\n if (!fs.existsSync(manifestPath)) {\n throw new ManifestError(`Manifest file not found: ${manifestPath}`);\n }\n\n const content = fs.readFileSync(manifestPath, \"utf-8\");\n const ext = path.extname(manifestPath).toLowerCase();\n\n if (ext === \".json\") {\n return parseJsonManifest(content, manifestPath);\n }\n\n if (ext === \".txt\") {\n return parseTxtManifest(content, manifestPath);\n }\n\n // Try JSON first, then TXT\n try {\n return parseJsonManifest(content, manifestPath);\n } catch {\n return parseTxtManifest(content, manifestPath);\n }\n}\n\n/**\n * Parse a JSON format manifest using Zod schema validation\n */\nfunction parseJsonManifest(content: string, manifestPath: string): Manifest {\n const data = parseJsonContent(content, manifestPath);\n\n // First validate basic structure\n if (!data || typeof data !== \"object\") {\n throw new ManifestError(`Manifest ${manifestPath} must be a JSON object`);\n }\n\n // Try multi-account (v2) format first using Zod schema\n if (isMultiAccountManifestSchema(data)) {\n return validateMultiAccountManifestWithResources(data, manifestPath);\n }\n\n // Try legacy (v1) format using Zod schema\n if (isLegacyManifestSchema(data)) {\n return validateLegacyManifestWithResources(data, manifestPath);\n }\n\n // Fallback to manual validation for better error messages\n return parseFallbackManifest(data as Record<string, unknown>, manifestPath);\n}\n\n/**\n * Fallback parser for manifests that don't match Zod schemas\n */\nfunction parseFallbackManifest(obj: Record<string, unknown>, manifestPath: string): Manifest {\n if (\"accounts\" in obj) {\n return parseMultiAccountManifestFallback(obj, manifestPath);\n }\n\n validateJsonStructure(obj, manifestPath);\n const resources = extractAndValidateResources(obj.resources as unknown[], manifestPath);\n const project = typeof obj.project === \"string\" ? obj.project : undefined;\n\n return { project, resources };\n}\n\n/**\n * Validate multi-account manifest and its resources\n */\nfunction validateMultiAccountManifestWithResources(\n data: unknown,\n manifestPath: string\n): MultiAccountManifest {\n try {\n const manifest = validateMultiAccountManifest(data);\n\n // Validate account keys and resources\n for (const [accountKey, account] of Object.entries(manifest.accounts)) {\n // Validate account key format (must be \"aws:xxx\" or \"gcp:xxx\")\n if (!isValidAccountKey(accountKey)) {\n throw new ManifestError(\n `Manifest ${manifestPath} has invalid account key: \"${accountKey}\". Expected format: \"aws:<account-id>\" or \"gcp:<project-id>\"`\n );\n }\n\n // Validate each resource is a valid ARN or GCP path\n const invalidResources = account.resources.filter((r) => !isValidResource(r));\n if (invalidResources.length > 0) {\n throw new ManifestError(\n `Manifest ${manifestPath} account \"${accountKey}\" contains invalid resources: ${invalidResources.join(\", \")}`\n );\n }\n }\n\n return manifest;\n } catch (error) {\n if (error instanceof ManifestError) {\n throw error;\n }\n // Convert Zod errors to ManifestError\n const message = error instanceof Error ? error.message : \"Unknown error\";\n throw new ManifestError(`Invalid manifest ${manifestPath}: ${message}`);\n }\n}\n\n/**\n * Validate legacy manifest and its resources\n */\nfunction validateLegacyManifestWithResources(\n data: unknown,\n manifestPath: string\n): LegacyManifest {\n try {\n const manifest = validateLegacyManifest(data);\n\n // Additionally validate each resource is a valid ARN or GCP path\n const invalidResources = manifest.resources.filter((r) => !isValidResource(r));\n if (invalidResources.length > 0) {\n throw new ManifestError(\n `Manifest ${manifestPath} contains invalid resources: ${invalidResources.join(\", \")}`\n );\n }\n\n return manifest;\n } catch (error) {\n if (error instanceof ManifestError) {\n throw error;\n }\n // Convert Zod errors to ManifestError\n const message = error instanceof Error ? error.message : \"Unknown error\";\n throw new ManifestError(`Invalid manifest ${manifestPath}: ${message}`);\n }\n}\n\n/**\n * Fallback parser for multi-account manifest with detailed error messages\n */\nfunction parseMultiAccountManifestFallback(\n obj: Record<string, unknown>,\n manifestPath: string\n): MultiAccountManifest {\n const accountsRaw = obj.accounts as Record<string, unknown>;\n const accounts: Record<string, ManifestAccount> = {};\n\n for (const [key, value] of Object.entries(accountsRaw)) {\n accounts[key] = parseAccountEntry(key, value, manifestPath);\n }\n\n const project = typeof obj.project === \"string\" ? obj.project : undefined;\n\n return { version: 2, project, accounts };\n}\n\n/**\n * Validate and parse a single account entry from manifest\n */\nfunction parseAccountEntry(\n key: string,\n value: unknown,\n manifestPath: string\n): ManifestAccount {\n const parsedKey = parseAccountKey(key);\n if (!parsedKey) {\n throw new ManifestError(\n `Manifest ${manifestPath} has invalid account key: \"${key}\". Expected format: \"aws:<account-id>\" or \"gcp:<project-id>\"`\n );\n }\n\n if (!value || typeof value !== \"object\") {\n throw new ManifestError(`Manifest ${manifestPath} account \"${key}\" must be an object`);\n }\n\n const accountObj = value as Record<string, unknown>;\n if (!Array.isArray(accountObj.resources)) {\n throw new ManifestError(`Manifest ${manifestPath} account \"${key}\" must have a \"resources\" array`);\n }\n\n const resources = extractAndValidateResources(accountObj.resources, manifestPath);\n const alias = typeof accountObj.alias === \"string\" ? accountObj.alias : undefined;\n\n return { alias, resources };\n}\n\nfunction parseJsonContent(content: string, manifestPath: string): unknown {\n try {\n return JSON.parse(content);\n } catch (error) {\n const message = error instanceof Error ? error.message : \"Unknown error\";\n throw new ManifestError(`Invalid JSON in manifest ${manifestPath}: ${message}`);\n }\n}\n\nfunction validateJsonStructure(data: unknown, manifestPath: string): void {\n if (!data || typeof data !== \"object\") {\n throw new ManifestError(`Manifest ${manifestPath} must be a JSON object`);\n }\n\n const obj = data as Record<string, unknown>;\n if (!Array.isArray(obj.resources)) {\n throw new ManifestError(`Manifest ${manifestPath} must have a \"resources\" array`);\n }\n}\n\nfunction extractAndValidateResources(items: unknown[], manifestPath: string): string[] {\n const resources: string[] = [];\n const invalidResources: string[] = [];\n\n for (const item of items) {\n if (typeof item !== \"string\") {\n throw new ManifestError(\n `Manifest ${manifestPath} contains non-string resource: ${JSON.stringify(item)}`\n );\n }\n if (!isValidResource(item)) {\n invalidResources.push(item);\n } else {\n resources.push(item);\n }\n }\n\n if (invalidResources.length > 0) {\n throw new ManifestError(\n `Manifest ${manifestPath} contains invalid resources: ${invalidResources.join(\", \")}`\n );\n }\n\n return resources;\n}\n\n/**\n * Parse a TXT format manifest (one resource per line, # for comments)\n */\nfunction parseTxtManifest(content: string, manifestPath: string): Manifest {\n const lines = content.split(\"\\n\");\n const resources: string[] = [];\n const invalidResources: { line: number; value: string }[] = [];\n\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i].trim();\n\n // Skip empty lines and comments\n if (!line || line.startsWith(\"#\")) {\n continue;\n }\n\n if (!isValidResource(line)) {\n invalidResources.push({ line: i + 1, value: line });\n } else {\n resources.push(line);\n }\n }\n\n if (invalidResources.length > 0) {\n const details = invalidResources.map((a) => `line ${a.line}: \"${a.value}\"`).join(\", \");\n throw new ManifestError(`Manifest ${manifestPath} contains invalid resources: ${details}`);\n }\n\n return { resources };\n}\n","/**\n * ARN parsing utilities\n *\n * ARN format: arn:partition:service:region:account-id:resource\n * or: arn:partition:service:region:account-id:resource-type/resource-id\n * or: arn:partition:service:region:account-id:resource-type:resource-id\n */\n\nimport type { ParsedArn } from \"./types.js\";\n\ninterface ResourceParts {\n resourceType: string;\n resourceId: string;\n}\n\n/**\n * Validate that a string is a valid ARN format\n */\nexport function isValidArn(arn: string): boolean {\n if (!arn.startsWith(\"arn:\")) {\n return false;\n }\n const parts = arn.split(\":\");\n // ARN must have at least 6 parts: arn:partition:service:region:account:resource\n return parts.length >= 6;\n}\n\n/**\n * Parse an ARN string into its components\n */\nexport function parseArn(arn: string): ParsedArn | null {\n if (!isValidArn(arn)) {\n return null;\n }\n\n const parts = arn.split(\":\");\n const [, partition, service, region, accountId, ...resourceParts] = parts;\n\n // Resource can contain colons, so we need to rejoin\n const resource = resourceParts.join(\":\");\n\n // Parse resource type and ID based on service-specific patterns\n const { resourceType, resourceId } = parseResource(service, resource);\n\n return {\n cloud: \"aws\" as const,\n partition,\n service,\n region,\n accountId,\n resourceType,\n resourceId,\n raw: arn,\n };\n}\n\n// Service-specific parsers\nconst serviceParsers: Record<string, (resource: string) => ResourceParts> = {\n s3: parseS3Resource,\n lambda: parseLambdaResource,\n dynamodb: parseDynamoDBResource,\n sqs: (resource) => ({ resourceType: \"queue\", resourceId: resource }),\n sns: (resource) => ({ resourceType: \"topic\", resourceId: resource }),\n iam: parseIAMResource,\n secretsmanager: parseSecretsManagerResource,\n logs: parseLogsResource,\n};\n\nfunction parseS3Resource(resource: string): ResourceParts {\n if (resource.includes(\"/\")) {\n const [bucket, ...keyParts] = resource.split(\"/\");\n return { resourceType: \"object\", resourceId: `${bucket}/${keyParts.join(\"/\")}` };\n }\n return { resourceType: \"bucket\", resourceId: resource };\n}\n\nfunction parseLambdaResource(resource: string): ResourceParts {\n if (resource.startsWith(\"function:\")) {\n const funcName = resource.slice(\"function:\".length);\n const colonIndex = funcName.indexOf(\":\");\n const resourceId = colonIndex !== -1 ? funcName.slice(0, colonIndex) : funcName;\n return { resourceType: \"function\", resourceId };\n }\n if (resource.startsWith(\"layer:\")) {\n const rest = resource.slice(\"layer:\".length);\n const colonIndex = rest.indexOf(\":\");\n const resourceId = colonIndex !== -1 ? rest.slice(0, colonIndex) : rest;\n return { resourceType: \"layer\", resourceId };\n }\n return { resourceType: \"function\", resourceId: resource };\n}\n\nfunction parseDynamoDBResource(resource: string): ResourceParts {\n if (resource.startsWith(\"table/\")) {\n const rest = resource.slice(\"table/\".length);\n const indexPos = rest.indexOf(\"/index/\");\n const resourceType = indexPos !== -1 ? \"index\" : \"table\";\n return { resourceType, resourceId: rest };\n }\n return { resourceType: \"table\", resourceId: resource };\n}\n\nfunction parseIAMResource(resource: string): ResourceParts {\n const prefixes = [\"role/\", \"user/\", \"policy/\"];\n for (const prefix of prefixes) {\n if (resource.startsWith(prefix)) {\n return {\n resourceType: prefix.slice(0, -1),\n resourceId: resource.slice(prefix.length),\n };\n }\n }\n const colonIndex = resource.indexOf(\":\");\n if (colonIndex !== -1) {\n return {\n resourceType: resource.slice(0, colonIndex),\n resourceId: resource.slice(colonIndex + 1),\n };\n }\n return { resourceType: \"\", resourceId: resource };\n}\n\nfunction parseSecretsManagerResource(resource: string): ResourceParts {\n if (resource.startsWith(\"secret:\")) {\n return { resourceType: \"secret\", resourceId: resource.slice(\"secret:\".length) };\n }\n return { resourceType: \"secret\", resourceId: resource };\n}\n\nfunction parseLogsResource(resource: string): ResourceParts {\n if (resource.startsWith(\"log-group:\")) {\n let logGroupName = resource.slice(\"log-group:\".length);\n if (logGroupName.endsWith(\":*\")) {\n logGroupName = logGroupName.slice(0, -2);\n }\n return { resourceType: \"log-group\", resourceId: logGroupName };\n }\n return { resourceType: \"log-group\", resourceId: resource };\n}\n\nfunction parseGenericResource(resource: string): ResourceParts {\n if (resource.includes(\"/\")) {\n const slashIndex = resource.indexOf(\"/\");\n return {\n resourceType: resource.slice(0, slashIndex),\n resourceId: resource.slice(slashIndex + 1),\n };\n }\n if (resource.includes(\":\")) {\n const colonIndex = resource.indexOf(\":\");\n return {\n resourceType: resource.slice(0, colonIndex),\n resourceId: resource.slice(colonIndex + 1),\n };\n }\n return { resourceType: \"\", resourceId: resource };\n}\n\n/**\n * Parse the resource portion of an ARN into type and ID\n */\nfunction parseResource(service: string, resource: string): ResourceParts {\n const parser = serviceParsers[service] as ((r: string) => ResourceParts) | undefined;\n return parser ? parser(resource) : parseGenericResource(resource);\n}\n","/**\n * GCP resource path parsing utilities\n *\n * GCP resource paths follow patterns like:\n * - projects/{project}/locations/{location}/services/{service} (Cloud Run)\n * - projects/{project}/serviceAccounts/{email} (IAM Service Accounts)\n * - projects/{project}/secrets/{secret} (Secret Manager)\n * - projects/{project}/locations/{location}/repositories/{repo} (Artifact Registry)\n */\n\nimport type { ParsedGcpResource } from \"./types.js\";\n\n/**\n * Validate that a string is a valid GCP resource path\n */\nexport function isValidGcpResource(path: string): boolean {\n return path.startsWith(\"projects/\") && path.split(\"/\").length >= 3;\n}\n\n/**\n * Parse a GCP resource path into its components\n */\nexport function parseGcpResource(path: string): ParsedGcpResource | null {\n if (!isValidGcpResource(path)) {\n return null;\n }\n\n const parts = path.split(\"/\");\n if (parts[0] !== \"projects\" || parts.length < 3) {\n return null;\n }\n\n const project = parts[1];\n const result = parseResourcePath(parts.slice(2), path, project);\n return result;\n}\n\n/**\n * Parse the resource-specific part of the path\n */\nfunction parseResourcePath(\n parts: string[],\n raw: string,\n project: string\n): ParsedGcpResource | null {\n // Service Accounts: projects/{project}/serviceAccounts/{email}\n if (parts[0] === \"serviceAccounts\" && parts.length >= 2) {\n return gcpResource({\n project,\n service: \"iam\",\n location: \"global\",\n resourceType: \"serviceAccounts\",\n resourceId: parts.slice(1).join(\"/\"),\n raw,\n });\n }\n\n // Secrets: projects/{project}/secrets/{secret}\n if (parts[0] === \"secrets\" && parts.length >= 2) {\n return gcpResource({\n project,\n service: \"secretmanager\",\n location: \"global\",\n resourceType: \"secrets\",\n resourceId: parts[1],\n raw,\n });\n }\n\n // Location-based resources\n if (parts[0] === \"locations\" && parts.length >= 4) {\n const location = parts[1];\n const resourceType = parts[2];\n const resourceId = parts.slice(3).join(\"/\");\n\n const service = getServiceFromResourceType(resourceType);\n return gcpResource({\n project,\n service,\n location,\n resourceType,\n resourceId,\n raw,\n });\n }\n\n // Unknown format\n return null;\n}\n\n/**\n * Map resource types to GCP service names\n */\nfunction getServiceFromResourceType(resourceType: string): string {\n const serviceMap: Record<string, string> = {\n services: \"run\",\n repositories: \"artifactregistry\",\n functions: \"cloudfunctions\",\n buckets: \"storage\",\n instances: \"compute\",\n clusters: \"container\",\n };\n return serviceMap[resourceType] ?? resourceType;\n}\n\ninterface GcpResourceParams {\n project: string;\n service: string;\n location: string;\n resourceType: string;\n resourceId: string;\n raw: string;\n}\n\n/**\n * Create a ParsedGcpResource object\n */\nfunction gcpResource(params: GcpResourceParams): ParsedGcpResource {\n return { cloud: \"gcp\", ...params };\n}\n","/**\n * Zod schemas for runtime validation of infra manifests and resources\n *\n * These schemas validate external inputs like manifest files,\n * stack exports, ARNs, and GCP resource paths at runtime.\n */\n\nimport { z } from \"zod\";\n\n// =============================================================================\n// Cloud Provider Types\n// =============================================================================\n\n/**\n * Cloud provider schema\n */\nexport const CloudProviderSchema = z.enum([\"aws\", \"gcp\"]);\nexport type CloudProvider = z.infer<typeof CloudProviderSchema>;\n\n/**\n * Account key schema - format: \"provider:accountId\"\n * Examples: \"aws:123456789012\", \"gcp:my-project-id\"\n */\nexport const AccountKeySchema = z\n .string()\n .regex(\n /^(aws|gcp):.+$/,\n \"Invalid account key format. Expected: provider:accountId (e.g., aws:123456789012, gcp:my-project)\"\n );\ntype AccountKey = z.infer<typeof AccountKeySchema>;\n\n// =============================================================================\n// AWS Resource Schemas\n// =============================================================================\n\n/**\n * ARN schema - validates AWS ARN format\n *\n * Format: arn:partition:service:region:account-id:resource\n */\nexport const ArnSchema = z\n .string()\n .regex(\n /^arn:(aws|aws-cn|aws-us-gov):[a-z0-9-]+:[a-z0-9-]*:[0-9]*:.+$/,\n \"Invalid ARN format. Expected: arn:partition:service:region:account-id:resource\"\n );\nexport type Arn = z.infer<typeof ArnSchema>;\n\n/**\n * Parsed ARN schema - components extracted from an ARN\n */\nexport const ParsedArnSchema = z.object({\n /** Cloud provider (always \"aws\" for ARNs) */\n cloud: z.literal(\"aws\"),\n\n /** AWS partition (aws, aws-cn, aws-us-gov) */\n partition: z.string(),\n\n /** AWS service (s3, lambda, rds, etc.) */\n service: z.string(),\n\n /** AWS region (empty for global services like S3, IAM) */\n region: z.string(),\n\n /** AWS account ID (empty for S3 buckets) */\n accountId: z.string(),\n\n /** Resource type (e.g., function, table, bucket) */\n resourceType: z.string(),\n\n /** Resource name/identifier */\n resourceId: z.string(),\n\n /** Original ARN string */\n raw: z.string(),\n});\nexport type ParsedArn = z.infer<typeof ParsedArnSchema>;\n\n// =============================================================================\n// GCP Resource Schemas\n// =============================================================================\n\n/**\n * GCP resource path schema - validates GCP resource path format\n *\n * Examples:\n * - projects/my-project/locations/us-central1/functions/my-func\n * - projects/my-project/topics/my-topic\n * - projects/my-project/subscriptions/my-sub\n */\nexport const GcpResourcePathSchema = z\n .string()\n .regex(\n /^projects\\/[^/]+\\/.+$/,\n \"Invalid GCP resource path format. Expected: projects/{project-id}/...\"\n );\nexport type GcpResourcePath = z.infer<typeof GcpResourcePathSchema>;\n\n/**\n * Parsed GCP resource schema - components extracted from a GCP resource path\n */\nexport const ParsedGcpResourceSchema = z.object({\n /** Cloud provider (always \"gcp\" for GCP resources) */\n cloud: z.literal(\"gcp\"),\n\n /** GCP project ID */\n project: z.string(),\n\n /** GCP service (run, iam, secretmanager, artifactregistry, etc.) */\n service: z.string(),\n\n /** Location/region (us-central1, global, etc.) */\n location: z.string(),\n\n /** Resource type (services, serviceAccounts, secrets, repositories, etc.) */\n resourceType: z.string(),\n\n /** Resource name/ID */\n resourceId: z.string(),\n\n /** Original resource path */\n raw: z.string(),\n});\nexport type ParsedGcpResource = z.infer<typeof ParsedGcpResourceSchema>;\n\n/**\n * Generic resource identifier - can be AWS ARN or GCP resource path\n */\nexport const ResourceIdentifierSchema = z.union([ArnSchema, GcpResourcePathSchema]);\nexport type ResourceIdentifier = z.infer<typeof ResourceIdentifierSchema>;\n\n// =============================================================================\n// Account Types\n// =============================================================================\n\n/**\n * Account identifier schema - parsed from account key\n */\nexport const AccountIdSchema = z.object({\n /** Cloud provider */\n cloud: CloudProviderSchema,\n\n /** AWS account ID or GCP project ID */\n id: z.string(),\n});\nexport type AccountId = z.infer<typeof AccountIdSchema>;\n\n/**\n * Account entry in a multi-account manifest\n */\nexport const ManifestAccountSchema = z.object({\n /** Optional human-readable alias for this account */\n alias: z.string().optional(),\n\n /** List of resource identifiers (ARNs or GCP resource paths) */\n resources: z.array(z.string()),\n});\nexport type ManifestAccount = z.infer<typeof ManifestAccountSchema>;\n\n// =============================================================================\n// Manifest Schemas\n// =============================================================================\n\n/**\n * V2 Multi-account manifest schema\n *\n * Resources are grouped by cloud account (AWS account ID or GCP project ID)\n */\nexport const MultiAccountManifestSchema = z.object({\n /** Manifest version - must be 2 for multi-account format */\n version: z.literal(2),\n\n /** Optional project name */\n project: z.string().optional(),\n\n /** Resources grouped by account key (e.g., \"aws:123456789012\", \"gcp:my-project\") */\n accounts: z.record(z.string(), ManifestAccountSchema),\n});\nexport type MultiAccountManifest = z.infer<typeof MultiAccountManifestSchema>;\n\n/**\n * Legacy manifest schema (v1) - flat array of resources\n */\nexport const LegacyManifestSchema = z.object({\n /** Optional manifest version (1 or undefined for legacy) */\n version: z.literal(1).optional(),\n\n /** Optional project name */\n project: z.string().optional(),\n\n /** Flat list of resource identifiers */\n resources: z.array(z.string()),\n});\nexport type LegacyManifest = z.infer<typeof LegacyManifestSchema>;\n\n/**\n * Any manifest schema - accepts either v1 or v2 format\n */\nexport const ManifestSchema = z.union([MultiAccountManifestSchema, LegacyManifestSchema]);\nexport type Manifest = z.infer<typeof ManifestSchema>;\n\n// =============================================================================\n// Scan Result Schemas\n// =============================================================================\n\n/**\n * Result of checking a single resource\n */\nexport const ResourceCheckResultSchema = z.object({\n /** The resource ARN or GCP path */\n arn: z.string(),\n\n /** Whether the resource exists */\n exists: z.boolean(),\n\n /** Error message if check failed */\n error: z.string().optional(),\n\n /** Service name (e.g., s3, lambda, run) */\n service: z.string(),\n\n /** Resource type (e.g., bucket, function) */\n resourceType: z.string(),\n\n /** Resource identifier */\n resourceId: z.string(),\n});\nexport type ResourceCheckResult = z.infer<typeof ResourceCheckResultSchema>;\n\n/**\n * Scan summary statistics\n */\nexport const InfraScanSummarySchema = z.object({\n /** Total resources checked */\n total: z.number().int().nonnegative(),\n\n /** Resources that exist */\n found: z.number().int().nonnegative(),\n\n /** Resources that don't exist */\n missing: z.number().int().nonnegative(),\n\n /** Resources that couldn't be checked (errors) */\n errors: z.number().int().nonnegative(),\n});\nexport type InfraScanSummary = z.infer<typeof InfraScanSummarySchema>;\n\n/**\n * Per-account scan results\n */\nconst AccountScanResultSchema = z.object({\n /** Account alias if provided */\n alias: z.string().optional(),\n\n /** Individual resource check results */\n results: z.array(ResourceCheckResultSchema),\n\n /** Summary statistics for this account */\n summary: InfraScanSummarySchema,\n});\nexport type AccountScanResult = z.infer<typeof AccountScanResultSchema>;\n\n/**\n * Full infrastructure scan result\n */\nexport const InfraScanResultSchema = z.object({\n /** Path to the manifest file */\n manifest: z.string(),\n\n /** Project name */\n project: z.string().optional(),\n\n /** Individual resource check results */\n results: z.array(ResourceCheckResultSchema),\n\n /** Summary statistics */\n summary: InfraScanSummarySchema,\n\n /** Per-account results (only present for multi-account manifests) */\n accountResults: z.record(z.string(), AccountScanResultSchema).optional(),\n});\nexport type InfraScanResult = z.infer<typeof InfraScanResultSchema>;\n\n// =============================================================================\n// Scan Options Schemas\n// =============================================================================\n\n/**\n * Options for programmatic API\n */\nexport interface ScanInfraOptions {\n /** Path to manifest file */\n manifestPath?: string;\n\n /** Path to config file */\n configPath?: string;\n\n /** Filter to specific account (by alias or account key like \"aws:123\") */\n account?: string;\n}\n\n/**\n * Options for CLI handler\n */\nexport type RunInfraScanOptions = ScanInfraOptions & {\n /** Output format */\n format?: \"text\" | \"json\";\n};\n\n// =============================================================================\n// Pulumi Stack Export Schemas\n// =============================================================================\n\n/**\n * Pulumi resource in stack export\n */\nexport const PulumiResourceSchema = z.object({\n urn: z.string().optional(),\n type: z.string().optional(),\n inputs: z.record(z.string(), z.unknown()).optional(),\n outputs: z.record(z.string(), z.unknown()).optional(),\n});\nexport type PulumiResource = z.infer<typeof PulumiResourceSchema>;\n\n/**\n * Pulumi stack export schema (simplified)\n */\nexport const PulumiStackExportSchema = z.object({\n version: z.number().optional(),\n deployment: z\n .object({\n manifest: z\n .object({\n time: z.string().optional(),\n magic: z.string().optional(),\n version: z.string().optional(),\n })\n .optional(),\n resources: z.array(PulumiResourceSchema).optional(),\n })\n .optional(),\n});\nexport type PulumiStackExport = z.infer<typeof PulumiStackExportSchema>;\n\n// =============================================================================\n// Validation Functions\n// =============================================================================\n\n/**\n * Validate an ARN string\n * @throws ZodError if invalid\n */\nexport function validateArn(arn: string): Arn {\n return ArnSchema.parse(arn);\n}\n\n/**\n * Check if a string is a valid ARN format\n */\nexport function isValidArnFormat(arn: string): boolean {\n return ArnSchema.safeParse(arn).success;\n}\n\n/**\n * Validate a GCP resource path\n * @throws ZodError if invalid\n */\nexport function validateGcpResourcePath(path: string): GcpResourcePath {\n return GcpResourcePathSchema.parse(path);\n}\n\n/**\n * Check if a string is a valid GCP resource path\n */\nexport function isValidGcpResourcePath(path: string): boolean {\n return GcpResourcePathSchema.safeParse(path).success;\n}\n\n/**\n * Validate an account key string\n * @throws ZodError if invalid\n */\nexport function validateAccountKey(key: string): AccountKey {\n return AccountKeySchema.parse(key);\n}\n\n/**\n * Check if a string is a valid account key\n */\nexport function isValidAccountKey(key: string): boolean {\n return AccountKeySchema.safeParse(key).success;\n}\n\n/**\n * Validate a legacy (v1) manifest\n * @throws ZodError if invalid\n */\nexport function validateLegacyManifest(data: unknown): LegacyManifest {\n return LegacyManifestSchema.parse(data);\n}\n\n/**\n * Validate a multi-account (v2) manifest\n * @throws ZodError if invalid\n */\nexport function validateMultiAccountManifest(data: unknown): MultiAccountManifest {\n return MultiAccountManifestSchema.parse(data);\n}\n\n/**\n * Validate any manifest format (v1 or v2)\n * @throws ZodError if invalid\n */\nexport function validateManifest(data: unknown): Manifest {\n return ManifestSchema.parse(data);\n}\n\n/**\n * Check if data is a valid multi-account (v2) manifest\n */\nexport function isMultiAccountManifestSchema(data: unknown): data is MultiAccountManifest {\n return MultiAccountManifestSchema.safeParse(data).success;\n}\n\n/**\n * Check if data is a valid legacy (v1) manifest\n */\nexport function isLegacyManifestSchema(data: unknown): data is LegacyManifest {\n return LegacyManifestSchema.safeParse(data).success;\n}\n\n/**\n * Validate a Pulumi stack export\n * @throws ZodError if invalid\n */\nexport function validateStackExport(data: unknown): PulumiStackExport {\n return PulumiStackExportSchema.parse(data);\n}\n\n"],"mappings":"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","names":["path","path"]}
|
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
isMultiAccountManifest,
|
|
5
5
|
isValidArn,
|
|
6
6
|
isValidGcpResource
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-T2PWT2B5.js";
|
|
8
8
|
|
|
9
9
|
// src/infra/generate.ts
|
|
10
10
|
import * as fs from "fs";
|
|
@@ -311,4 +311,4 @@ export {
|
|
|
311
311
|
generateMultiAccountFromFile,
|
|
312
312
|
generateWithMerge
|
|
313
313
|
};
|
|
314
|
-
//# sourceMappingURL=chunk-
|
|
314
|
+
//# sourceMappingURL=chunk-XZERDHOH.js.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
CACHE,
|
|
3
3
|
TIMEOUTS
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-RR4OEIAZ.js";
|
|
5
5
|
|
|
6
6
|
// src/core/registry.ts
|
|
7
7
|
import * as fs2 from "fs";
|
|
@@ -258,8 +258,10 @@ var hooksConfigSchema = z.object({
|
|
|
258
258
|
// e.g., ["pre-commit", "pre-push"]
|
|
259
259
|
commands: hookCommandsSchema,
|
|
260
260
|
// e.g., { "pre-commit": ["lint-staged"] }
|
|
261
|
-
protected_branches: z.array(z.string()).optional()
|
|
261
|
+
protected_branches: z.array(z.string()).optional(),
|
|
262
262
|
// e.g., ["main", "master"] - verify pre-push prevents direct pushes
|
|
263
|
+
templates: z.record(z.string(), z.string()).optional()
|
|
264
|
+
// Maps hook name → expected file content, e.g., { "pre-commit" = "pnpm lint-staged" }
|
|
263
265
|
}).strict().optional();
|
|
264
266
|
var ciCommandsValueSchema = z.union([
|
|
265
267
|
z.array(z.string()),
|
|
@@ -639,6 +641,19 @@ var defaultConfig = {
|
|
|
639
641
|
};
|
|
640
642
|
|
|
641
643
|
// src/core/loader.ts
|
|
644
|
+
function stripSymbols(obj) {
|
|
645
|
+
if (obj === null || typeof obj !== "object") {
|
|
646
|
+
return obj;
|
|
647
|
+
}
|
|
648
|
+
if (Array.isArray(obj)) {
|
|
649
|
+
return obj.map(stripSymbols);
|
|
650
|
+
}
|
|
651
|
+
const result = {};
|
|
652
|
+
for (const key of Object.keys(obj)) {
|
|
653
|
+
result[key] = stripSymbols(obj[key]);
|
|
654
|
+
}
|
|
655
|
+
return result;
|
|
656
|
+
}
|
|
642
657
|
var CONFIG_FILE_NAME = "standards.toml";
|
|
643
658
|
var ConfigError = class extends Error {
|
|
644
659
|
constructor(message) {
|
|
@@ -695,7 +710,7 @@ function resolveConfigPath(configPath) {
|
|
|
695
710
|
function parseTomlFile(filePath) {
|
|
696
711
|
try {
|
|
697
712
|
const content = fs.readFileSync(filePath, "utf-8");
|
|
698
|
-
return TOML.parse(content);
|
|
713
|
+
return stripSymbols(TOML.parse(content));
|
|
699
714
|
} catch (error) {
|
|
700
715
|
const message = error instanceof Error ? error.message : "Unknown error";
|
|
701
716
|
throw new ConfigError(`Failed to parse ${CONFIG_FILE_NAME}: ${message}`);
|
|
@@ -704,7 +719,7 @@ function parseTomlFile(filePath) {
|
|
|
704
719
|
function validateConfig(rawConfig) {
|
|
705
720
|
const result = configSchema.safeParse(rawConfig);
|
|
706
721
|
if (!result.success) {
|
|
707
|
-
const errors = result.error.issues.map((e) => ` - ${e.path.join(".")}: ${e.message}`).join("\n");
|
|
722
|
+
const errors = result.error.issues.map((e) => ` - ${e.path.map((p) => typeof p === "symbol" ? p.description ?? "[symbol]" : p).join(".")}: ${e.message}`).join("\n");
|
|
708
723
|
throw new ConfigError(`Invalid ${CONFIG_FILE_NAME} configuration:
|
|
709
724
|
${errors}`);
|
|
710
725
|
}
|
|
@@ -890,14 +905,17 @@ function mergeProcess(c, dc) {
|
|
|
890
905
|
forbidden_files: mergeProcessForbiddenFiles(c.process, dc.process)
|
|
891
906
|
};
|
|
892
907
|
}
|
|
908
|
+
function mergeInfra(c, dc) {
|
|
909
|
+
return {
|
|
910
|
+
enabled: c.infra?.enabled ?? dc.infra?.enabled ?? false,
|
|
911
|
+
manifest: c.infra?.manifest ?? dc.infra?.manifest ?? "infra-manifest.json"
|
|
912
|
+
};
|
|
913
|
+
}
|
|
893
914
|
function mergeWithDefaults(config) {
|
|
894
915
|
return {
|
|
895
916
|
code: mergeCode(config, defaultConfig),
|
|
896
917
|
process: mergeProcess(config, defaultConfig),
|
|
897
|
-
infra:
|
|
898
|
-
enabled: config.infra?.enabled ?? defaultConfig.infra?.enabled ?? false,
|
|
899
|
-
manifest: config.infra?.manifest ?? defaultConfig.infra?.manifest ?? "infra-manifest.json"
|
|
900
|
-
},
|
|
918
|
+
infra: mergeInfra(config, defaultConfig),
|
|
901
919
|
monorepo: config.monorepo
|
|
902
920
|
};
|
|
903
921
|
}
|
|
@@ -927,7 +945,7 @@ function detectCodeownersOverrides(registryConfig, projectConfig) {
|
|
|
927
945
|
return projectRules.map((rule) => checkRuleOverride(rule, registryMap.get(rule.pattern))).filter((o) => o !== null);
|
|
928
946
|
}
|
|
929
947
|
async function loadRegistryConfig(extendsConfig, configDir) {
|
|
930
|
-
const registryModule = await import("./registry-
|
|
948
|
+
const registryModule = await import("./registry-7CTKYGKA.js");
|
|
931
949
|
const loc = registryModule.parseRegistryUrl(extendsConfig.registry, configDir);
|
|
932
950
|
const registryDir = await registryModule.fetchRegistry(loc);
|
|
933
951
|
let config = {};
|
|
@@ -952,6 +970,19 @@ async function loadConfigWithOverrides(configPath) {
|
|
|
952
970
|
}
|
|
953
971
|
|
|
954
972
|
// src/core/registry.ts
|
|
973
|
+
function stripSymbols2(obj) {
|
|
974
|
+
if (obj === null || typeof obj !== "object") {
|
|
975
|
+
return obj;
|
|
976
|
+
}
|
|
977
|
+
if (Array.isArray(obj)) {
|
|
978
|
+
return obj.map(stripSymbols2);
|
|
979
|
+
}
|
|
980
|
+
const result = {};
|
|
981
|
+
for (const key of Object.keys(obj)) {
|
|
982
|
+
result[key] = stripSymbols2(obj[key]);
|
|
983
|
+
}
|
|
984
|
+
return result;
|
|
985
|
+
}
|
|
955
986
|
function detectAuthMethod() {
|
|
956
987
|
if (process.env.CONFORM_REGISTRY_TOKEN || process.env.GITHUB_TOKEN) {
|
|
957
988
|
return "token";
|
|
@@ -1075,14 +1106,14 @@ function loadRuleset(registryDir, rulesetName) {
|
|
|
1075
1106
|
const content = fs2.readFileSync(rulesetPath, "utf-8");
|
|
1076
1107
|
let parsed;
|
|
1077
1108
|
try {
|
|
1078
|
-
parsed = toml.parse(content);
|
|
1109
|
+
parsed = stripSymbols2(toml.parse(content));
|
|
1079
1110
|
} catch (error) {
|
|
1080
1111
|
const message = error instanceof Error ? error.message : String(error);
|
|
1081
1112
|
throw new ConfigError(`Failed to parse ruleset ${rulesetName}: ${message}`);
|
|
1082
1113
|
}
|
|
1083
1114
|
const result = configSchema.safeParse(parsed);
|
|
1084
1115
|
if (!result.success) {
|
|
1085
|
-
const errors = result.error.issues.map((e) => `${e.path.join(".")}: ${e.message}`).join(", ");
|
|
1116
|
+
const errors = result.error.issues.map((e) => `${e.path.map((p) => typeof p === "symbol" ? p.description ?? "[symbol]" : p).join(".")}: ${e.message}`).join(", ");
|
|
1086
1117
|
throw new ConfigError(`Invalid ruleset ${rulesetName}: ${errors}`);
|
|
1087
1118
|
}
|
|
1088
1119
|
return result.data;
|
|
@@ -1171,7 +1202,8 @@ function mergeHooksConfig(base, override) {
|
|
|
1171
1202
|
enabled: override.enabled,
|
|
1172
1203
|
require_husky: override.require_husky,
|
|
1173
1204
|
require_hooks: override.require_hooks ?? base?.require_hooks,
|
|
1174
|
-
commands: override.commands ?? base?.commands
|
|
1205
|
+
commands: override.commands ?? base?.commands,
|
|
1206
|
+
templates: override.templates ?? base?.templates
|
|
1175
1207
|
};
|
|
1176
1208
|
}
|
|
1177
1209
|
function mergeCiConfig(base, override) {
|
|
@@ -1381,4 +1413,4 @@ export {
|
|
|
1381
1413
|
getProjectRoot,
|
|
1382
1414
|
loadConfigWithOverrides
|
|
1383
1415
|
};
|
|
1384
|
-
//# sourceMappingURL=chunk-
|
|
1416
|
+
//# sourceMappingURL=chunk-ZKFWS3GU.js.map
|