@ankhorage/doctor 0.11.95 → 0.12.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/CHANGELOG.md +14 -0
- package/README.md +1 -1
- package/dist/analysis.js +22 -22
- package/dist/analysis.js.map +1 -1
- package/dist/bunPolicyAnalysis.d.ts.map +1 -1
- package/dist/bunPolicyAnalysis.js +5 -14
- package/dist/bunPolicyAnalysis.js.map +1 -1
- package/dist/cliLayoutAnalysis.d.ts.map +1 -1
- package/dist/cliLayoutAnalysis.js +39 -29
- package/dist/cliLayoutAnalysis.js.map +1 -1
- package/dist/repositorySourceRulesAnalysis.d.ts +16 -0
- package/dist/repositorySourceRulesAnalysis.d.ts.map +1 -0
- package/dist/repositorySourceRulesAnalysis.js +103 -0
- package/dist/repositorySourceRulesAnalysis.js.map +1 -0
- package/dist/sourceArchitectureAnalysis.d.ts +3 -6
- package/dist/sourceArchitectureAnalysis.d.ts.map +1 -1
- package/dist/sourceArchitectureAnalysis.js +93 -97
- package/dist/sourceArchitectureAnalysis.js.map +1 -1
- package/dist/utils/getRepositoryRule.d.ts +9 -0
- package/dist/utils/getRepositoryRule.d.ts.map +1 -0
- package/dist/utils/getRepositoryRule.js +15 -0
- package/dist/utils/getRepositoryRule.js.map +1 -0
- package/package.json +7 -5
- package/src/analysis.ts +22 -24
- package/src/bunPolicyAnalysis.ts +7 -17
- package/src/cliLayoutAnalysis.ts +51 -30
- package/src/repositorySourceRulesAnalysis.ts +153 -0
- package/src/sourceArchitectureAnalysis.ts +102 -177
- package/src/utils/getRepositoryRule.ts +20 -0
- package/dist/utils/getArchitecturePolicyRule.d.ts +0 -286
- package/dist/utils/getArchitecturePolicyRule.d.ts.map +0 -1
- package/dist/utils/getArchitecturePolicyRule.js +0 -12
- package/dist/utils/getArchitecturePolicyRule.js.map +0 -1
- package/src/utils/getArchitecturePolicyRule.ts +0 -15
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bunPolicyAnalysis.js","sourceRoot":"","sources":["../src/bunPolicyAnalysis.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,IAAI,EAAE,EAAE,MAAM,SAAS,CAAC;AACzC,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,OAAO,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;AAKjE,MAAM,UAAU,GAAG,iBAAiB,CAAC,OAAO,CAAC,GAAG,CAAC;AACjD,MAAM,WAAW,GAAG,2DAA2D,CAAC;AAChF,MAAM,mBAAmB,GAAG,wDAAwD,CAAC;AAKrF;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,qBAAqB,CACzC,MAA4B;IAE5B,IAAI,MAAM,CAAC,OAAO,KAAK,gBAAgB,IAAI,CAAC,MAAM,CAAC,cAAc,EAAE,CAAC;QAClE,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,MAAM,eAAe,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,UAAU,EAAE,cAAc,CAAC,CAAC;IACrE,MAAM,WAAW,GAAG,MAAM,eAAe,CAAC,eAAe,CAAC,CAAC;IAC3D,IAAI,WAAW,KAAK,IAAI;QAAE,OAAO,MAAM,CAAC;IAExC,MAAM,WAAW,GAAG;QAClB,GAAG,MAAM,CAAC,WAAW;QACrB,GAAG,uBAAuB,CAAC,WAAW,EAAE,eAAe,EAAE,MAAM,CAAC;QAChE,GAAG,CAAC,MAAM,wBAAwB,CAAC,MAAM,CAAC,CAAC;KAC5C,CAAC;IAEF,OAAO,eAAe,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;AAC9C,CAAC;AAED;;GAEG;AACH,SAAS,uBAAuB,CAC9B,WAAoC,EACpC,eAAuB,EACvB,MAA4B;IAE5B,MAAM,WAAW,GAAuB,EAAE,CAAC;IAC3C,MAAM,EAAE,cAAc,EAAE,GAAG,WAAW,CAAC;IACvC,IAAI,gBAAgB,CAAC,cAAc,CAAC,IAAI,cAAc,CAAC,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC;QAC1E,YAAY,CAAC,WAAW,EAAE;YACxB,MAAM,EAAE,cAAc;YACtB,QAAQ,EAAE,UAAU,CAAC,cAAc;YACnC,QAAQ,EAAE,6BAA6B;YACvC,IAAI,EAAE,eAAe;YACrB,OAAO,EAAE,MAAM,CAAC,OAAO;YACvB,IAAI,EAAE,iBAAiB,CAAC,KAAK,CAAC,cAAc;SAC7C,CAAC,CAAC;IACL,CAAC;IAED,MAAM,eAAe,GAAG,QAAQ,CAAC,WAAW,CAAC,eAAe,CAAC;QAC3D,CAAC,CAAC,WAAW,CAAC,eAAe;QAC7B,CAAC,CAAC,IAAI,CAAC;IACT,MAAM,QAAQ,GAAG,eAAe,EAAE,CAAC,YAAY,CAAC,CAAC;IACjD,IAAI,gBAAgB,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC/B,YAAY,CAAC,WAAW,EAAE;YACxB,MAAM,EAAE,QAAQ;YAChB,QAAQ,EAAE,UAAU,CAAC,UAAU;YAC/B,QAAQ,EAAE,yCAAyC;YACnD,IAAI,EAAE,eAAe;YACrB,OAAO,EAAE,MAAM,CAAC,OAAO;YACvB,IAAI,EAAE,iBAAiB,CAAC,KAAK,CAAC,QAAQ;SACvC,CAAC,CAAC;IACL,CAAC;IAED,OAAO,WAAW,CAAC;AACrB,CAAC;AAED;;GAEG;AACH,KAAK,UAAU,wBAAwB,CAAC,MAA4B;IAClE,MAAM,WAAW,GAAG,MAAM,OAAO,CAAC,GAAG,CACnC,UAAU,CAAC,eAAe,CAAC,GAAG,CAAC,KAAK,EAAE,QAAQ,EAAE,EAAE,CAAC,MAAM,eAAe,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC,CAC5F,CAAC;IACF,OAAO,WAAW,CAAC,IAAI,EAAE,CAAC;AAC5B,CAAC;AAED;;GAEG;AACH,KAAK,UAAU,eAAe,CAC5B,MAA4B,EAC5B,QAAwB;IAExB,MAAM,YAAY,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,UAAU,EAAE,QAAQ,CAAC,IAAI,CAAC,CAAC;IACjE,MAAM,QAAQ,GAAG,MAAM,cAAc,CAAC,YAAY,CAAC,CAAC;IACpD,IAAI,QAAQ,KAAK,IAAI,EAAE,CAAC;QACtB,OAAO,CAAC,wBAAwB,CAAC,MAAM,EAAE,QAAQ,EAAE,YAAY,EAAE,cAAc,CAAC,CAAC,CAAC;IACpF,CAAC;IAED,MAAM,KAAK,GAAG,mBAAmB,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IACjD,MAAM,MAAM,GAAG,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC;IAC1B,IAAI,CAAC,gBAAgB,CAAC,MAAM,CAAC,EAAE,CAAC;QAC9B,OAAO,CAAC,wBAAwB,CAAC,MAAM,EAAE,QAAQ,EAAE,YAAY,EAAE,eAAe,CAAC,CAAC,CAAC;IACrF,CAAC;IAED,MAAM,WAAW,GAAuB,EAAE,CAAC;IAC3C,YAAY,CAAC,WAAW,EAAE;QACxB,MAAM;QACN,QAAQ,EAAE,UAAU,CAAC,OAAO;QAC5B,QAAQ,EAAE,GAAG,QAAQ,CAAC,IAAI,cAAc;QACxC,IAAI,EAAE,YAAY;QAClB,OAAO,EAAE,MAAM,CAAC,OAAO;QACvB,IAAI,EAAE,iBAAiB,CAAC,QAAQ,CAAC,MAAM,CAAC;KACzC,CAAC,CAAC;IACH,OAAO,WAAW,CAAC;AACrB,CAAC;AAED;;GAEG;AACH,SAAS,wBAAwB,CAC/B,MAA4B,EAC5B,QAAwB,EACxB,YAAoB,EACpB,IAA8B;IAE9B,MAAM,MAAM,GACV,IAAI,KAAK,cAAc;QACrB,CAAC,CAAC,gCAAgC,QAAQ,CAAC,IAAI,GAAG;QAClD,CAAC,CAAC,GAAG,QAAQ,CAAC,IAAI,+BAA+B,CAAC;IACtD,MAAM,IAAI,GAAG,iBAAiB,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;IAEhD,OAAO;QACL,IAAI;QACJ,OAAO,EAAE,GAAG,MAAM,iBAAiB,UAAU,CAAC,OAAO,KAAK,WAAW,EAAE;QACvE,IAAI,EAAE,YAAY;QAClB,OAAO,EAAE,MAAM,CAAC,OAAO;QACvB,MAAM,EAAE,IAAI,CAAC,EAAE;QACf,QAAQ,EAAE,IAAI,CAAC,QAAQ;KACxB,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,SAAS,YAAY,CACnB,WAA+B,EAC/B,KAOC;IAED,IAAI,KAAK,CAAC,MAAM,KAAK,KAAK,CAAC,QAAQ;QAAE,OAAO;IAE5C,WAAW,CAAC,IAAI,CAAC;QACf,IAAI,EAAE,eAAe;QACrB,OAAO,EAAE,uBAAuB,KAAK,CAAC,QAAQ,eAAe,KAAK,CAAC,QAAQ,aAAa,KAAK,CAAC,MAAM,MAAM,WAAW,EAAE;QACvH,IAAI,EAAE,KAAK,CAAC,IAAI;QAChB,OAAO,EAAE,KAAK,CAAC,OAAO;QACtB,MAAM,EAAE,KAAK,CAAC,IAAI,CAAC,EAAE;QACrB,QAAQ,EAAE,KAAK,CAAC,IAAI,CAAC,QAAQ;KAC9B,CAAC,CAAC;AACL,CAAC;AAED;;GAEG;AACH,SAAS,iBAAiB,CAAC,MAAc;IACvC,MAAM,IAAI,GAAG,MAAM,CAAC,MAAM,CAAC,iBAAiB,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,EAAE,KAAK,MAAM,CAAC,CAAC;IACzF,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;QACvB,MAAM,IAAI,KAAK,CAAC,mCAAmC,MAAM,EAAE,CAAC,CAAC;IAC/D,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;GAEG;AACH,SAAS,eAAe,CACtB,MAA4B,EAC5B,WAAwC;IAExC,OAAO;QACL,GAAG,MAAM;QACT,WAAW;QACX,OAAO,EAAE,MAAM,CAAC,OAAO,KAAK,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,GAAG,MAAM,CAAC,OAAO,EAAE,WAAW,EAAE;KAC7E,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,KAAK,UAAU,eAAe,CAAC,eAAuB;IACpD,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC,CAAY,CAAC;QACjF,OAAO,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC;IAC1C,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED;;GAEG;AACH,KAAK,UAAU,cAAc,CAAC,QAAgB;IAC5C,IAAI,CAAC;QACH,OAAO,MAAM,EAAE,CAAC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;IAC7C,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,WAAW,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,IAAI,KAAK,QAAQ;YAAE,OAAO,IAAI,CAAC;QAC/D,MAAM,KAAK,CAAC;IACd,CAAC;AACH,CAAC;AAED;;GAEG;AACH,SAAS,QAAQ,CAAC,KAAc;IAC9B,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;AAC9E,CAAC;AAED;;GAEG;AACH,SAAS,gBAAgB,CAAC,KAAc;IACtC,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,IAAI,EAAE,KAAK,EAAE,CAAC;AAC1D,CAAC;AAED;;GAEG;AACH,SAAS,WAAW,CAAC,KAAc;IACjC,OAAO,KAAK,YAAY,KAAK,IAAI,MAAM,IAAI,KAAK,CAAC;AACnD,CAAC","sourcesContent":["import { promises as fs } from 'node:fs';\nimport path from 'node:path';\n\nimport { REPOSITORY_POLICY } from '@ankhorage/policy/repository';\n\nimport type { DoctorAnalysisResult } from './analysis.js';\nimport type { DoctorDiagnostic } from './diagnostics.js';\n\nconst BUN_POLICY = REPOSITORY_POLICY.runtime.bun;\nconst REPAIR_HINT = 'Run \"ankh devtools sync\" to repair the managed Bun state.';\nconst BUN_VERSION_PATTERN = /^\\s*bun-version:\\s*['\"]?([^'\"\\s#]+)['\"]?\\s*(?:#.*)?$/mu;\n\ntype RepositoryPolicyRule = (typeof REPOSITORY_POLICY.rules)[keyof typeof REPOSITORY_POLICY.rules];\ntype WorkflowTarget = (typeof BUN_POLICY.workflowTargets)[number];\n\n/***\n * Applies canonical repository Bun-policy diagnostics to a Doctor analysis result.\n */\nexport async function applyBunRuntimePolicy(\n result: DoctorAnalysisResult,\n): Promise<DoctorAnalysisResult> {\n if (result.profile !== 'public-package' || !result.hasPackageJson) {\n return result;\n }\n\n const packageJsonPath = path.join(result.targetPath, 'package.json');\n const packageJson = await readPackageJson(packageJsonPath);\n if (packageJson === null) return result;\n\n const diagnostics = [\n ...result.diagnostics,\n ...analyzePackageBunPolicy(packageJson, packageJsonPath, result),\n ...(await analyzeWorkflowBunPolicy(result)),\n ];\n\n return withDiagnostics(result, diagnostics);\n}\n\n/***\n * Reports package-manifest drift from the canonical Bun runtime contract.\n */\nfunction analyzePackageBunPolicy(\n packageJson: Record<string, unknown>,\n packageJsonPath: string,\n result: DoctorAnalysisResult,\n): DoctorDiagnostic[] {\n const diagnostics: DoctorDiagnostic[] = [];\n const { packageManager } = packageJson;\n if (isNonEmptyString(packageManager) && packageManager.startsWith('bun@')) {\n pushMismatch(diagnostics, {\n actual: packageManager,\n expected: BUN_POLICY.packageManager,\n location: 'package.json#packageManager',\n path: packageJsonPath,\n profile: result.profile,\n rule: REPOSITORY_POLICY.rules.packageManager,\n });\n }\n\n const devDependencies = isRecord(packageJson.devDependencies)\n ? packageJson.devDependencies\n : null;\n const bunTypes = devDependencies?.['@types/bun'];\n if (isNonEmptyString(bunTypes)) {\n pushMismatch(diagnostics, {\n actual: bunTypes,\n expected: BUN_POLICY.typesRange,\n location: 'package.json#devDependencies.@types/bun',\n path: packageJsonPath,\n profile: result.profile,\n rule: REPOSITORY_POLICY.rules.bunTypes,\n });\n }\n\n return diagnostics;\n}\n\n/***\n * Reports managed workflow drift from the canonical Bun setup version.\n */\nasync function analyzeWorkflowBunPolicy(result: DoctorAnalysisResult): Promise<DoctorDiagnostic[]> {\n const diagnostics = await Promise.all(\n BUN_POLICY.workflowTargets.map(async (workflow) => await analyzeWorkflow(result, workflow)),\n );\n return diagnostics.flat();\n}\n\n/***\n * Checks one managed workflow for the canonical Bun setup version.\n */\nasync function analyzeWorkflow(\n result: DoctorAnalysisResult,\n workflow: WorkflowTarget,\n): Promise<DoctorDiagnostic[]> {\n const workflowPath = path.join(result.targetPath, workflow.path);\n const contents = await readTextOrNull(workflowPath);\n if (contents === null) {\n return [createWorkflowDiagnostic(result, workflow, workflowPath, 'missing-path')];\n }\n\n const match = BUN_VERSION_PATTERN.exec(contents);\n const actual = match?.[1];\n if (!isNonEmptyString(actual)) {\n return [createWorkflowDiagnostic(result, workflow, workflowPath, 'field-missing')];\n }\n\n const diagnostics: DoctorDiagnostic[] = [];\n pushMismatch(diagnostics, {\n actual,\n expected: BUN_POLICY.version,\n location: `${workflow.path}#bun-version`,\n path: workflowPath,\n profile: result.profile,\n rule: getRepositoryRule(workflow.ruleId),\n });\n return diagnostics;\n}\n\n/***\n * Creates a missing or malformed managed-workflow diagnostic from central rule metadata.\n */\nfunction createWorkflowDiagnostic(\n result: DoctorAnalysisResult,\n workflow: WorkflowTarget,\n workflowPath: string,\n code: DoctorDiagnostic['code'],\n): DoctorDiagnostic {\n const detail =\n code === 'missing-path'\n ? `Managed workflow is missing: ${workflow.path}.`\n : `${workflow.path} does not define bun-version.`;\n const rule = getRepositoryRule(workflow.ruleId);\n\n return {\n code,\n message: `${detail} Expected Bun ${BUN_POLICY.version}. ${REPAIR_HINT}`,\n path: workflowPath,\n profile: result.profile,\n ruleId: rule.id,\n severity: rule.severity,\n };\n}\n\n/***\n * Adds one value-mismatch diagnostic using the owning Policy rule metadata.\n */\nfunction pushMismatch(\n diagnostics: DoctorDiagnostic[],\n input: {\n readonly actual: string;\n readonly expected: string;\n readonly location: string;\n readonly path: string;\n readonly profile: DoctorAnalysisResult['profile'];\n readonly rule: RepositoryPolicyRule;\n },\n): void {\n if (input.actual === input.expected) return;\n\n diagnostics.push({\n code: 'field-invalid',\n message: `Bun policy drift at ${input.location}: expected \"${input.expected}\", found \"${input.actual}\". ${REPAIR_HINT}`,\n path: input.path,\n profile: input.profile,\n ruleId: input.rule.id,\n severity: input.rule.severity,\n });\n}\n\n/***\n * Resolves one stable repository rule by its Policy-owned id.\n */\nfunction getRepositoryRule(ruleId: string): RepositoryPolicyRule {\n const rule = Object.values(REPOSITORY_POLICY.rules).find((entry) => entry.id === ruleId);\n if (rule === undefined) {\n throw new Error(`Unknown repository policy rule: ${ruleId}`);\n }\n return rule;\n}\n\n/***\n * Copies updated diagnostics into the analysis result and optional fix plan.\n */\nfunction withDiagnostics(\n result: DoctorAnalysisResult,\n diagnostics: readonly DoctorDiagnostic[],\n): DoctorAnalysisResult {\n return {\n ...result,\n diagnostics,\n fixPlan: result.fixPlan === null ? null : { ...result.fixPlan, diagnostics },\n };\n}\n\n/***\n * Reads a package manifest when it contains a JSON object.\n */\nasync function readPackageJson(packageJsonPath: string): Promise<Record<string, unknown> | null> {\n try {\n const parsed = JSON.parse(await fs.readFile(packageJsonPath, 'utf8')) as unknown;\n return isRecord(parsed) ? parsed : null;\n } catch {\n return null;\n }\n}\n\n/***\n * Reads a UTF-8 file while treating a missing managed path as absent.\n */\nasync function readTextOrNull(filePath: string): Promise<string | null> {\n try {\n return await fs.readFile(filePath, 'utf8');\n } catch (error) {\n if (isNodeError(error) && error.code === 'ENOENT') return null;\n throw error;\n }\n}\n\n/***\n * Narrows unknown JSON data to a non-array record.\n */\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\n/***\n * Narrows a value to a non-empty string.\n */\nfunction isNonEmptyString(value: unknown): value is string {\n return typeof value === 'string' && value.trim() !== '';\n}\n\n/***\n * Narrows unknown failures to Node errors with stable error codes.\n */\nfunction isNodeError(error: unknown): error is NodeJS.ErrnoException {\n return error instanceof Error && 'code' in error;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"bunPolicyAnalysis.js","sourceRoot":"","sources":["../src/bunPolicyAnalysis.ts"],"names":[],"mappings":"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","sourcesContent":["import { promises as fs } from 'node:fs';\nimport path from 'node:path';\n\nimport { REPOSITORY_RULE_IDS, REPOSITORY_RULE_METADATA } from '@ankhorage/rules-repository';\n\nimport type { DoctorAnalysisResult } from './analysis.js';\nimport type { DoctorDiagnostic } from './diagnostics.js';\nimport { getRepositoryRule } from './utils/getRepositoryRule.js';\n\nconst BUN_POLICY = REPOSITORY_RULE_METADATA.runtime.bun;\nconst REPAIR_HINT = 'Run \"ankh devtools sync\" to repair the managed Bun state.';\nconst BUN_VERSION_PATTERN = /^\\s*bun-version:\\s*['\"]?([^'\"\\s#]+)['\"]?\\s*(?:#.*)?$/mu;\n\ntype RepositoryRule = ReturnType<typeof getRepositoryRule>;\ntype WorkflowTarget = (typeof BUN_POLICY.workflowTargets)[number];\n\n/***\n * Applies canonical repository Bun-policy diagnostics to a Doctor analysis result.\n */\nexport async function applyBunRuntimePolicy(\n result: DoctorAnalysisResult,\n): Promise<DoctorAnalysisResult> {\n if (result.profile !== 'public-package' || !result.hasPackageJson) {\n return result;\n }\n\n const packageJsonPath = path.join(result.targetPath, 'package.json');\n const packageJson = await readPackageJson(packageJsonPath);\n if (packageJson === null) return result;\n\n const diagnostics = [\n ...result.diagnostics,\n ...analyzePackageBunPolicy(packageJson, packageJsonPath, result),\n ...(await analyzeWorkflowBunPolicy(result)),\n ];\n\n return withDiagnostics(result, diagnostics);\n}\n\n/***\n * Reports package-manifest drift from the canonical Bun runtime contract.\n */\nfunction analyzePackageBunPolicy(\n packageJson: Record<string, unknown>,\n packageJsonPath: string,\n result: DoctorAnalysisResult,\n): DoctorDiagnostic[] {\n const diagnostics: DoctorDiagnostic[] = [];\n const { packageManager } = packageJson;\n if (isNonEmptyString(packageManager) && packageManager.startsWith('bun@')) {\n pushMismatch(diagnostics, {\n actual: packageManager,\n expected: BUN_POLICY.packageManager,\n location: 'package.json#packageManager',\n path: packageJsonPath,\n profile: result.profile,\n rule: getRepositoryRule(REPOSITORY_RULE_IDS.bunPackageManager),\n });\n }\n\n const devDependencies = isRecord(packageJson.devDependencies)\n ? packageJson.devDependencies\n : null;\n const bunTypes = devDependencies?.['@types/bun'];\n if (isNonEmptyString(bunTypes)) {\n pushMismatch(diagnostics, {\n actual: bunTypes,\n expected: BUN_POLICY.typesRange,\n location: 'package.json#devDependencies.@types/bun',\n path: packageJsonPath,\n profile: result.profile,\n rule: getRepositoryRule(REPOSITORY_RULE_IDS.bunTypes),\n });\n }\n\n return diagnostics;\n}\n\n/***\n * Reports managed workflow drift from the canonical Bun setup version.\n */\nasync function analyzeWorkflowBunPolicy(result: DoctorAnalysisResult): Promise<DoctorDiagnostic[]> {\n const diagnostics = await Promise.all(\n BUN_POLICY.workflowTargets.map(async (workflow) => await analyzeWorkflow(result, workflow)),\n );\n return diagnostics.flat();\n}\n\n/***\n * Checks one managed workflow for the canonical Bun setup version.\n */\nasync function analyzeWorkflow(\n result: DoctorAnalysisResult,\n workflow: WorkflowTarget,\n): Promise<DoctorDiagnostic[]> {\n const workflowPath = path.join(result.targetPath, workflow.path);\n const contents = await readTextOrNull(workflowPath);\n if (contents === null) {\n return [createWorkflowDiagnostic(result, workflow, workflowPath, 'missing-path')];\n }\n\n const match = BUN_VERSION_PATTERN.exec(contents);\n const actual = match?.[1];\n if (!isNonEmptyString(actual)) {\n return [createWorkflowDiagnostic(result, workflow, workflowPath, 'field-missing')];\n }\n\n const diagnostics: DoctorDiagnostic[] = [];\n pushMismatch(diagnostics, {\n actual,\n expected: BUN_POLICY.version,\n location: `${workflow.path}#bun-version`,\n path: workflowPath,\n profile: result.profile,\n rule: getRepositoryRule(workflow.ruleId),\n });\n return diagnostics;\n}\n\n/***\n * Creates a missing or malformed managed-workflow diagnostic from central rule metadata.\n */\nfunction createWorkflowDiagnostic(\n result: DoctorAnalysisResult,\n workflow: WorkflowTarget,\n workflowPath: string,\n code: DoctorDiagnostic['code'],\n): DoctorDiagnostic {\n const detail =\n code === 'missing-path'\n ? `Managed workflow is missing: ${workflow.path}.`\n : `${workflow.path} does not define bun-version.`;\n const rule = getRepositoryRule(workflow.ruleId);\n\n return {\n code,\n message: `${detail} Expected Bun ${BUN_POLICY.version}. ${REPAIR_HINT}`,\n path: workflowPath,\n profile: result.profile,\n ruleId: rule.id,\n severity: rule.severity,\n };\n}\n\n/***\n * Adds one value-mismatch diagnostic using the owning Policy rule metadata.\n */\nfunction pushMismatch(\n diagnostics: DoctorDiagnostic[],\n input: {\n readonly actual: string;\n readonly expected: string;\n readonly location: string;\n readonly path: string;\n readonly profile: DoctorAnalysisResult['profile'];\n readonly rule: RepositoryRule;\n },\n): void {\n if (input.actual === input.expected) return;\n\n diagnostics.push({\n code: 'field-invalid',\n message: `Bun policy drift at ${input.location}: expected \"${input.expected}\", found \"${input.actual}\". ${REPAIR_HINT}`,\n path: input.path,\n profile: input.profile,\n ruleId: input.rule.id,\n severity: input.rule.severity,\n });\n}\n\n/***\n * Copies updated diagnostics into the analysis result and optional fix plan.\n */\nfunction withDiagnostics(\n result: DoctorAnalysisResult,\n diagnostics: readonly DoctorDiagnostic[],\n): DoctorAnalysisResult {\n return {\n ...result,\n diagnostics,\n fixPlan: result.fixPlan === null ? null : { ...result.fixPlan, diagnostics },\n };\n}\n\n/***\n * Reads a package manifest when it contains a JSON object.\n */\nasync function readPackageJson(packageJsonPath: string): Promise<Record<string, unknown> | null> {\n try {\n const parsed = JSON.parse(await fs.readFile(packageJsonPath, 'utf8')) as unknown;\n return isRecord(parsed) ? parsed : null;\n } catch {\n return null;\n }\n}\n\n/***\n * Reads a UTF-8 file while treating a missing managed path as absent.\n */\nasync function readTextOrNull(filePath: string): Promise<string | null> {\n try {\n return await fs.readFile(filePath, 'utf8');\n } catch (error) {\n if (isNodeError(error) && error.code === 'ENOENT') return null;\n throw error;\n }\n}\n\n/***\n * Narrows unknown JSON data to a non-array record.\n */\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\n/***\n * Narrows a value to a non-empty string.\n */\nfunction isNonEmptyString(value: unknown): value is string {\n return typeof value === 'string' && value.trim() !== '';\n}\n\n/***\n * Narrows unknown failures to Node errors with stable error codes.\n */\nfunction isNodeError(error: unknown): error is NodeJS.ErrnoException {\n return error instanceof Error && 'code' in error;\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cliLayoutAnalysis.d.ts","sourceRoot":"","sources":["../src/cliLayoutAnalysis.ts"],"names":[],"mappings":"AAKA,OAAO,EAEL,KAAK,qBAAqB,EAC1B,KAAK,oBAAoB,EAC1B,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"cliLayoutAnalysis.d.ts","sourceRoot":"","sources":["../src/cliLayoutAnalysis.ts"],"names":[],"mappings":"AAKA,OAAO,EAEL,KAAK,qBAAqB,EAC1B,KAAK,oBAAoB,EAC1B,MAAM,eAAe,CAAC;AAyDvB,wBAAsB,gCAAgC,CACpD,OAAO,EAAE,qBAAqB,GAC7B,OAAO,CAAC,oBAAoB,CAAC,CAkC/B"}
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import { promises as fs } from 'node:fs';
|
|
2
2
|
import path from 'node:path';
|
|
3
|
-
import {
|
|
3
|
+
import { REPOSITORY_RULE_IDS, REPOSITORY_RULE_METADATA } from '@ankhorage/rules-repository';
|
|
4
4
|
import { analyzeDoctorTarget, } from './analysis.js';
|
|
5
5
|
import { analyzeAppManifestTarget } from './manifestAnalysis.js';
|
|
6
|
+
import { analyzeRepositorySourceRules } from './repositorySourceRulesAnalysis.js';
|
|
6
7
|
import { analyzeSourceArchitecture } from './sourceArchitectureAnalysis.js';
|
|
7
|
-
import {
|
|
8
|
-
const CLI_POLICY =
|
|
9
|
-
const DEPENDENCY_POLICY =
|
|
8
|
+
import { getRepositoryRule } from './utils/getRepositoryRule.js';
|
|
9
|
+
const CLI_POLICY = REPOSITORY_RULE_METADATA.cli;
|
|
10
|
+
const DEPENDENCY_POLICY = REPOSITORY_RULE_METADATA.dependencies;
|
|
10
11
|
const ACTIVE_SOURCE_ROOTS = ['src', 'app', 'apps', 'packages', 'scripts'];
|
|
11
12
|
const ACTIVE_SOURCE_EXTENSIONS = new Set([
|
|
12
13
|
'.cjs',
|
|
@@ -84,7 +85,7 @@ async function analyzeTargetArchitecture(request) {
|
|
|
84
85
|
message: 'Package CLI code must live under src/cli/; root src/cli.ts is not allowed.',
|
|
85
86
|
path: legacyRootCliPath,
|
|
86
87
|
profile: request.profile,
|
|
87
|
-
...
|
|
88
|
+
...repositoryRuleFields(REPOSITORY_RULE_IDS.cliRootFile),
|
|
88
89
|
});
|
|
89
90
|
}
|
|
90
91
|
if (cliCapable && exportsField?.[CLI_POLICY.packageExport] === undefined) {
|
|
@@ -93,7 +94,7 @@ async function analyzeTargetArchitecture(request) {
|
|
|
93
94
|
message: 'CLI-capable packages must export "./cli" from package.json and point it at the metadata-declared provider build output.',
|
|
94
95
|
path: request.packageJsonPath,
|
|
95
96
|
profile: request.profile,
|
|
96
|
-
...
|
|
97
|
+
...repositoryRuleFields(REPOSITORY_RULE_IDS.cliExport),
|
|
97
98
|
});
|
|
98
99
|
}
|
|
99
100
|
const dependencyEntries = collectDependencyEntries(request.packageJson);
|
|
@@ -102,19 +103,24 @@ async function analyzeTargetArchitecture(request) {
|
|
|
102
103
|
packageJsonPath: request.packageJsonPath,
|
|
103
104
|
profile: request.profile,
|
|
104
105
|
}));
|
|
105
|
-
const
|
|
106
|
+
const activeSource = await collectActiveSourceSnapshot(request.targetPath);
|
|
106
107
|
diagnostics.push(...validateActiveSourceImports({
|
|
107
|
-
activeSourceImports,
|
|
108
|
+
activeSourceImports: activeSource.imports,
|
|
108
109
|
profile: request.profile,
|
|
109
110
|
}));
|
|
111
|
+
diagnostics.push(...analyzeRepositorySourceRules({
|
|
112
|
+
files: activeSource.files,
|
|
113
|
+
imports: activeSource.imports,
|
|
114
|
+
profile: request.profile,
|
|
115
|
+
targetPath: request.targetPath,
|
|
116
|
+
}));
|
|
110
117
|
diagnostics.push(...(await analyzeSourceArchitecture({
|
|
111
|
-
activeSourceImports,
|
|
112
118
|
profile: request.profile,
|
|
113
119
|
targetPath: request.targetPath,
|
|
114
120
|
})));
|
|
115
121
|
if (request.packageJson.name === STUDIO_PACKAGE_NAME) {
|
|
116
122
|
diagnostics.push(...validateStudioOwnership({
|
|
117
|
-
activeSourceImports,
|
|
123
|
+
activeSourceImports: activeSource.imports,
|
|
118
124
|
dependencyEntries,
|
|
119
125
|
packageJsonPath: request.packageJsonPath,
|
|
120
126
|
profile: request.profile,
|
|
@@ -131,7 +137,7 @@ function validateDependencyArchitecture(request) {
|
|
|
131
137
|
message: `Dependency "${dependency.packageName}" in package.json.${dependency.fieldName} is an old ankhorage4 workspace alias. Depend on "${toOwningPackageSpecifier(dependency.packageName)}" directly.`,
|
|
132
138
|
path: request.packageJsonPath,
|
|
133
139
|
profile: request.profile,
|
|
134
|
-
...
|
|
140
|
+
...repositoryRuleFields(REPOSITORY_RULE_IDS.compatibilityDependency),
|
|
135
141
|
});
|
|
136
142
|
}
|
|
137
143
|
if (request.profile === 'public-package' &&
|
|
@@ -141,7 +147,7 @@ function validateDependencyArchitecture(request) {
|
|
|
141
147
|
message: `Published dependency "${dependency.packageName}" must resolve from a registry version, not "${dependency.version}". Standalone packages cannot rely on local/workspace/Git package state.`,
|
|
142
148
|
path: request.packageJsonPath,
|
|
143
149
|
profile: request.profile,
|
|
144
|
-
...
|
|
150
|
+
...repositoryRuleFields(REPOSITORY_RULE_IDS.localProtocolDependency),
|
|
145
151
|
});
|
|
146
152
|
}
|
|
147
153
|
if (dependency.packageName.includes(DEPENDENCY_POLICY.legacySourceMarker) ||
|
|
@@ -151,7 +157,7 @@ function validateDependencyArchitecture(request) {
|
|
|
151
157
|
message: `Dependency "${dependency.packageName}" references ankhorage4 as active source. Depend on the extracted owning @ankhorage/* package instead.`,
|
|
152
158
|
path: request.packageJsonPath,
|
|
153
159
|
profile: request.profile,
|
|
154
|
-
...
|
|
160
|
+
...repositoryRuleFields(REPOSITORY_RULE_IDS.legacySourceDependency),
|
|
155
161
|
});
|
|
156
162
|
}
|
|
157
163
|
}
|
|
@@ -166,7 +172,7 @@ function validateActiveSourceImports(request) {
|
|
|
166
172
|
message: `Import "${sourceImport.specifier}" is an old ankhorage4 compatibility boundary. Import "${toOwningPackageSpecifier(sourceImport.specifier)}" directly from the owning package.`,
|
|
167
173
|
path: sourceImport.filePath,
|
|
168
174
|
profile: request.profile,
|
|
169
|
-
...
|
|
175
|
+
...repositoryRuleFields(REPOSITORY_RULE_IDS.compatibilityImport),
|
|
170
176
|
});
|
|
171
177
|
}
|
|
172
178
|
if (sourceImport.specifier.includes(DEPENDENCY_POLICY.legacySourceMarker)) {
|
|
@@ -175,7 +181,7 @@ function validateActiveSourceImports(request) {
|
|
|
175
181
|
message: `Import "${sourceImport.specifier}" references ankhorage4 as active source. Import the extracted owning @ankhorage/* package instead.`,
|
|
176
182
|
path: sourceImport.filePath,
|
|
177
183
|
profile: request.profile,
|
|
178
|
-
...
|
|
184
|
+
...repositoryRuleFields(REPOSITORY_RULE_IDS.legacySourceImport),
|
|
179
185
|
});
|
|
180
186
|
}
|
|
181
187
|
}
|
|
@@ -229,21 +235,25 @@ function validateStudioOwnership(request) {
|
|
|
229
235
|
}
|
|
230
236
|
return diagnostics;
|
|
231
237
|
}
|
|
232
|
-
|
|
233
|
-
|
|
238
|
+
/***
|
|
239
|
+
* Collects active production files and their static import specifiers once for repository checks.
|
|
240
|
+
*/
|
|
241
|
+
async function collectActiveSourceSnapshot(targetPath) {
|
|
242
|
+
const files = [];
|
|
234
243
|
for (const sourceRoot of ACTIVE_SOURCE_ROOTS) {
|
|
235
244
|
const sourceRootPath = path.join(targetPath, sourceRoot);
|
|
236
|
-
if (
|
|
237
|
-
|
|
238
|
-
}
|
|
239
|
-
for (const filePath of await listActiveSourceFiles(sourceRootPath)) {
|
|
240
|
-
const source = await fs.readFile(filePath, 'utf8');
|
|
241
|
-
for (const specifier of extractImportSpecifiers(source)) {
|
|
242
|
-
imports.push({ filePath, specifier });
|
|
243
|
-
}
|
|
245
|
+
if (await pathExists(sourceRootPath)) {
|
|
246
|
+
files.push(...(await listActiveSourceFiles(sourceRootPath)));
|
|
244
247
|
}
|
|
245
248
|
}
|
|
246
|
-
|
|
249
|
+
const imports = (await Promise.all(files.map(async (filePath) => {
|
|
250
|
+
const source = await fs.readFile(filePath, 'utf8');
|
|
251
|
+
return extractImportSpecifiers(source).map((specifier) => ({ filePath, specifier }));
|
|
252
|
+
}))).flat();
|
|
253
|
+
return {
|
|
254
|
+
files: [...files].sort((left, right) => left.localeCompare(right)),
|
|
255
|
+
imports,
|
|
256
|
+
};
|
|
247
257
|
}
|
|
248
258
|
async function listActiveSourceFiles(rootPath) {
|
|
249
259
|
const files = [];
|
|
@@ -317,9 +327,9 @@ function hasAnkhProvider(packageJson) {
|
|
|
317
327
|
function isPackageImport(specifier, packageName) {
|
|
318
328
|
return specifier === packageName || specifier.startsWith(`${packageName}/`);
|
|
319
329
|
}
|
|
320
|
-
/*** Resolve
|
|
321
|
-
function
|
|
322
|
-
const rule =
|
|
330
|
+
/*** Resolve repository Rules metadata for package and source-boundary diagnostics. */
|
|
331
|
+
function repositoryRuleFields(ruleId) {
|
|
332
|
+
const rule = getRepositoryRule(ruleId);
|
|
323
333
|
return { ruleId: rule.id, severity: rule.severity };
|
|
324
334
|
}
|
|
325
335
|
function toOwningPackageSpecifier(specifier) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cliLayoutAnalysis.js","sourceRoot":"","sources":["../src/cliLayoutAnalysis.ts"],"names":[],"mappings":"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","sourcesContent":["import { promises as fs } from 'node:fs';\nimport path from 'node:path';\n\nimport { ARCHITECTURE_POLICY } from '@ankhorage/policy/architecture';\n\nimport {\n analyzeDoctorTarget,\n type DoctorAnalysisRequest,\n type DoctorAnalysisResult,\n} from './analysis.js';\nimport type { DoctorDiagnostic, DoctorPolicyProfile } from './diagnostics.js';\nimport { analyzeAppManifestTarget } from './manifestAnalysis.js';\nimport { analyzeSourceArchitecture } from './sourceArchitectureAnalysis.js';\nimport { getArchitecturePolicyRule } from './utils/getArchitecturePolicyRule.js';\n\nconst CLI_POLICY = ARCHITECTURE_POLICY.cli;\nconst DEPENDENCY_POLICY = ARCHITECTURE_POLICY.dependencies;\nconst ACTIVE_SOURCE_ROOTS = ['src', 'app', 'apps', 'packages', 'scripts'] as const;\nconst ACTIVE_SOURCE_EXTENSIONS = new Set([\n '.cjs',\n '.cts',\n '.js',\n '.jsx',\n '.mjs',\n '.mts',\n '.ts',\n '.tsx',\n]);\nconst IGNORED_SOURCE_DIRECTORIES = new Set([\n '.expo',\n '.git',\n '.next',\n '__fixtures__',\n '__tests__',\n 'build',\n 'coverage',\n 'dist',\n 'docs',\n 'fixtures',\n 'node_modules',\n 'paradox',\n 'test',\n 'tests',\n]);\nconst STUDIO_PACKAGE_NAME = '@ankhorage/studio';\nconst STUDIO_DND_PACKAGE_NAME = '@ankhorage/react-native-reanimated-dnd-web';\nconst STUDIO_RUNTIME_PACKAGE_NAME = '@ankhorage/runtime';\nconst DEPENDENCY_FIELD_NAMES = [\n 'dependencies',\n 'devDependencies',\n 'optionalDependencies',\n 'peerDependencies',\n] as const;\n\ninterface ActiveSourceImport {\n readonly filePath: string;\n readonly specifier: string;\n}\n\ninterface DependencyEntry {\n readonly fieldName: (typeof DEPENDENCY_FIELD_NAMES)[number];\n readonly packageName: string;\n readonly version: string;\n}\n\nexport async function analyzeDoctorTargetWithCliLayout(\n request: DoctorAnalysisRequest,\n): Promise<DoctorAnalysisResult> {\n const manifestResult = await analyzeAppManifestTarget(request);\n if (manifestResult !== null) {\n return manifestResult;\n }\n\n const result = await analyzeDoctorTarget(request);\n if (!result.hasPackageJson) {\n return result;\n }\n\n const packageJsonPath = path.join(result.targetPath, 'package.json');\n const packageJson = await readPackageJson(packageJsonPath);\n if (packageJson === null) {\n return result;\n }\n\n const architectureDiagnostics = await analyzeTargetArchitecture({\n packageJson,\n packageJsonPath,\n profile: result.profile,\n targetPath: result.targetPath,\n });\n if (architectureDiagnostics.length === 0) {\n return result;\n }\n\n const diagnostics = [...result.diagnostics, ...architectureDiagnostics];\n\n return {\n ...result,\n diagnostics,\n fixPlan: result.fixPlan === null ? null : { ...result.fixPlan, diagnostics },\n };\n}\n\nasync function analyzeTargetArchitecture(request: {\n readonly packageJson: Record<string, unknown>;\n readonly packageJsonPath: string;\n readonly profile: DoctorPolicyProfile;\n readonly targetPath: string;\n}): Promise<DoctorDiagnostic[]> {\n const diagnostics: DoctorDiagnostic[] = [];\n const legacyRootCliPath = path.join(request.targetPath, CLI_POLICY.legacyRootFile);\n const exportsField = isRecord(request.packageJson.exports) ? request.packageJson.exports : null;\n const cliCapable =\n hasAnkhProvider(request.packageJson) || exportsField?.[CLI_POLICY.packageExport] !== undefined;\n\n if (await pathExists(legacyRootCliPath)) {\n diagnostics.push({\n code: 'field-invalid',\n message: 'Package CLI code must live under src/cli/; root src/cli.ts is not allowed.',\n path: legacyRootCliPath,\n profile: request.profile,\n ...architectureRuleFields(CLI_POLICY.legacyRootRuleId),\n });\n }\n\n if (cliCapable && exportsField?.[CLI_POLICY.packageExport] === undefined) {\n diagnostics.push({\n code: 'field-missing',\n message:\n 'CLI-capable packages must export \"./cli\" from package.json and point it at the metadata-declared provider build output.',\n path: request.packageJsonPath,\n profile: request.profile,\n ...architectureRuleFields(CLI_POLICY.exportRuleId),\n });\n }\n\n const dependencyEntries = collectDependencyEntries(request.packageJson);\n diagnostics.push(\n ...validateDependencyArchitecture({\n dependencyEntries,\n packageJsonPath: request.packageJsonPath,\n profile: request.profile,\n }),\n );\n\n const activeSourceImports = await collectActiveSourceImports(request.targetPath);\n diagnostics.push(\n ...validateActiveSourceImports({\n activeSourceImports,\n profile: request.profile,\n }),\n );\n diagnostics.push(\n ...(await analyzeSourceArchitecture({\n activeSourceImports,\n profile: request.profile,\n targetPath: request.targetPath,\n })),\n );\n\n if (request.packageJson.name === STUDIO_PACKAGE_NAME) {\n diagnostics.push(\n ...validateStudioOwnership({\n activeSourceImports,\n dependencyEntries,\n packageJsonPath: request.packageJsonPath,\n profile: request.profile,\n }),\n );\n }\n\n return diagnostics;\n}\n\nfunction validateDependencyArchitecture(request: {\n readonly dependencyEntries: readonly DependencyEntry[];\n readonly packageJsonPath: string;\n readonly profile: DoctorPolicyProfile;\n}): DoctorDiagnostic[] {\n const diagnostics: DoctorDiagnostic[] = [];\n\n for (const dependency of request.dependencyEntries) {\n if (dependency.packageName.startsWith(DEPENDENCY_POLICY.compatibilityPackagePrefix)) {\n diagnostics.push({\n code: 'field-invalid',\n message: `Dependency \"${dependency.packageName}\" in package.json.${dependency.fieldName} is an old ankhorage4 workspace alias. Depend on \"${toOwningPackageSpecifier(dependency.packageName)}\" directly.`,\n path: request.packageJsonPath,\n profile: request.profile,\n ...architectureRuleFields(DEPENDENCY_POLICY.rules.compatibilityDependency),\n });\n }\n\n if (\n request.profile === 'public-package' &&\n DEPENDENCY_POLICY.localProtocolPrefixes.some((prefix) =>\n dependency.version.startsWith(prefix),\n )\n ) {\n diagnostics.push({\n code: 'field-invalid',\n message: `Published dependency \"${dependency.packageName}\" must resolve from a registry version, not \"${dependency.version}\". Standalone packages cannot rely on local/workspace/Git package state.`,\n path: request.packageJsonPath,\n profile: request.profile,\n ...architectureRuleFields(DEPENDENCY_POLICY.rules.localProtocolDependency),\n });\n }\n\n if (\n dependency.packageName.includes(DEPENDENCY_POLICY.legacySourceMarker) ||\n dependency.version.includes(DEPENDENCY_POLICY.legacySourceMarker)\n ) {\n diagnostics.push({\n code: 'field-invalid',\n message: `Dependency \"${dependency.packageName}\" references ankhorage4 as active source. Depend on the extracted owning @ankhorage/* package instead.`,\n path: request.packageJsonPath,\n profile: request.profile,\n ...architectureRuleFields(DEPENDENCY_POLICY.rules.legacySourceDependency),\n });\n }\n }\n\n return diagnostics;\n}\n\nfunction validateActiveSourceImports(request: {\n readonly activeSourceImports: readonly ActiveSourceImport[];\n readonly profile: DoctorPolicyProfile;\n}): DoctorDiagnostic[] {\n const diagnostics: DoctorDiagnostic[] = [];\n\n for (const sourceImport of request.activeSourceImports) {\n if (sourceImport.specifier.startsWith(DEPENDENCY_POLICY.compatibilityPackagePrefix)) {\n diagnostics.push({\n code: 'field-invalid',\n message: `Import \"${sourceImport.specifier}\" is an old ankhorage4 compatibility boundary. Import \"${toOwningPackageSpecifier(sourceImport.specifier)}\" directly from the owning package.`,\n path: sourceImport.filePath,\n profile: request.profile,\n ...architectureRuleFields(DEPENDENCY_POLICY.rules.compatibilityImport),\n });\n }\n\n if (sourceImport.specifier.includes(DEPENDENCY_POLICY.legacySourceMarker)) {\n diagnostics.push({\n code: 'field-invalid',\n message: `Import \"${sourceImport.specifier}\" references ankhorage4 as active source. Import the extracted owning @ankhorage/* package instead.`,\n path: sourceImport.filePath,\n profile: request.profile,\n ...architectureRuleFields(DEPENDENCY_POLICY.rules.legacySourceImport),\n });\n }\n }\n\n return diagnostics;\n}\n\nfunction validateStudioOwnership(request: {\n readonly activeSourceImports: readonly ActiveSourceImport[];\n readonly dependencyEntries: readonly DependencyEntry[];\n readonly packageJsonPath: string;\n readonly profile: DoctorPolicyProfile;\n}): DoctorDiagnostic[] {\n const diagnostics: DoctorDiagnostic[] = [];\n const directDependencies = new Set(\n request.dependencyEntries\n .filter((dependency) => dependency.fieldName === 'dependencies')\n .map((dependency) => dependency.packageName),\n );\n const importedSpecifiers = request.activeSourceImports.map(\n (sourceImport) => sourceImport.specifier,\n );\n\n if (!directDependencies.has(STUDIO_DND_PACKAGE_NAME)) {\n diagnostics.push({\n code: 'missing-dependency',\n message: `@ankhorage/studio must depend directly on \"${STUDIO_DND_PACKAGE_NAME}\" for Studio drag-and-drop ownership.`,\n path: request.packageJsonPath,\n profile: request.profile,\n ruleId: 'studio.dependencies.dnd.required',\n severity: 'error',\n });\n }\n\n if (!directDependencies.has(STUDIO_RUNTIME_PACKAGE_NAME)) {\n diagnostics.push({\n code: 'missing-dependency',\n message: `@ankhorage/studio must depend directly on \"${STUDIO_RUNTIME_PACKAGE_NAME}\" for runtime ownership.`,\n path: request.packageJsonPath,\n profile: request.profile,\n ruleId: 'studio.dependencies.runtime.required',\n severity: 'error',\n });\n }\n\n if (\n !importedSpecifiers.some((specifier) => isPackageImport(specifier, STUDIO_DND_PACKAGE_NAME))\n ) {\n diagnostics.push({\n code: 'field-missing',\n message: `@ankhorage/studio must import \"${STUDIO_DND_PACKAGE_NAME}\" directly from its Studio DnD boundary.`,\n path: request.packageJsonPath,\n profile: request.profile,\n ruleId: 'studio.imports.dnd.required',\n severity: 'error',\n });\n }\n\n if (\n !importedSpecifiers.some((specifier) => isPackageImport(specifier, STUDIO_RUNTIME_PACKAGE_NAME))\n ) {\n diagnostics.push({\n code: 'field-missing',\n message: `@ankhorage/studio must import \"${STUDIO_RUNTIME_PACKAGE_NAME}\" directly from the owning runtime package.`,\n path: request.packageJsonPath,\n profile: request.profile,\n ruleId: 'studio.imports.runtime.required',\n severity: 'error',\n });\n }\n\n return diagnostics;\n}\n\nasync function collectActiveSourceImports(targetPath: string): Promise<ActiveSourceImport[]> {\n const imports: ActiveSourceImport[] = [];\n\n for (const sourceRoot of ACTIVE_SOURCE_ROOTS) {\n const sourceRootPath = path.join(targetPath, sourceRoot);\n if (!(await pathExists(sourceRootPath))) {\n continue;\n }\n\n for (const filePath of await listActiveSourceFiles(sourceRootPath)) {\n const source = await fs.readFile(filePath, 'utf8');\n for (const specifier of extractImportSpecifiers(source)) {\n imports.push({ filePath, specifier });\n }\n }\n }\n\n return imports;\n}\n\nasync function listActiveSourceFiles(rootPath: string): Promise<string[]> {\n const files: string[] = [];\n await visit(rootPath);\n return files.sort((left, right) => left.localeCompare(right));\n\n async function visit(currentPath: string): Promise<void> {\n const entries = await fs.readdir(currentPath, { withFileTypes: true });\n\n for (const entry of entries) {\n if (entry.isDirectory()) {\n if (!IGNORED_SOURCE_DIRECTORIES.has(entry.name)) {\n await visit(path.join(currentPath, entry.name));\n }\n continue;\n }\n\n if (!entry.isFile()) {\n continue;\n }\n\n const filePath = path.join(currentPath, entry.name);\n if (isActiveSourceFile(filePath)) {\n files.push(filePath);\n }\n }\n }\n}\n\nfunction isActiveSourceFile(filePath: string): boolean {\n if (!ACTIVE_SOURCE_EXTENSIONS.has(path.extname(filePath))) {\n return false;\n }\n\n return !/(?:^|\\.)(?:spec|test)\\.[cm]?[jt]sx?$/.test(path.basename(filePath));\n}\n\nfunction extractImportSpecifiers(source: string): string[] {\n const specifiers = new Set<string>();\n const patterns = [\n /\\b(?:import|export)\\s+(?:type\\s+)?(?:[^;]*?\\sfrom\\s*)?['\"]([^'\"]+)['\"]/g,\n /\\b(?:import|require)\\s*\\(\\s*['\"]([^'\"]+)['\"]\\s*\\)/g,\n ];\n\n for (const pattern of patterns) {\n for (const match of source.matchAll(pattern)) {\n const [, specifier] = match;\n if (specifier !== undefined) {\n specifiers.add(specifier);\n }\n }\n }\n\n return [...specifiers].sort((left, right) => left.localeCompare(right));\n}\n\nfunction collectDependencyEntries(packageJson: Record<string, unknown>): DependencyEntry[] {\n const entries: DependencyEntry[] = [];\n\n for (const fieldName of DEPENDENCY_FIELD_NAMES) {\n const dependencies = isRecord(packageJson[fieldName]) ? packageJson[fieldName] : null;\n if (dependencies === null) {\n continue;\n }\n\n for (const [packageName, version] of Object.entries(dependencies)) {\n if (isNonEmptyString(version)) {\n entries.push({ fieldName, packageName, version });\n }\n }\n }\n\n return entries.sort((left, right) => {\n const fieldComparison = left.fieldName.localeCompare(right.fieldName);\n return fieldComparison !== 0\n ? fieldComparison\n : left.packageName.localeCompare(right.packageName);\n });\n}\n\nfunction hasAnkhProvider(packageJson: Record<string, unknown>): boolean {\n const ankh = isRecord(packageJson.ankh) ? packageJson.ankh : null;\n return isNonEmptyString(ankh?.provider);\n}\n\nfunction isPackageImport(specifier: string, packageName: string): boolean {\n return specifier === packageName || specifier.startsWith(`${packageName}/`);\n}\n\n/*** Resolve Policy-owned rule metadata for generic architecture diagnostics. */\nfunction architectureRuleFields(\n ruleId: DoctorDiagnostic['ruleId'],\n): Pick<DoctorDiagnostic, 'ruleId' | 'severity'> {\n const rule = getArchitecturePolicyRule(ruleId);\n return { ruleId: rule.id, severity: rule.severity };\n}\n\nfunction toOwningPackageSpecifier(specifier: string): string {\n return `@ankhorage/${specifier.slice(DEPENDENCY_POLICY.compatibilityPackagePrefix.length)}`;\n}\n\nasync function readPackageJson(packageJsonPath: string): Promise<Record<string, unknown> | null> {\n try {\n const parsed: unknown = JSON.parse(await fs.readFile(packageJsonPath, 'utf8'));\n return isRecord(parsed) ? parsed : null;\n } catch {\n return null;\n }\n}\n\nasync function pathExists(targetPath: string): Promise<boolean> {\n try {\n await fs.access(targetPath);\n return true;\n } catch {\n return false;\n }\n}\n\nfunction isNonEmptyString(value: unknown): value is string {\n return typeof value === 'string' && value.trim().length > 0;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n"]}
|
|
1
|
+
{"version":3,"file":"cliLayoutAnalysis.js","sourceRoot":"","sources":["../src/cliLayoutAnalysis.ts"],"names":[],"mappings":"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","sourcesContent":["import { promises as fs } from 'node:fs';\nimport path from 'node:path';\n\nimport { REPOSITORY_RULE_IDS, REPOSITORY_RULE_METADATA } from '@ankhorage/rules-repository';\n\nimport {\n analyzeDoctorTarget,\n type DoctorAnalysisRequest,\n type DoctorAnalysisResult,\n} from './analysis.js';\nimport type { DoctorDiagnostic, DoctorPolicyProfile } from './diagnostics.js';\nimport { analyzeAppManifestTarget } from './manifestAnalysis.js';\nimport { analyzeRepositorySourceRules } from './repositorySourceRulesAnalysis.js';\nimport { analyzeSourceArchitecture } from './sourceArchitectureAnalysis.js';\nimport { getRepositoryRule } from './utils/getRepositoryRule.js';\n\nconst CLI_POLICY = REPOSITORY_RULE_METADATA.cli;\nconst DEPENDENCY_POLICY = REPOSITORY_RULE_METADATA.dependencies;\nconst ACTIVE_SOURCE_ROOTS = ['src', 'app', 'apps', 'packages', 'scripts'] as const;\nconst ACTIVE_SOURCE_EXTENSIONS = new Set([\n '.cjs',\n '.cts',\n '.js',\n '.jsx',\n '.mjs',\n '.mts',\n '.ts',\n '.tsx',\n]);\nconst IGNORED_SOURCE_DIRECTORIES = new Set([\n '.expo',\n '.git',\n '.next',\n '__fixtures__',\n '__tests__',\n 'build',\n 'coverage',\n 'dist',\n 'docs',\n 'fixtures',\n 'node_modules',\n 'paradox',\n 'test',\n 'tests',\n]);\nconst STUDIO_PACKAGE_NAME = '@ankhorage/studio';\nconst STUDIO_DND_PACKAGE_NAME = '@ankhorage/react-native-reanimated-dnd-web';\nconst STUDIO_RUNTIME_PACKAGE_NAME = '@ankhorage/runtime';\nconst DEPENDENCY_FIELD_NAMES = [\n 'dependencies',\n 'devDependencies',\n 'optionalDependencies',\n 'peerDependencies',\n] as const;\n\ninterface ActiveSourceImport {\n readonly filePath: string;\n readonly specifier: string;\n}\n\ninterface DependencyEntry {\n readonly fieldName: (typeof DEPENDENCY_FIELD_NAMES)[number];\n readonly packageName: string;\n readonly version: string;\n}\n\nexport async function analyzeDoctorTargetWithCliLayout(\n request: DoctorAnalysisRequest,\n): Promise<DoctorAnalysisResult> {\n const manifestResult = await analyzeAppManifestTarget(request);\n if (manifestResult !== null) {\n return manifestResult;\n }\n\n const result = await analyzeDoctorTarget(request);\n if (!result.hasPackageJson) {\n return result;\n }\n\n const packageJsonPath = path.join(result.targetPath, 'package.json');\n const packageJson = await readPackageJson(packageJsonPath);\n if (packageJson === null) {\n return result;\n }\n\n const architectureDiagnostics = await analyzeTargetArchitecture({\n packageJson,\n packageJsonPath,\n profile: result.profile,\n targetPath: result.targetPath,\n });\n if (architectureDiagnostics.length === 0) {\n return result;\n }\n\n const diagnostics = [...result.diagnostics, ...architectureDiagnostics];\n\n return {\n ...result,\n diagnostics,\n fixPlan: result.fixPlan === null ? null : { ...result.fixPlan, diagnostics },\n };\n}\n\nasync function analyzeTargetArchitecture(request: {\n readonly packageJson: Record<string, unknown>;\n readonly packageJsonPath: string;\n readonly profile: DoctorPolicyProfile;\n readonly targetPath: string;\n}): Promise<DoctorDiagnostic[]> {\n const diagnostics: DoctorDiagnostic[] = [];\n const legacyRootCliPath = path.join(request.targetPath, CLI_POLICY.legacyRootFile);\n const exportsField = isRecord(request.packageJson.exports) ? request.packageJson.exports : null;\n const cliCapable =\n hasAnkhProvider(request.packageJson) || exportsField?.[CLI_POLICY.packageExport] !== undefined;\n\n if (await pathExists(legacyRootCliPath)) {\n diagnostics.push({\n code: 'field-invalid',\n message: 'Package CLI code must live under src/cli/; root src/cli.ts is not allowed.',\n path: legacyRootCliPath,\n profile: request.profile,\n ...repositoryRuleFields(REPOSITORY_RULE_IDS.cliRootFile),\n });\n }\n\n if (cliCapable && exportsField?.[CLI_POLICY.packageExport] === undefined) {\n diagnostics.push({\n code: 'field-missing',\n message:\n 'CLI-capable packages must export \"./cli\" from package.json and point it at the metadata-declared provider build output.',\n path: request.packageJsonPath,\n profile: request.profile,\n ...repositoryRuleFields(REPOSITORY_RULE_IDS.cliExport),\n });\n }\n\n const dependencyEntries = collectDependencyEntries(request.packageJson);\n diagnostics.push(\n ...validateDependencyArchitecture({\n dependencyEntries,\n packageJsonPath: request.packageJsonPath,\n profile: request.profile,\n }),\n );\n\n const activeSource = await collectActiveSourceSnapshot(request.targetPath);\n diagnostics.push(\n ...validateActiveSourceImports({\n activeSourceImports: activeSource.imports,\n profile: request.profile,\n }),\n );\n diagnostics.push(\n ...analyzeRepositorySourceRules({\n files: activeSource.files,\n imports: activeSource.imports,\n profile: request.profile,\n targetPath: request.targetPath,\n }),\n );\n diagnostics.push(\n ...(await analyzeSourceArchitecture({\n profile: request.profile,\n targetPath: request.targetPath,\n })),\n );\n\n if (request.packageJson.name === STUDIO_PACKAGE_NAME) {\n diagnostics.push(\n ...validateStudioOwnership({\n activeSourceImports: activeSource.imports,\n dependencyEntries,\n packageJsonPath: request.packageJsonPath,\n profile: request.profile,\n }),\n );\n }\n\n return diagnostics;\n}\n\nfunction validateDependencyArchitecture(request: {\n readonly dependencyEntries: readonly DependencyEntry[];\n readonly packageJsonPath: string;\n readonly profile: DoctorPolicyProfile;\n}): DoctorDiagnostic[] {\n const diagnostics: DoctorDiagnostic[] = [];\n\n for (const dependency of request.dependencyEntries) {\n if (dependency.packageName.startsWith(DEPENDENCY_POLICY.compatibilityPackagePrefix)) {\n diagnostics.push({\n code: 'field-invalid',\n message: `Dependency \"${dependency.packageName}\" in package.json.${dependency.fieldName} is an old ankhorage4 workspace alias. Depend on \"${toOwningPackageSpecifier(dependency.packageName)}\" directly.`,\n path: request.packageJsonPath,\n profile: request.profile,\n ...repositoryRuleFields(REPOSITORY_RULE_IDS.compatibilityDependency),\n });\n }\n\n if (\n request.profile === 'public-package' &&\n DEPENDENCY_POLICY.localProtocolPrefixes.some((prefix) =>\n dependency.version.startsWith(prefix),\n )\n ) {\n diagnostics.push({\n code: 'field-invalid',\n message: `Published dependency \"${dependency.packageName}\" must resolve from a registry version, not \"${dependency.version}\". Standalone packages cannot rely on local/workspace/Git package state.`,\n path: request.packageJsonPath,\n profile: request.profile,\n ...repositoryRuleFields(REPOSITORY_RULE_IDS.localProtocolDependency),\n });\n }\n\n if (\n dependency.packageName.includes(DEPENDENCY_POLICY.legacySourceMarker) ||\n dependency.version.includes(DEPENDENCY_POLICY.legacySourceMarker)\n ) {\n diagnostics.push({\n code: 'field-invalid',\n message: `Dependency \"${dependency.packageName}\" references ankhorage4 as active source. Depend on the extracted owning @ankhorage/* package instead.`,\n path: request.packageJsonPath,\n profile: request.profile,\n ...repositoryRuleFields(REPOSITORY_RULE_IDS.legacySourceDependency),\n });\n }\n }\n\n return diagnostics;\n}\n\nfunction validateActiveSourceImports(request: {\n readonly activeSourceImports: readonly ActiveSourceImport[];\n readonly profile: DoctorPolicyProfile;\n}): DoctorDiagnostic[] {\n const diagnostics: DoctorDiagnostic[] = [];\n\n for (const sourceImport of request.activeSourceImports) {\n if (sourceImport.specifier.startsWith(DEPENDENCY_POLICY.compatibilityPackagePrefix)) {\n diagnostics.push({\n code: 'field-invalid',\n message: `Import \"${sourceImport.specifier}\" is an old ankhorage4 compatibility boundary. Import \"${toOwningPackageSpecifier(sourceImport.specifier)}\" directly from the owning package.`,\n path: sourceImport.filePath,\n profile: request.profile,\n ...repositoryRuleFields(REPOSITORY_RULE_IDS.compatibilityImport),\n });\n }\n\n if (sourceImport.specifier.includes(DEPENDENCY_POLICY.legacySourceMarker)) {\n diagnostics.push({\n code: 'field-invalid',\n message: `Import \"${sourceImport.specifier}\" references ankhorage4 as active source. Import the extracted owning @ankhorage/* package instead.`,\n path: sourceImport.filePath,\n profile: request.profile,\n ...repositoryRuleFields(REPOSITORY_RULE_IDS.legacySourceImport),\n });\n }\n }\n\n return diagnostics;\n}\n\nfunction validateStudioOwnership(request: {\n readonly activeSourceImports: readonly ActiveSourceImport[];\n readonly dependencyEntries: readonly DependencyEntry[];\n readonly packageJsonPath: string;\n readonly profile: DoctorPolicyProfile;\n}): DoctorDiagnostic[] {\n const diagnostics: DoctorDiagnostic[] = [];\n const directDependencies = new Set(\n request.dependencyEntries\n .filter((dependency) => dependency.fieldName === 'dependencies')\n .map((dependency) => dependency.packageName),\n );\n const importedSpecifiers = request.activeSourceImports.map(\n (sourceImport) => sourceImport.specifier,\n );\n\n if (!directDependencies.has(STUDIO_DND_PACKAGE_NAME)) {\n diagnostics.push({\n code: 'missing-dependency',\n message: `@ankhorage/studio must depend directly on \"${STUDIO_DND_PACKAGE_NAME}\" for Studio drag-and-drop ownership.`,\n path: request.packageJsonPath,\n profile: request.profile,\n ruleId: 'studio.dependencies.dnd.required',\n severity: 'error',\n });\n }\n\n if (!directDependencies.has(STUDIO_RUNTIME_PACKAGE_NAME)) {\n diagnostics.push({\n code: 'missing-dependency',\n message: `@ankhorage/studio must depend directly on \"${STUDIO_RUNTIME_PACKAGE_NAME}\" for runtime ownership.`,\n path: request.packageJsonPath,\n profile: request.profile,\n ruleId: 'studio.dependencies.runtime.required',\n severity: 'error',\n });\n }\n\n if (\n !importedSpecifiers.some((specifier) => isPackageImport(specifier, STUDIO_DND_PACKAGE_NAME))\n ) {\n diagnostics.push({\n code: 'field-missing',\n message: `@ankhorage/studio must import \"${STUDIO_DND_PACKAGE_NAME}\" directly from its Studio DnD boundary.`,\n path: request.packageJsonPath,\n profile: request.profile,\n ruleId: 'studio.imports.dnd.required',\n severity: 'error',\n });\n }\n\n if (\n !importedSpecifiers.some((specifier) => isPackageImport(specifier, STUDIO_RUNTIME_PACKAGE_NAME))\n ) {\n diagnostics.push({\n code: 'field-missing',\n message: `@ankhorage/studio must import \"${STUDIO_RUNTIME_PACKAGE_NAME}\" directly from the owning runtime package.`,\n path: request.packageJsonPath,\n profile: request.profile,\n ruleId: 'studio.imports.runtime.required',\n severity: 'error',\n });\n }\n\n return diagnostics;\n}\n\ninterface ActiveSourceSnapshot {\n readonly files: readonly string[];\n readonly imports: readonly ActiveSourceImport[];\n}\n\n/***\n * Collects active production files and their static import specifiers once for repository checks.\n */\nasync function collectActiveSourceSnapshot(targetPath: string): Promise<ActiveSourceSnapshot> {\n const files: string[] = [];\n\n for (const sourceRoot of ACTIVE_SOURCE_ROOTS) {\n const sourceRootPath = path.join(targetPath, sourceRoot);\n if (await pathExists(sourceRootPath)) {\n files.push(...(await listActiveSourceFiles(sourceRootPath)));\n }\n }\n\n const imports = (\n await Promise.all(\n files.map(async (filePath) => {\n const source = await fs.readFile(filePath, 'utf8');\n return extractImportSpecifiers(source).map((specifier) => ({ filePath, specifier }));\n }),\n )\n ).flat();\n\n return {\n files: [...files].sort((left, right) => left.localeCompare(right)),\n imports,\n };\n}\n\nasync function listActiveSourceFiles(rootPath: string): Promise<string[]> {\n const files: string[] = [];\n await visit(rootPath);\n return files.sort((left, right) => left.localeCompare(right));\n\n async function visit(currentPath: string): Promise<void> {\n const entries = await fs.readdir(currentPath, { withFileTypes: true });\n\n for (const entry of entries) {\n if (entry.isDirectory()) {\n if (!IGNORED_SOURCE_DIRECTORIES.has(entry.name)) {\n await visit(path.join(currentPath, entry.name));\n }\n continue;\n }\n\n if (!entry.isFile()) {\n continue;\n }\n\n const filePath = path.join(currentPath, entry.name);\n if (isActiveSourceFile(filePath)) {\n files.push(filePath);\n }\n }\n }\n}\n\nfunction isActiveSourceFile(filePath: string): boolean {\n if (!ACTIVE_SOURCE_EXTENSIONS.has(path.extname(filePath))) {\n return false;\n }\n\n return !/(?:^|\\.)(?:spec|test)\\.[cm]?[jt]sx?$/.test(path.basename(filePath));\n}\n\nfunction extractImportSpecifiers(source: string): string[] {\n const specifiers = new Set<string>();\n const patterns = [\n /\\b(?:import|export)\\s+(?:type\\s+)?(?:[^;]*?\\sfrom\\s*)?['\"]([^'\"]+)['\"]/g,\n /\\b(?:import|require)\\s*\\(\\s*['\"]([^'\"]+)['\"]\\s*\\)/g,\n ];\n\n for (const pattern of patterns) {\n for (const match of source.matchAll(pattern)) {\n const [, specifier] = match;\n if (specifier !== undefined) {\n specifiers.add(specifier);\n }\n }\n }\n\n return [...specifiers].sort((left, right) => left.localeCompare(right));\n}\n\nfunction collectDependencyEntries(packageJson: Record<string, unknown>): DependencyEntry[] {\n const entries: DependencyEntry[] = [];\n\n for (const fieldName of DEPENDENCY_FIELD_NAMES) {\n const dependencies = isRecord(packageJson[fieldName]) ? packageJson[fieldName] : null;\n if (dependencies === null) {\n continue;\n }\n\n for (const [packageName, version] of Object.entries(dependencies)) {\n if (isNonEmptyString(version)) {\n entries.push({ fieldName, packageName, version });\n }\n }\n }\n\n return entries.sort((left, right) => {\n const fieldComparison = left.fieldName.localeCompare(right.fieldName);\n return fieldComparison !== 0\n ? fieldComparison\n : left.packageName.localeCompare(right.packageName);\n });\n}\n\nfunction hasAnkhProvider(packageJson: Record<string, unknown>): boolean {\n const ankh = isRecord(packageJson.ankh) ? packageJson.ankh : null;\n return isNonEmptyString(ankh?.provider);\n}\n\nfunction isPackageImport(specifier: string, packageName: string): boolean {\n return specifier === packageName || specifier.startsWith(`${packageName}/`);\n}\n\n/*** Resolve repository Rules metadata for package and source-boundary diagnostics. */\nfunction repositoryRuleFields(\n ruleId: DoctorDiagnostic['ruleId'],\n): Pick<DoctorDiagnostic, 'ruleId' | 'severity'> {\n const rule = getRepositoryRule(ruleId);\n return { ruleId: rule.id, severity: rule.severity };\n}\n\nfunction toOwningPackageSpecifier(specifier: string): string {\n return `@ankhorage/${specifier.slice(DEPENDENCY_POLICY.compatibilityPackagePrefix.length)}`;\n}\n\nasync function readPackageJson(packageJsonPath: string): Promise<Record<string, unknown> | null> {\n try {\n const parsed: unknown = JSON.parse(await fs.readFile(packageJsonPath, 'utf8'));\n return isRecord(parsed) ? parsed : null;\n } catch {\n return null;\n }\n}\n\nasync function pathExists(targetPath: string): Promise<boolean> {\n try {\n await fs.access(targetPath);\n return true;\n } catch {\n return false;\n }\n}\n\nfunction isNonEmptyString(value: unknown): value is string {\n return typeof value === 'string' && value.trim().length > 0;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n"]}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { DoctorDiagnostic, DoctorPolicyProfile } from './diagnostics.js';
|
|
2
|
+
interface RepositorySourceRulesInput {
|
|
3
|
+
readonly files: readonly string[];
|
|
4
|
+
readonly imports: readonly {
|
|
5
|
+
readonly filePath: string;
|
|
6
|
+
readonly specifier: string;
|
|
7
|
+
}[];
|
|
8
|
+
readonly profile: DoctorPolicyProfile;
|
|
9
|
+
readonly targetPath: string;
|
|
10
|
+
}
|
|
11
|
+
/***
|
|
12
|
+
* Evaluates repository-owned source rules from Doctor's normalized active-source snapshot.
|
|
13
|
+
*/
|
|
14
|
+
export declare function analyzeRepositorySourceRules(input: RepositorySourceRulesInput): readonly DoctorDiagnostic[];
|
|
15
|
+
export {};
|
|
16
|
+
//# sourceMappingURL=repositorySourceRulesAnalysis.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"repositorySourceRulesAnalysis.d.ts","sourceRoot":"","sources":["../src/repositorySourceRulesAnalysis.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EACV,gBAAgB,EAEhB,mBAAmB,EAEpB,MAAM,kBAAkB,CAAC;AAO1B,UAAU,0BAA0B;IAClC,QAAQ,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;IAClC,QAAQ,CAAC,OAAO,EAAE,SAAS;QACzB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;QAC1B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;KAC5B,EAAE,CAAC;IACJ,QAAQ,CAAC,OAAO,EAAE,mBAAmB,CAAC;IACtC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;CAC7B;AAED;;GAEG;AACH,wBAAgB,4BAA4B,CAC1C,KAAK,EAAE,0BAA0B,GAChC,SAAS,gBAAgB,EAAE,CAuB7B"}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import { evaluateRepository, REPOSITORY_RULE_IDS, } from '@ankhorage/rules-repository';
|
|
3
|
+
const ENABLED_REPOSITORY_SOURCE_RULES = [
|
|
4
|
+
REPOSITORY_RULE_IDS.catchAllDirectory,
|
|
5
|
+
REPOSITORY_RULE_IDS.importOutsideRoot,
|
|
6
|
+
];
|
|
7
|
+
/***
|
|
8
|
+
* Evaluates repository-owned source rules from Doctor's normalized active-source snapshot.
|
|
9
|
+
*/
|
|
10
|
+
export function analyzeRepositorySourceRules(input) {
|
|
11
|
+
const result = evaluateRepository({
|
|
12
|
+
cliCommands: [],
|
|
13
|
+
imports: input.imports.map((sourceImport) => toImportFact(input.targetPath, sourceImport)),
|
|
14
|
+
packageJson: {
|
|
15
|
+
dependencies: [],
|
|
16
|
+
exports: [],
|
|
17
|
+
fields: {},
|
|
18
|
+
scripts: {},
|
|
19
|
+
},
|
|
20
|
+
paths: collectDirectoryFacts(input.targetPath, input.files),
|
|
21
|
+
workflowBunVersions: {},
|
|
22
|
+
}, {
|
|
23
|
+
config: {
|
|
24
|
+
version: 1,
|
|
25
|
+
rules: ENABLED_REPOSITORY_SOURCE_RULES.map((id) => ({ enabled: true, id })),
|
|
26
|
+
},
|
|
27
|
+
});
|
|
28
|
+
return result.findings.map((finding) => toDoctorDiagnostic(input, finding));
|
|
29
|
+
}
|
|
30
|
+
/***
|
|
31
|
+
* Converts one active source import into portable repository-boundary evidence.
|
|
32
|
+
*/
|
|
33
|
+
function toImportFact(targetPath, sourceImport) {
|
|
34
|
+
return {
|
|
35
|
+
path: toPortableRelativePath(targetPath, sourceImport.filePath),
|
|
36
|
+
specifier: sourceImport.specifier,
|
|
37
|
+
...(sourceImport.specifier.startsWith('.')
|
|
38
|
+
? { escapesRepositoryRoot: importEscapesRepository(targetPath, sourceImport) }
|
|
39
|
+
: {}),
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
/***
|
|
43
|
+
* Determines whether one relative source import resolves outside the standalone repository root.
|
|
44
|
+
*/
|
|
45
|
+
function importEscapesRepository(targetPath, sourceImport) {
|
|
46
|
+
const resolvedTarget = path.resolve(path.dirname(sourceImport.filePath), sourceImport.specifier);
|
|
47
|
+
const relativeTarget = path.relative(targetPath, resolvedTarget);
|
|
48
|
+
return relativeTarget.startsWith('..') || path.isAbsolute(relativeTarget);
|
|
49
|
+
}
|
|
50
|
+
/***
|
|
51
|
+
* Derives unique directory facts from active production source files.
|
|
52
|
+
*/
|
|
53
|
+
function collectDirectoryFacts(targetPath, files) {
|
|
54
|
+
const directories = new Set();
|
|
55
|
+
for (const filePath of files) {
|
|
56
|
+
const segments = toPortableRelativePath(targetPath, filePath).split('/').filter(Boolean);
|
|
57
|
+
for (let index = 1; index < segments.length; index += 1) {
|
|
58
|
+
directories.add(segments.slice(0, index).join('/'));
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return [...directories]
|
|
62
|
+
.sort((left, right) => left.localeCompare(right))
|
|
63
|
+
.map((directoryPath) => ({ kind: 'directory', path: directoryPath }));
|
|
64
|
+
}
|
|
65
|
+
/***
|
|
66
|
+
* Converts one repository path into stable forward-slash relative form.
|
|
67
|
+
*/
|
|
68
|
+
function toPortableRelativePath(targetPath, filePath) {
|
|
69
|
+
return path.relative(targetPath, filePath).split(path.sep).join('/');
|
|
70
|
+
}
|
|
71
|
+
/***
|
|
72
|
+
* Converts one repository Rule finding into Doctor's stable diagnostic surface.
|
|
73
|
+
*/
|
|
74
|
+
function toDoctorDiagnostic(input, finding) {
|
|
75
|
+
const subjectPath = finding.sourceLocation?.path ?? finding.subjects[0]?.path ?? '.';
|
|
76
|
+
return {
|
|
77
|
+
code: 'field-invalid',
|
|
78
|
+
message: finding.message,
|
|
79
|
+
path: path.resolve(input.targetPath, subjectPath),
|
|
80
|
+
profile: input.profile,
|
|
81
|
+
ruleId: toDoctorRuleId(finding.ruleId),
|
|
82
|
+
severity: toDoctorSeverity(finding.severity),
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
/***
|
|
86
|
+
* Narrows repository-provider ids to the source rules Doctor intentionally evaluates here.
|
|
87
|
+
*/
|
|
88
|
+
function toDoctorRuleId(ruleId) {
|
|
89
|
+
const match = ENABLED_REPOSITORY_SOURCE_RULES.find((candidate) => candidate === ruleId);
|
|
90
|
+
if (match === undefined)
|
|
91
|
+
throw new Error('Unexpected Doctor repository source rule: ' + ruleId);
|
|
92
|
+
return match;
|
|
93
|
+
}
|
|
94
|
+
/***
|
|
95
|
+
* Narrows generic Rules severity to Doctor's diagnostic severity surface.
|
|
96
|
+
*/
|
|
97
|
+
function toDoctorSeverity(severity) {
|
|
98
|
+
if (severity === 'info') {
|
|
99
|
+
throw new Error('Doctor does not support info-level repository diagnostics.');
|
|
100
|
+
}
|
|
101
|
+
return severity;
|
|
102
|
+
}
|
|
103
|
+
//# sourceMappingURL=repositorySourceRulesAnalysis.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"repositorySourceRulesAnalysis.js","sourceRoot":"","sources":["../src/repositorySourceRulesAnalysis.ts"],"names":[],"mappings":"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","sourcesContent":["import path from 'node:path';\n\nimport {\n evaluateRepository,\n REPOSITORY_RULE_IDS,\n type RepositoryImportFact,\n type RepositoryPathFact,\n} from '@ankhorage/rules-repository';\n\nimport type {\n DoctorDiagnostic,\n DoctorDiagnosticSeverity,\n DoctorPolicyProfile,\n DoctorRuleId,\n} from './diagnostics.js';\n\nconst ENABLED_REPOSITORY_SOURCE_RULES = [\n REPOSITORY_RULE_IDS.catchAllDirectory,\n REPOSITORY_RULE_IDS.importOutsideRoot,\n] as const satisfies readonly DoctorRuleId[];\n\ninterface RepositorySourceRulesInput {\n readonly files: readonly string[];\n readonly imports: readonly {\n readonly filePath: string;\n readonly specifier: string;\n }[];\n readonly profile: DoctorPolicyProfile;\n readonly targetPath: string;\n}\n\n/***\n * Evaluates repository-owned source rules from Doctor's normalized active-source snapshot.\n */\nexport function analyzeRepositorySourceRules(\n input: RepositorySourceRulesInput,\n): readonly DoctorDiagnostic[] {\n const result = evaluateRepository(\n {\n cliCommands: [],\n imports: input.imports.map((sourceImport) => toImportFact(input.targetPath, sourceImport)),\n packageJson: {\n dependencies: [],\n exports: [],\n fields: {},\n scripts: {},\n },\n paths: collectDirectoryFacts(input.targetPath, input.files),\n workflowBunVersions: {},\n },\n {\n config: {\n version: 1,\n rules: ENABLED_REPOSITORY_SOURCE_RULES.map((id) => ({ enabled: true, id })),\n },\n },\n );\n\n return result.findings.map((finding) => toDoctorDiagnostic(input, finding));\n}\n\n/***\n * Converts one active source import into portable repository-boundary evidence.\n */\nfunction toImportFact(\n targetPath: string,\n sourceImport: RepositorySourceRulesInput['imports'][number],\n): RepositoryImportFact {\n return {\n path: toPortableRelativePath(targetPath, sourceImport.filePath),\n specifier: sourceImport.specifier,\n ...(sourceImport.specifier.startsWith('.')\n ? { escapesRepositoryRoot: importEscapesRepository(targetPath, sourceImport) }\n : {}),\n };\n}\n\n/***\n * Determines whether one relative source import resolves outside the standalone repository root.\n */\nfunction importEscapesRepository(\n targetPath: string,\n sourceImport: RepositorySourceRulesInput['imports'][number],\n): boolean {\n const resolvedTarget = path.resolve(path.dirname(sourceImport.filePath), sourceImport.specifier);\n const relativeTarget = path.relative(targetPath, resolvedTarget);\n return relativeTarget.startsWith('..') || path.isAbsolute(relativeTarget);\n}\n\n/***\n * Derives unique directory facts from active production source files.\n */\nfunction collectDirectoryFacts(\n targetPath: string,\n files: readonly string[],\n): readonly RepositoryPathFact[] {\n const directories = new Set<string>();\n\n for (const filePath of files) {\n const segments = toPortableRelativePath(targetPath, filePath).split('/').filter(Boolean);\n for (let index = 1; index < segments.length; index += 1) {\n directories.add(segments.slice(0, index).join('/'));\n }\n }\n\n return [...directories]\n .sort((left, right) => left.localeCompare(right))\n .map((directoryPath) => ({ kind: 'directory' as const, path: directoryPath }));\n}\n\n/***\n * Converts one repository path into stable forward-slash relative form.\n */\nfunction toPortableRelativePath(targetPath: string, filePath: string): string {\n return path.relative(targetPath, filePath).split(path.sep).join('/');\n}\n\n/***\n * Converts one repository Rule finding into Doctor's stable diagnostic surface.\n */\nfunction toDoctorDiagnostic(\n input: RepositorySourceRulesInput,\n finding: ReturnType<typeof evaluateRepository>['findings'][number],\n): DoctorDiagnostic {\n const subjectPath = finding.sourceLocation?.path ?? finding.subjects[0]?.path ?? '.';\n return {\n code: 'field-invalid',\n message: finding.message,\n path: path.resolve(input.targetPath, subjectPath),\n profile: input.profile,\n ruleId: toDoctorRuleId(finding.ruleId),\n severity: toDoctorSeverity(finding.severity),\n };\n}\n\n/***\n * Narrows repository-provider ids to the source rules Doctor intentionally evaluates here.\n */\nfunction toDoctorRuleId(ruleId: string): DoctorRuleId {\n const match = ENABLED_REPOSITORY_SOURCE_RULES.find((candidate) => candidate === ruleId);\n if (match === undefined) throw new Error('Unexpected Doctor repository source rule: ' + ruleId);\n return match;\n}\n\n/***\n * Narrows generic Rules severity to Doctor's diagnostic severity surface.\n */\nfunction toDoctorSeverity(severity: 'error' | 'info' | 'warning'): DoctorDiagnosticSeverity {\n if (severity === 'info') {\n throw new Error('Doctor does not support info-level repository diagnostics.');\n }\n return severity;\n}\n"]}
|
|
@@ -1,14 +1,11 @@
|
|
|
1
1
|
import type { DoctorDiagnostic, DoctorPolicyProfile } from './diagnostics.js';
|
|
2
|
-
interface SourceImport {
|
|
3
|
-
readonly filePath: string;
|
|
4
|
-
readonly specifier: string;
|
|
5
|
-
}
|
|
6
2
|
interface AnalyzeSourceArchitectureInput {
|
|
7
|
-
readonly activeSourceImports: readonly SourceImport[];
|
|
8
3
|
readonly profile: DoctorPolicyProfile;
|
|
9
4
|
readonly targetPath: string;
|
|
10
5
|
}
|
|
11
|
-
/***
|
|
6
|
+
/***
|
|
7
|
+
* Evaluates Doctor's currently enforced source-architecture subset through rules-architecture.
|
|
8
|
+
*/
|
|
12
9
|
export declare function analyzeSourceArchitecture(input: AnalyzeSourceArchitectureInput): Promise<DoctorDiagnostic[]>;
|
|
13
10
|
export {};
|
|
14
11
|
//# sourceMappingURL=sourceArchitectureAnalysis.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sourceArchitectureAnalysis.d.ts","sourceRoot":"","sources":["../src/sourceArchitectureAnalysis.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"sourceArchitectureAnalysis.d.ts","sourceRoot":"","sources":["../src/sourceArchitectureAnalysis.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EACV,gBAAgB,EAEhB,mBAAmB,EAEpB,MAAM,kBAAkB,CAAC;AA2B1B,UAAU,8BAA8B;IACtC,QAAQ,CAAC,OAAO,EAAE,mBAAmB,CAAC;IACtC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;CAC7B;AAED;;GAEG;AACH,wBAAsB,yBAAyB,CAC7C,KAAK,EAAE,8BAA8B,GACpC,OAAO,CAAC,gBAAgB,EAAE,CAAC,CAY7B"}
|