@ankhorage/doctor 0.10.40 → 0.11.1

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.
@@ -1,7 +1,7 @@
1
1
  export type DoctorDiagnosticCode = 'field-invalid' | 'field-missing' | 'invalid-ankh-metadata' | 'invalid-app-manifest-json' | 'invalid-package-json' | 'missing-dependency' | 'missing-path' | 'missing-script' | 'provider-source-import-failed' | 'provider-source-missing' | 'target-not-directory' | 'target-not-found' | 'unsupported-target';
2
2
  export type DoctorDiagnosticSeverity = 'error' | 'warning';
3
3
  export type DoctorPolicyProfile = 'app-manifest' | 'integration-monorepo' | 'public-package' | 'unknown';
4
- export type DoctorRuleId = 'integration.package.ankh.disallowed' | 'integration.package.private.required' | 'integration.package.workspaces.required' | 'manifest.auth.authorization.engine.valid' | 'manifest.auth.authorization.kind.valid' | 'manifest.auth.authorization.valid-shape' | 'manifest.auth.flow.post-sign-in.distinct' | 'manifest.auth.flow.route.required' | 'manifest.auth.flow.route.unique' | 'manifest.auth.flow.route.valid' | 'manifest.auth.flow.unauthorized-route.valid' | 'manifest.auth.flow.valid-shape' | 'manifest.auth.oauth.callback-route.valid' | 'manifest.auth.oauth.callback-target.configured' | 'manifest.auth.oauth.provider.credentials-ref.required' | 'manifest.auth.oauth.provider.credentials-ref.valid' | 'manifest.auth.oauth.provider.duplicate' | 'manifest.auth.oauth.provider.inline-secret.disallowed' | 'manifest.auth.oauth.providers.valid' | 'manifest.auth.oauth.setup.supported' | 'manifest.auth.oauth.valid-shape' | 'manifest.auth.valid-shape' | 'manifest.deploy.targets.enabled' | 'manifest.deploy.targets.legacy-web' | 'manifest.deploy.valid-shape' | 'manifest.infra.valid-shape' | 'manifest.json.readable' | 'manifest.json.valid' | 'manifest.root.valid-shape' | 'manifest.secret-store.provider.required' | 'manifest.secret-store.provider.valid' | 'manifest.secret-store.valid-shape' | 'manifest.settings.auth-flow.removed' | 'package.ankh.capabilities.match-provider' | 'package.ankh.present.valid-shape' | 'package.ankh.provider-path.required' | 'package.ankh.required-for-provider' | 'package.cli.export.required' | 'package.cli.root-file.disallowed' | 'package.dependencies.ankh-workspace-alias.disallowed' | 'package.dependencies.ankhorage4-source.disallowed' | 'package.dependencies.changesets.required' | 'package.dependencies.devtools.required' | 'package.dependencies.paradox.required' | 'package.dependencies.types-bun.policy' | 'package.dependencies.types-bun.required' | 'package.dependencies.types-node.required' | 'package.dependencies.typescript.required' | 'package.imports.ankh-workspace-alias.disallowed' | 'package.imports.ankhorage4-source.disallowed' | 'package.json.bugs.required' | 'package.json.description.required' | 'package.json.exports.required' | 'package.json.files.required' | 'package.json.homepage.required' | 'package.json.keywords.required' | 'package.json.license.required' | 'package.json.name.required' | 'package.json.package-manager.bun' | 'package.json.package-manager.policy' | 'package.json.package-manager.required' | 'package.json.private.public-package-disallowed' | 'package.json.publish-config.public' | 'package.json.publish-config.required' | 'package.json.repository.required' | 'package.json.type.module' | 'package.json.type.required' | 'package.json.version.required' | 'package.scripts.build.required' | 'package.scripts.changeset-status.required' | 'package.scripts.changeset.required' | 'package.scripts.docs.required' | 'package.scripts.format-check.required' | 'package.scripts.format.required' | 'package.scripts.knip.required' | 'package.scripts.lint-fix.required' | 'package.scripts.lint.required' | 'package.scripts.test.required' | 'package.scripts.typecheck.required' | 'package.scripts.version-packages.required' | 'provider.commands.match-capabilities' | 'provider.commands.match-handlers' | 'provider.source.importable' | 'provider.source.required' | 'repo.changelog.required' | 'repo.changeset.required' | 'repo.license.required' | 'repo.readme.required' | 'repo.workflows.ci.bun-policy' | 'repo.workflows.release.bun-policy' | 'repo.workflows.required' | 'studio.dependencies.dnd.required' | 'studio.dependencies.runtime.required' | 'studio.imports.dnd.required' | 'studio.imports.runtime.required' | 'target.package-json.required' | 'target.path.directory' | 'target.path.exists' | 'target.profile.supported' | 'target.repo-markers.required';
4
+ export type DoctorRuleId = 'integration.package.ankh.disallowed' | 'integration.package.private.required' | 'integration.package.workspaces.required' | 'manifest.auth.authorization.engine.valid' | 'manifest.auth.authorization.kind.valid' | 'manifest.auth.authorization.valid-shape' | 'manifest.auth.flow.post-sign-in.distinct' | 'manifest.auth.flow.route.required' | 'manifest.auth.flow.route.unique' | 'manifest.auth.flow.route.valid' | 'manifest.auth.flow.unauthorized-route.valid' | 'manifest.auth.flow.valid-shape' | 'manifest.auth.oauth.callback-route.valid' | 'manifest.auth.oauth.callback-target.configured' | 'manifest.auth.oauth.provider.credentials-ref.required' | 'manifest.auth.oauth.provider.credentials-ref.valid' | 'manifest.auth.oauth.provider.duplicate' | 'manifest.auth.oauth.provider.inline-secret.disallowed' | 'manifest.auth.oauth.providers.valid' | 'manifest.auth.oauth.setup.supported' | 'manifest.auth.oauth.valid-shape' | 'manifest.auth.valid-shape' | 'manifest.deploy.targets.enabled' | 'manifest.deploy.targets.legacy-web' | 'manifest.deploy.valid-shape' | 'manifest.infra.valid-shape' | 'manifest.json.readable' | 'manifest.json.valid' | 'manifest.root.valid-shape' | 'manifest.secret-store.provider.required' | 'manifest.secret-store.provider.valid' | 'manifest.secret-store.valid-shape' | 'manifest.settings.auth-flow.removed' | 'package.ankh.capabilities.match-provider' | 'package.ankh.present.valid-shape' | 'package.ankh.provider-path.required' | 'package.ankh.required-for-provider' | 'package.cli.export.required' | 'package.cli.root-file.disallowed' | 'package.architecture.application-outward-import.disallowed' | 'package.architecture.catch-all-directory.disallowed' | 'package.architecture.domain-outward-import.disallowed' | 'package.architecture.delivery-concrete-adapter-import.disallowed' | 'package.architecture.port-outward-import.disallowed' | 'package.architecture.role-combination.invalid' | 'package.dependencies.ankh-workspace-alias.disallowed' | 'package.dependencies.ankhorage4-source.disallowed' | 'package.dependencies.local-protocol.disallowed' | 'package.dependencies.changesets.required' | 'package.dependencies.devtools.required' | 'package.dependencies.paradox.required' | 'package.dependencies.types-bun.policy' | 'package.dependencies.types-bun.required' | 'package.dependencies.types-node.required' | 'package.dependencies.typescript.required' | 'package.imports.ankh-workspace-alias.disallowed' | 'package.imports.ankhorage4-source.disallowed' | 'package.imports.outside-root.disallowed' | 'package.json.bugs.required' | 'package.json.description.required' | 'package.json.exports.required' | 'package.json.files.required' | 'package.json.homepage.required' | 'package.json.keywords.required' | 'package.json.license.required' | 'package.json.name.required' | 'package.json.package-manager.bun' | 'package.json.package-manager.policy' | 'package.json.package-manager.required' | 'package.json.private.public-package-disallowed' | 'package.json.publish-config.public' | 'package.json.publish-config.required' | 'package.json.repository.required' | 'package.json.type.module' | 'package.json.type.required' | 'package.json.version.required' | 'package.scripts.build.required' | 'package.scripts.changeset-status.required' | 'package.scripts.changeset.required' | 'package.scripts.docs.required' | 'package.scripts.format-check.required' | 'package.scripts.format.required' | 'package.scripts.knip.required' | 'package.scripts.lint-fix.required' | 'package.scripts.lint.required' | 'package.scripts.test.required' | 'package.scripts.standalone.required' | 'package.scripts.typecheck.required' | 'package.scripts.version-packages.required' | 'provider.commands.match-capabilities' | 'provider.commands.match-handlers' | 'provider.source.importable' | 'provider.source.required' | 'repo.changelog.required' | 'repo.changeset.required' | 'repo.license.required' | 'repo.readme.required' | 'repo.workflows.ci.bun-policy' | 'repo.workflows.release.bun-policy' | 'repo.workflows.required' | 'studio.dependencies.dnd.required' | 'studio.dependencies.runtime.required' | 'studio.imports.dnd.required' | 'studio.imports.runtime.required' | 'target.package-json.required' | 'target.path.directory' | 'target.path.exists' | 'target.profile.supported' | 'target.repo-markers.required';
5
5
  export interface DoctorDiagnostic {
6
6
  readonly code: DoctorDiagnosticCode;
7
7
  readonly message: string;
@@ -1 +1 @@
1
- {"version":3,"file":"diagnostics.d.ts","sourceRoot":"","sources":["../src/diagnostics.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"diagnostics.d.ts","sourceRoot":"","sources":["../src/diagnostics.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"diagnostics.js","sourceRoot":"","sources":["../src/diagnostics.ts"],"names":[],"mappings":"AAiIA,MAAM,UAAU,qBAAqB,CAAC,WAAwC;IAC5E,OAAO,WAAW,CAAC,MAAM,CAAC,CAAC,UAAU,EAAE,EAAE,CAAC,UAAU,CAAC,QAAQ,KAAK,OAAO,CAAC,CAAC,MAAM,CAAC;AACpF,CAAC;AAED,MAAM,UAAU,uBAAuB,CAAC,WAAwC;IAC9E,OAAO,WAAW,CAAC,MAAM,CAAC,CAAC,UAAU,EAAE,EAAE,CAAC,UAAU,CAAC,QAAQ,KAAK,SAAS,CAAC,CAAC,MAAM,CAAC;AACtF,CAAC","sourcesContent":["export type DoctorDiagnosticCode =\n | 'field-invalid'\n | 'field-missing'\n | 'invalid-ankh-metadata'\n | 'invalid-app-manifest-json'\n | 'invalid-package-json'\n | 'missing-dependency'\n | 'missing-path'\n | 'missing-script'\n | 'provider-source-import-failed'\n | 'provider-source-missing'\n | 'target-not-directory'\n | 'target-not-found'\n | 'unsupported-target';\n\nexport type DoctorDiagnosticSeverity = 'error' | 'warning';\nexport type DoctorPolicyProfile =\n 'app-manifest' | 'integration-monorepo' | 'public-package' | 'unknown';\nexport type DoctorRuleId =\n | 'integration.package.ankh.disallowed'\n | 'integration.package.private.required'\n | 'integration.package.workspaces.required'\n | 'manifest.auth.authorization.engine.valid'\n | 'manifest.auth.authorization.kind.valid'\n | 'manifest.auth.authorization.valid-shape'\n | 'manifest.auth.flow.post-sign-in.distinct'\n | 'manifest.auth.flow.route.required'\n | 'manifest.auth.flow.route.unique'\n | 'manifest.auth.flow.route.valid'\n | 'manifest.auth.flow.unauthorized-route.valid'\n | 'manifest.auth.flow.valid-shape'\n | 'manifest.auth.oauth.callback-route.valid'\n | 'manifest.auth.oauth.callback-target.configured'\n | 'manifest.auth.oauth.provider.credentials-ref.required'\n | 'manifest.auth.oauth.provider.credentials-ref.valid'\n | 'manifest.auth.oauth.provider.duplicate'\n | 'manifest.auth.oauth.provider.inline-secret.disallowed'\n | 'manifest.auth.oauth.providers.valid'\n | 'manifest.auth.oauth.setup.supported'\n | 'manifest.auth.oauth.valid-shape'\n | 'manifest.auth.valid-shape'\n | 'manifest.deploy.targets.enabled'\n | 'manifest.deploy.targets.legacy-web'\n | 'manifest.deploy.valid-shape'\n | 'manifest.infra.valid-shape'\n | 'manifest.json.readable'\n | 'manifest.json.valid'\n | 'manifest.root.valid-shape'\n | 'manifest.secret-store.provider.required'\n | 'manifest.secret-store.provider.valid'\n | 'manifest.secret-store.valid-shape'\n | 'manifest.settings.auth-flow.removed'\n | 'package.ankh.capabilities.match-provider'\n | 'package.ankh.present.valid-shape'\n | 'package.ankh.provider-path.required'\n | 'package.ankh.required-for-provider'\n | 'package.cli.export.required'\n | 'package.cli.root-file.disallowed'\n | 'package.dependencies.ankh-workspace-alias.disallowed'\n | 'package.dependencies.ankhorage4-source.disallowed'\n | 'package.dependencies.changesets.required'\n | 'package.dependencies.devtools.required'\n | 'package.dependencies.paradox.required'\n | 'package.dependencies.types-bun.policy'\n | 'package.dependencies.types-bun.required'\n | 'package.dependencies.types-node.required'\n | 'package.dependencies.typescript.required'\n | 'package.imports.ankh-workspace-alias.disallowed'\n | 'package.imports.ankhorage4-source.disallowed'\n | 'package.json.bugs.required'\n | 'package.json.description.required'\n | 'package.json.exports.required'\n | 'package.json.files.required'\n | 'package.json.homepage.required'\n | 'package.json.keywords.required'\n | 'package.json.license.required'\n | 'package.json.name.required'\n | 'package.json.package-manager.bun'\n | 'package.json.package-manager.policy'\n | 'package.json.package-manager.required'\n | 'package.json.private.public-package-disallowed'\n | 'package.json.publish-config.public'\n | 'package.json.publish-config.required'\n | 'package.json.repository.required'\n | 'package.json.type.module'\n | 'package.json.type.required'\n | 'package.json.version.required'\n | 'package.scripts.build.required'\n | 'package.scripts.changeset-status.required'\n | 'package.scripts.changeset.required'\n | 'package.scripts.docs.required'\n | 'package.scripts.format-check.required'\n | 'package.scripts.format.required'\n | 'package.scripts.knip.required'\n | 'package.scripts.lint-fix.required'\n | 'package.scripts.lint.required'\n | 'package.scripts.test.required'\n | 'package.scripts.typecheck.required'\n | 'package.scripts.version-packages.required'\n | 'provider.commands.match-capabilities'\n | 'provider.commands.match-handlers'\n | 'provider.source.importable'\n | 'provider.source.required'\n | 'repo.changelog.required'\n | 'repo.changeset.required'\n | 'repo.license.required'\n | 'repo.readme.required'\n | 'repo.workflows.ci.bun-policy'\n | 'repo.workflows.release.bun-policy'\n | 'repo.workflows.required'\n | 'studio.dependencies.dnd.required'\n | 'studio.dependencies.runtime.required'\n | 'studio.imports.dnd.required'\n | 'studio.imports.runtime.required'\n | 'target.package-json.required'\n | 'target.path.directory'\n | 'target.path.exists'\n | 'target.profile.supported'\n | 'target.repo-markers.required';\n\nexport interface DoctorDiagnostic {\n readonly code: DoctorDiagnosticCode;\n readonly message: string;\n readonly path: string;\n readonly profile: DoctorPolicyProfile;\n readonly ruleId: DoctorRuleId;\n readonly severity: DoctorDiagnosticSeverity;\n}\n\nexport function countErrorDiagnostics(diagnostics: readonly DoctorDiagnostic[]): number {\n return diagnostics.filter((diagnostic) => diagnostic.severity === 'error').length;\n}\n\nexport function countWarningDiagnostics(diagnostics: readonly DoctorDiagnostic[]): number {\n return diagnostics.filter((diagnostic) => diagnostic.severity === 'warning').length;\n}\n"]}
1
+ {"version":3,"file":"diagnostics.js","sourceRoot":"","sources":["../src/diagnostics.ts"],"names":[],"mappings":"AA0IA,MAAM,UAAU,qBAAqB,CAAC,WAAwC;IAC5E,OAAO,WAAW,CAAC,MAAM,CAAC,CAAC,UAAU,EAAE,EAAE,CAAC,UAAU,CAAC,QAAQ,KAAK,OAAO,CAAC,CAAC,MAAM,CAAC;AACpF,CAAC;AAED,MAAM,UAAU,uBAAuB,CAAC,WAAwC;IAC9E,OAAO,WAAW,CAAC,MAAM,CAAC,CAAC,UAAU,EAAE,EAAE,CAAC,UAAU,CAAC,QAAQ,KAAK,SAAS,CAAC,CAAC,MAAM,CAAC;AACtF,CAAC","sourcesContent":["export type DoctorDiagnosticCode =\n | 'field-invalid'\n | 'field-missing'\n | 'invalid-ankh-metadata'\n | 'invalid-app-manifest-json'\n | 'invalid-package-json'\n | 'missing-dependency'\n | 'missing-path'\n | 'missing-script'\n | 'provider-source-import-failed'\n | 'provider-source-missing'\n | 'target-not-directory'\n | 'target-not-found'\n | 'unsupported-target';\n\nexport type DoctorDiagnosticSeverity = 'error' | 'warning';\nexport type DoctorPolicyProfile =\n 'app-manifest' | 'integration-monorepo' | 'public-package' | 'unknown';\nexport type DoctorRuleId =\n | 'integration.package.ankh.disallowed'\n | 'integration.package.private.required'\n | 'integration.package.workspaces.required'\n | 'manifest.auth.authorization.engine.valid'\n | 'manifest.auth.authorization.kind.valid'\n | 'manifest.auth.authorization.valid-shape'\n | 'manifest.auth.flow.post-sign-in.distinct'\n | 'manifest.auth.flow.route.required'\n | 'manifest.auth.flow.route.unique'\n | 'manifest.auth.flow.route.valid'\n | 'manifest.auth.flow.unauthorized-route.valid'\n | 'manifest.auth.flow.valid-shape'\n | 'manifest.auth.oauth.callback-route.valid'\n | 'manifest.auth.oauth.callback-target.configured'\n | 'manifest.auth.oauth.provider.credentials-ref.required'\n | 'manifest.auth.oauth.provider.credentials-ref.valid'\n | 'manifest.auth.oauth.provider.duplicate'\n | 'manifest.auth.oauth.provider.inline-secret.disallowed'\n | 'manifest.auth.oauth.providers.valid'\n | 'manifest.auth.oauth.setup.supported'\n | 'manifest.auth.oauth.valid-shape'\n | 'manifest.auth.valid-shape'\n | 'manifest.deploy.targets.enabled'\n | 'manifest.deploy.targets.legacy-web'\n | 'manifest.deploy.valid-shape'\n | 'manifest.infra.valid-shape'\n | 'manifest.json.readable'\n | 'manifest.json.valid'\n | 'manifest.root.valid-shape'\n | 'manifest.secret-store.provider.required'\n | 'manifest.secret-store.provider.valid'\n | 'manifest.secret-store.valid-shape'\n | 'manifest.settings.auth-flow.removed'\n | 'package.ankh.capabilities.match-provider'\n | 'package.ankh.present.valid-shape'\n | 'package.ankh.provider-path.required'\n | 'package.ankh.required-for-provider'\n | 'package.cli.export.required'\n | 'package.cli.root-file.disallowed'\n | 'package.architecture.application-outward-import.disallowed'\n | 'package.architecture.catch-all-directory.disallowed'\n | 'package.architecture.domain-outward-import.disallowed'\n | 'package.architecture.delivery-concrete-adapter-import.disallowed'\n | 'package.architecture.port-outward-import.disallowed'\n | 'package.architecture.role-combination.invalid'\n | 'package.dependencies.ankh-workspace-alias.disallowed'\n | 'package.dependencies.ankhorage4-source.disallowed'\n | 'package.dependencies.local-protocol.disallowed'\n | 'package.dependencies.changesets.required'\n | 'package.dependencies.devtools.required'\n | 'package.dependencies.paradox.required'\n | 'package.dependencies.types-bun.policy'\n | 'package.dependencies.types-bun.required'\n | 'package.dependencies.types-node.required'\n | 'package.dependencies.typescript.required'\n | 'package.imports.ankh-workspace-alias.disallowed'\n | 'package.imports.ankhorage4-source.disallowed'\n | 'package.imports.outside-root.disallowed'\n | 'package.json.bugs.required'\n | 'package.json.description.required'\n | 'package.json.exports.required'\n | 'package.json.files.required'\n | 'package.json.homepage.required'\n | 'package.json.keywords.required'\n | 'package.json.license.required'\n | 'package.json.name.required'\n | 'package.json.package-manager.bun'\n | 'package.json.package-manager.policy'\n | 'package.json.package-manager.required'\n | 'package.json.private.public-package-disallowed'\n | 'package.json.publish-config.public'\n | 'package.json.publish-config.required'\n | 'package.json.repository.required'\n | 'package.json.type.module'\n | 'package.json.type.required'\n | 'package.json.version.required'\n | 'package.scripts.build.required'\n | 'package.scripts.changeset-status.required'\n | 'package.scripts.changeset.required'\n | 'package.scripts.docs.required'\n | 'package.scripts.format-check.required'\n | 'package.scripts.format.required'\n | 'package.scripts.knip.required'\n | 'package.scripts.lint-fix.required'\n | 'package.scripts.lint.required'\n | 'package.scripts.test.required'\n | 'package.scripts.standalone.required'\n | 'package.scripts.typecheck.required'\n | 'package.scripts.version-packages.required'\n | 'provider.commands.match-capabilities'\n | 'provider.commands.match-handlers'\n | 'provider.source.importable'\n | 'provider.source.required'\n | 'repo.changelog.required'\n | 'repo.changeset.required'\n | 'repo.license.required'\n | 'repo.readme.required'\n | 'repo.workflows.ci.bun-policy'\n | 'repo.workflows.release.bun-policy'\n | 'repo.workflows.required'\n | 'studio.dependencies.dnd.required'\n | 'studio.dependencies.runtime.required'\n | 'studio.imports.dnd.required'\n | 'studio.imports.runtime.required'\n | 'target.package-json.required'\n | 'target.path.directory'\n | 'target.path.exists'\n | 'target.profile.supported'\n | 'target.repo-markers.required';\n\nexport interface DoctorDiagnostic {\n readonly code: DoctorDiagnosticCode;\n readonly message: string;\n readonly path: string;\n readonly profile: DoctorPolicyProfile;\n readonly ruleId: DoctorRuleId;\n readonly severity: DoctorDiagnosticSeverity;\n}\n\nexport function countErrorDiagnostics(diagnostics: readonly DoctorDiagnostic[]): number {\n return diagnostics.filter((diagnostic) => diagnostic.severity === 'error').length;\n}\n\nexport function countWarningDiagnostics(diagnostics: readonly DoctorDiagnostic[]): number {\n return diagnostics.filter((diagnostic) => diagnostic.severity === 'warning').length;\n}\n"]}
@@ -0,0 +1,14 @@
1
+ import type { DoctorDiagnostic, DoctorPolicyProfile } from './diagnostics.js';
2
+ interface SourceImport {
3
+ readonly filePath: string;
4
+ readonly specifier: string;
5
+ }
6
+ interface AnalyzeSourceArchitectureInput {
7
+ readonly activeSourceImports: readonly SourceImport[];
8
+ readonly profile: DoctorPolicyProfile;
9
+ readonly targetPath: string;
10
+ }
11
+ /*** Validate folder-role combinations and inward source dependency direction. */
12
+ export declare function analyzeSourceArchitecture(input: AnalyzeSourceArchitectureInput): Promise<DoctorDiagnostic[]>;
13
+ export {};
14
+ //# sourceMappingURL=sourceArchitectureAnalysis.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sourceArchitectureAnalysis.d.ts","sourceRoot":"","sources":["../src/sourceArchitectureAnalysis.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AAE9E,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;AA4BD,iFAAiF;AACjF,wBAAsB,yBAAyB,CAC7C,KAAK,EAAE,8BAA8B,GACpC,OAAO,CAAC,gBAAgB,EAAE,CAAC,CAE7B"}
@@ -0,0 +1,169 @@
1
+ import { promises as fs } from 'node:fs';
2
+ import path from 'node:path';
3
+ const CATCH_ALL_DIRECTORIES = ['common', 'helpers', 'shared'];
4
+ const INNER_ROLE_SEGMENTS = new Set(['domain', 'core']);
5
+ const APPLICATION_ROLE_SEGMENTS = new Set(['application']);
6
+ const PORT_ROLE_SEGMENTS = new Set(['ports']);
7
+ const DOMAIN_OUTWARD_SEGMENTS = new Set([
8
+ 'adapters',
9
+ 'app',
10
+ 'application',
11
+ 'cli',
12
+ 'composition',
13
+ 'host',
14
+ 'infrastructure',
15
+ 'platform',
16
+ ]);
17
+ const APPLICATION_OUTWARD_SEGMENTS = new Set([
18
+ 'adapters',
19
+ 'app',
20
+ 'cli',
21
+ 'composition',
22
+ 'host',
23
+ 'infrastructure',
24
+ 'platform',
25
+ ]);
26
+ const PORT_OUTWARD_SEGMENTS = APPLICATION_OUTWARD_SEGMENTS;
27
+ const INWARD_FEATURE_ROLES = ['application', 'contracts', 'domain', 'planning', 'ports'];
28
+ /*** Validate folder-role combinations and inward source dependency direction. */
29
+ export async function analyzeSourceArchitecture(input) {
30
+ return [...(await analyzeDirectoryVocabularyAsync(input)), ...analyzeImportDirection(input)];
31
+ }
32
+ /*** Validate architectural directory names only when a repository has introduced them. */
33
+ async function analyzeDirectoryVocabularyAsync(input) {
34
+ const diagnostics = [];
35
+ const sourceRoot = path.join(input.targetPath, 'src');
36
+ for (const directoryName of CATCH_ALL_DIRECTORIES) {
37
+ const directoryPath = path.join(sourceRoot, directoryName);
38
+ if (await pathExistsAsync(directoryPath)) {
39
+ diagnostics.push(createDiagnostic(input, directoryPath, 'package.architecture.catch-all-directory.disallowed', `src/${directoryName}/ is a generic catch-all. Move code to an owning domain, feature, package edge, or utils/ according to the repository profile.`));
40
+ }
41
+ }
42
+ diagnostics.push(...(await analyzeFeatureCombinationsAsync(input, sourceRoot)));
43
+ return diagnostics;
44
+ }
45
+ /*** Validate feature-first role combinations without requiring unused ceremonial layers. */
46
+ async function analyzeFeatureCombinationsAsync(input, sourceRoot) {
47
+ const featuresRoot = path.join(sourceRoot, 'features');
48
+ if (!(await pathExistsAsync(featuresRoot)))
49
+ return [];
50
+ const featureEntries = (await fs.readdir(featuresRoot, { withFileTypes: true }))
51
+ .filter((entry) => entry.isDirectory())
52
+ .sort((left, right) => left.name.localeCompare(right.name));
53
+ const diagnostics = [];
54
+ for (const entry of featureEntries) {
55
+ const featureRoot = path.join(featuresRoot, entry.name);
56
+ const roleNames = new Set((await fs.readdir(featureRoot, { withFileTypes: true }))
57
+ .filter((child) => child.isDirectory())
58
+ .map((child) => child.name));
59
+ const hasInwardRole = INWARD_FEATURE_ROLES.some((role) => roleNames.has(role));
60
+ if (roleNames.has('adapters') && !hasInwardRole) {
61
+ diagnostics.push(createDiagnostic(input, path.join(featureRoot, 'adapters'), 'package.architecture.role-combination.invalid', `Feature "${entry.name}" declares adapters/ without domain, application, ports, contracts, or planning policy to adapt to.`));
62
+ }
63
+ if (roleNames.has('composition') &&
64
+ !['adapters', 'application', 'ports', 'planning'].some((role) => roleNames.has(role))) {
65
+ diagnostics.push(createDiagnostic(input, path.join(featureRoot, 'composition'), 'package.architecture.role-combination.invalid', `Feature "${entry.name}" declares composition/ without application, ports, planning, or adapters to wire.`));
66
+ }
67
+ }
68
+ return diagnostics;
69
+ }
70
+ /*** Validate that relative source imports point inward across recognized architecture roles. */
71
+ function analyzeImportDirection(input) {
72
+ return input.activeSourceImports.flatMap((sourceImport) => analyzeOneImportDirection(input, sourceImport));
73
+ }
74
+ /*** Validate one relative import against repository and layer boundaries. */
75
+ function analyzeOneImportDirection(input, sourceImport) {
76
+ if (!sourceImport.specifier.startsWith('.'))
77
+ return [];
78
+ const targetPath = path.resolve(path.dirname(sourceImport.filePath), sourceImport.specifier);
79
+ const escaped = createEscapedRepositoryDiagnostic(input, sourceImport, targetPath);
80
+ if (escaped !== null)
81
+ return [escaped];
82
+ return analyzeRecognizedRoleDirection(input, sourceImport, targetPath);
83
+ }
84
+ /*** Report a relative import that escapes the standalone repository boundary. */
85
+ function createEscapedRepositoryDiagnostic(input, sourceImport, targetPath) {
86
+ const relativeTarget = path.relative(input.targetPath, targetPath);
87
+ if (!relativeTarget.startsWith('..') && !path.isAbsolute(relativeTarget))
88
+ return null;
89
+ return createDiagnostic(input, sourceImport.filePath, 'package.imports.outside-root.disallowed', `Relative import "${sourceImport.specifier}" escapes the standalone repository root.`);
90
+ }
91
+ /*** Apply inward dependency rules for recognized source architecture roles. */
92
+ function analyzeRecognizedRoleDirection(input, sourceImport, targetPath) {
93
+ const sourceSegments = splitRelativePath(input.targetPath, sourceImport.filePath);
94
+ const targetSegments = splitRelativePath(input.targetPath, targetPath);
95
+ if (isThinDeliveryAdapter(sourceSegments) && targetSegments.includes('adapters')) {
96
+ return [
97
+ createDiagnostic(input, sourceImport.filePath, 'package.architecture.delivery-concrete-adapter-import.disallowed', `Thin delivery adapter must not wire concrete adapter implementation through "${sourceImport.specifier}". Import an application operation or composition boundary instead.`),
98
+ ];
99
+ }
100
+ const role = resolveSourceRole(sourceSegments);
101
+ if (role === null)
102
+ return [];
103
+ return createOutwardImportDiagnostic(input, sourceImport, targetSegments, role.forbiddenSegments, role.ruleId, role.label);
104
+ }
105
+ /*** Check whether a source file belongs to a thin CLI command delivery boundary. */
106
+ function isThinDeliveryAdapter(sourceSegments) {
107
+ const cliIndex = sourceSegments.indexOf('cli');
108
+ return cliIndex >= 0 && sourceSegments[cliIndex + 1] === 'commands';
109
+ }
110
+ /*** Resolve the dependency rule owned by one recognized inner source role. */
111
+ function resolveSourceRole(sourceSegments) {
112
+ if (sourceSegments.some((segment) => INNER_ROLE_SEGMENTS.has(segment))) {
113
+ return {
114
+ forbiddenSegments: DOMAIN_OUTWARD_SEGMENTS,
115
+ label: 'Domain/core policy',
116
+ ruleId: 'package.architecture.domain-outward-import.disallowed',
117
+ };
118
+ }
119
+ if (sourceSegments.some((segment) => APPLICATION_ROLE_SEGMENTS.has(segment))) {
120
+ return {
121
+ forbiddenSegments: APPLICATION_OUTWARD_SEGMENTS,
122
+ label: 'Application/use-case code',
123
+ ruleId: 'package.architecture.application-outward-import.disallowed',
124
+ };
125
+ }
126
+ if (sourceSegments.some((segment) => PORT_ROLE_SEGMENTS.has(segment))) {
127
+ return {
128
+ forbiddenSegments: PORT_OUTWARD_SEGMENTS,
129
+ label: 'Port contracts',
130
+ ruleId: 'package.architecture.port-outward-import.disallowed',
131
+ };
132
+ }
133
+ return null;
134
+ }
135
+ /*** Report one outward dependency when the imported path crosses a forbidden role. */
136
+ function createOutwardImportDiagnostic(input, sourceImport, targetSegments, forbiddenSegments, ruleId, roleLabel) {
137
+ const outwardRole = targetSegments.find((segment) => forbiddenSegments.has(segment));
138
+ if (outwardRole === undefined)
139
+ return [];
140
+ return [
141
+ createDiagnostic(input, sourceImport.filePath, ruleId, `${roleLabel} must not import outward ${outwardRole}/ implementation through "${sourceImport.specifier}".`),
142
+ ];
143
+ }
144
+ /*** Split one repository path into normalized architecture segments. */
145
+ function splitRelativePath(targetPath, filePath) {
146
+ return path.relative(targetPath, filePath).split(path.sep).filter(Boolean);
147
+ }
148
+ /*** Build one deterministic architecture diagnostic. */
149
+ function createDiagnostic(input, diagnosticPath, ruleId, message) {
150
+ return {
151
+ code: 'field-invalid',
152
+ message,
153
+ path: diagnosticPath,
154
+ profile: input.profile,
155
+ ruleId,
156
+ severity: 'error',
157
+ };
158
+ }
159
+ /*** Check whether a filesystem path exists. */
160
+ async function pathExistsAsync(targetPath) {
161
+ try {
162
+ await fs.access(targetPath);
163
+ return true;
164
+ }
165
+ catch {
166
+ return false;
167
+ }
168
+ }
169
+ //# sourceMappingURL=sourceArchitectureAnalysis.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sourceArchitectureAnalysis.js","sourceRoot":"","sources":["../src/sourceArchitectureAnalysis.ts"],"names":[],"mappings":"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","sourcesContent":["import { promises as fs } from 'node:fs';\nimport path from 'node:path';\n\nimport type { DoctorDiagnostic, DoctorPolicyProfile } from './diagnostics.js';\n\ninterface SourceImport {\n readonly filePath: string;\n readonly specifier: string;\n}\n\ninterface AnalyzeSourceArchitectureInput {\n readonly activeSourceImports: readonly SourceImport[];\n readonly profile: DoctorPolicyProfile;\n readonly targetPath: string;\n}\n\nconst CATCH_ALL_DIRECTORIES = ['common', 'helpers', 'shared'] as const;\nconst INNER_ROLE_SEGMENTS = new Set(['domain', 'core']);\nconst APPLICATION_ROLE_SEGMENTS = new Set(['application']);\nconst PORT_ROLE_SEGMENTS = new Set(['ports']);\nconst DOMAIN_OUTWARD_SEGMENTS = new Set([\n 'adapters',\n 'app',\n 'application',\n 'cli',\n 'composition',\n 'host',\n 'infrastructure',\n 'platform',\n]);\nconst APPLICATION_OUTWARD_SEGMENTS = new Set([\n 'adapters',\n 'app',\n 'cli',\n 'composition',\n 'host',\n 'infrastructure',\n 'platform',\n]);\nconst PORT_OUTWARD_SEGMENTS = APPLICATION_OUTWARD_SEGMENTS;\nconst INWARD_FEATURE_ROLES = ['application', 'contracts', 'domain', 'planning', 'ports'] as const;\n\n/*** Validate folder-role combinations and inward source dependency direction. */\nexport async function analyzeSourceArchitecture(\n input: AnalyzeSourceArchitectureInput,\n): Promise<DoctorDiagnostic[]> {\n return [...(await analyzeDirectoryVocabularyAsync(input)), ...analyzeImportDirection(input)];\n}\n\n/*** Validate architectural directory names only when a repository has introduced them. */\nasync function analyzeDirectoryVocabularyAsync(\n input: AnalyzeSourceArchitectureInput,\n): Promise<DoctorDiagnostic[]> {\n const diagnostics: DoctorDiagnostic[] = [];\n const sourceRoot = path.join(input.targetPath, 'src');\n\n for (const directoryName of CATCH_ALL_DIRECTORIES) {\n const directoryPath = path.join(sourceRoot, directoryName);\n if (await pathExistsAsync(directoryPath)) {\n diagnostics.push(\n createDiagnostic(\n input,\n directoryPath,\n 'package.architecture.catch-all-directory.disallowed',\n `src/${directoryName}/ is a generic catch-all. Move code to an owning domain, feature, package edge, or utils/ according to the repository profile.`,\n ),\n );\n }\n }\n\n diagnostics.push(...(await analyzeFeatureCombinationsAsync(input, sourceRoot)));\n return diagnostics;\n}\n\n/*** Validate feature-first role combinations without requiring unused ceremonial layers. */\nasync function analyzeFeatureCombinationsAsync(\n input: AnalyzeSourceArchitectureInput,\n sourceRoot: string,\n): Promise<DoctorDiagnostic[]> {\n const featuresRoot = path.join(sourceRoot, 'features');\n if (!(await pathExistsAsync(featuresRoot))) return [];\n\n const featureEntries = (await fs.readdir(featuresRoot, { withFileTypes: true }))\n .filter((entry) => entry.isDirectory())\n .sort((left, right) => left.name.localeCompare(right.name));\n const diagnostics: DoctorDiagnostic[] = [];\n\n for (const entry of featureEntries) {\n const featureRoot = path.join(featuresRoot, entry.name);\n const roleNames = new Set(\n (await fs.readdir(featureRoot, { withFileTypes: true }))\n .filter((child) => child.isDirectory())\n .map((child) => child.name),\n );\n const hasInwardRole = INWARD_FEATURE_ROLES.some((role) => roleNames.has(role));\n\n if (roleNames.has('adapters') && !hasInwardRole) {\n diagnostics.push(\n createDiagnostic(\n input,\n path.join(featureRoot, 'adapters'),\n 'package.architecture.role-combination.invalid',\n `Feature \"${entry.name}\" declares adapters/ without domain, application, ports, contracts, or planning policy to adapt to.`,\n ),\n );\n }\n\n if (\n roleNames.has('composition') &&\n !['adapters', 'application', 'ports', 'planning'].some((role) => roleNames.has(role))\n ) {\n diagnostics.push(\n createDiagnostic(\n input,\n path.join(featureRoot, 'composition'),\n 'package.architecture.role-combination.invalid',\n `Feature \"${entry.name}\" declares composition/ without application, ports, planning, or adapters to wire.`,\n ),\n );\n }\n }\n\n return diagnostics;\n}\n\n/*** Validate that relative source imports point inward across recognized architecture roles. */\nfunction analyzeImportDirection(input: AnalyzeSourceArchitectureInput): DoctorDiagnostic[] {\n return input.activeSourceImports.flatMap((sourceImport) =>\n analyzeOneImportDirection(input, sourceImport),\n );\n}\n\n/*** Validate one relative import against repository and layer boundaries. */\nfunction analyzeOneImportDirection(\n input: AnalyzeSourceArchitectureInput,\n sourceImport: SourceImport,\n): DoctorDiagnostic[] {\n if (!sourceImport.specifier.startsWith('.')) return [];\n\n const targetPath = path.resolve(path.dirname(sourceImport.filePath), sourceImport.specifier);\n const escaped = createEscapedRepositoryDiagnostic(input, sourceImport, targetPath);\n if (escaped !== null) return [escaped];\n\n return analyzeRecognizedRoleDirection(input, sourceImport, targetPath);\n}\n\n/*** Report a relative import that escapes the standalone repository boundary. */\nfunction createEscapedRepositoryDiagnostic(\n input: AnalyzeSourceArchitectureInput,\n sourceImport: SourceImport,\n targetPath: string,\n): DoctorDiagnostic | null {\n const relativeTarget = path.relative(input.targetPath, targetPath);\n if (!relativeTarget.startsWith('..') && !path.isAbsolute(relativeTarget)) return null;\n\n return createDiagnostic(\n input,\n sourceImport.filePath,\n 'package.imports.outside-root.disallowed',\n `Relative import \"${sourceImport.specifier}\" escapes the standalone repository root.`,\n );\n}\n\n/*** Apply inward dependency rules for recognized source architecture roles. */\nfunction analyzeRecognizedRoleDirection(\n input: AnalyzeSourceArchitectureInput,\n sourceImport: SourceImport,\n targetPath: string,\n): DoctorDiagnostic[] {\n const sourceSegments = splitRelativePath(input.targetPath, sourceImport.filePath);\n const targetSegments = splitRelativePath(input.targetPath, targetPath);\n if (isThinDeliveryAdapter(sourceSegments) && targetSegments.includes('adapters')) {\n return [\n createDiagnostic(\n input,\n sourceImport.filePath,\n 'package.architecture.delivery-concrete-adapter-import.disallowed',\n `Thin delivery adapter must not wire concrete adapter implementation through \"${sourceImport.specifier}\". Import an application operation or composition boundary instead.`,\n ),\n ];\n }\n\n const role = resolveSourceRole(sourceSegments);\n if (role === null) return [];\n\n return createOutwardImportDiagnostic(\n input,\n sourceImport,\n targetSegments,\n role.forbiddenSegments,\n role.ruleId,\n role.label,\n );\n}\n\n/*** Check whether a source file belongs to a thin CLI command delivery boundary. */\nfunction isThinDeliveryAdapter(sourceSegments: readonly string[]): boolean {\n const cliIndex = sourceSegments.indexOf('cli');\n return cliIndex >= 0 && sourceSegments[cliIndex + 1] === 'commands';\n}\n\n/*** Resolve the dependency rule owned by one recognized inner source role. */\nfunction resolveSourceRole(sourceSegments: readonly string[]): SourceRoleRule | null {\n if (sourceSegments.some((segment) => INNER_ROLE_SEGMENTS.has(segment))) {\n return {\n forbiddenSegments: DOMAIN_OUTWARD_SEGMENTS,\n label: 'Domain/core policy',\n ruleId: 'package.architecture.domain-outward-import.disallowed',\n };\n }\n if (sourceSegments.some((segment) => APPLICATION_ROLE_SEGMENTS.has(segment))) {\n return {\n forbiddenSegments: APPLICATION_OUTWARD_SEGMENTS,\n label: 'Application/use-case code',\n ruleId: 'package.architecture.application-outward-import.disallowed',\n };\n }\n if (sourceSegments.some((segment) => PORT_ROLE_SEGMENTS.has(segment))) {\n return {\n forbiddenSegments: PORT_OUTWARD_SEGMENTS,\n label: 'Port contracts',\n ruleId: 'package.architecture.port-outward-import.disallowed',\n };\n }\n return null;\n}\n\ninterface SourceRoleRule {\n readonly forbiddenSegments: ReadonlySet<string>;\n readonly label: string;\n readonly ruleId:\n | 'package.architecture.application-outward-import.disallowed'\n | 'package.architecture.domain-outward-import.disallowed'\n | 'package.architecture.port-outward-import.disallowed';\n}\n\n/*** Report one outward dependency when the imported path crosses a forbidden role. */\nfunction createOutwardImportDiagnostic(\n input: AnalyzeSourceArchitectureInput,\n sourceImport: SourceImport,\n targetSegments: readonly string[],\n forbiddenSegments: ReadonlySet<string>,\n ruleId:\n | 'package.architecture.application-outward-import.disallowed'\n | 'package.architecture.domain-outward-import.disallowed'\n | 'package.architecture.port-outward-import.disallowed',\n roleLabel: string,\n): DoctorDiagnostic[] {\n const outwardRole = targetSegments.find((segment) => forbiddenSegments.has(segment));\n if (outwardRole === undefined) return [];\n\n return [\n createDiagnostic(\n input,\n sourceImport.filePath,\n ruleId,\n `${roleLabel} must not import outward ${outwardRole}/ implementation through \"${sourceImport.specifier}\".`,\n ),\n ];\n}\n\n/*** Split one repository path into normalized architecture segments. */\nfunction splitRelativePath(targetPath: string, filePath: string): readonly string[] {\n return path.relative(targetPath, filePath).split(path.sep).filter(Boolean);\n}\n\n/*** Build one deterministic architecture diagnostic. */\nfunction createDiagnostic(\n input: AnalyzeSourceArchitectureInput,\n diagnosticPath: string,\n ruleId: DoctorDiagnostic['ruleId'],\n message: string,\n): DoctorDiagnostic {\n return {\n code: 'field-invalid',\n message,\n path: diagnosticPath,\n profile: input.profile,\n ruleId,\n severity: 'error',\n };\n}\n\n/*** Check whether a filesystem path exists. */\nasync function pathExistsAsync(targetPath: string): Promise<boolean> {\n try {\n await fs.access(targetPath);\n return true;\n } catch {\n return false;\n }\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ankhorage/doctor",
3
- "version": "0.10.40",
3
+ "version": "0.11.1",
4
4
  "description": "Executable Ankh provider and standalone CLI for lightweight repo and package compliance diagnostics.",
5
5
  "license": "MIT",
6
6
  "publishConfig": {
@@ -74,10 +74,11 @@
74
74
  "changeset": "ankhorage-changeset",
75
75
  "changeset:status": "ankhorage-changeset status --since=origin/main",
76
76
  "version-packages": "ankhorage-changeset version",
77
- "knip:check": "ankhorage-knip"
77
+ "knip:check": "ankhorage-knip",
78
+ "test:standalone": "bun test tests/standaloneContract.test.ts"
78
79
  },
79
80
  "dependencies": {
80
- "@ankhorage/contracts": "^22.8.1",
81
+ "@ankhorage/contracts": "^22.8.2",
81
82
  "@ankhorage/supabase-auth": "^1.2.26"
82
83
  },
83
84
  "devDependencies": {
package/src/analysis.ts CHANGED
@@ -71,6 +71,7 @@ const REQUIRED_PUBLIC_PACKAGE_SCRIPTS = [
71
71
  'format',
72
72
  'format:check',
73
73
  'test',
74
+ 'test:standalone',
74
75
  'knip:check',
75
76
  'docs',
76
77
  'changeset',
@@ -1225,6 +1226,8 @@ function mapScriptRule(scriptName: (typeof REQUIRED_PUBLIC_PACKAGE_SCRIPTS)[numb
1225
1226
  return 'package.scripts.format-check.required';
1226
1227
  case 'test':
1227
1228
  return 'package.scripts.test.required';
1229
+ case 'test:standalone':
1230
+ return 'package.scripts.standalone.required';
1228
1231
  case 'knip:check':
1229
1232
  return 'package.scripts.knip.required';
1230
1233
  case 'docs':
@@ -8,6 +8,7 @@ import {
8
8
  } from './analysis.js';
9
9
  import type { DoctorDiagnostic, DoctorPolicyProfile } from './diagnostics.js';
10
10
  import { analyzeAppManifestTarget } from './manifestAnalysis.js';
11
+ import { analyzeSourceArchitecture } from './sourceArchitectureAnalysis.js';
11
12
 
12
13
  const LEGACY_ROOT_CLI_SOURCE = path.join('src', 'cli.ts');
13
14
  const ACTIVE_SOURCE_ROOTS = ['src', 'app', 'apps', 'packages', 'scripts'] as const;
@@ -147,6 +148,13 @@ async function analyzeTargetArchitecture(request: {
147
148
  profile: request.profile,
148
149
  }),
149
150
  );
151
+ diagnostics.push(
152
+ ...(await analyzeSourceArchitecture({
153
+ activeSourceImports,
154
+ profile: request.profile,
155
+ targetPath: request.targetPath,
156
+ })),
157
+ );
150
158
 
151
159
  if (request.packageJson.name === STUDIO_PACKAGE_NAME) {
152
160
  diagnostics.push(
@@ -181,6 +189,20 @@ function validateDependencyArchitecture(request: {
181
189
  });
182
190
  }
183
191
 
192
+ if (
193
+ request.profile === 'public-package' &&
194
+ /^(?:file:|link:|workspace:|github:|git(?:\+[^:]+)?:)/u.test(dependency.version)
195
+ ) {
196
+ diagnostics.push({
197
+ code: 'field-invalid',
198
+ message: `Published dependency "${dependency.packageName}" must resolve from a registry version, not "${dependency.version}". Standalone packages cannot rely on local/workspace/Git package state.`,
199
+ path: request.packageJsonPath,
200
+ profile: request.profile,
201
+ ruleId: 'package.dependencies.local-protocol.disallowed',
202
+ severity: 'error',
203
+ });
204
+ }
205
+
184
206
  if (
185
207
  dependency.packageName.includes('ankhorage4') ||
186
208
  dependency.version.includes('ankhorage4')
@@ -56,8 +56,15 @@ export type DoctorRuleId =
56
56
  | 'package.ankh.required-for-provider'
57
57
  | 'package.cli.export.required'
58
58
  | 'package.cli.root-file.disallowed'
59
+ | 'package.architecture.application-outward-import.disallowed'
60
+ | 'package.architecture.catch-all-directory.disallowed'
61
+ | 'package.architecture.domain-outward-import.disallowed'
62
+ | 'package.architecture.delivery-concrete-adapter-import.disallowed'
63
+ | 'package.architecture.port-outward-import.disallowed'
64
+ | 'package.architecture.role-combination.invalid'
59
65
  | 'package.dependencies.ankh-workspace-alias.disallowed'
60
66
  | 'package.dependencies.ankhorage4-source.disallowed'
67
+ | 'package.dependencies.local-protocol.disallowed'
61
68
  | 'package.dependencies.changesets.required'
62
69
  | 'package.dependencies.devtools.required'
63
70
  | 'package.dependencies.paradox.required'
@@ -67,6 +74,7 @@ export type DoctorRuleId =
67
74
  | 'package.dependencies.typescript.required'
68
75
  | 'package.imports.ankh-workspace-alias.disallowed'
69
76
  | 'package.imports.ankhorage4-source.disallowed'
77
+ | 'package.imports.outside-root.disallowed'
70
78
  | 'package.json.bugs.required'
71
79
  | 'package.json.description.required'
72
80
  | 'package.json.exports.required'
@@ -95,6 +103,7 @@ export type DoctorRuleId =
95
103
  | 'package.scripts.lint-fix.required'
96
104
  | 'package.scripts.lint.required'
97
105
  | 'package.scripts.test.required'
106
+ | 'package.scripts.standalone.required'
98
107
  | 'package.scripts.typecheck.required'
99
108
  | 'package.scripts.version-packages.required'
100
109
  | 'provider.commands.match-capabilities'