@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
|
@@ -61,10 +61,7 @@ const getStringValue = (node: AstNode): string | null => {
|
|
|
61
61
|
* Returns the string value if a simple `const <name> = '...'` or `"..."` is
|
|
62
62
|
* found in the source. Returns null for anything more complex.
|
|
63
63
|
*/
|
|
64
|
-
const
|
|
65
|
-
name: string,
|
|
66
|
-
sourceCode: string
|
|
67
|
-
): string | null => {
|
|
64
|
+
const deriveConstString = (name: string, sourceCode: string): string | null => {
|
|
68
65
|
const pattern = new RegExp(
|
|
69
66
|
`const\\s+${name}\\s*=\\s*(?:'([^']*)'|"([^"]*)")`
|
|
70
67
|
);
|
|
@@ -84,11 +81,11 @@ const resolveIdentifierElement = (
|
|
|
84
81
|
if (!name) {
|
|
85
82
|
return null;
|
|
86
83
|
}
|
|
87
|
-
return
|
|
84
|
+
return deriveConstString(name, sourceCode);
|
|
88
85
|
};
|
|
89
86
|
|
|
90
87
|
/** Resolve an array element to a static trail ID when possible. */
|
|
91
|
-
const
|
|
88
|
+
const deriveCrossElementId = (
|
|
92
89
|
element: AstNode,
|
|
93
90
|
sourceCode: string
|
|
94
91
|
): string | null => {
|
|
@@ -125,28 +122,62 @@ const getCrossElements = (config: AstNode): readonly AstNode[] | null => {
|
|
|
125
122
|
return elements ?? null;
|
|
126
123
|
};
|
|
127
124
|
|
|
128
|
-
|
|
129
|
-
const
|
|
125
|
+
interface DeclaredCrosses {
|
|
126
|
+
/** Statically resolved trail IDs from string literals / const identifiers. */
|
|
127
|
+
readonly ids: ReadonlySet<string>;
|
|
128
|
+
/**
|
|
129
|
+
* True if any element could not be statically resolved (e.g. trail object
|
|
130
|
+
* reference like `crosses: [showGist]`). When true, "undeclared" diagnostics
|
|
131
|
+
* are softened from error to warn since the declared set is incomplete.
|
|
132
|
+
*/
|
|
133
|
+
readonly hasUnresolved: boolean;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Collect string IDs from array elements, resolving identifiers when possible.
|
|
138
|
+
*
|
|
139
|
+
* Trail-object references (`crosses: [showGist]`) cannot be resolved at lint
|
|
140
|
+
* time; they're normalized at runtime by `trail()`. When any entry is
|
|
141
|
+
* unresolved, `hasUnresolved` is set so callers can soften diagnostics.
|
|
142
|
+
*/
|
|
143
|
+
/** Classify a single element and accumulate into the id set. */
|
|
144
|
+
const classifyCrossElement = (
|
|
145
|
+
element: AstNode,
|
|
146
|
+
sourceCode: string,
|
|
147
|
+
ids: Set<string>
|
|
148
|
+
): boolean => {
|
|
149
|
+
const resolved = deriveCrossElementId(element, sourceCode);
|
|
150
|
+
if (!resolved) {
|
|
151
|
+
// Element could not be statically resolved
|
|
152
|
+
return true;
|
|
153
|
+
}
|
|
154
|
+
ids.add(resolved);
|
|
155
|
+
return false;
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
const resolveDeclaredCrossElements = (
|
|
130
159
|
elements: readonly AstNode[],
|
|
131
160
|
sourceCode: string
|
|
132
|
-
):
|
|
161
|
+
): DeclaredCrosses => {
|
|
133
162
|
const ids = new Set<string>();
|
|
163
|
+
let hasUnresolved = false;
|
|
134
164
|
for (const element of elements) {
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
ids.add(resolved);
|
|
165
|
+
if (classifyCrossElement(element, sourceCode, ids)) {
|
|
166
|
+
hasUnresolved = true;
|
|
138
167
|
}
|
|
139
168
|
}
|
|
140
|
-
return ids;
|
|
169
|
+
return { hasUnresolved, ids };
|
|
141
170
|
};
|
|
142
171
|
|
|
143
|
-
/** Extract
|
|
172
|
+
/** Extract declared crosses from a `crosses: [...]` array. */
|
|
144
173
|
const extractDeclaredCrosses = (
|
|
145
174
|
config: AstNode,
|
|
146
175
|
sourceCode: string
|
|
147
|
-
):
|
|
176
|
+
): DeclaredCrosses => {
|
|
148
177
|
const elements = getCrossElements(config);
|
|
149
|
-
return elements
|
|
178
|
+
return elements
|
|
179
|
+
? resolveDeclaredCrossElements(elements, sourceCode)
|
|
180
|
+
: { hasUnresolved: false, ids: new Set() };
|
|
150
181
|
};
|
|
151
182
|
|
|
152
183
|
// ---------------------------------------------------------------------------
|
|
@@ -173,33 +204,25 @@ const extractMemberPair = (
|
|
|
173
204
|
return objName && propName ? { objName, propName } : null;
|
|
174
205
|
};
|
|
175
206
|
|
|
176
|
-
/** Extract the first argument string from a CallExpression's arguments list. */
|
|
177
|
-
const extractFirstStringArg = (node: AstNode): string | null => {
|
|
178
|
-
const args = node['arguments'] as readonly AstNode[] | undefined;
|
|
179
|
-
if (!args || args.length === 0) {
|
|
180
|
-
return null;
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
const [firstArg] = args;
|
|
184
|
-
if (!firstArg || !isStringLiteral(firstArg)) {
|
|
185
|
-
return null;
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
return getStringValue(firstArg);
|
|
189
|
-
};
|
|
190
|
-
|
|
191
207
|
/**
|
|
192
208
|
* Extract the second parameter name from a blaze function node.
|
|
193
209
|
*
|
|
194
|
-
* Handles `(input, ctx) => ...`, `async (input, context) => ...`,
|
|
195
|
-
* `function(input, ctx) { ... }
|
|
210
|
+
* Handles `(input, ctx) => ...`, `async (input, context) => ...`,
|
|
211
|
+
* `function(input, ctx) { ... }`, and defaulted params like
|
|
212
|
+
* `(input, ctx = fallback) => ...` (AssignmentPattern whose `.left` is the
|
|
213
|
+
* Identifier).
|
|
196
214
|
*/
|
|
197
215
|
const extractContextParamName = (blazeBody: AstNode): string | null => {
|
|
198
216
|
const params = blazeBody['params'] as readonly AstNode[] | undefined;
|
|
199
217
|
if (!params || params.length < 2) {
|
|
200
218
|
return null;
|
|
201
219
|
}
|
|
202
|
-
|
|
220
|
+
const [, param] = params;
|
|
221
|
+
if (param?.type === 'AssignmentPattern') {
|
|
222
|
+
const { left } = param as unknown as { left?: AstNode };
|
|
223
|
+
return identifierName(left);
|
|
224
|
+
}
|
|
225
|
+
return identifierName(param);
|
|
203
226
|
};
|
|
204
227
|
|
|
205
228
|
/** Check if a callee is a member-style cross call: <ctxName>.cross(...). */
|
|
@@ -211,38 +234,151 @@ const isMemberCrossCall = (
|
|
|
211
234
|
return !!pair && ctxNames.has(pair.objName) && pair.propName === 'cross';
|
|
212
235
|
};
|
|
213
236
|
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
const
|
|
237
|
+
interface ExtractedCrossCall {
|
|
238
|
+
readonly ids: readonly string[];
|
|
239
|
+
readonly hasUnresolved: boolean;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
const unresolvedCross = (): ExtractedCrossCall => ({
|
|
243
|
+
hasUnresolved: true,
|
|
244
|
+
ids: [],
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
const resolveBatchCrossTupleTarget = (
|
|
248
|
+
element: AstNode,
|
|
249
|
+
sourceCode: string
|
|
250
|
+
): string | null => {
|
|
251
|
+
if (element.type !== 'ArrayExpression') {
|
|
252
|
+
return null;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
const tupleElements = element['elements'] as readonly AstNode[] | undefined;
|
|
256
|
+
const [target] = tupleElements ?? [];
|
|
257
|
+
return target ? deriveCrossElementId(target, sourceCode) : null;
|
|
258
|
+
};
|
|
259
|
+
|
|
260
|
+
const collectBatchCrossId = (
|
|
261
|
+
element: AstNode,
|
|
262
|
+
sourceCode: string,
|
|
263
|
+
ids: string[]
|
|
264
|
+
): boolean => {
|
|
265
|
+
const resolved = resolveBatchCrossTupleTarget(element, sourceCode);
|
|
266
|
+
if (!resolved) {
|
|
267
|
+
return true;
|
|
268
|
+
}
|
|
269
|
+
ids.push(resolved);
|
|
270
|
+
return false;
|
|
271
|
+
};
|
|
272
|
+
|
|
273
|
+
/** Extract statically-resolved trail IDs from `ctx.cross([[trail, input], ...])`. */
|
|
274
|
+
const extractBatchCrossIds = (
|
|
275
|
+
firstArg: AstNode | undefined,
|
|
276
|
+
sourceCode: string
|
|
277
|
+
): ExtractedCrossCall | null => {
|
|
278
|
+
if (firstArg?.type !== 'ArrayExpression') {
|
|
279
|
+
return null;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
const elements = firstArg['elements'] as readonly AstNode[] | undefined;
|
|
283
|
+
const ids: string[] = [];
|
|
284
|
+
let hasUnresolved = false;
|
|
285
|
+
|
|
286
|
+
for (const element of elements ?? []) {
|
|
287
|
+
if (collectBatchCrossId(element, sourceCode, ids)) {
|
|
288
|
+
hasUnresolved = true;
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
return { hasUnresolved, ids };
|
|
293
|
+
};
|
|
294
|
+
|
|
295
|
+
const extractDirectCrossIds = (
|
|
296
|
+
firstArg: AstNode | undefined
|
|
297
|
+
): ExtractedCrossCall | null => {
|
|
298
|
+
if (!firstArg || !isStringLiteral(firstArg)) {
|
|
299
|
+
return null;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
const value = getStringValue(firstArg);
|
|
303
|
+
return value ? { hasUnresolved: false, ids: [value] } : unresolvedCross();
|
|
304
|
+
};
|
|
305
|
+
|
|
306
|
+
const isCrossCallExpression = (
|
|
307
|
+
callee: AstNode,
|
|
308
|
+
ctxNames: ReadonlySet<string>
|
|
309
|
+
): boolean =>
|
|
310
|
+
isMemberCrossCall(callee, ctxNames) || identifierName(callee) === 'cross';
|
|
311
|
+
|
|
312
|
+
const extractCrossFirstArg = (node: AstNode): AstNode | undefined => {
|
|
313
|
+
const args = node['arguments'] as readonly AstNode[] | undefined;
|
|
314
|
+
return args?.[0];
|
|
315
|
+
};
|
|
316
|
+
|
|
317
|
+
const resolveCrossCallNode = (
|
|
220
318
|
node: AstNode,
|
|
221
319
|
ctxNames: ReadonlySet<string>
|
|
222
|
-
):
|
|
320
|
+
): AstNode | null => {
|
|
223
321
|
if (node.type !== 'CallExpression') {
|
|
224
322
|
return null;
|
|
225
323
|
}
|
|
226
324
|
|
|
227
325
|
const callee = node['callee'] as AstNode | undefined;
|
|
228
|
-
if (!callee) {
|
|
326
|
+
if (!callee || !isCrossCallExpression(callee, ctxNames)) {
|
|
229
327
|
return null;
|
|
230
328
|
}
|
|
231
329
|
|
|
232
|
-
|
|
233
|
-
|
|
330
|
+
return node;
|
|
331
|
+
};
|
|
332
|
+
|
|
333
|
+
const resolveCrossCallTargets = (
|
|
334
|
+
firstArg: AstNode | undefined,
|
|
335
|
+
sourceCode: string
|
|
336
|
+
): ExtractedCrossCall => {
|
|
337
|
+
const direct = extractDirectCrossIds(firstArg);
|
|
338
|
+
if (direct) {
|
|
339
|
+
return direct;
|
|
234
340
|
}
|
|
235
341
|
|
|
236
|
-
|
|
237
|
-
|
|
342
|
+
const batch = extractBatchCrossIds(firstArg, sourceCode);
|
|
343
|
+
return batch ?? unresolvedCross();
|
|
344
|
+
};
|
|
345
|
+
|
|
346
|
+
/**
|
|
347
|
+
* Check if a node is a `<ctxName>.cross(...)` call and return any statically
|
|
348
|
+
* resolvable target IDs.
|
|
349
|
+
*
|
|
350
|
+
* Also matches bare `cross(...)` calls from destructuring. When the first
|
|
351
|
+
* argument is a non-string expression (e.g. a trail object identifier like
|
|
352
|
+
* `ctx.cross(showGist, input)`), marks the call as unresolved so callers can
|
|
353
|
+
* track that a cross call exists but its target cannot be statically resolved.
|
|
354
|
+
*/
|
|
355
|
+
const extractCrossCall = (
|
|
356
|
+
node: AstNode,
|
|
357
|
+
ctxNames: ReadonlySet<string>,
|
|
358
|
+
sourceCode: string
|
|
359
|
+
): ExtractedCrossCall | null => {
|
|
360
|
+
const crossCall = resolveCrossCallNode(node, ctxNames);
|
|
361
|
+
if (!crossCall) {
|
|
362
|
+
return null;
|
|
238
363
|
}
|
|
239
364
|
|
|
240
|
-
return
|
|
365
|
+
return resolveCrossCallTargets(extractCrossFirstArg(crossCall), sourceCode);
|
|
241
366
|
};
|
|
242
367
|
|
|
243
|
-
/**
|
|
368
|
+
/**
|
|
369
|
+
* Build the set of context parameter names to match against.
|
|
370
|
+
*
|
|
371
|
+
* Returns ONLY the actual second-parameter name from the blaze signature.
|
|
372
|
+
* No seeded defaults: if the blaze has no second parameter, the returned set
|
|
373
|
+
* is empty and no `ctx.cross(...)` / `context.cross(...)` calls are tracked
|
|
374
|
+
* for that blaze. An unrelated closure-scoped `ctx` identifier is not the
|
|
375
|
+
* trail context and must not be treated as one.
|
|
376
|
+
*
|
|
377
|
+
* Mirrors `fires-declarations.ts` and `resource-declarations.ts` for the same
|
|
378
|
+
* reason.
|
|
379
|
+
*/
|
|
244
380
|
const buildCtxNames = (body: AstNode): ReadonlySet<string> => {
|
|
245
|
-
const ctxNames = new Set(
|
|
381
|
+
const ctxNames = new Set<string>();
|
|
246
382
|
const paramName = extractContextParamName(body);
|
|
247
383
|
if (paramName) {
|
|
248
384
|
ctxNames.add(paramName);
|
|
@@ -250,22 +386,59 @@ const buildCtxNames = (body: AstNode): ReadonlySet<string> => {
|
|
|
250
386
|
return ctxNames;
|
|
251
387
|
};
|
|
252
388
|
|
|
389
|
+
interface CalledCrosses {
|
|
390
|
+
/** Statically resolved trail IDs from string literal arguments. */
|
|
391
|
+
readonly ids: ReadonlySet<string>;
|
|
392
|
+
/**
|
|
393
|
+
* True if any `ctx.cross()` call used a non-string first argument (e.g.
|
|
394
|
+
* `ctx.cross(showGist, input)`). When true, "unused declaration"
|
|
395
|
+
* diagnostics are softened since the call may target a declared entry.
|
|
396
|
+
*/
|
|
397
|
+
readonly hasUnresolved: boolean;
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
/** Collect cross call results from a single blaze body. */
|
|
401
|
+
const collectCrossCallsFromBody = (
|
|
402
|
+
body: AstNode,
|
|
403
|
+
ids: Set<string>,
|
|
404
|
+
sourceCode: string
|
|
405
|
+
): boolean => {
|
|
406
|
+
const ctxNames = buildCtxNames(body);
|
|
407
|
+
let foundUnresolved = false;
|
|
408
|
+
|
|
409
|
+
walk(body, (node) => {
|
|
410
|
+
const extracted = extractCrossCall(node, ctxNames, sourceCode);
|
|
411
|
+
if (!extracted) {
|
|
412
|
+
return;
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
if (extracted.hasUnresolved) {
|
|
416
|
+
foundUnresolved = true;
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
for (const id of extracted.ids) {
|
|
420
|
+
ids.add(id);
|
|
421
|
+
}
|
|
422
|
+
});
|
|
423
|
+
|
|
424
|
+
return foundUnresolved;
|
|
425
|
+
};
|
|
426
|
+
|
|
253
427
|
/** Walk blaze bodies and collect all statically resolvable ctx.cross() trail IDs. */
|
|
254
|
-
const extractCalledCrosses = (
|
|
428
|
+
const extractCalledCrosses = (
|
|
429
|
+
config: AstNode,
|
|
430
|
+
sourceCode: string
|
|
431
|
+
): CalledCrosses => {
|
|
255
432
|
const ids = new Set<string>();
|
|
433
|
+
let hasUnresolved = false;
|
|
256
434
|
|
|
257
435
|
for (const body of findBlazeBodies(config)) {
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
const id = extractCrossCallId(node, ctxNames);
|
|
262
|
-
if (id) {
|
|
263
|
-
ids.add(id);
|
|
264
|
-
}
|
|
265
|
-
});
|
|
436
|
+
if (collectCrossCallsFromBody(body, ids, sourceCode)) {
|
|
437
|
+
hasUnresolved = true;
|
|
438
|
+
}
|
|
266
439
|
}
|
|
267
440
|
|
|
268
|
-
return ids;
|
|
441
|
+
return { hasUnresolved, ids };
|
|
269
442
|
};
|
|
270
443
|
|
|
271
444
|
// ---------------------------------------------------------------------------
|
|
@@ -276,13 +449,16 @@ const buildUndeclaredDiagnostic = (
|
|
|
276
449
|
trailId: string,
|
|
277
450
|
crossedId: string,
|
|
278
451
|
filePath: string,
|
|
279
|
-
line: number
|
|
452
|
+
line: number,
|
|
453
|
+
softened = false
|
|
280
454
|
): WardenDiagnostic => ({
|
|
281
455
|
filePath,
|
|
282
456
|
line,
|
|
283
|
-
message:
|
|
457
|
+
message: softened
|
|
458
|
+
? `Trail "${trailId}": ctx.cross('${crossedId}') called but '${crossedId}' is not declared in crosses (may be declared via trail object references)`
|
|
459
|
+
: `Trail "${trailId}": ctx.cross('${crossedId}') called but '${crossedId}' is not declared in crosses`,
|
|
284
460
|
rule: 'cross-declarations',
|
|
285
|
-
severity: 'error',
|
|
461
|
+
severity: softened ? 'warn' : 'error',
|
|
286
462
|
});
|
|
287
463
|
|
|
288
464
|
const buildUnusedDiagnostic = (
|
|
@@ -306,13 +482,24 @@ const buildUnusedDiagnostic = (
|
|
|
306
482
|
const reportUndeclared = (
|
|
307
483
|
called: ReadonlySet<string>,
|
|
308
484
|
declared: ReadonlySet<string>,
|
|
309
|
-
ctx: {
|
|
485
|
+
ctx: {
|
|
486
|
+
trailId: string;
|
|
487
|
+
filePath: string;
|
|
488
|
+
line: number;
|
|
489
|
+
softened?: boolean;
|
|
490
|
+
},
|
|
310
491
|
diagnostics: WardenDiagnostic[]
|
|
311
492
|
): void => {
|
|
312
493
|
for (const id of called) {
|
|
313
494
|
if (!declared.has(id)) {
|
|
314
495
|
diagnostics.push(
|
|
315
|
-
buildUndeclaredDiagnostic(
|
|
496
|
+
buildUndeclaredDiagnostic(
|
|
497
|
+
ctx.trailId,
|
|
498
|
+
id,
|
|
499
|
+
ctx.filePath,
|
|
500
|
+
ctx.line,
|
|
501
|
+
ctx.softened
|
|
502
|
+
)
|
|
316
503
|
);
|
|
317
504
|
}
|
|
318
505
|
}
|
|
@@ -341,17 +528,38 @@ const checkTrailDefinition = (
|
|
|
341
528
|
diagnostics: WardenDiagnostic[]
|
|
342
529
|
): void => {
|
|
343
530
|
const declared = extractDeclaredCrosses(def.config, sourceCode);
|
|
344
|
-
const called = extractCalledCrosses(def.config);
|
|
345
|
-
|
|
346
|
-
if (
|
|
531
|
+
const called = extractCalledCrosses(def.config, sourceCode);
|
|
532
|
+
|
|
533
|
+
if (
|
|
534
|
+
declared.ids.size === 0 &&
|
|
535
|
+
!declared.hasUnresolved &&
|
|
536
|
+
called.ids.size === 0 &&
|
|
537
|
+
!called.hasUnresolved
|
|
538
|
+
) {
|
|
347
539
|
return;
|
|
348
540
|
}
|
|
349
541
|
|
|
350
542
|
const line = offsetToLine(sourceCode, def.start);
|
|
351
543
|
const ctx = { filePath, line, trailId: def.id };
|
|
352
544
|
|
|
353
|
-
|
|
354
|
-
|
|
545
|
+
// When the declared array contains trail object references we can't resolve,
|
|
546
|
+
// downgrade "undeclared" diagnostics from error to warn. The developer still
|
|
547
|
+
// sees genuinely undeclared calls, but we can't statically prove the call
|
|
548
|
+
// isn't covered by a trail object entry the runtime will normalize.
|
|
549
|
+
reportUndeclared(
|
|
550
|
+
called.ids,
|
|
551
|
+
declared.ids,
|
|
552
|
+
{ ...ctx, softened: declared.hasUnresolved },
|
|
553
|
+
diagnostics
|
|
554
|
+
);
|
|
555
|
+
|
|
556
|
+
// When all ctx.cross() calls are statically resolved, report unused
|
|
557
|
+
// declarations. When some calls use trail object references (unresolved),
|
|
558
|
+
// skip — a declared string like 'gist.show' might be the target of an
|
|
559
|
+
// unresolved `ctx.cross(showGist)` call, producing false positives.
|
|
560
|
+
if (!called.hasUnresolved) {
|
|
561
|
+
reportUnused(declared.ids, called.ids, ctx, diagnostics);
|
|
562
|
+
}
|
|
355
563
|
};
|
|
356
564
|
|
|
357
565
|
// ---------------------------------------------------------------------------
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import {
|
|
2
|
+
collectCrossTargetTrailIds,
|
|
3
|
+
findConfigProperty,
|
|
4
|
+
findTrailDefinitions,
|
|
5
|
+
getStringValue,
|
|
6
|
+
isStringLiteral,
|
|
7
|
+
offsetToLine,
|
|
8
|
+
parse,
|
|
9
|
+
} from './ast.js';
|
|
10
|
+
import type { AstNode } from './ast.js';
|
|
11
|
+
import { isTestFile } from './scan.js';
|
|
12
|
+
import type {
|
|
13
|
+
ProjectAwareWardenRule,
|
|
14
|
+
ProjectContext,
|
|
15
|
+
WardenDiagnostic,
|
|
16
|
+
} from './types.js';
|
|
17
|
+
|
|
18
|
+
const isNonEmptyActivationValue = (onValue: AstNode): boolean => {
|
|
19
|
+
// Identifier reference (e.g. `on: signalsArray`) — conservatively treat as
|
|
20
|
+
// having activation to avoid false positives. We can't cheaply resolve what
|
|
21
|
+
// the identifier binds to, so assume it's a non-empty activation.
|
|
22
|
+
if (onValue.type === 'Identifier') {
|
|
23
|
+
return true;
|
|
24
|
+
}
|
|
25
|
+
if (onValue.type !== 'ArrayExpression') {
|
|
26
|
+
return false;
|
|
27
|
+
}
|
|
28
|
+
const elements = onValue['elements'] as readonly AstNode[] | undefined;
|
|
29
|
+
return (elements?.length ?? 0) > 0;
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
const hasOnActivation = (config: AstNode): boolean => {
|
|
33
|
+
const onProp = findConfigProperty(config, 'on');
|
|
34
|
+
const onValue = onProp?.value as AstNode | undefined;
|
|
35
|
+
return onValue ? isNonEmptyActivationValue(onValue) : false;
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
const hasExplicitInternalVisibility = (config: AstNode): boolean => {
|
|
39
|
+
const visibilityProp = findConfigProperty(config, 'visibility');
|
|
40
|
+
const visibilityValue = visibilityProp?.value as AstNode | undefined;
|
|
41
|
+
return (
|
|
42
|
+
!!visibilityValue &&
|
|
43
|
+
isStringLiteral(visibilityValue) &&
|
|
44
|
+
getStringValue(visibilityValue) === 'internal'
|
|
45
|
+
);
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
/** Check legacy `meta: { internal: true }` convention (mirrors runtime effectiveVisibility). */
|
|
49
|
+
const hasLegacyMetaInternal = (config: AstNode): boolean => {
|
|
50
|
+
const metaProp = findConfigProperty(config, 'meta');
|
|
51
|
+
const metaValue = metaProp?.value as AstNode | undefined;
|
|
52
|
+
if (!metaValue || metaValue.type !== 'ObjectExpression') {
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
const internalProp = findConfigProperty(metaValue, 'internal');
|
|
56
|
+
const internalValue = internalProp?.value as AstNode | undefined;
|
|
57
|
+
return (
|
|
58
|
+
internalValue?.type === 'BooleanLiteral' &&
|
|
59
|
+
(internalValue as unknown as { value: boolean }).value === true
|
|
60
|
+
);
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
const isInternalTrail = (config: AstNode): boolean =>
|
|
64
|
+
hasExplicitInternalVisibility(config) || hasLegacyMetaInternal(config);
|
|
65
|
+
|
|
66
|
+
const buildDeadInternalTrailDiagnostic = (
|
|
67
|
+
trailId: string,
|
|
68
|
+
filePath: string,
|
|
69
|
+
line: number
|
|
70
|
+
): WardenDiagnostic => ({
|
|
71
|
+
filePath,
|
|
72
|
+
line,
|
|
73
|
+
message: `Trail "${trailId}" is marked visibility: 'internal' but nothing crosses it and it has no on: activation. Internal trails should stay reachable through ctx.cross() or reactive activation.`,
|
|
74
|
+
rule: 'dead-internal-trail',
|
|
75
|
+
severity: 'warn',
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
const checkDeadInternalTrails = (
|
|
79
|
+
ast: AstNode | null,
|
|
80
|
+
sourceCode: string,
|
|
81
|
+
filePath: string,
|
|
82
|
+
crossedTrailIds: ReadonlySet<string>
|
|
83
|
+
): readonly WardenDiagnostic[] => {
|
|
84
|
+
if (isTestFile(filePath) || !ast) {
|
|
85
|
+
return [];
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const diagnostics: WardenDiagnostic[] = [];
|
|
89
|
+
|
|
90
|
+
for (const def of findTrailDefinitions(ast)) {
|
|
91
|
+
if (def.kind !== 'trail' || !isInternalTrail(def.config)) {
|
|
92
|
+
continue;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (hasOnActivation(def.config) || crossedTrailIds.has(def.id)) {
|
|
96
|
+
continue;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
diagnostics.push(
|
|
100
|
+
buildDeadInternalTrailDiagnostic(
|
|
101
|
+
def.id,
|
|
102
|
+
filePath,
|
|
103
|
+
offsetToLine(sourceCode, def.start)
|
|
104
|
+
)
|
|
105
|
+
);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
return diagnostics;
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
export const deadInternalTrail: ProjectAwareWardenRule = {
|
|
112
|
+
check(sourceCode: string, filePath: string): readonly WardenDiagnostic[] {
|
|
113
|
+
const ast = parse(filePath, sourceCode);
|
|
114
|
+
return checkDeadInternalTrails(
|
|
115
|
+
ast,
|
|
116
|
+
sourceCode,
|
|
117
|
+
filePath,
|
|
118
|
+
ast ? collectCrossTargetTrailIds(ast, sourceCode) : new Set<string>()
|
|
119
|
+
);
|
|
120
|
+
},
|
|
121
|
+
checkWithContext(
|
|
122
|
+
sourceCode: string,
|
|
123
|
+
filePath: string,
|
|
124
|
+
context: ProjectContext
|
|
125
|
+
): readonly WardenDiagnostic[] {
|
|
126
|
+
const ast = parse(filePath, sourceCode);
|
|
127
|
+
const localCrossTargetTrailIds = ast
|
|
128
|
+
? collectCrossTargetTrailIds(ast, sourceCode)
|
|
129
|
+
: new Set<string>();
|
|
130
|
+
return checkDeadInternalTrails(
|
|
131
|
+
ast,
|
|
132
|
+
sourceCode,
|
|
133
|
+
filePath,
|
|
134
|
+
context.crossTargetTrailIds ?? localCrossTargetTrailIds
|
|
135
|
+
);
|
|
136
|
+
},
|
|
137
|
+
description:
|
|
138
|
+
'Warn when an internal trail has no crossings anywhere in the project and no on: activation.',
|
|
139
|
+
name: 'dead-internal-trail',
|
|
140
|
+
severity: 'warn',
|
|
141
|
+
};
|