@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.
Files changed (34) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/README.md +1 -1
  3. package/dist/analysis.js +22 -22
  4. package/dist/analysis.js.map +1 -1
  5. package/dist/bunPolicyAnalysis.d.ts.map +1 -1
  6. package/dist/bunPolicyAnalysis.js +5 -14
  7. package/dist/bunPolicyAnalysis.js.map +1 -1
  8. package/dist/cliLayoutAnalysis.d.ts.map +1 -1
  9. package/dist/cliLayoutAnalysis.js +39 -29
  10. package/dist/cliLayoutAnalysis.js.map +1 -1
  11. package/dist/repositorySourceRulesAnalysis.d.ts +16 -0
  12. package/dist/repositorySourceRulesAnalysis.d.ts.map +1 -0
  13. package/dist/repositorySourceRulesAnalysis.js +103 -0
  14. package/dist/repositorySourceRulesAnalysis.js.map +1 -0
  15. package/dist/sourceArchitectureAnalysis.d.ts +3 -6
  16. package/dist/sourceArchitectureAnalysis.d.ts.map +1 -1
  17. package/dist/sourceArchitectureAnalysis.js +93 -97
  18. package/dist/sourceArchitectureAnalysis.js.map +1 -1
  19. package/dist/utils/getRepositoryRule.d.ts +9 -0
  20. package/dist/utils/getRepositoryRule.d.ts.map +1 -0
  21. package/dist/utils/getRepositoryRule.js +15 -0
  22. package/dist/utils/getRepositoryRule.js.map +1 -0
  23. package/package.json +7 -5
  24. package/src/analysis.ts +22 -24
  25. package/src/bunPolicyAnalysis.ts +7 -17
  26. package/src/cliLayoutAnalysis.ts +51 -30
  27. package/src/repositorySourceRulesAnalysis.ts +153 -0
  28. package/src/sourceArchitectureAnalysis.ts +102 -177
  29. package/src/utils/getRepositoryRule.ts +20 -0
  30. package/dist/utils/getArchitecturePolicyRule.d.ts +0 -286
  31. package/dist/utils/getArchitecturePolicyRule.d.ts.map +0 -1
  32. package/dist/utils/getArchitecturePolicyRule.js +0 -12
  33. package/dist/utils/getArchitecturePolicyRule.js.map +0 -1
  34. package/src/utils/getArchitecturePolicyRule.ts +0 -15
