@ontrails/warden 1.0.0-beta.13 → 1.0.0-beta.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-lint.log +1 -1
- package/CHANGELOG.md +30 -0
- package/README.md +31 -20
- package/dist/cli.d.ts +19 -2
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +261 -64
- package/dist/cli.js.map +1 -1
- package/dist/draft.d.ts +5 -0
- package/dist/draft.d.ts.map +1 -0
- package/dist/draft.js +16 -0
- package/dist/draft.js.map +1 -0
- package/dist/drift.d.ts +10 -7
- package/dist/drift.d.ts.map +1 -1
- package/dist/drift.js +50 -16
- package/dist/drift.js.map +1 -1
- package/dist/formatters.d.ts +2 -1
- package/dist/formatters.d.ts.map +1 -1
- package/dist/formatters.js +15 -4
- package/dist/formatters.js.map +1 -1
- package/dist/index.d.ts +9 -17
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +10 -17
- package/dist/index.js.map +1 -1
- package/dist/rules/ast.d.ts +412 -7
- package/dist/rules/ast.d.ts.map +1 -1
- package/dist/rules/ast.js +1847 -102
- package/dist/rules/ast.js.map +1 -1
- package/dist/rules/circular-refs.d.ts +6 -0
- package/dist/rules/circular-refs.d.ts.map +1 -0
- package/dist/rules/circular-refs.js +83 -0
- package/dist/rules/circular-refs.js.map +1 -0
- package/dist/rules/context-no-surface-types.d.ts.map +1 -1
- package/dist/rules/context-no-surface-types.js +59 -3
- package/dist/rules/context-no-surface-types.js.map +1 -1
- package/dist/rules/contour-exists.d.ts +7 -0
- package/dist/rules/contour-exists.d.ts.map +1 -0
- package/dist/rules/contour-exists.js +113 -0
- package/dist/rules/contour-exists.js.map +1 -0
- package/dist/rules/contour-ids.d.ts +10 -0
- package/dist/rules/contour-ids.d.ts.map +1 -0
- package/dist/rules/contour-ids.js +12 -0
- package/dist/rules/contour-ids.js.map +1 -0
- package/dist/rules/cross-declarations.d.ts.map +1 -1
- package/dist/rules/cross-declarations.js +171 -57
- package/dist/rules/cross-declarations.js.map +1 -1
- package/dist/rules/dead-internal-trail.d.ts +3 -0
- package/dist/rules/dead-internal-trail.d.ts.map +1 -0
- package/dist/rules/dead-internal-trail.js +80 -0
- package/dist/rules/dead-internal-trail.js.map +1 -0
- package/dist/rules/draft-file-marking.d.ts +6 -0
- package/dist/rules/draft-file-marking.d.ts.map +1 -0
- package/dist/rules/draft-file-marking.js +87 -0
- package/dist/rules/draft-file-marking.js.map +1 -0
- package/dist/rules/draft-visible-debt.d.ts +12 -0
- package/dist/rules/draft-visible-debt.d.ts.map +1 -0
- package/dist/rules/draft-visible-debt.js +50 -0
- package/dist/rules/draft-visible-debt.js.map +1 -0
- package/dist/rules/error-mapping-completeness.d.ts +13 -0
- package/dist/rules/error-mapping-completeness.d.ts.map +1 -0
- package/dist/rules/error-mapping-completeness.js +160 -0
- package/dist/rules/error-mapping-completeness.js.map +1 -0
- package/dist/rules/example-valid.d.ts +6 -0
- package/dist/rules/example-valid.d.ts.map +1 -0
- package/dist/rules/example-valid.js +203 -0
- package/dist/rules/example-valid.js.map +1 -0
- package/dist/rules/fires-declarations.d.ts +16 -0
- package/dist/rules/fires-declarations.d.ts.map +1 -0
- package/dist/rules/fires-declarations.js +444 -0
- package/dist/rules/fires-declarations.js.map +1 -0
- package/dist/rules/implementation-returns-result.d.ts +9 -0
- package/dist/rules/implementation-returns-result.d.ts.map +1 -1
- package/dist/rules/implementation-returns-result.js +638 -76
- package/dist/rules/implementation-returns-result.js.map +1 -1
- package/dist/rules/incomplete-accessor-for-standard-op.d.ts +30 -0
- package/dist/rules/incomplete-accessor-for-standard-op.d.ts.map +1 -0
- package/dist/rules/incomplete-accessor-for-standard-op.js +226 -0
- package/dist/rules/incomplete-accessor-for-standard-op.js.map +1 -0
- package/dist/rules/incomplete-crud.d.ts +21 -0
- package/dist/rules/incomplete-crud.d.ts.map +1 -0
- package/dist/rules/incomplete-crud.js +368 -0
- package/dist/rules/incomplete-crud.js.map +1 -0
- package/dist/rules/index.d.ts +40 -7
- package/dist/rules/index.d.ts.map +1 -1
- package/dist/rules/index.js +91 -15
- package/dist/rules/index.js.map +1 -1
- package/dist/rules/intent-propagation.d.ts +3 -0
- package/dist/rules/intent-propagation.d.ts.map +1 -0
- package/dist/rules/intent-propagation.js +57 -0
- package/dist/rules/intent-propagation.js.map +1 -0
- package/dist/rules/missing-reconcile.d.ts +3 -0
- package/dist/rules/missing-reconcile.d.ts.map +1 -0
- package/dist/rules/missing-reconcile.js +44 -0
- package/dist/rules/missing-reconcile.js.map +1 -0
- package/dist/rules/missing-visibility.d.ts +3 -0
- package/dist/rules/missing-visibility.d.ts.map +1 -0
- package/dist/rules/missing-visibility.js +63 -0
- package/dist/rules/missing-visibility.js.map +1 -0
- package/dist/rules/no-direct-impl-in-route.d.ts.map +1 -1
- package/dist/rules/no-direct-impl-in-route.js +0 -3
- package/dist/rules/no-direct-impl-in-route.js.map +1 -1
- package/dist/rules/no-direct-implementation-call.js +1 -1
- package/dist/rules/no-direct-implementation-call.js.map +1 -1
- package/dist/rules/no-sync-result-assumption.d.ts.map +1 -1
- package/dist/rules/no-sync-result-assumption.js +870 -61
- package/dist/rules/no-sync-result-assumption.js.map +1 -1
- package/dist/rules/no-throw-in-detour-recover.d.ts +3 -0
- package/dist/rules/no-throw-in-detour-recover.d.ts.map +1 -0
- package/dist/rules/no-throw-in-detour-recover.js +147 -0
- package/dist/rules/no-throw-in-detour-recover.js.map +1 -0
- package/dist/rules/no-throw-in-detour-target.d.ts +4 -1
- package/dist/rules/no-throw-in-detour-target.d.ts.map +1 -1
- package/dist/rules/no-throw-in-detour-target.js +6 -3
- package/dist/rules/no-throw-in-detour-target.js.map +1 -1
- package/dist/rules/no-throw-in-implementation.d.ts +4 -2
- package/dist/rules/no-throw-in-implementation.d.ts.map +1 -1
- package/dist/rules/no-throw-in-implementation.js +6 -4
- package/dist/rules/no-throw-in-implementation.js.map +1 -1
- package/dist/rules/on-references-exist.d.ts +14 -0
- package/dist/rules/on-references-exist.d.ts.map +1 -0
- package/dist/rules/on-references-exist.js +109 -0
- package/dist/rules/on-references-exist.js.map +1 -0
- package/dist/rules/orphaned-signal.d.ts +3 -0
- package/dist/rules/orphaned-signal.d.ts.map +1 -0
- package/dist/rules/orphaned-signal.js +67 -0
- package/dist/rules/orphaned-signal.js.map +1 -0
- package/dist/rules/permit-governance.d.ts +3 -0
- package/dist/rules/permit-governance.d.ts.map +1 -0
- package/dist/rules/permit-governance.js +15 -0
- package/dist/rules/permit-governance.js.map +1 -0
- package/dist/rules/reference-exists.d.ts +6 -0
- package/dist/rules/reference-exists.d.ts.map +1 -0
- package/dist/rules/reference-exists.js +47 -0
- package/dist/rules/reference-exists.js.map +1 -0
- package/dist/rules/registry-names.d.ts +8 -0
- package/dist/rules/registry-names.d.ts.map +1 -0
- package/dist/rules/registry-names.js +83 -0
- package/dist/rules/registry-names.js.map +1 -0
- package/dist/rules/resource-declarations.d.ts +14 -0
- package/dist/rules/resource-declarations.d.ts.map +1 -0
- package/dist/rules/resource-declarations.js +413 -0
- package/dist/rules/resource-declarations.js.map +1 -0
- package/dist/rules/resource-exists.d.ts +6 -0
- package/dist/rules/resource-exists.d.ts.map +1 -0
- package/dist/rules/resource-exists.js +90 -0
- package/dist/rules/resource-exists.js.map +1 -0
- package/dist/rules/resource-id-grammar.d.ts +3 -0
- package/dist/rules/resource-id-grammar.d.ts.map +1 -0
- package/dist/rules/resource-id-grammar.js +39 -0
- package/dist/rules/resource-id-grammar.js.map +1 -0
- package/dist/rules/specs.d.ts.map +1 -1
- package/dist/rules/specs.js +5 -1
- package/dist/rules/specs.js.map +1 -1
- package/dist/rules/types.d.ts +53 -4
- package/dist/rules/types.d.ts.map +1 -1
- package/dist/rules/unreachable-detour-shadowing.d.ts +3 -0
- package/dist/rules/unreachable-detour-shadowing.d.ts.map +1 -0
- package/dist/rules/unreachable-detour-shadowing.js +202 -0
- package/dist/rules/unreachable-detour-shadowing.js.map +1 -0
- package/dist/rules/valid-describe-refs.d.ts.map +1 -1
- package/dist/rules/valid-describe-refs.js +132 -16
- package/dist/rules/valid-describe-refs.js.map +1 -1
- package/dist/rules/valid-detour-contract.d.ts +3 -0
- package/dist/rules/valid-detour-contract.d.ts.map +1 -0
- package/dist/rules/valid-detour-contract.js +47 -0
- package/dist/rules/valid-detour-contract.js.map +1 -0
- package/dist/rules/valid-detour-refs.d.ts.map +1 -1
- package/dist/rules/valid-detour-refs.js +73 -82
- package/dist/rules/valid-detour-refs.js.map +1 -1
- package/dist/rules/warden-export-symmetry.d.ts +7 -0
- package/dist/rules/warden-export-symmetry.d.ts.map +1 -0
- package/dist/rules/warden-export-symmetry.js +352 -0
- package/dist/rules/warden-export-symmetry.js.map +1 -0
- package/dist/rules/warden-rules-use-ast.d.ts +17 -0
- package/dist/rules/warden-rules-use-ast.d.ts.map +1 -0
- package/dist/rules/warden-rules-use-ast.js +778 -0
- package/dist/rules/warden-rules-use-ast.js.map +1 -0
- package/dist/trails/circular-refs.trail.d.ts +24 -0
- package/dist/trails/circular-refs.trail.d.ts.map +1 -0
- package/dist/trails/circular-refs.trail.js +29 -0
- package/dist/trails/circular-refs.trail.js.map +1 -0
- package/dist/trails/context-no-surface-types.trail.d.ts +2 -2
- package/dist/trails/context-no-surface-types.trail.d.ts.map +1 -1
- package/dist/trails/context-no-trailhead-types.trail.d.ts +2 -2
- package/dist/trails/context-no-trailhead-types.trail.d.ts.map +1 -1
- package/dist/trails/contour-exists.trail.d.ts +24 -0
- package/dist/trails/contour-exists.trail.d.ts.map +1 -0
- package/dist/trails/contour-exists.trail.js +21 -0
- package/dist/trails/contour-exists.trail.js.map +1 -0
- package/dist/trails/cross-declarations.trail.d.ts +2 -2
- package/dist/trails/cross-declarations.trail.d.ts.map +1 -1
- package/dist/trails/dead-internal-trail.trail.d.ts +24 -0
- package/dist/trails/dead-internal-trail.trail.d.ts.map +1 -0
- package/dist/trails/dead-internal-trail.trail.js +26 -0
- package/dist/trails/dead-internal-trail.trail.js.map +1 -0
- package/dist/trails/{provision-declarations.trail.d.ts → draft-file-marking.trail.d.ts} +3 -3
- package/dist/trails/draft-file-marking.trail.d.ts.map +1 -0
- package/dist/trails/draft-file-marking.trail.js +16 -0
- package/dist/trails/draft-file-marking.trail.js.map +1 -0
- package/dist/trails/draft-visible-debt.trail.d.ts +13 -0
- package/dist/trails/draft-visible-debt.trail.d.ts.map +1 -0
- package/dist/trails/draft-visible-debt.trail.js +16 -0
- package/dist/trails/draft-visible-debt.trail.js.map +1 -0
- package/dist/trails/error-mapping-completeness.trail.d.ts +13 -0
- package/dist/trails/error-mapping-completeness.trail.d.ts.map +1 -0
- package/dist/trails/error-mapping-completeness.trail.js +29 -0
- package/dist/trails/error-mapping-completeness.trail.js.map +1 -0
- package/dist/trails/{follow-declarations.trail.d.ts → example-valid.trail.d.ts} +3 -3
- package/dist/trails/example-valid.trail.d.ts.map +1 -0
- package/dist/trails/example-valid.trail.js +25 -0
- package/dist/trails/example-valid.trail.js.map +1 -0
- package/dist/trails/fires-declarations.trail.d.ts +13 -0
- package/dist/trails/fires-declarations.trail.d.ts.map +1 -0
- package/dist/trails/fires-declarations.trail.js +22 -0
- package/dist/trails/fires-declarations.trail.js.map +1 -0
- package/dist/trails/implementation-returns-result.trail.d.ts +2 -2
- package/dist/trails/implementation-returns-result.trail.d.ts.map +1 -1
- package/dist/trails/incomplete-accessor-for-standard-op.trail.d.ts +12 -0
- package/dist/trails/incomplete-accessor-for-standard-op.trail.d.ts.map +1 -0
- package/dist/trails/incomplete-accessor-for-standard-op.trail.js +60 -0
- package/dist/trails/incomplete-accessor-for-standard-op.trail.js.map +1 -0
- package/dist/trails/incomplete-crud.trail.d.ts +24 -0
- package/dist/trails/incomplete-crud.trail.d.ts.map +1 -0
- package/dist/trails/incomplete-crud.trail.js +39 -0
- package/dist/trails/incomplete-crud.trail.js.map +1 -0
- package/dist/trails/index.d.ts +29 -7
- package/dist/trails/index.d.ts.map +1 -1
- package/dist/trails/index.js +28 -6
- package/dist/trails/index.js.map +1 -1
- package/dist/trails/intent-propagation.trail.d.ts +24 -0
- package/dist/trails/intent-propagation.trail.d.ts.map +1 -0
- package/dist/trails/intent-propagation.trail.js +30 -0
- package/dist/trails/intent-propagation.trail.js.map +1 -0
- package/dist/trails/missing-reconcile.trail.d.ts +24 -0
- package/dist/trails/missing-reconcile.trail.d.ts.map +1 -0
- package/dist/trails/missing-reconcile.trail.js +33 -0
- package/dist/trails/missing-reconcile.trail.js.map +1 -0
- package/dist/trails/missing-visibility.trail.d.ts +24 -0
- package/dist/trails/missing-visibility.trail.d.ts.map +1 -0
- package/dist/trails/missing-visibility.trail.js +22 -0
- package/dist/trails/missing-visibility.trail.js.map +1 -0
- package/dist/trails/no-direct-impl-in-route.trail.d.ts +2 -2
- package/dist/trails/no-direct-impl-in-route.trail.d.ts.map +1 -1
- package/dist/trails/no-direct-implementation-call.trail.d.ts +2 -2
- package/dist/trails/no-direct-implementation-call.trail.d.ts.map +1 -1
- package/dist/trails/no-sync-result-assumption.trail.d.ts +2 -2
- package/dist/trails/no-sync-result-assumption.trail.d.ts.map +1 -1
- package/dist/trails/no-throw-in-detour-recover.trail.d.ts +13 -0
- package/dist/trails/no-throw-in-detour-recover.trail.d.ts.map +1 -0
- package/dist/trails/no-throw-in-detour-recover.trail.js +24 -0
- package/dist/trails/no-throw-in-detour-recover.trail.js.map +1 -0
- package/dist/trails/no-throw-in-detour-target.trail.d.ts +13 -3
- package/dist/trails/no-throw-in-detour-target.trail.d.ts.map +1 -1
- package/dist/trails/no-throw-in-implementation.trail.d.ts +2 -2
- package/dist/trails/no-throw-in-implementation.trail.d.ts.map +1 -1
- package/dist/trails/on-references-exist.trail.d.ts +24 -0
- package/dist/trails/on-references-exist.trail.d.ts.map +1 -0
- package/dist/trails/on-references-exist.trail.js +21 -0
- package/dist/trails/on-references-exist.trail.js.map +1 -0
- package/dist/trails/orphaned-signal.trail.d.ts +24 -0
- package/dist/trails/orphaned-signal.trail.d.ts.map +1 -0
- package/dist/trails/orphaned-signal.trail.js +36 -0
- package/dist/trails/orphaned-signal.trail.js.map +1 -0
- package/dist/trails/permit-governance.trail.d.ts +12 -0
- package/dist/trails/permit-governance.trail.d.ts.map +1 -0
- package/dist/trails/permit-governance.trail.js +47 -0
- package/dist/trails/permit-governance.trail.js.map +1 -0
- package/dist/trails/prefer-schema-inference.trail.d.ts +2 -2
- package/dist/trails/prefer-schema-inference.trail.d.ts.map +1 -1
- package/dist/trails/reference-exists.trail.d.ts +24 -0
- package/dist/trails/reference-exists.trail.d.ts.map +1 -0
- package/dist/trails/reference-exists.trail.js +25 -0
- package/dist/trails/reference-exists.trail.js.map +1 -0
- package/dist/trails/resource-declarations.trail.d.ts +13 -0
- package/dist/trails/resource-declarations.trail.d.ts.map +1 -0
- package/dist/trails/{provision-declarations.trail.js → resource-declarations.trail.js} +7 -7
- package/dist/trails/resource-declarations.trail.js.map +1 -0
- package/dist/trails/resource-exists.trail.d.ts +24 -0
- package/dist/trails/resource-exists.trail.d.ts.map +1 -0
- package/dist/trails/{provision-exists.trail.js → resource-exists.trail.js} +8 -8
- package/dist/trails/resource-exists.trail.js.map +1 -0
- package/dist/trails/resource-id-grammar.trail.d.ts +13 -0
- package/dist/trails/resource-id-grammar.trail.d.ts.map +1 -0
- package/dist/trails/resource-id-grammar.trail.js +38 -0
- package/dist/trails/resource-id-grammar.trail.js.map +1 -0
- package/dist/trails/run.d.ts +25 -9
- package/dist/trails/run.d.ts.map +1 -1
- package/dist/trails/run.js +63 -19
- package/dist/trails/run.js.map +1 -1
- package/dist/trails/schema.d.ts +28 -3
- package/dist/trails/schema.d.ts.map +1 -1
- package/dist/trails/schema.js +57 -4
- package/dist/trails/schema.js.map +1 -1
- package/dist/trails/unreachable-detour-shadowing.trail.d.ts +13 -0
- package/dist/trails/unreachable-detour-shadowing.trail.d.ts.map +1 -0
- package/dist/trails/unreachable-detour-shadowing.trail.js +44 -0
- package/dist/trails/unreachable-detour-shadowing.trail.js.map +1 -0
- package/dist/trails/valid-describe-refs.trail.d.ts +12 -3
- package/dist/trails/valid-describe-refs.trail.d.ts.map +1 -1
- package/dist/trails/valid-detour-contract.trail.d.ts +12 -0
- package/dist/trails/valid-detour-contract.trail.d.ts.map +1 -0
- package/dist/trails/valid-detour-contract.trail.js +66 -0
- package/dist/trails/valid-detour-contract.trail.js.map +1 -0
- package/dist/trails/valid-detour-refs.trail.d.ts +13 -3
- package/dist/trails/valid-detour-refs.trail.d.ts.map +1 -1
- package/dist/trails/warden-export-symmetry.trail.d.ts +13 -0
- package/dist/trails/warden-export-symmetry.trail.d.ts.map +1 -0
- package/dist/trails/warden-export-symmetry.trail.js +16 -0
- package/dist/trails/warden-export-symmetry.trail.js.map +1 -0
- package/dist/trails/warden-rules-use-ast.trail.d.ts +13 -0
- package/dist/trails/warden-rules-use-ast.trail.d.ts.map +1 -0
- package/dist/trails/warden-rules-use-ast.trail.js +41 -0
- package/dist/trails/warden-rules-use-ast.trail.js.map +1 -0
- package/dist/trails/wrap-rule.d.ts +16 -2
- package/dist/trails/wrap-rule.d.ts.map +1 -1
- package/dist/trails/wrap-rule.js +71 -11
- package/dist/trails/wrap-rule.js.map +1 -1
- package/package.json +7 -4
- package/src/__tests__/ast.test.ts +613 -0
- package/src/__tests__/circular-refs.test.ts +121 -0
- package/src/__tests__/cli.test.ts +360 -32
- package/src/__tests__/contour-exists.test.ts +203 -0
- package/src/__tests__/cross-declarations.test.ts +245 -0
- package/src/__tests__/dead-internal-trail.test.ts +81 -0
- package/src/__tests__/draft-rules-context.test.ts +150 -0
- package/src/__tests__/drift.test.ts +75 -5
- package/src/__tests__/error-mapping-completeness.test.ts +56 -0
- package/src/__tests__/example-valid.test.ts +101 -0
- package/src/__tests__/fires-declarations-param-destructure.test.ts +54 -0
- package/src/__tests__/fires-declarations.test.ts +652 -0
- package/src/__tests__/formatters.test.ts +2 -2
- package/src/__tests__/implementation-returns-result.test.ts +1016 -2
- package/src/__tests__/incomplete-accessor-for-standard-op.test.ts +337 -0
- package/src/__tests__/incomplete-crud.test.ts +498 -0
- package/src/__tests__/intent-propagation.test.ts +116 -0
- package/src/__tests__/missing-reconcile.test.ts +154 -0
- package/src/__tests__/missing-visibility.test.ts +108 -0
- package/src/__tests__/no-sync-result-assumption.test.ts +870 -39
- package/src/__tests__/no-throw-in-detour-recover.test.ts +93 -0
- package/src/__tests__/no-throw-in-implementation.test.ts +88 -0
- package/src/__tests__/on-references-exist.test.ts +151 -0
- package/src/__tests__/orphaned-signal.test.ts +137 -0
- package/src/__tests__/permit-governance.test.ts +66 -0
- package/src/__tests__/reference-exists.test.ts +281 -0
- package/src/__tests__/resource-declarations.test.ts +448 -0
- package/src/__tests__/resource-exists.test.ts +122 -0
- package/src/__tests__/resource-id-grammar.test.ts +50 -0
- package/src/__tests__/rules.test.ts +17 -77
- package/src/__tests__/topo-aware-rule.test.ts +257 -0
- package/src/__tests__/trails.test.ts +2 -2
- package/src/__tests__/unreachable-detour-shadowing.test.ts +128 -0
- package/src/__tests__/valid-describe-refs.test.ts +183 -0
- package/src/__tests__/valid-detour-contract.test.ts +86 -0
- package/src/__tests__/warden-export-symmetry.test.ts +251 -0
- package/src/__tests__/warden-rules-use-ast.test.ts +468 -0
- package/src/__tests__/wrap-rule.test.ts +3 -3
- package/src/cli.ts +458 -91
- package/src/draft.ts +22 -0
- package/src/drift.ts +63 -21
- package/src/formatters.ts +15 -4
- package/src/index.ts +62 -23
- package/src/rules/ast.ts +2715 -119
- package/src/rules/circular-refs.ts +154 -0
- package/src/rules/{context-no-trailhead-types.ts → context-no-surface-types.ts} +72 -12
- package/src/rules/contour-exists.ts +251 -0
- package/src/rules/contour-ids.ts +15 -0
- package/src/rules/cross-declarations.ts +277 -69
- package/src/rules/dead-internal-trail.ts +141 -0
- package/src/rules/draft-file-marking.ts +160 -0
- package/src/rules/draft-visible-debt.ts +87 -0
- package/src/rules/error-mapping-completeness.ts +273 -0
- package/src/rules/example-valid.ts +401 -0
- package/src/rules/fires-declarations.ts +609 -0
- package/src/rules/implementation-returns-result.ts +1042 -122
- package/src/rules/incomplete-accessor-for-standard-op.ts +315 -0
- package/src/rules/incomplete-crud.ts +579 -0
- package/src/rules/index.ts +95 -16
- package/src/rules/intent-propagation.ts +142 -0
- package/src/rules/missing-reconcile.ts +98 -0
- package/src/rules/missing-visibility.ts +110 -0
- package/src/rules/no-direct-impl-in-route.ts +0 -4
- package/src/rules/no-direct-implementation-call.ts +1 -1
- package/src/rules/no-sync-result-assumption.ts +1134 -96
- package/src/rules/no-throw-in-detour-recover.ts +225 -0
- package/src/rules/no-throw-in-implementation.ts +6 -4
- package/src/rules/on-references-exist.ts +194 -0
- package/src/rules/orphaned-signal.ts +150 -0
- package/src/rules/permit-governance.ts +25 -0
- package/src/rules/reference-exists.ts +98 -0
- package/src/rules/registry-names.ts +83 -0
- package/src/rules/{provision-declarations.ts → resource-declarations.ts} +208 -138
- package/src/rules/{provision-exists.ts → resource-exists.ts} +48 -51
- package/src/rules/resource-id-grammar.ts +65 -0
- package/src/rules/specs.ts +5 -1
- package/src/rules/types.ts +57 -4
- package/src/rules/unreachable-detour-shadowing.ts +375 -0
- package/src/rules/valid-describe-refs.ts +160 -32
- package/src/rules/valid-detour-contract.ts +78 -0
- package/src/rules/warden-export-symmetry.ts +533 -0
- package/src/rules/warden-rules-use-ast.ts +996 -0
- package/src/trails/circular-refs.trail.ts +29 -0
- package/src/trails/{context-no-trailhead-types.trail.ts → context-no-surface-types.trail.ts} +4 -4
- package/src/trails/contour-exists.trail.ts +21 -0
- package/src/trails/dead-internal-trail.trail.ts +26 -0
- package/src/trails/draft-file-marking.trail.ts +16 -0
- package/src/trails/draft-visible-debt.trail.ts +16 -0
- package/src/trails/error-mapping-completeness.trail.ts +29 -0
- package/src/trails/example-valid.trail.ts +25 -0
- package/src/trails/fires-declarations.trail.ts +22 -0
- package/src/trails/incomplete-accessor-for-standard-op.trail.ts +76 -0
- package/src/trails/incomplete-crud.trail.ts +39 -0
- package/src/trails/index.ts +40 -7
- package/src/trails/intent-propagation.trail.ts +30 -0
- package/src/trails/missing-reconcile.trail.ts +33 -0
- package/src/trails/missing-visibility.trail.ts +22 -0
- package/src/trails/no-throw-in-detour-recover.trail.ts +24 -0
- package/src/trails/on-references-exist.trail.ts +21 -0
- package/src/trails/orphaned-signal.trail.ts +36 -0
- package/src/trails/permit-governance.trail.ts +51 -0
- package/src/trails/reference-exists.trail.ts +25 -0
- package/src/trails/{provision-declarations.trail.ts → resource-declarations.trail.ts} +6 -6
- package/src/trails/{provision-exists.trail.ts → resource-exists.trail.ts} +7 -7
- package/src/trails/resource-id-grammar.trail.ts +39 -0
- package/src/trails/run.ts +121 -24
- package/src/trails/schema.ts +66 -4
- package/src/trails/unreachable-detour-shadowing.trail.ts +45 -0
- package/src/trails/valid-detour-contract.trail.ts +71 -0
- package/src/trails/warden-export-symmetry.trail.ts +16 -0
- package/src/trails/warden-rules-use-ast.trail.ts +45 -0
- package/src/trails/wrap-rule.ts +104 -12
- package/tsconfig.tests.json +10 -0
- package/tsconfig.tsbuildinfo +1 -1
- package/dist/rules/follow-declarations.d.ts +0 -13
- package/dist/rules/follow-declarations.d.ts.map +0 -1
- package/dist/rules/follow-declarations.js +0 -264
- package/dist/rules/follow-declarations.js.map +0 -1
- package/dist/rules/provision-declarations.d.ts +0 -14
- package/dist/rules/provision-declarations.d.ts.map +0 -1
- package/dist/rules/provision-declarations.js +0 -344
- package/dist/rules/provision-declarations.js.map +0 -1
- package/dist/rules/provision-exists.d.ts +0 -6
- package/dist/rules/provision-exists.d.ts.map +0 -1
- package/dist/rules/provision-exists.js +0 -89
- package/dist/rules/provision-exists.js.map +0 -1
- package/dist/rules/service-declarations.d.ts +0 -16
- package/dist/rules/service-declarations.d.ts.map +0 -1
- package/dist/rules/service-declarations.js +0 -346
- package/dist/rules/service-declarations.js.map +0 -1
- package/dist/rules/service-exists.d.ts +0 -8
- package/dist/rules/service-exists.d.ts.map +0 -1
- package/dist/rules/service-exists.js +0 -91
- package/dist/rules/service-exists.js.map +0 -1
- package/dist/trails/follow-declarations.trail.d.ts.map +0 -1
- package/dist/trails/follow-declarations.trail.js +0 -22
- package/dist/trails/follow-declarations.trail.js.map +0 -1
- package/dist/trails/provision-declarations.trail.d.ts.map +0 -1
- package/dist/trails/provision-declarations.trail.js.map +0 -1
- package/dist/trails/provision-exists.trail.d.ts +0 -15
- package/dist/trails/provision-exists.trail.d.ts.map +0 -1
- package/dist/trails/provision-exists.trail.js.map +0 -1
- package/dist/trails/service-declarations.trail.d.ts +0 -26
- package/dist/trails/service-declarations.trail.d.ts.map +0 -1
- package/dist/trails/service-declarations.trail.js +0 -27
- package/dist/trails/service-declarations.trail.js.map +0 -1
- package/dist/trails/service-exists.trail.d.ts +0 -32
- package/dist/trails/service-exists.trail.d.ts.map +0 -1
- package/dist/trails/service-exists.trail.js +0 -29
- package/dist/trails/service-exists.trail.js.map +0 -1
- package/src/__tests__/no-throw-in-detour-target.test.ts +0 -78
- package/src/__tests__/provision-declarations.test.ts +0 -318
- package/src/__tests__/provision-exists.test.ts +0 -122
- package/src/rules/no-throw-in-detour-target.ts +0 -150
- package/src/rules/valid-detour-refs.ts +0 -187
- package/src/trails/no-throw-in-detour-target.trail.ts +0 -20
- package/src/trails/valid-detour-refs.trail.ts +0 -24
package/src/cli.ts
CHANGED
|
@@ -8,20 +8,29 @@
|
|
|
8
8
|
import { resolve } from 'node:path';
|
|
9
9
|
|
|
10
10
|
import type { Topo } from '@ontrails/core';
|
|
11
|
+
import { getContourReferences } from '@ontrails/core';
|
|
11
12
|
|
|
12
13
|
import type { DriftResult } from './drift.js';
|
|
13
14
|
import { checkDrift } from './drift.js';
|
|
14
15
|
import {
|
|
15
|
-
|
|
16
|
-
|
|
16
|
+
collectContourDefinitionIds,
|
|
17
|
+
collectContourReferenceTargetsByName,
|
|
18
|
+
collectCrudTableIds as collectCrudTableIdsFromAst,
|
|
19
|
+
collectCrossTargetTrailIds,
|
|
20
|
+
collectOnTargetSignalIds as collectOnTargetSignalIdsFromAst,
|
|
21
|
+
collectReconcileTableIds as collectReconcileTableIdsFromAst,
|
|
22
|
+
collectResourceDefinitionIds,
|
|
23
|
+
collectSignalDefinitionIds,
|
|
24
|
+
collectTrailIntentsById,
|
|
17
25
|
findTrailDefinitions,
|
|
18
26
|
parse,
|
|
19
|
-
walk,
|
|
20
27
|
} from './rules/ast.js';
|
|
21
|
-
import {
|
|
28
|
+
import { collectFileCrudCoverage } from './rules/incomplete-crud.js';
|
|
29
|
+
import { wardenRules, wardenTopoRules } from './rules/index.js';
|
|
22
30
|
import type {
|
|
23
31
|
ProjectAwareWardenRule,
|
|
24
32
|
ProjectContext,
|
|
33
|
+
TopoAwareWardenRule,
|
|
25
34
|
WardenDiagnostic,
|
|
26
35
|
WardenRule,
|
|
27
36
|
} from './rules/types.js';
|
|
@@ -36,8 +45,25 @@ export interface WardenOptions {
|
|
|
36
45
|
readonly lintOnly?: boolean | undefined;
|
|
37
46
|
/** Only run drift detection, skip lint rules */
|
|
38
47
|
readonly driftOnly?: boolean | undefined;
|
|
39
|
-
/**
|
|
48
|
+
/**
|
|
49
|
+
* App topology for drift detection. When provided, enables real trailhead
|
|
50
|
+
* lock comparison and unlocks the topo-aware rule dispatch path.
|
|
51
|
+
*
|
|
52
|
+
* @remarks
|
|
53
|
+
* Topo-aware rules (both built-in `wardenTopoRules` and `extraTopoRules`)
|
|
54
|
+
* only fire when a `Topo` is supplied. Runs without a topo silently skip
|
|
55
|
+
* topo-aware dispatch — callers that depend on a topo-aware rule firing
|
|
56
|
+
* must pass `topo` explicitly.
|
|
57
|
+
*/
|
|
40
58
|
readonly topo?: Topo | undefined;
|
|
59
|
+
/**
|
|
60
|
+
* Extra topo-aware rules to run in addition to the built-in registry.
|
|
61
|
+
*
|
|
62
|
+
* Primarily a test hook — production callers should register rules via
|
|
63
|
+
* `wardenTopoRules` in `rules/index.ts`. These rules are only invoked
|
|
64
|
+
* when `topo` is also supplied (see `topo` remarks).
|
|
65
|
+
*/
|
|
66
|
+
readonly extraTopoRules?: readonly TopoAwareWardenRule[] | undefined;
|
|
41
67
|
}
|
|
42
68
|
|
|
43
69
|
/**
|
|
@@ -92,6 +118,103 @@ interface SourceFile {
|
|
|
92
118
|
readonly sourceCode: string;
|
|
93
119
|
}
|
|
94
120
|
|
|
121
|
+
interface MutableProjectContext {
|
|
122
|
+
contourReferencesByName: Map<string, Set<string>>;
|
|
123
|
+
crudTableIds: Set<string>;
|
|
124
|
+
crossTargetTrailIds: Set<string>;
|
|
125
|
+
crudCoverageByEntity: Map<string, Set<string>>;
|
|
126
|
+
knownContourIds: Set<string>;
|
|
127
|
+
knownResourceIds: Set<string>;
|
|
128
|
+
knownSignalIds: Set<string>;
|
|
129
|
+
knownTrailIds: Set<string>;
|
|
130
|
+
onTargetSignalIds: Set<string>;
|
|
131
|
+
reconcileTableIds: Set<string>;
|
|
132
|
+
trailIntentsById: Map<string, 'destroy' | 'read' | 'write'>;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const createMutableProjectContext = (): MutableProjectContext => ({
|
|
136
|
+
contourReferencesByName: new Map<string, Set<string>>(),
|
|
137
|
+
crossTargetTrailIds: new Set<string>(),
|
|
138
|
+
crudCoverageByEntity: new Map<string, Set<string>>(),
|
|
139
|
+
crudTableIds: new Set<string>(),
|
|
140
|
+
knownContourIds: new Set<string>(),
|
|
141
|
+
knownResourceIds: new Set<string>(),
|
|
142
|
+
knownSignalIds: new Set<string>(),
|
|
143
|
+
knownTrailIds: new Set<string>(),
|
|
144
|
+
onTargetSignalIds: new Set<string>(),
|
|
145
|
+
reconcileTableIds: new Set<string>(),
|
|
146
|
+
trailIntentsById: new Map<string, 'destroy' | 'read' | 'write'>(),
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
const addContourReferenceTargets = (
|
|
150
|
+
context: MutableProjectContext,
|
|
151
|
+
contourName: string,
|
|
152
|
+
targets: readonly string[]
|
|
153
|
+
): void => {
|
|
154
|
+
const existing = context.contourReferencesByName.get(contourName);
|
|
155
|
+
if (existing) {
|
|
156
|
+
for (const target of targets) {
|
|
157
|
+
existing.add(target);
|
|
158
|
+
}
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
context.contourReferencesByName.set(contourName, new Set(targets));
|
|
163
|
+
};
|
|
164
|
+
|
|
165
|
+
const toProjectContext = (context: MutableProjectContext): ProjectContext => ({
|
|
166
|
+
...(context.contourReferencesByName.size > 0
|
|
167
|
+
? {
|
|
168
|
+
contourReferencesByName: new Map(
|
|
169
|
+
[...context.contourReferencesByName.entries()].map(
|
|
170
|
+
([name, targets]) => [name, [...targets]]
|
|
171
|
+
)
|
|
172
|
+
),
|
|
173
|
+
}
|
|
174
|
+
: {}),
|
|
175
|
+
...(context.crudTableIds.size > 0
|
|
176
|
+
? { crudTableIds: context.crudTableIds }
|
|
177
|
+
: {}),
|
|
178
|
+
...(context.crudCoverageByEntity.size > 0
|
|
179
|
+
? {
|
|
180
|
+
crudCoverageByEntity: new Map(
|
|
181
|
+
[...context.crudCoverageByEntity.entries()].map(
|
|
182
|
+
([entityId, operations]) => [
|
|
183
|
+
entityId,
|
|
184
|
+
new Set(operations) as ReadonlySet<string>,
|
|
185
|
+
]
|
|
186
|
+
)
|
|
187
|
+
),
|
|
188
|
+
}
|
|
189
|
+
: {}),
|
|
190
|
+
crossTargetTrailIds: context.crossTargetTrailIds,
|
|
191
|
+
knownContourIds: context.knownContourIds,
|
|
192
|
+
knownResourceIds: context.knownResourceIds,
|
|
193
|
+
knownSignalIds: context.knownSignalIds,
|
|
194
|
+
knownTrailIds: context.knownTrailIds,
|
|
195
|
+
...(context.onTargetSignalIds.size > 0
|
|
196
|
+
? { onTargetSignalIds: context.onTargetSignalIds }
|
|
197
|
+
: {}),
|
|
198
|
+
...(context.reconcileTableIds.size > 0
|
|
199
|
+
? { reconcileTableIds: context.reconcileTableIds }
|
|
200
|
+
: {}),
|
|
201
|
+
trailIntentsById: context.trailIntentsById,
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
const collectKnownContourIds = (
|
|
205
|
+
sourceCode: string,
|
|
206
|
+
filePath: string,
|
|
207
|
+
knownContourIds: Set<string>
|
|
208
|
+
): void => {
|
|
209
|
+
const ast = parse(filePath, sourceCode);
|
|
210
|
+
if (!ast) {
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
for (const id of collectContourDefinitionIds(ast)) {
|
|
214
|
+
knownContourIds.add(id);
|
|
215
|
+
}
|
|
216
|
+
};
|
|
217
|
+
|
|
95
218
|
const collectKnownTrailIds = (
|
|
96
219
|
sourceCode: string,
|
|
97
220
|
filePath: string,
|
|
@@ -106,44 +229,122 @@ const collectKnownTrailIds = (
|
|
|
106
229
|
}
|
|
107
230
|
};
|
|
108
231
|
|
|
109
|
-
const
|
|
232
|
+
const collectCrossedTrailIds = (
|
|
110
233
|
sourceCode: string,
|
|
111
234
|
filePath: string,
|
|
112
|
-
|
|
235
|
+
crossTargetTrailIds: Set<string>
|
|
113
236
|
): void => {
|
|
114
237
|
const ast = parse(filePath, sourceCode);
|
|
115
238
|
if (!ast) {
|
|
116
239
|
return;
|
|
117
240
|
}
|
|
118
|
-
for (const
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
241
|
+
for (const id of collectCrossTargetTrailIds(ast, sourceCode)) {
|
|
242
|
+
crossTargetTrailIds.add(id);
|
|
243
|
+
}
|
|
244
|
+
};
|
|
245
|
+
|
|
246
|
+
const collectKnownResourceIds = (
|
|
247
|
+
sourceCode: string,
|
|
248
|
+
filePath: string,
|
|
249
|
+
knownResourceIds: Set<string>
|
|
250
|
+
): void => {
|
|
251
|
+
const ast = parse(filePath, sourceCode);
|
|
252
|
+
if (!ast) {
|
|
253
|
+
return;
|
|
254
|
+
}
|
|
255
|
+
for (const id of collectResourceDefinitionIds(ast)) {
|
|
256
|
+
knownResourceIds.add(id);
|
|
257
|
+
}
|
|
258
|
+
};
|
|
259
|
+
|
|
260
|
+
const collectKnownSignalIds = (
|
|
261
|
+
sourceCode: string,
|
|
262
|
+
filePath: string,
|
|
263
|
+
knownSignalIds: Set<string>
|
|
264
|
+
): void => {
|
|
265
|
+
const ast = parse(filePath, sourceCode);
|
|
266
|
+
if (!ast) {
|
|
267
|
+
return;
|
|
268
|
+
}
|
|
269
|
+
for (const id of collectSignalDefinitionIds(ast)) {
|
|
270
|
+
knownSignalIds.add(id);
|
|
271
|
+
}
|
|
272
|
+
};
|
|
273
|
+
|
|
274
|
+
const collectTrailIntents = (
|
|
275
|
+
sourceCode: string,
|
|
276
|
+
filePath: string,
|
|
277
|
+
trailIntentsById: Map<string, 'destroy' | 'read' | 'write'>
|
|
278
|
+
): void => {
|
|
279
|
+
const ast = parse(filePath, sourceCode);
|
|
280
|
+
if (!ast) {
|
|
281
|
+
return;
|
|
282
|
+
}
|
|
283
|
+
for (const [id, intent] of collectTrailIntentsById(ast)) {
|
|
284
|
+
trailIntentsById.set(id, intent);
|
|
285
|
+
}
|
|
286
|
+
};
|
|
287
|
+
|
|
288
|
+
const collectCrudTableIds = (
|
|
289
|
+
sourceCode: string,
|
|
290
|
+
filePath: string,
|
|
291
|
+
crudTableIds: Set<string>
|
|
292
|
+
): void => {
|
|
293
|
+
const ast = parse(filePath, sourceCode);
|
|
294
|
+
if (!ast) {
|
|
295
|
+
return;
|
|
296
|
+
}
|
|
297
|
+
for (const id of collectCrudTableIdsFromAst(ast)) {
|
|
298
|
+
crudTableIds.add(id);
|
|
299
|
+
}
|
|
300
|
+
};
|
|
301
|
+
|
|
302
|
+
const collectOnTargetSignalIds = (
|
|
303
|
+
sourceCode: string,
|
|
304
|
+
filePath: string,
|
|
305
|
+
onTargetSignalIds: Set<string>
|
|
306
|
+
): void => {
|
|
307
|
+
const ast = parse(filePath, sourceCode);
|
|
308
|
+
if (!ast) {
|
|
309
|
+
return;
|
|
310
|
+
}
|
|
311
|
+
for (const id of collectOnTargetSignalIdsFromAst(ast, sourceCode)) {
|
|
312
|
+
onTargetSignalIds.add(id);
|
|
313
|
+
}
|
|
314
|
+
};
|
|
315
|
+
|
|
316
|
+
const collectCrudCoverageByEntity = (
|
|
317
|
+
sourceCode: string,
|
|
318
|
+
filePath: string,
|
|
319
|
+
coverageByEntity: Map<string, Set<string>>
|
|
320
|
+
): void => {
|
|
321
|
+
const ast = parse(filePath, sourceCode);
|
|
322
|
+
if (!ast) {
|
|
323
|
+
return;
|
|
324
|
+
}
|
|
325
|
+
for (const [entityId, operations] of collectFileCrudCoverage(
|
|
326
|
+
ast,
|
|
327
|
+
sourceCode
|
|
328
|
+
)) {
|
|
329
|
+
const bucket = coverageByEntity.get(entityId) ?? new Set<string>();
|
|
330
|
+
for (const operation of operations) {
|
|
331
|
+
bucket.add(operation);
|
|
122
332
|
}
|
|
123
|
-
|
|
124
|
-
walk(detoursProp, (node) => {
|
|
125
|
-
if (node.type !== 'Literal') {
|
|
126
|
-
return;
|
|
127
|
-
}
|
|
128
|
-
const val = (node as unknown as { value?: string }).value;
|
|
129
|
-
if (val && val.includes('.')) {
|
|
130
|
-
detourTargetTrailIds.add(val);
|
|
131
|
-
}
|
|
132
|
-
});
|
|
333
|
+
coverageByEntity.set(entityId, bucket);
|
|
133
334
|
}
|
|
134
335
|
};
|
|
135
336
|
|
|
136
|
-
const
|
|
337
|
+
const collectReconcileTableIds = (
|
|
137
338
|
sourceCode: string,
|
|
138
339
|
filePath: string,
|
|
139
|
-
|
|
340
|
+
reconcileTableIds: Set<string>
|
|
140
341
|
): void => {
|
|
141
342
|
const ast = parse(filePath, sourceCode);
|
|
142
343
|
if (!ast) {
|
|
143
344
|
return;
|
|
144
345
|
}
|
|
145
|
-
for (const id of
|
|
146
|
-
|
|
346
|
+
for (const id of collectReconcileTableIdsFromAst(ast)) {
|
|
347
|
+
reconcileTableIds.add(id);
|
|
147
348
|
}
|
|
148
349
|
};
|
|
149
350
|
|
|
@@ -166,92 +367,231 @@ const loadSourceFiles = async (
|
|
|
166
367
|
return sourceFiles;
|
|
167
368
|
};
|
|
168
369
|
|
|
169
|
-
const
|
|
170
|
-
appTopo: Topo
|
|
171
|
-
|
|
172
|
-
|
|
370
|
+
const collectTopoKnownIds = (
|
|
371
|
+
appTopo: Topo,
|
|
372
|
+
context: MutableProjectContext
|
|
373
|
+
): void => {
|
|
374
|
+
for (const name of appTopo.contours.keys()) {
|
|
375
|
+
context.knownContourIds.add(name);
|
|
376
|
+
}
|
|
173
377
|
|
|
378
|
+
for (const id of appTopo.trails.keys()) {
|
|
379
|
+
context.knownTrailIds.add(id);
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
for (const id of appTopo.resources.keys()) {
|
|
383
|
+
context.knownResourceIds.add(id);
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
for (const id of appTopo.signals.keys()) {
|
|
387
|
+
context.knownSignalIds.add(id);
|
|
388
|
+
}
|
|
389
|
+
};
|
|
390
|
+
|
|
391
|
+
const collectTopoCrossesAndIntents = (
|
|
392
|
+
appTopo: Topo,
|
|
393
|
+
context: MutableProjectContext
|
|
394
|
+
): void => {
|
|
174
395
|
for (const trail of appTopo.trails.values()) {
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
if (!detours) {
|
|
179
|
-
continue;
|
|
180
|
-
}
|
|
181
|
-
for (const targets of Object.values(detours)) {
|
|
182
|
-
for (const id of targets) {
|
|
183
|
-
detourTargetTrailIds.add(id);
|
|
184
|
-
}
|
|
396
|
+
context.trailIntentsById.set(trail.id, trail.intent);
|
|
397
|
+
for (const crossedTrailId of trail.crosses) {
|
|
398
|
+
context.crossTargetTrailIds.add(crossedTrailId);
|
|
185
399
|
}
|
|
186
400
|
}
|
|
401
|
+
};
|
|
402
|
+
|
|
403
|
+
const collectTopoContourReferences = (
|
|
404
|
+
appTopo: Topo,
|
|
405
|
+
context: MutableProjectContext
|
|
406
|
+
): void => {
|
|
407
|
+
for (const contour of appTopo.listContours()) {
|
|
408
|
+
addContourReferenceTargets(
|
|
409
|
+
context,
|
|
410
|
+
contour.name,
|
|
411
|
+
getContourReferences(contour).map((reference) => reference.contour)
|
|
412
|
+
);
|
|
413
|
+
}
|
|
414
|
+
};
|
|
187
415
|
|
|
188
|
-
|
|
416
|
+
const collectTopoTrailContext = (
|
|
417
|
+
appTopo: Topo,
|
|
418
|
+
context: MutableProjectContext
|
|
419
|
+
): void => {
|
|
420
|
+
collectTopoKnownIds(appTopo, context);
|
|
421
|
+
collectTopoCrossesAndIntents(appTopo, context);
|
|
422
|
+
collectTopoContourReferences(appTopo, context);
|
|
189
423
|
};
|
|
190
424
|
|
|
191
|
-
const
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
425
|
+
const collectFileKnownIds = (
|
|
426
|
+
sourceFile: SourceFile,
|
|
427
|
+
context: MutableProjectContext
|
|
428
|
+
): void => {
|
|
429
|
+
collectKnownContourIds(
|
|
430
|
+
sourceFile.sourceCode,
|
|
431
|
+
sourceFile.filePath,
|
|
432
|
+
context.knownContourIds
|
|
433
|
+
);
|
|
434
|
+
collectKnownTrailIds(
|
|
435
|
+
sourceFile.sourceCode,
|
|
436
|
+
sourceFile.filePath,
|
|
437
|
+
context.knownTrailIds
|
|
438
|
+
);
|
|
439
|
+
collectKnownResourceIds(
|
|
440
|
+
sourceFile.sourceCode,
|
|
441
|
+
sourceFile.filePath,
|
|
442
|
+
context.knownResourceIds
|
|
443
|
+
);
|
|
444
|
+
collectKnownSignalIds(
|
|
445
|
+
sourceFile.sourceCode,
|
|
446
|
+
sourceFile.filePath,
|
|
447
|
+
context.knownSignalIds
|
|
448
|
+
);
|
|
449
|
+
};
|
|
195
450
|
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
451
|
+
const collectFileTrailRelationships = (
|
|
452
|
+
sourceFile: SourceFile,
|
|
453
|
+
context: MutableProjectContext
|
|
454
|
+
): void => {
|
|
455
|
+
collectCrossedTrailIds(
|
|
456
|
+
sourceFile.sourceCode,
|
|
457
|
+
sourceFile.filePath,
|
|
458
|
+
context.crossTargetTrailIds
|
|
459
|
+
);
|
|
460
|
+
collectTrailIntents(
|
|
461
|
+
sourceFile.sourceCode,
|
|
462
|
+
sourceFile.filePath,
|
|
463
|
+
context.trailIntentsById
|
|
464
|
+
);
|
|
465
|
+
};
|
|
466
|
+
|
|
467
|
+
const collectFileSupplementalProjectContext = (
|
|
468
|
+
sourceFile: SourceFile,
|
|
469
|
+
context: MutableProjectContext
|
|
470
|
+
): void => {
|
|
471
|
+
collectCrudTableIds(
|
|
472
|
+
sourceFile.sourceCode,
|
|
473
|
+
sourceFile.filePath,
|
|
474
|
+
context.crudTableIds
|
|
475
|
+
);
|
|
476
|
+
collectOnTargetSignalIds(
|
|
477
|
+
sourceFile.sourceCode,
|
|
478
|
+
sourceFile.filePath,
|
|
479
|
+
context.onTargetSignalIds
|
|
480
|
+
);
|
|
481
|
+
collectReconcileTableIds(
|
|
482
|
+
sourceFile.sourceCode,
|
|
483
|
+
sourceFile.filePath,
|
|
484
|
+
context.reconcileTableIds
|
|
485
|
+
);
|
|
486
|
+
collectCrudCoverageByEntity(
|
|
487
|
+
sourceFile.sourceCode,
|
|
488
|
+
sourceFile.filePath,
|
|
489
|
+
context.crudCoverageByEntity
|
|
490
|
+
);
|
|
201
491
|
};
|
|
202
492
|
|
|
203
|
-
const
|
|
204
|
-
|
|
493
|
+
const collectFileProjectContext = (
|
|
494
|
+
sourceFile: SourceFile,
|
|
495
|
+
context: MutableProjectContext
|
|
496
|
+
): void => {
|
|
497
|
+
collectFileKnownIds(sourceFile, context);
|
|
498
|
+
collectFileTrailRelationships(sourceFile, context);
|
|
499
|
+
collectFileSupplementalProjectContext(sourceFile, context);
|
|
500
|
+
};
|
|
501
|
+
|
|
502
|
+
const collectFileContourReferences = (
|
|
503
|
+
sourceFile: SourceFile,
|
|
504
|
+
context: MutableProjectContext
|
|
505
|
+
): void => {
|
|
506
|
+
const ast = parse(sourceFile.filePath, sourceFile.sourceCode);
|
|
507
|
+
if (!ast) {
|
|
508
|
+
return;
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
const referencesByName = collectContourReferenceTargetsByName(
|
|
512
|
+
ast,
|
|
513
|
+
context.knownContourIds
|
|
514
|
+
);
|
|
515
|
+
for (const [contourName, targets] of referencesByName) {
|
|
516
|
+
addContourReferenceTargets(context, contourName, targets);
|
|
517
|
+
}
|
|
518
|
+
};
|
|
519
|
+
|
|
520
|
+
const buildProjectContext = (
|
|
521
|
+
sourceFiles: readonly SourceFile[],
|
|
522
|
+
appTopo?: Topo | undefined
|
|
205
523
|
): ProjectContext => {
|
|
206
|
-
const
|
|
207
|
-
|
|
208
|
-
|
|
524
|
+
const context = createMutableProjectContext();
|
|
525
|
+
|
|
526
|
+
if (appTopo) {
|
|
527
|
+
collectTopoTrailContext(appTopo, context);
|
|
528
|
+
for (const sourceFile of sourceFiles) {
|
|
529
|
+
collectFileSupplementalProjectContext(sourceFile, context);
|
|
530
|
+
}
|
|
531
|
+
} else {
|
|
532
|
+
for (const sourceFile of sourceFiles) {
|
|
533
|
+
collectFileProjectContext(sourceFile, context);
|
|
534
|
+
}
|
|
535
|
+
}
|
|
209
536
|
|
|
210
537
|
for (const sourceFile of sourceFiles) {
|
|
211
|
-
|
|
212
|
-
sourceFile.sourceCode,
|
|
213
|
-
sourceFile.filePath,
|
|
214
|
-
knownTrailIds
|
|
215
|
-
);
|
|
216
|
-
collectKnownProvisionIds(
|
|
217
|
-
sourceFile.sourceCode,
|
|
218
|
-
sourceFile.filePath,
|
|
219
|
-
knownProvisionIds
|
|
220
|
-
);
|
|
221
|
-
collectDetourTargetTrailIds(
|
|
222
|
-
sourceFile.sourceCode,
|
|
223
|
-
sourceFile.filePath,
|
|
224
|
-
detourTargetTrailIds
|
|
225
|
-
);
|
|
538
|
+
collectFileContourReferences(sourceFile, context);
|
|
226
539
|
}
|
|
227
540
|
|
|
228
|
-
return
|
|
229
|
-
detourTargetTrailIds,
|
|
230
|
-
knownProvisionIds,
|
|
231
|
-
knownTrailIds,
|
|
232
|
-
};
|
|
541
|
+
return toProjectContext(context);
|
|
233
542
|
};
|
|
234
543
|
|
|
235
544
|
const isProjectAwareRule = (rule: WardenRule): rule is ProjectAwareWardenRule =>
|
|
236
545
|
'checkWithContext' in rule;
|
|
237
546
|
|
|
547
|
+
const topoRuleFailureDiagnostic = (
|
|
548
|
+
rule: TopoAwareWardenRule,
|
|
549
|
+
error: unknown
|
|
550
|
+
): WardenDiagnostic => {
|
|
551
|
+
const cause = error instanceof Error ? error : new Error(String(error));
|
|
552
|
+
return {
|
|
553
|
+
filePath: '<topo>',
|
|
554
|
+
line: 1,
|
|
555
|
+
message: `Topo-aware rule "${rule.name}" threw: ${cause.message}`,
|
|
556
|
+
rule: rule.name,
|
|
557
|
+
severity: 'error',
|
|
558
|
+
};
|
|
559
|
+
};
|
|
560
|
+
|
|
238
561
|
/**
|
|
239
|
-
*
|
|
562
|
+
* Run all registered topo-aware rules against the resolved topo.
|
|
563
|
+
*
|
|
564
|
+
* Topo-aware rules fire exactly once per run (not per file) because they
|
|
565
|
+
* inspect the compiled trail graph, not source text.
|
|
240
566
|
*/
|
|
241
|
-
const
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
): Promise<WardenDiagnostic[]> => {
|
|
245
|
-
const
|
|
246
|
-
const
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
567
|
+
const lintTopo = async (
|
|
568
|
+
appTopo: Topo,
|
|
569
|
+
extraTopoRules: readonly TopoAwareWardenRule[]
|
|
570
|
+
): Promise<readonly WardenDiagnostic[]> => {
|
|
571
|
+
const diagnostics: WardenDiagnostic[] = [];
|
|
572
|
+
const rules: readonly TopoAwareWardenRule[] = [
|
|
573
|
+
...wardenTopoRules.values(),
|
|
574
|
+
...extraTopoRules,
|
|
575
|
+
];
|
|
576
|
+
for (const rule of rules) {
|
|
577
|
+
try {
|
|
578
|
+
diagnostics.push(...(await rule.checkTopo(appTopo)));
|
|
579
|
+
} catch (error) {
|
|
580
|
+
diagnostics.push(topoRuleFailureDiagnostic(rule, error));
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
return diagnostics;
|
|
584
|
+
};
|
|
250
585
|
|
|
586
|
+
const lintSourceFiles = (
|
|
587
|
+
sourceFiles: readonly SourceFile[],
|
|
588
|
+
context: ProjectContext
|
|
589
|
+
): readonly WardenDiagnostic[] => {
|
|
590
|
+
const diagnostics: WardenDiagnostic[] = [];
|
|
251
591
|
for (const sourceFile of sourceFiles) {
|
|
252
592
|
for (const rule of wardenRules.values()) {
|
|
253
593
|
if (isProjectAwareRule(rule)) {
|
|
254
|
-
|
|
594
|
+
diagnostics.push(
|
|
255
595
|
...rule.checkWithContext(
|
|
256
596
|
sourceFile.sourceCode,
|
|
257
597
|
sourceFile.filePath,
|
|
@@ -260,12 +600,31 @@ const lintFiles = async (
|
|
|
260
600
|
);
|
|
261
601
|
continue;
|
|
262
602
|
}
|
|
263
|
-
|
|
264
|
-
allDiagnostics.push(
|
|
603
|
+
diagnostics.push(
|
|
265
604
|
...rule.check(sourceFile.sourceCode, sourceFile.filePath)
|
|
266
605
|
);
|
|
267
606
|
}
|
|
268
607
|
}
|
|
608
|
+
return diagnostics;
|
|
609
|
+
};
|
|
610
|
+
|
|
611
|
+
/**
|
|
612
|
+
* Lint all files against all warden rules.
|
|
613
|
+
*/
|
|
614
|
+
const lintFiles = async (
|
|
615
|
+
rootDir: string,
|
|
616
|
+
appTopo?: Topo | undefined,
|
|
617
|
+
extraTopoRules: readonly TopoAwareWardenRule[] = []
|
|
618
|
+
): Promise<WardenDiagnostic[]> => {
|
|
619
|
+
const sourceFiles = await loadSourceFiles(rootDir);
|
|
620
|
+
const context = buildProjectContext(sourceFiles, appTopo);
|
|
621
|
+
const allDiagnostics: WardenDiagnostic[] = [
|
|
622
|
+
...lintSourceFiles(sourceFiles, context),
|
|
623
|
+
];
|
|
624
|
+
|
|
625
|
+
if (appTopo) {
|
|
626
|
+
allDiagnostics.push(...(await lintTopo(appTopo, extraTopoRules)));
|
|
627
|
+
}
|
|
269
628
|
|
|
270
629
|
return allDiagnostics;
|
|
271
630
|
};
|
|
@@ -279,7 +638,7 @@ export const runWarden = async (
|
|
|
279
638
|
const rootDir = resolve(options.rootDir ?? process.cwd());
|
|
280
639
|
const allDiagnostics = options.driftOnly
|
|
281
640
|
? []
|
|
282
|
-
: await lintFiles(rootDir, options.topo);
|
|
641
|
+
: await lintFiles(rootDir, options.topo, options.extraTopoRules ?? []);
|
|
283
642
|
const drift = options.lintOnly
|
|
284
643
|
? null
|
|
285
644
|
: await checkDrift(rootDir, options.topo);
|
|
@@ -293,7 +652,10 @@ export const runWarden = async (
|
|
|
293
652
|
diagnostics: allDiagnostics,
|
|
294
653
|
drift,
|
|
295
654
|
errorCount,
|
|
296
|
-
passed:
|
|
655
|
+
passed:
|
|
656
|
+
errorCount === 0 &&
|
|
657
|
+
!(drift?.stale ?? false) &&
|
|
658
|
+
drift?.blockedReason === undefined,
|
|
297
659
|
warnCount,
|
|
298
660
|
};
|
|
299
661
|
};
|
|
@@ -327,8 +689,11 @@ const formatDriftSection = (drift: DriftResult | null): string[] => {
|
|
|
327
689
|
if (drift === null) {
|
|
328
690
|
return [];
|
|
329
691
|
}
|
|
692
|
+
if (drift.blockedReason !== undefined) {
|
|
693
|
+
return [`Drift: blocked (${drift.blockedReason})`, ''];
|
|
694
|
+
}
|
|
330
695
|
const label = drift.stale
|
|
331
|
-
? 'Drift:
|
|
696
|
+
? 'Drift: trails.lock is stale (regenerate with `trails topo export`)'
|
|
332
697
|
: 'Drift: clean';
|
|
333
698
|
return [label, ''];
|
|
334
699
|
};
|
|
@@ -344,7 +709,9 @@ const formatResultLine = (report: WardenReport): string => {
|
|
|
344
709
|
if (report.errorCount > 0) {
|
|
345
710
|
parts.push(`${report.errorCount} errors`);
|
|
346
711
|
}
|
|
347
|
-
if (report.drift?.
|
|
712
|
+
if (report.drift?.blockedReason !== undefined) {
|
|
713
|
+
parts.push('established exports blocked');
|
|
714
|
+
} else if (report.drift?.stale) {
|
|
348
715
|
parts.push('drift detected');
|
|
349
716
|
}
|
|
350
717
|
return `Result: FAIL (${parts.join(', ')})`;
|
package/src/draft.ts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { basename } from 'node:path';
|
|
2
|
+
|
|
3
|
+
export const DRAFT_FILE_PREFIX = '_draft.';
|
|
4
|
+
export const DRAFT_FILE_SEGMENT = '.draft.';
|
|
5
|
+
|
|
6
|
+
const DRAFT_TRAILING_SEGMENT = /\.draft(?=\.[^.]+$)/;
|
|
7
|
+
|
|
8
|
+
export const isDraftMarkedFile = (filePath: string): boolean => {
|
|
9
|
+
const fileName = basename(filePath);
|
|
10
|
+
return (
|
|
11
|
+
fileName.startsWith(DRAFT_FILE_PREFIX) ||
|
|
12
|
+
fileName.includes(DRAFT_FILE_SEGMENT)
|
|
13
|
+
);
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export const stripDraftFileMarkers = (fileName: string): string => {
|
|
17
|
+
if (fileName.startsWith(DRAFT_FILE_PREFIX)) {
|
|
18
|
+
return fileName.slice(DRAFT_FILE_PREFIX.length);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
return fileName.replace(DRAFT_TRAILING_SEGMENT, '');
|
|
22
|
+
};
|