@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
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Validates that
|
|
2
|
+
* Validates that resource access matches the declared `resources` array.
|
|
3
3
|
*
|
|
4
4
|
* Statically analyzes trail `blaze` functions to find `db.from(ctx)` and
|
|
5
|
-
* `ctx.
|
|
6
|
-
* `
|
|
5
|
+
* `ctx.resource('db.main')` calls and compares them against the declared
|
|
6
|
+
* `resources: [...]` array in the trail config. Reports errors for undeclared
|
|
7
7
|
* access and warnings for unused declarations.
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
import {
|
|
11
|
-
|
|
11
|
+
collectNamedResourceIds,
|
|
12
12
|
extractFirstStringArg,
|
|
13
13
|
findConfigProperty,
|
|
14
14
|
findBlazeBodies,
|
|
@@ -25,15 +25,15 @@ import { isTestFile } from './scan.js';
|
|
|
25
25
|
import type { WardenDiagnostic, WardenRule } from './types.js';
|
|
26
26
|
|
|
27
27
|
// ---------------------------------------------------------------------------
|
|
28
|
-
//
|
|
28
|
+
// Resource declaration extraction
|
|
29
29
|
// ---------------------------------------------------------------------------
|
|
30
30
|
|
|
31
|
-
interface
|
|
31
|
+
interface DeclaredResource {
|
|
32
32
|
readonly id: string | null;
|
|
33
33
|
readonly name: string | null;
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
-
interface
|
|
36
|
+
interface CalledResources {
|
|
37
37
|
readonly fromNames: ReadonlySet<string>;
|
|
38
38
|
readonly lookupIds: ReadonlySet<string>;
|
|
39
39
|
readonly lookupNames: ReadonlySet<string>;
|
|
@@ -59,25 +59,25 @@ const extractMemberPair = (
|
|
|
59
59
|
return objName && propName ? { objName, propName } : null;
|
|
60
60
|
};
|
|
61
61
|
|
|
62
|
-
/** Check if a node is an inline `
|
|
63
|
-
const
|
|
62
|
+
/** Check if a node is an inline `resource('id', ...)` call. */
|
|
63
|
+
const isInlineResourceCall = (node: AstNode): boolean => {
|
|
64
64
|
if (node.type !== 'CallExpression') {
|
|
65
65
|
return false;
|
|
66
66
|
}
|
|
67
67
|
return (
|
|
68
68
|
identifierName((node as unknown as { callee?: AstNode }).callee) ===
|
|
69
|
-
'
|
|
69
|
+
'resource'
|
|
70
70
|
);
|
|
71
71
|
};
|
|
72
72
|
|
|
73
|
-
/** Get `
|
|
74
|
-
const
|
|
75
|
-
const
|
|
76
|
-
if (!
|
|
73
|
+
/** Get `resources` array elements from a trail config. */
|
|
74
|
+
const getResourceElements = (config: AstNode): readonly AstNode[] => {
|
|
75
|
+
const resourcesProp = findConfigProperty(config, 'resources');
|
|
76
|
+
if (!resourcesProp) {
|
|
77
77
|
return [];
|
|
78
78
|
}
|
|
79
79
|
|
|
80
|
-
const arrayNode =
|
|
80
|
+
const arrayNode = resourcesProp.value;
|
|
81
81
|
if (!arrayNode || (arrayNode as AstNode).type !== 'ArrayExpression') {
|
|
82
82
|
return [];
|
|
83
83
|
}
|
|
@@ -88,15 +88,15 @@ const getProvisionElements = (config: AstNode): readonly AstNode[] => {
|
|
|
88
88
|
return elements ?? [];
|
|
89
89
|
};
|
|
90
90
|
|
|
91
|
-
/** Extract one declared
|
|
92
|
-
const
|
|
91
|
+
/** Extract one declared resource from a `resources` array element. */
|
|
92
|
+
const extractDeclaredResource = (
|
|
93
93
|
element: AstNode,
|
|
94
|
-
|
|
95
|
-
):
|
|
94
|
+
resourceIdsByName: ReadonlyMap<string, string>
|
|
95
|
+
): DeclaredResource | null => {
|
|
96
96
|
if (element.type === 'Identifier') {
|
|
97
97
|
const name = identifierName(element);
|
|
98
98
|
return {
|
|
99
|
-
id: name ? (
|
|
99
|
+
id: name ? (resourceIdsByName.get(name) ?? null) : null,
|
|
100
100
|
name,
|
|
101
101
|
};
|
|
102
102
|
}
|
|
@@ -105,39 +105,113 @@ const extractDeclaredProvision = (
|
|
|
105
105
|
return { id: getStringValue(element), name: null };
|
|
106
106
|
}
|
|
107
107
|
|
|
108
|
-
if (
|
|
108
|
+
if (isInlineResourceCall(element)) {
|
|
109
109
|
return { id: extractFirstStringArg(element), name: null };
|
|
110
110
|
}
|
|
111
111
|
|
|
112
112
|
return null;
|
|
113
113
|
};
|
|
114
114
|
|
|
115
|
-
/** Extract declared
|
|
116
|
-
const
|
|
115
|
+
/** Extract declared resources from a trail config's `resources` array. */
|
|
116
|
+
const extractDeclaredResources = (
|
|
117
117
|
config: AstNode,
|
|
118
|
-
|
|
119
|
-
): readonly
|
|
120
|
-
|
|
121
|
-
const
|
|
122
|
-
return
|
|
118
|
+
resourceIdsByName: ReadonlyMap<string, string>
|
|
119
|
+
): readonly DeclaredResource[] =>
|
|
120
|
+
getResourceElements(config).flatMap((element) => {
|
|
121
|
+
const resource = extractDeclaredResource(element, resourceIdsByName);
|
|
122
|
+
return resource ? [resource] : [];
|
|
123
123
|
});
|
|
124
124
|
|
|
125
125
|
// ---------------------------------------------------------------------------
|
|
126
|
-
// Called
|
|
126
|
+
// Called resource extraction
|
|
127
127
|
// ---------------------------------------------------------------------------
|
|
128
128
|
|
|
129
|
-
/** Extract the second parameter
|
|
130
|
-
const
|
|
129
|
+
/** Extract the raw second parameter node from a blaze function. */
|
|
130
|
+
const extractContextParamNode = (blazeBody: AstNode): AstNode | null => {
|
|
131
131
|
const params = blazeBody['params'] as readonly AstNode[] | undefined;
|
|
132
132
|
if (!params || params.length < 2) {
|
|
133
133
|
return null;
|
|
134
134
|
}
|
|
135
|
-
return
|
|
135
|
+
return params[1] ?? null;
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Extract the second parameter name from a blaze function node.
|
|
140
|
+
*
|
|
141
|
+
* Returns null when the parameter is not a plain Identifier (e.g. when the
|
|
142
|
+
* author destructures `{ resource }` in the parameter list). Parameter-level
|
|
143
|
+
* destructuring is handled separately by `collectParamResourceAliases`.
|
|
144
|
+
*
|
|
145
|
+
* Also handles defaulted parameters like `(input, ctx = fallback) => ...`
|
|
146
|
+
* (AssignmentPattern whose `.left` is the Identifier). Without this, valid
|
|
147
|
+
* signatures would silently drop out of ctx-access analysis.
|
|
148
|
+
*/
|
|
149
|
+
const extractContextParamName = (blazeBody: AstNode): string | null => {
|
|
150
|
+
const param = extractContextParamNode(blazeBody);
|
|
151
|
+
if (!param) {
|
|
152
|
+
return null;
|
|
153
|
+
}
|
|
154
|
+
if (param.type === 'AssignmentPattern') {
|
|
155
|
+
const { left } = param as unknown as { left?: AstNode };
|
|
156
|
+
return identifierName(left);
|
|
157
|
+
}
|
|
158
|
+
return identifierName(param);
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
/** Extract the alias name from a Property node whose key is `resource`. */
|
|
162
|
+
const extractResourceAlias = (property: AstNode): string | null => {
|
|
163
|
+
if (property.type !== 'Property') {
|
|
164
|
+
return null;
|
|
165
|
+
}
|
|
166
|
+
const keyName = identifierName(
|
|
167
|
+
(property as unknown as { key?: AstNode }).key
|
|
168
|
+
);
|
|
169
|
+
if (keyName !== 'resource') {
|
|
170
|
+
return null;
|
|
171
|
+
}
|
|
172
|
+
return (
|
|
173
|
+
identifierName((property as unknown as { value?: AstNode }).value) ??
|
|
174
|
+
keyName
|
|
175
|
+
);
|
|
136
176
|
};
|
|
137
177
|
|
|
138
|
-
/**
|
|
178
|
+
/**
|
|
179
|
+
* Collect `resource` aliases bound via parameter-level destructuring.
|
|
180
|
+
*
|
|
181
|
+
* Recognizes `(input, { resource }) => ...` and `(input, { resource: r }) => ...`.
|
|
182
|
+
* When the blaze author destructures in the parameter list, there is no
|
|
183
|
+
* enclosing `ctx` identifier to track — we seed the resource alias set directly
|
|
184
|
+
* from the ObjectPattern in `params[1]`.
|
|
185
|
+
*/
|
|
186
|
+
const collectParamResourceAliases = (body: AstNode): ReadonlySet<string> => {
|
|
187
|
+
const param = extractContextParamNode(body);
|
|
188
|
+
if (!param || param.type !== 'ObjectPattern') {
|
|
189
|
+
return new Set();
|
|
190
|
+
}
|
|
191
|
+
const aliases = new Set<string>();
|
|
192
|
+
const properties = param['properties'] as readonly AstNode[] | undefined;
|
|
193
|
+
for (const property of properties ?? []) {
|
|
194
|
+
const alias = extractResourceAlias(property);
|
|
195
|
+
if (alias) {
|
|
196
|
+
aliases.add(alias);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
return aliases;
|
|
200
|
+
};
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Build the set of context parameter names to match against.
|
|
204
|
+
*
|
|
205
|
+
* Returns ONLY the actual second-parameter name from the blaze signature.
|
|
206
|
+
* No seeded defaults: if the blaze has no second parameter, the returned set
|
|
207
|
+
* is empty and no `ctx.resource(...)` / `context.resource(...)` calls are
|
|
208
|
+
* tracked for that blaze. An unrelated closure-scoped `ctx` identifier is not
|
|
209
|
+
* the trail context and must not be treated as one.
|
|
210
|
+
*
|
|
211
|
+
* Mirrors `fires-declarations.ts` `buildCtxNames` for the same reason.
|
|
212
|
+
*/
|
|
139
213
|
const buildCtxNames = (body: AstNode): ReadonlySet<string> => {
|
|
140
|
-
const ctxNames = new Set(
|
|
214
|
+
const ctxNames = new Set<string>();
|
|
141
215
|
const paramName = extractContextParamName(body);
|
|
142
216
|
if (paramName) {
|
|
143
217
|
ctxNames.add(paramName);
|
|
@@ -189,63 +263,63 @@ const extractFromCallName = (
|
|
|
189
263
|
: null;
|
|
190
264
|
};
|
|
191
265
|
|
|
192
|
-
/** Check if a callee is a member-style `ctx.
|
|
193
|
-
const
|
|
266
|
+
/** Check if a callee is a member-style `ctx.resource(...)` call. */
|
|
267
|
+
const isMemberResourceCall = (
|
|
194
268
|
callee: AstNode,
|
|
195
269
|
ctxNames: ReadonlySet<string>
|
|
196
270
|
): boolean => {
|
|
197
271
|
const pair = extractMemberPair(callee);
|
|
198
|
-
return !!pair && ctxNames.has(pair.objName) && pair.propName === '
|
|
272
|
+
return !!pair && ctxNames.has(pair.objName) && pair.propName === 'resource';
|
|
199
273
|
};
|
|
200
274
|
|
|
201
|
-
/** Extract `ctx.
|
|
202
|
-
const
|
|
275
|
+
/** Extract `ctx.resource(db)` and destructured `resource(db)` lookup names. */
|
|
276
|
+
const extractLookupResourceName = (
|
|
203
277
|
node: AstNode,
|
|
204
278
|
ctxNames: ReadonlySet<string>,
|
|
205
|
-
|
|
279
|
+
resourceAliases: ReadonlySet<string>
|
|
206
280
|
): string | null => {
|
|
207
281
|
const callee = extractCallCallee(node);
|
|
208
282
|
if (!callee) {
|
|
209
283
|
return null;
|
|
210
284
|
}
|
|
211
285
|
|
|
212
|
-
if (
|
|
286
|
+
if (isMemberResourceCall(callee, ctxNames)) {
|
|
213
287
|
return extractFirstIdentifierArg(node);
|
|
214
288
|
}
|
|
215
289
|
|
|
216
|
-
if (
|
|
290
|
+
if (resourceAliases.has(identifierName(callee) ?? '')) {
|
|
217
291
|
return extractFirstIdentifierArg(node);
|
|
218
292
|
}
|
|
219
293
|
|
|
220
294
|
return null;
|
|
221
295
|
};
|
|
222
296
|
|
|
223
|
-
/** Extract `ctx.
|
|
224
|
-
const
|
|
297
|
+
/** Extract `ctx.resource('id')` and destructured `resource('id')` lookup IDs. */
|
|
298
|
+
const extractLookupResourceId = (
|
|
225
299
|
node: AstNode,
|
|
226
300
|
ctxNames: ReadonlySet<string>,
|
|
227
|
-
|
|
301
|
+
resourceAliases: ReadonlySet<string>
|
|
228
302
|
): string | null => {
|
|
229
303
|
const callee = extractCallCallee(node);
|
|
230
304
|
if (!callee) {
|
|
231
305
|
return null;
|
|
232
306
|
}
|
|
233
307
|
|
|
234
|
-
if (
|
|
308
|
+
if (isMemberResourceCall(callee, ctxNames)) {
|
|
235
309
|
return extractFirstStringArg(node);
|
|
236
310
|
}
|
|
237
311
|
|
|
238
312
|
const calleeName = identifierName(callee);
|
|
239
313
|
const args = node['arguments'] as readonly AstNode[] | undefined;
|
|
240
|
-
if (calleeName &&
|
|
314
|
+
if (calleeName && resourceAliases.has(calleeName) && args?.length === 1) {
|
|
241
315
|
return extractFirstStringArg(node);
|
|
242
316
|
}
|
|
243
317
|
|
|
244
318
|
return null;
|
|
245
319
|
};
|
|
246
320
|
|
|
247
|
-
/** Collect local aliases for the
|
|
248
|
-
const
|
|
321
|
+
/** Collect local aliases for the resource accessor (e.g. `const { resource } = ctx`). */
|
|
322
|
+
const collectResourceAliases = (
|
|
249
323
|
body: AstNode,
|
|
250
324
|
ctxNames: ReadonlySet<string>
|
|
251
325
|
): ReadonlySet<string> => {
|
|
@@ -267,7 +341,7 @@ const collectProvisionAliases = (
|
|
|
267
341
|
const keyName = identifierName(
|
|
268
342
|
(property as unknown as { key?: AstNode }).key
|
|
269
343
|
);
|
|
270
|
-
if (keyName !== '
|
|
344
|
+
if (keyName !== 'resource') {
|
|
271
345
|
return [];
|
|
272
346
|
}
|
|
273
347
|
|
|
@@ -300,15 +374,17 @@ const collectProvisionAliases = (
|
|
|
300
374
|
return aliases;
|
|
301
375
|
};
|
|
302
376
|
|
|
303
|
-
/** Walk blaze bodies and collect
|
|
304
|
-
const
|
|
377
|
+
/** Walk blaze bodies and collect resource access that can be resolved statically. */
|
|
378
|
+
const extractCalledResources = (config: AstNode): CalledResources => {
|
|
305
379
|
const fromNames = new Set<string>();
|
|
306
380
|
const lookupIds = new Set<string>();
|
|
307
381
|
const lookupNames = new Set<string>();
|
|
308
382
|
|
|
309
383
|
for (const body of findBlazeBodies(config)) {
|
|
310
384
|
const ctxNames = buildCtxNames(body);
|
|
311
|
-
const
|
|
385
|
+
const paramAliases = collectParamResourceAliases(body);
|
|
386
|
+
const bodyAliases = collectResourceAliases(body, ctxNames);
|
|
387
|
+
const resourceAliases = new Set([...paramAliases, ...bodyAliases]);
|
|
312
388
|
|
|
313
389
|
walkScope(body, (node) => {
|
|
314
390
|
const fromName = extractFromCallName(node, ctxNames);
|
|
@@ -316,19 +392,15 @@ const extractCalledProvisions = (config: AstNode): CalledProvisions => {
|
|
|
316
392
|
fromNames.add(fromName);
|
|
317
393
|
}
|
|
318
394
|
|
|
319
|
-
const lookupId =
|
|
320
|
-
node,
|
|
321
|
-
ctxNames,
|
|
322
|
-
provisionAliases
|
|
323
|
-
);
|
|
395
|
+
const lookupId = extractLookupResourceId(node, ctxNames, resourceAliases);
|
|
324
396
|
if (lookupId) {
|
|
325
397
|
lookupIds.add(lookupId);
|
|
326
398
|
}
|
|
327
399
|
|
|
328
|
-
const lookupName =
|
|
400
|
+
const lookupName = extractLookupResourceName(
|
|
329
401
|
node,
|
|
330
402
|
ctxNames,
|
|
331
|
-
|
|
403
|
+
resourceAliases
|
|
332
404
|
);
|
|
333
405
|
if (lookupName) {
|
|
334
406
|
lookupNames.add(lookupName);
|
|
@@ -343,58 +415,58 @@ const extractCalledProvisions = (config: AstNode): CalledProvisions => {
|
|
|
343
415
|
// Diagnostics
|
|
344
416
|
// ---------------------------------------------------------------------------
|
|
345
417
|
|
|
346
|
-
const
|
|
347
|
-
|
|
418
|
+
const renderDeclaredResource = (resource: DeclaredResource): string =>
|
|
419
|
+
resource.name ?? resource.id ?? '<unknown>';
|
|
348
420
|
|
|
349
421
|
const buildUndeclaredFromDiagnostic = (
|
|
350
422
|
trailId: string,
|
|
351
|
-
|
|
423
|
+
resourceName: string,
|
|
352
424
|
filePath: string,
|
|
353
425
|
line: number
|
|
354
426
|
): WardenDiagnostic => ({
|
|
355
427
|
filePath,
|
|
356
428
|
line,
|
|
357
|
-
message: `Trail "${trailId}": ${
|
|
358
|
-
rule: '
|
|
429
|
+
message: `Trail "${trailId}": ${resourceName}.from(ctx) called but '${resourceName}' is not declared in resources`,
|
|
430
|
+
rule: 'resource-declarations',
|
|
359
431
|
severity: 'error',
|
|
360
432
|
});
|
|
361
433
|
|
|
362
434
|
const buildUndeclaredLookupDiagnostic = (
|
|
363
435
|
trailId: string,
|
|
364
|
-
|
|
436
|
+
resourceId: string,
|
|
365
437
|
filePath: string,
|
|
366
438
|
line: number
|
|
367
439
|
): WardenDiagnostic => ({
|
|
368
440
|
filePath,
|
|
369
441
|
line,
|
|
370
|
-
message: `Trail "${trailId}": ctx.
|
|
371
|
-
rule: '
|
|
442
|
+
message: `Trail "${trailId}": ctx.resource('${resourceId}') called but '${resourceId}' is not declared in resources`,
|
|
443
|
+
rule: 'resource-declarations',
|
|
372
444
|
severity: 'error',
|
|
373
445
|
});
|
|
374
446
|
|
|
375
447
|
const buildUndeclaredLookupNameDiagnostic = (
|
|
376
448
|
trailId: string,
|
|
377
|
-
|
|
449
|
+
resourceName: string,
|
|
378
450
|
filePath: string,
|
|
379
451
|
line: number
|
|
380
452
|
): WardenDiagnostic => ({
|
|
381
453
|
filePath,
|
|
382
454
|
line,
|
|
383
|
-
message: `Trail "${trailId}": ctx.
|
|
384
|
-
rule: '
|
|
455
|
+
message: `Trail "${trailId}": ctx.resource(${resourceName}) called but '${resourceName}' is not declared in resources`,
|
|
456
|
+
rule: 'resource-declarations',
|
|
385
457
|
severity: 'error',
|
|
386
458
|
});
|
|
387
459
|
|
|
388
460
|
const buildUnusedDiagnostic = (
|
|
389
461
|
trailId: string,
|
|
390
|
-
|
|
462
|
+
declaredResource: DeclaredResource,
|
|
391
463
|
filePath: string,
|
|
392
464
|
line: number
|
|
393
465
|
): WardenDiagnostic => ({
|
|
394
466
|
filePath,
|
|
395
467
|
line,
|
|
396
|
-
message: `Trail "${trailId}": '${
|
|
397
|
-
rule: '
|
|
468
|
+
message: `Trail "${trailId}": '${renderDeclaredResource(declaredResource)}' declared in resources but never used`,
|
|
469
|
+
rule: 'resource-declarations',
|
|
398
470
|
severity: 'warn',
|
|
399
471
|
});
|
|
400
472
|
|
|
@@ -402,21 +474,21 @@ const buildUnusedDiagnostic = (
|
|
|
402
474
|
// Comparison
|
|
403
475
|
// ---------------------------------------------------------------------------
|
|
404
476
|
|
|
405
|
-
const
|
|
406
|
-
|
|
407
|
-
|
|
477
|
+
const resourceWasUsed = (
|
|
478
|
+
declaredResource: DeclaredResource,
|
|
479
|
+
calledResources: CalledResources
|
|
408
480
|
): boolean => {
|
|
409
481
|
if (
|
|
410
|
-
|
|
411
|
-
(
|
|
412
|
-
|
|
482
|
+
declaredResource.name &&
|
|
483
|
+
(calledResources.fromNames.has(declaredResource.name) ||
|
|
484
|
+
calledResources.lookupNames.has(declaredResource.name))
|
|
413
485
|
) {
|
|
414
486
|
return true;
|
|
415
487
|
}
|
|
416
488
|
|
|
417
489
|
if (
|
|
418
|
-
|
|
419
|
-
|
|
490
|
+
declaredResource.id &&
|
|
491
|
+
calledResources.lookupIds.has(declaredResource.id)
|
|
420
492
|
) {
|
|
421
493
|
return true;
|
|
422
494
|
}
|
|
@@ -425,35 +497,33 @@ const provisionWasUsed = (
|
|
|
425
497
|
};
|
|
426
498
|
|
|
427
499
|
const buildDeclaredNames = (
|
|
428
|
-
|
|
500
|
+
declaredResources: readonly DeclaredResource[]
|
|
429
501
|
): ReadonlySet<string> =>
|
|
430
502
|
new Set(
|
|
431
|
-
|
|
432
|
-
|
|
503
|
+
declaredResources.flatMap((resource) =>
|
|
504
|
+
resource.name ? [resource.name] : []
|
|
433
505
|
)
|
|
434
506
|
);
|
|
435
507
|
|
|
436
508
|
const buildDeclaredIds = (
|
|
437
|
-
|
|
509
|
+
declaredResources: readonly DeclaredResource[]
|
|
438
510
|
): ReadonlySet<string> =>
|
|
439
511
|
new Set(
|
|
440
|
-
|
|
441
|
-
provision.id ? [provision.id] : []
|
|
442
|
-
)
|
|
512
|
+
declaredResources.flatMap((resource) => (resource.id ? [resource.id] : []))
|
|
443
513
|
);
|
|
444
514
|
|
|
445
515
|
const reportUndeclaredFromCalls = (
|
|
446
516
|
trailId: string,
|
|
447
517
|
filePath: string,
|
|
448
518
|
line: number,
|
|
449
|
-
|
|
519
|
+
calledResources: CalledResources,
|
|
450
520
|
declaredNames: ReadonlySet<string>,
|
|
451
521
|
diagnostics: WardenDiagnostic[]
|
|
452
522
|
): void => {
|
|
453
|
-
for (const
|
|
454
|
-
if (!declaredNames.has(
|
|
523
|
+
for (const resourceName of calledResources.fromNames) {
|
|
524
|
+
if (!declaredNames.has(resourceName)) {
|
|
455
525
|
diagnostics.push(
|
|
456
|
-
buildUndeclaredFromDiagnostic(trailId,
|
|
526
|
+
buildUndeclaredFromDiagnostic(trailId, resourceName, filePath, line)
|
|
457
527
|
);
|
|
458
528
|
}
|
|
459
529
|
}
|
|
@@ -463,19 +533,19 @@ const reportUndeclaredLookupCalls = (
|
|
|
463
533
|
trailId: string,
|
|
464
534
|
filePath: string,
|
|
465
535
|
line: number,
|
|
466
|
-
|
|
536
|
+
calledResources: CalledResources,
|
|
467
537
|
declaredIds: ReadonlySet<string>,
|
|
468
538
|
declaredNames: ReadonlySet<string>,
|
|
469
539
|
diagnostics: WardenDiagnostic[]
|
|
470
540
|
): void => {
|
|
471
|
-
for (const
|
|
472
|
-
// Name-based lookup checks remain reliable even when an imported
|
|
541
|
+
for (const resourceName of calledResources.lookupNames) {
|
|
542
|
+
// Name-based lookup checks remain reliable even when an imported resource ID
|
|
473
543
|
// cannot be resolved locally.
|
|
474
|
-
if (!declaredNames.has(
|
|
544
|
+
if (!declaredNames.has(resourceName)) {
|
|
475
545
|
diagnostics.push(
|
|
476
546
|
buildUndeclaredLookupNameDiagnostic(
|
|
477
547
|
trailId,
|
|
478
|
-
|
|
548
|
+
resourceName,
|
|
479
549
|
filePath,
|
|
480
550
|
line
|
|
481
551
|
)
|
|
@@ -483,10 +553,10 @@ const reportUndeclaredLookupCalls = (
|
|
|
483
553
|
}
|
|
484
554
|
}
|
|
485
555
|
|
|
486
|
-
for (const
|
|
487
|
-
if (!declaredIds.has(
|
|
556
|
+
for (const resourceId of calledResources.lookupIds) {
|
|
557
|
+
if (!declaredIds.has(resourceId)) {
|
|
488
558
|
diagnostics.push(
|
|
489
|
-
buildUndeclaredLookupDiagnostic(trailId,
|
|
559
|
+
buildUndeclaredLookupDiagnostic(trailId, resourceId, filePath, line)
|
|
490
560
|
);
|
|
491
561
|
}
|
|
492
562
|
}
|
|
@@ -496,54 +566,54 @@ const reportUnusedDeclarations = (
|
|
|
496
566
|
trailId: string,
|
|
497
567
|
filePath: string,
|
|
498
568
|
line: number,
|
|
499
|
-
|
|
500
|
-
|
|
569
|
+
declaredResources: readonly DeclaredResource[],
|
|
570
|
+
calledResources: CalledResources,
|
|
501
571
|
diagnostics: WardenDiagnostic[]
|
|
502
572
|
): void => {
|
|
503
|
-
for (const
|
|
504
|
-
if (
|
|
573
|
+
for (const declaredResource of declaredResources) {
|
|
574
|
+
if (resourceWasUsed(declaredResource, calledResources)) {
|
|
505
575
|
continue;
|
|
506
576
|
}
|
|
507
577
|
|
|
508
|
-
if (
|
|
578
|
+
if (declaredResource.name && declaredResource.id === null) {
|
|
509
579
|
continue;
|
|
510
580
|
}
|
|
511
581
|
|
|
512
582
|
diagnostics.push(
|
|
513
|
-
buildUnusedDiagnostic(trailId,
|
|
583
|
+
buildUnusedDiagnostic(trailId, declaredResource, filePath, line)
|
|
514
584
|
);
|
|
515
585
|
}
|
|
516
586
|
};
|
|
517
587
|
|
|
518
|
-
const
|
|
519
|
-
|
|
520
|
-
|
|
588
|
+
const hasNoResourceActivity = (
|
|
589
|
+
declaredResources: readonly DeclaredResource[],
|
|
590
|
+
calledResources: CalledResources
|
|
521
591
|
): boolean =>
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
592
|
+
declaredResources.length === 0 &&
|
|
593
|
+
calledResources.fromNames.size === 0 &&
|
|
594
|
+
calledResources.lookupIds.size === 0 &&
|
|
595
|
+
calledResources.lookupNames.size === 0;
|
|
526
596
|
|
|
527
597
|
const analyzeTrailServices = (
|
|
528
598
|
def: { config: AstNode; start: number },
|
|
529
599
|
sourceCode: string,
|
|
530
|
-
|
|
600
|
+
resourceIdsByName: ReadonlyMap<string, string>
|
|
531
601
|
): {
|
|
532
|
-
readonly
|
|
602
|
+
readonly calledResources: CalledResources;
|
|
533
603
|
readonly declaredIds: ReadonlySet<string>;
|
|
534
604
|
readonly declaredNames: ReadonlySet<string>;
|
|
535
|
-
readonly
|
|
605
|
+
readonly declaredResources: readonly DeclaredResource[];
|
|
536
606
|
readonly line: number;
|
|
537
607
|
} => {
|
|
538
|
-
const
|
|
608
|
+
const declaredResources = extractDeclaredResources(
|
|
539
609
|
def.config,
|
|
540
|
-
|
|
610
|
+
resourceIdsByName
|
|
541
611
|
);
|
|
542
612
|
return {
|
|
543
|
-
|
|
544
|
-
declaredIds: buildDeclaredIds(
|
|
545
|
-
declaredNames: buildDeclaredNames(
|
|
546
|
-
|
|
613
|
+
calledResources: extractCalledResources(def.config),
|
|
614
|
+
declaredIds: buildDeclaredIds(declaredResources),
|
|
615
|
+
declaredNames: buildDeclaredNames(declaredResources),
|
|
616
|
+
declaredResources,
|
|
547
617
|
line: offsetToLine(sourceCode, def.start),
|
|
548
618
|
};
|
|
549
619
|
};
|
|
@@ -552,18 +622,18 @@ const checkTrailDefinition = (
|
|
|
552
622
|
def: { id: string; config: AstNode; start: number },
|
|
553
623
|
filePath: string,
|
|
554
624
|
sourceCode: string,
|
|
555
|
-
|
|
625
|
+
resourceIdsByName: ReadonlyMap<string, string>,
|
|
556
626
|
diagnostics: WardenDiagnostic[]
|
|
557
627
|
): void => {
|
|
558
628
|
const {
|
|
559
|
-
|
|
629
|
+
calledResources,
|
|
560
630
|
declaredIds,
|
|
561
631
|
declaredNames,
|
|
562
|
-
|
|
632
|
+
declaredResources,
|
|
563
633
|
line,
|
|
564
|
-
} = analyzeTrailServices(def, sourceCode,
|
|
634
|
+
} = analyzeTrailServices(def, sourceCode, resourceIdsByName);
|
|
565
635
|
|
|
566
|
-
if (
|
|
636
|
+
if (hasNoResourceActivity(declaredResources, calledResources)) {
|
|
567
637
|
return;
|
|
568
638
|
}
|
|
569
639
|
|
|
@@ -571,7 +641,7 @@ const checkTrailDefinition = (
|
|
|
571
641
|
def.id,
|
|
572
642
|
filePath,
|
|
573
643
|
line,
|
|
574
|
-
|
|
644
|
+
calledResources,
|
|
575
645
|
declaredNames,
|
|
576
646
|
diagnostics
|
|
577
647
|
);
|
|
@@ -579,7 +649,7 @@ const checkTrailDefinition = (
|
|
|
579
649
|
def.id,
|
|
580
650
|
filePath,
|
|
581
651
|
line,
|
|
582
|
-
|
|
652
|
+
calledResources,
|
|
583
653
|
declaredIds,
|
|
584
654
|
declaredNames,
|
|
585
655
|
diagnostics
|
|
@@ -588,8 +658,8 @@ const checkTrailDefinition = (
|
|
|
588
658
|
def.id,
|
|
589
659
|
filePath,
|
|
590
660
|
line,
|
|
591
|
-
|
|
592
|
-
|
|
661
|
+
declaredResources,
|
|
662
|
+
calledResources,
|
|
593
663
|
diagnostics
|
|
594
664
|
);
|
|
595
665
|
};
|
|
@@ -599,9 +669,9 @@ const checkTrailDefinition = (
|
|
|
599
669
|
// ---------------------------------------------------------------------------
|
|
600
670
|
|
|
601
671
|
/**
|
|
602
|
-
* Validates that
|
|
672
|
+
* Validates that resource access aligns with declared `resources` arrays.
|
|
603
673
|
*/
|
|
604
|
-
export const
|
|
674
|
+
export const resourceDeclarations: WardenRule = {
|
|
605
675
|
check(sourceCode: string, filePath: string): readonly WardenDiagnostic[] {
|
|
606
676
|
if (isTestFile(filePath)) {
|
|
607
677
|
return [];
|
|
@@ -613,14 +683,14 @@ export const provisionDeclarations: WardenRule = {
|
|
|
613
683
|
}
|
|
614
684
|
|
|
615
685
|
const diagnostics: WardenDiagnostic[] = [];
|
|
616
|
-
const
|
|
686
|
+
const resourceIdsByName = collectNamedResourceIds(ast);
|
|
617
687
|
|
|
618
688
|
for (const def of findTrailDefinitions(ast)) {
|
|
619
689
|
checkTrailDefinition(
|
|
620
690
|
def,
|
|
621
691
|
filePath,
|
|
622
692
|
sourceCode,
|
|
623
|
-
|
|
693
|
+
resourceIdsByName,
|
|
624
694
|
diagnostics
|
|
625
695
|
);
|
|
626
696
|
}
|
|
@@ -628,7 +698,7 @@ export const provisionDeclarations: WardenRule = {
|
|
|
628
698
|
return diagnostics;
|
|
629
699
|
},
|
|
630
700
|
description:
|
|
631
|
-
'Ensure
|
|
632
|
-
name: '
|
|
701
|
+
'Ensure resource.from(ctx) and ctx.resource() calls match the declared resources array in trail definitions.',
|
|
702
|
+
name: 'resource-declarations',
|
|
633
703
|
severity: 'error',
|
|
634
704
|
};
|