@@ -1 +1 @@
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_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;AAwDvB,wBAAsB,gCAAgC,CACpD,OAAO,EAAE,qBAAqB,GAC7B,OAAO,CAAC,oBAAoB,CAAC,CAkC/B"}
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 { ARCHITECTURE_POLICY } from '@ankhorage/policy/architecture';
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 { getArchitecturePolicyRule } from './utils/getArchitecturePolicyRule.js';
8
- const CLI_POLICY = ARCHITECTURE_POLICY.cli;
9
- const DEPENDENCY_POLICY = ARCHITECTURE_POLICY.dependencies;
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
- ...architectureRuleFields(CLI_POLICY.legacyRootRuleId),
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
- ...architectureRuleFields(CLI_POLICY.exportRuleId),
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 activeSourceImports = await collectActiveSourceImports(request.targetPath);
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
- ...architectureRuleFields(DEPENDENCY_POLICY.rules.compatibilityDependency),
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
- ...architectureRuleFields(DEPENDENCY_POLICY.rules.localProtocolDependency),
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
- ...architectureRuleFields(DEPENDENCY_POLICY.rules.legacySourceDependency),
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
- ...architectureRuleFields(DEPENDENCY_POLICY.rules.compatibilityImport),
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
- ...architectureRuleFields(DEPENDENCY_POLICY.rules.legacySourceImport),
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
- async function collectActiveSourceImports(targetPath) {
233
- const imports = [];
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 (!(await pathExists(sourceRootPath))) {
237
- continue;
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
- return imports;
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 Policy-owned rule metadata for generic architecture diagnostics. */
321
- function architectureRuleFields(ruleId) {
322
- const rule = getArchitecturePolicyRule(ruleId);
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":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,OAAO,EACL,kBAAkB,EAClB,mBAAmB,GAGpB,MAAM,6BAA6B,CAAC;AASrC,MAAM,+BAA+B,GAAG;IACtC,mBAAmB,CAAC,iBAAiB;IACrC,mBAAmB,CAAC,iBAAiB;CACK,CAAC;AAY7C;;GAEG;AACH,MAAM,UAAU,4BAA4B,CAC1C,KAAiC;IAEjC,MAAM,MAAM,GAAG,kBAAkB,CAC/B;QACE,WAAW,EAAE,EAAE;QACf,OAAO,EAAE,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,YAAY,EAAE,EAAE,CAAC,YAAY,CAAC,KAAK,CAAC,UAAU,EAAE,YAAY,CAAC,CAAC;QAC1F,WAAW,EAAE;YACX,YAAY,EAAE,EAAE;YAChB,OAAO,EAAE,EAAE;YACX,MAAM,EAAE,EAAE;YACV,OAAO,EAAE,EAAE;SACZ;QACD,KAAK,EAAE,qBAAqB,CAAC,KAAK,CAAC,UAAU,EAAE,KAAK,CAAC,KAAK,CAAC;QAC3D,mBAAmB,EAAE,EAAE;KACxB,EACD;QACE,MAAM,EAAE;YACN,OAAO,EAAE,CAAC;YACV,KAAK,EAAE,+BAA+B,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC,CAAC;SAC5E;KACF,CACF,CAAC;IAEF,OAAO,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,kBAAkB,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC;AAC9E,CAAC;AAED;;GAEG;AACH,SAAS,YAAY,CACnB,UAAkB,EAClB,YAA2D;IAE3D,OAAO;QACL,IAAI,EAAE,sBAAsB,CAAC,UAAU,EAAE,YAAY,CAAC,QAAQ,CAAC;QAC/D,SAAS,EAAE,YAAY,CAAC,SAAS;QACjC,GAAG,CAAC,YAAY,CAAC,SAAS,CAAC,UAAU,CAAC,GAAG,CAAC;YACxC,CAAC,CAAC,EAAE,qBAAqB,EAAE,uBAAuB,CAAC,UAAU,EAAE,YAAY,CAAC,EAAE;YAC9E,CAAC,CAAC,EAAE,CAAC;KACR,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,SAAS,uBAAuB,CAC9B,UAAkB,EAClB,YAA2D;IAE3D,MAAM,cAAc,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,YAAY,CAAC,QAAQ,CAAC,EAAE,YAAY,CAAC,SAAS,CAAC,CAAC;IACjG,MAAM,cAAc,GAAG,IAAI,CAAC,QAAQ,CAAC,UAAU,EAAE,cAAc,CAAC,CAAC;IACjE,OAAO,cAAc,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,UAAU,CAAC,cAAc,CAAC,CAAC;AAC5E,CAAC;AAED;;GAEG;AACH,SAAS,qBAAqB,CAC5B,UAAkB,EAClB,KAAwB;IAExB,MAAM,WAAW,GAAG,IAAI,GAAG,EAAU,CAAC;IAEtC,KAAK,MAAM,QAAQ,IAAI,KAAK,EAAE,CAAC;QAC7B,MAAM,QAAQ,GAAG,sBAAsB,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;QACzF,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,QAAQ,CAAC,MAAM,EAAE,KAAK,IAAI,CAAC,EAAE,CAAC;YACxD,WAAW,CAAC,GAAG,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;QACtD,CAAC;IACH,CAAC;IAED,OAAO,CAAC,GAAG,WAAW,CAAC;SACpB,IAAI,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,IAAI,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC;SAChD,GAAG,CAAC,CAAC,aAAa,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,WAAoB,EAAE,IAAI,EAAE,aAAa,EAAE,CAAC,CAAC,CAAC;AACnF,CAAC;AAED;;GAEG;AACH,SAAS,sBAAsB,CAAC,UAAkB,EAAE,QAAgB;IAClE,OAAO,IAAI,CAAC,QAAQ,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AACvE,CAAC;AAED;;GAEG;AACH,SAAS,kBAAkB,CACzB,KAAiC,EACjC,OAAkE;IAElE,MAAM,WAAW,GAAG,OAAO,CAAC,cAAc,EAAE,IAAI,IAAI,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,IAAI,IAAI,GAAG,CAAC;IACrF,OAAO;QACL,IAAI,EAAE,eAAe;QACrB,OAAO,EAAE,OAAO,CAAC,OAAO;QACxB,IAAI,EAAE,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,UAAU,EAAE,WAAW,CAAC;QACjD,OAAO,EAAE,KAAK,CAAC,OAAO;QACtB,MAAM,EAAE,cAAc,CAAC,OAAO,CAAC,MAAM,CAAC;QACtC,QAAQ,EAAE,gBAAgB,CAAC,OAAO,CAAC,QAAQ,CAAC;KAC7C,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,SAAS,cAAc,CAAC,MAAc;IACpC,MAAM,KAAK,GAAG,+BAA+B,CAAC,IAAI,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,SAAS,KAAK,MAAM,CAAC,CAAC;IACxF,IAAI,KAAK,KAAK,SAAS;QAAE,MAAM,IAAI,KAAK,CAAC,4CAA4C,GAAG,MAAM,CAAC,CAAC;IAChG,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;GAEG;AACH,SAAS,gBAAgB,CAAC,QAAsC;IAC9D,IAAI,QAAQ,KAAK,MAAM,EAAE,CAAC;QACxB,MAAM,IAAI,KAAK,CAAC,4DAA4D,CAAC,CAAC;IAChF,CAAC;IACD,OAAO,QAAQ,CAAC;AAClB,CAAC","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
- /*** Validate folder-role combinations and inward source dependency direction. */
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,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AAG9E,UAAU,YAAY;IACpB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC5B;AAED,UAAU,8BAA8B;IACtC,QAAQ,CAAC,mBAAmB,EAAE,SAAS,YAAY,EAAE,CAAC;IACtD,QAAQ,CAAC,OAAO,EAAE,mBAAmB,CAAC;IACtC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;CAC7B;AAID,iFAAiF;AACjF,wBAAsB,yBAAyB,CAC7C,KAAK,EAAE,8BAA8B,GACpC,OAAO,CAAC,gBAAgB,EAAE,CAAC,CAE7B"}
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"}