@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
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
import {
|
|
2
|
+
findConfigProperty,
|
|
3
|
+
findTrailDefinitions,
|
|
4
|
+
identifierName,
|
|
5
|
+
offsetToLine,
|
|
6
|
+
parse,
|
|
7
|
+
walk,
|
|
8
|
+
walkScope,
|
|
9
|
+
} from './ast.js';
|
|
10
|
+
import type { AstNode } from './ast.js';
|
|
11
|
+
import { isTestFile } from './scan.js';
|
|
12
|
+
import type { WardenDiagnostic, WardenRule } from './types.js';
|
|
13
|
+
|
|
14
|
+
const TRANSPARENT_WRAPPER_TYPES = new Set([
|
|
15
|
+
'ParenthesizedExpression',
|
|
16
|
+
'TSAsExpression',
|
|
17
|
+
'TSSatisfiesExpression',
|
|
18
|
+
'TSNonNullExpression',
|
|
19
|
+
'TSTypeAssertion',
|
|
20
|
+
]);
|
|
21
|
+
|
|
22
|
+
const FUNCTION_TYPES = new Set([
|
|
23
|
+
'ArrowFunctionExpression',
|
|
24
|
+
'FunctionDeclaration',
|
|
25
|
+
'FunctionExpression',
|
|
26
|
+
]);
|
|
27
|
+
|
|
28
|
+
const unwrapExpression = (node: AstNode | undefined): AstNode | undefined => {
|
|
29
|
+
let current = node;
|
|
30
|
+
while (current && TRANSPARENT_WRAPPER_TYPES.has(current.type)) {
|
|
31
|
+
current = current['expression'] as AstNode | undefined;
|
|
32
|
+
}
|
|
33
|
+
return current;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
const getDetourElements = (config: AstNode): readonly (AstNode | null)[] => {
|
|
37
|
+
const detoursProp = findConfigProperty(config, 'detours');
|
|
38
|
+
if (!detoursProp) {
|
|
39
|
+
return [];
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const detoursValue = unwrapExpression(
|
|
43
|
+
detoursProp.value as AstNode | undefined
|
|
44
|
+
);
|
|
45
|
+
if (!detoursValue || detoursValue.type !== 'ArrayExpression') {
|
|
46
|
+
return [];
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return (
|
|
50
|
+
((detoursValue as AstNode)['elements'] as readonly (AstNode | null)[]) ?? []
|
|
51
|
+
);
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
const getFunctionBody = (node: AstNode | undefined): AstNode | undefined => {
|
|
55
|
+
if (!node || !FUNCTION_TYPES.has(node.type)) {
|
|
56
|
+
return undefined;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const { body } = node;
|
|
60
|
+
return Array.isArray(body) ? undefined : (body as AstNode | undefined);
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
interface RecoverBodyMatch {
|
|
64
|
+
readonly index: number;
|
|
65
|
+
readonly trailId: string;
|
|
66
|
+
readonly body: AstNode;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const collectFunctionRecoverBinding = (
|
|
70
|
+
bindings: Map<string, AstNode>,
|
|
71
|
+
node: AstNode
|
|
72
|
+
): boolean => {
|
|
73
|
+
if (node.type !== 'FunctionDeclaration') {
|
|
74
|
+
return false;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const name = identifierName(node['id'] as AstNode | undefined);
|
|
78
|
+
if (name) {
|
|
79
|
+
bindings.set(name, node);
|
|
80
|
+
}
|
|
81
|
+
return true;
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
const collectVariableRecoverBinding = (
|
|
85
|
+
bindings: Map<string, AstNode>,
|
|
86
|
+
node: AstNode
|
|
87
|
+
): void => {
|
|
88
|
+
if (node.type !== 'VariableDeclarator') {
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const name = identifierName(node['id'] as AstNode | undefined);
|
|
93
|
+
const init = unwrapExpression(node['init'] as AstNode | undefined);
|
|
94
|
+
if (!name || !init || !FUNCTION_TYPES.has(init.type)) {
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
bindings.set(name, init);
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
const collectRecoverBinding = (
|
|
102
|
+
bindings: Map<string, AstNode>,
|
|
103
|
+
node: AstNode
|
|
104
|
+
): void => {
|
|
105
|
+
if (collectFunctionRecoverBinding(bindings, node)) {
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
collectVariableRecoverBinding(bindings, node);
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
const collectRecoverBindings = (ast: AstNode): ReadonlyMap<string, AstNode> => {
|
|
113
|
+
const bindings = new Map<string, AstNode>();
|
|
114
|
+
|
|
115
|
+
walk(ast, (node) => {
|
|
116
|
+
collectRecoverBinding(bindings, node);
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
return bindings;
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
const resolveRecoverBody = (
|
|
123
|
+
node: AstNode | undefined,
|
|
124
|
+
bindings: ReadonlyMap<string, AstNode>
|
|
125
|
+
): AstNode | undefined => {
|
|
126
|
+
const unwrapped = unwrapExpression(node);
|
|
127
|
+
if (!unwrapped) {
|
|
128
|
+
return undefined;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const inlineBody = getFunctionBody(unwrapped);
|
|
132
|
+
if (inlineBody) {
|
|
133
|
+
return inlineBody;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
const bindingName = identifierName(unwrapped);
|
|
137
|
+
if (!bindingName) {
|
|
138
|
+
return undefined;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
return getFunctionBody(bindings.get(bindingName));
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
const resolveRecoverBodyFromElement = (
|
|
145
|
+
element: AstNode | null,
|
|
146
|
+
bindings: ReadonlyMap<string, AstNode>
|
|
147
|
+
): AstNode | undefined => {
|
|
148
|
+
if (!element || element.type !== 'ObjectExpression') {
|
|
149
|
+
return undefined;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
const recoverProp = findConfigProperty(element, 'recover');
|
|
153
|
+
return resolveRecoverBody(
|
|
154
|
+
recoverProp?.value as AstNode | undefined,
|
|
155
|
+
bindings
|
|
156
|
+
);
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
const appendRecoverBodies = (
|
|
160
|
+
bodies: RecoverBodyMatch[],
|
|
161
|
+
definition: { readonly config: AstNode; readonly id: string },
|
|
162
|
+
bindings: ReadonlyMap<string, AstNode>
|
|
163
|
+
): void => {
|
|
164
|
+
const detourElements = getDetourElements(definition.config);
|
|
165
|
+
for (const [index, element] of detourElements.entries()) {
|
|
166
|
+
const body = resolveRecoverBodyFromElement(element, bindings);
|
|
167
|
+
if (!body) {
|
|
168
|
+
continue;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
bodies.push({ body, index, trailId: definition.id });
|
|
172
|
+
}
|
|
173
|
+
};
|
|
174
|
+
|
|
175
|
+
const findRecoverBodies = (ast: AstNode): readonly RecoverBodyMatch[] => {
|
|
176
|
+
const bindings = collectRecoverBindings(ast);
|
|
177
|
+
const bodies: RecoverBodyMatch[] = [];
|
|
178
|
+
|
|
179
|
+
for (const definition of findTrailDefinitions(ast)) {
|
|
180
|
+
if (definition.kind !== 'trail') {
|
|
181
|
+
continue;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
appendRecoverBodies(bodies, definition, bindings);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
return bodies;
|
|
188
|
+
};
|
|
189
|
+
|
|
190
|
+
export const noThrowInDetourRecover: WardenRule = {
|
|
191
|
+
check(sourceCode: string, filePath: string): readonly WardenDiagnostic[] {
|
|
192
|
+
if (isTestFile(filePath)) {
|
|
193
|
+
return [];
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
const ast = parse(filePath, sourceCode);
|
|
197
|
+
if (!ast) {
|
|
198
|
+
return [];
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
const diagnostics: WardenDiagnostic[] = [];
|
|
202
|
+
|
|
203
|
+
for (const recover of findRecoverBodies(ast)) {
|
|
204
|
+
walkScope(recover.body, (node) => {
|
|
205
|
+
if (node.type !== 'ThrowStatement') {
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
diagnostics.push({
|
|
210
|
+
filePath,
|
|
211
|
+
line: offsetToLine(sourceCode, node.start),
|
|
212
|
+
message: `Trail "${recover.trailId}" detour[${recover.index}] recover must not throw. Return Result.err() instead.`,
|
|
213
|
+
rule: 'no-throw-in-detour-recover',
|
|
214
|
+
severity: 'error',
|
|
215
|
+
});
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
return diagnostics;
|
|
220
|
+
},
|
|
221
|
+
description:
|
|
222
|
+
'Disallow throw statements inside detour recover functions. Use Result.err() instead.',
|
|
223
|
+
name: 'no-throw-in-detour-recover',
|
|
224
|
+
severity: 'error',
|
|
225
|
+
};
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Finds `throw` statements inside `blaze:` function bodies.
|
|
3
3
|
*
|
|
4
|
-
* Uses AST
|
|
5
|
-
*
|
|
4
|
+
* Uses scope-aware AST walking so throws inside nested callbacks
|
|
5
|
+
* (e.g. `.map()`, `.filter()`, inner helpers) are not attributed to
|
|
6
|
+
* the blaze body itself. ADR-0007 requires this class of false positive
|
|
7
|
+
* to be avoided — only throws in the blaze body scope should be flagged.
|
|
6
8
|
*/
|
|
7
9
|
|
|
8
|
-
import { findBlazeBodies, offsetToLine, parse,
|
|
10
|
+
import { findBlazeBodies, offsetToLine, parse, walkScope } from './ast.js';
|
|
9
11
|
import type { WardenDiagnostic, WardenRule } from './types.js';
|
|
10
12
|
|
|
11
13
|
export const noThrowInImplementation: WardenRule = {
|
|
@@ -18,7 +20,7 @@ export const noThrowInImplementation: WardenRule = {
|
|
|
18
20
|
const diagnostics: WardenDiagnostic[] = [];
|
|
19
21
|
|
|
20
22
|
for (const body of findBlazeBodies(ast)) {
|
|
21
|
-
|
|
23
|
+
walkScope(body, (node) => {
|
|
22
24
|
if (node.type === 'ThrowStatement') {
|
|
23
25
|
diagnostics.push({
|
|
24
26
|
filePath,
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Validates that every signal id declared in a trail's `on:` array resolves
|
|
3
|
+
* to a known signal definition somewhere in the project.
|
|
4
|
+
*
|
|
5
|
+
* Mirrors `resource-exists` structurally — collects local signal definitions
|
|
6
|
+
* for the standalone `check()` path and accepts a project-wide
|
|
7
|
+
* `knownSignalIds` set via `checkWithContext()`.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { isDraftId } from '@ontrails/core';
|
|
11
|
+
|
|
12
|
+
import {
|
|
13
|
+
collectSignalDefinitionIds,
|
|
14
|
+
findConfigProperty,
|
|
15
|
+
findTrailDefinitions,
|
|
16
|
+
getStringValue,
|
|
17
|
+
identifierName,
|
|
18
|
+
isStringLiteral,
|
|
19
|
+
offsetToLine,
|
|
20
|
+
parse,
|
|
21
|
+
deriveConstString,
|
|
22
|
+
} from './ast.js';
|
|
23
|
+
import type { AstNode } from './ast.js';
|
|
24
|
+
import { isTestFile } from './scan.js';
|
|
25
|
+
import type {
|
|
26
|
+
ProjectAwareWardenRule,
|
|
27
|
+
ProjectContext,
|
|
28
|
+
WardenDiagnostic,
|
|
29
|
+
} from './types.js';
|
|
30
|
+
|
|
31
|
+
// ---------------------------------------------------------------------------
|
|
32
|
+
// Declared `on:` extraction
|
|
33
|
+
// ---------------------------------------------------------------------------
|
|
34
|
+
|
|
35
|
+
const getOnElements = (config: AstNode): readonly AstNode[] => {
|
|
36
|
+
const onProp = findConfigProperty(config, 'on');
|
|
37
|
+
if (!onProp) {
|
|
38
|
+
return [];
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const arrayNode = onProp.value;
|
|
42
|
+
if (!arrayNode || (arrayNode as AstNode).type !== 'ArrayExpression') {
|
|
43
|
+
return [];
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const elements = (arrayNode as AstNode)['elements'] as
|
|
47
|
+
| readonly AstNode[]
|
|
48
|
+
| undefined;
|
|
49
|
+
return elements ?? [];
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Resolve an `on:` array element to a signal id when possible.
|
|
54
|
+
*
|
|
55
|
+
* Handles string literals and `const NAME = 'id'` identifier references.
|
|
56
|
+
* Object-form entries (e.g. `on: [someSignal]` where `someSignal` is a
|
|
57
|
+
* `Signal` value) cannot be statically resolved here and are skipped — the
|
|
58
|
+
* runtime normalizes them inside `trail()`, so skipping is safe. The tradeoff
|
|
59
|
+
* is that typo'd Signal imports won't be caught at lint time; the TypeScript
|
|
60
|
+
* compiler catches those instead.
|
|
61
|
+
*/
|
|
62
|
+
const extractOnElementId = (
|
|
63
|
+
element: AstNode,
|
|
64
|
+
sourceCode: string
|
|
65
|
+
): string | null => {
|
|
66
|
+
if (element.type === 'Identifier') {
|
|
67
|
+
const name = identifierName(element);
|
|
68
|
+
return name ? deriveConstString(name, sourceCode) : null;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (isStringLiteral(element)) {
|
|
72
|
+
return getStringValue(element);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return null;
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
const extractDeclaredOnIds = (
|
|
79
|
+
config: AstNode,
|
|
80
|
+
sourceCode: string
|
|
81
|
+
): readonly string[] => [
|
|
82
|
+
...new Set(
|
|
83
|
+
getOnElements(config).flatMap((element) => {
|
|
84
|
+
const id = extractOnElementId(element, sourceCode);
|
|
85
|
+
return id ? [id] : [];
|
|
86
|
+
})
|
|
87
|
+
),
|
|
88
|
+
];
|
|
89
|
+
|
|
90
|
+
// ---------------------------------------------------------------------------
|
|
91
|
+
// Diagnostics
|
|
92
|
+
// ---------------------------------------------------------------------------
|
|
93
|
+
|
|
94
|
+
const buildMissingSignalDiagnostic = (
|
|
95
|
+
trailId: string,
|
|
96
|
+
signalId: string,
|
|
97
|
+
filePath: string,
|
|
98
|
+
line: number
|
|
99
|
+
): WardenDiagnostic => ({
|
|
100
|
+
filePath,
|
|
101
|
+
line,
|
|
102
|
+
message: `Trail "${trailId}" declares on: "${signalId}" which is not a known signal in the project.`,
|
|
103
|
+
rule: 'on-references-exist',
|
|
104
|
+
severity: 'error',
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
const reportMissingSignals = (
|
|
108
|
+
def: { id: string; config: AstNode; start: number },
|
|
109
|
+
sourceCode: string,
|
|
110
|
+
filePath: string,
|
|
111
|
+
knownSignalIds: ReadonlySet<string>,
|
|
112
|
+
diagnostics: WardenDiagnostic[]
|
|
113
|
+
): void => {
|
|
114
|
+
const line = offsetToLine(sourceCode, def.start);
|
|
115
|
+
for (const signalId of extractDeclaredOnIds(def.config, sourceCode)) {
|
|
116
|
+
if (!knownSignalIds.has(signalId) && !isDraftId(signalId)) {
|
|
117
|
+
diagnostics.push(
|
|
118
|
+
buildMissingSignalDiagnostic(def.id, signalId, filePath, line)
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
const buildSignalDiagnostics = (
|
|
125
|
+
ast: AstNode,
|
|
126
|
+
sourceCode: string,
|
|
127
|
+
filePath: string,
|
|
128
|
+
knownSignalIds: ReadonlySet<string>
|
|
129
|
+
): readonly WardenDiagnostic[] => {
|
|
130
|
+
const diagnostics: WardenDiagnostic[] = [];
|
|
131
|
+
for (const def of findTrailDefinitions(ast)) {
|
|
132
|
+
if (def.kind !== 'trail') {
|
|
133
|
+
continue;
|
|
134
|
+
}
|
|
135
|
+
reportMissingSignals(
|
|
136
|
+
def,
|
|
137
|
+
sourceCode,
|
|
138
|
+
filePath,
|
|
139
|
+
knownSignalIds,
|
|
140
|
+
diagnostics
|
|
141
|
+
);
|
|
142
|
+
}
|
|
143
|
+
return diagnostics;
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
const checkOnReferences = (
|
|
147
|
+
ast: AstNode | null,
|
|
148
|
+
sourceCode: string,
|
|
149
|
+
filePath: string,
|
|
150
|
+
knownSignalIds: ReadonlySet<string>
|
|
151
|
+
): readonly WardenDiagnostic[] => {
|
|
152
|
+
if (isTestFile(filePath) || !ast) {
|
|
153
|
+
return [];
|
|
154
|
+
}
|
|
155
|
+
return buildSignalDiagnostics(ast, sourceCode, filePath, knownSignalIds);
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Checks that every `on:` reference resolves to a known signal definition.
|
|
160
|
+
*/
|
|
161
|
+
export const onReferencesExist: ProjectAwareWardenRule = {
|
|
162
|
+
check(sourceCode: string, filePath: string): readonly WardenDiagnostic[] {
|
|
163
|
+
const ast = parse(filePath, sourceCode);
|
|
164
|
+
if (!ast) {
|
|
165
|
+
return [];
|
|
166
|
+
}
|
|
167
|
+
return checkOnReferences(
|
|
168
|
+
ast,
|
|
169
|
+
sourceCode,
|
|
170
|
+
filePath,
|
|
171
|
+
collectSignalDefinitionIds(ast)
|
|
172
|
+
);
|
|
173
|
+
},
|
|
174
|
+
checkWithContext(
|
|
175
|
+
sourceCode: string,
|
|
176
|
+
filePath: string,
|
|
177
|
+
context: ProjectContext
|
|
178
|
+
): readonly WardenDiagnostic[] {
|
|
179
|
+
const ast = parse(filePath, sourceCode);
|
|
180
|
+
const localSignalIds = ast
|
|
181
|
+
? collectSignalDefinitionIds(ast)
|
|
182
|
+
: new Set<string>();
|
|
183
|
+
return checkOnReferences(
|
|
184
|
+
ast,
|
|
185
|
+
sourceCode,
|
|
186
|
+
filePath,
|
|
187
|
+
context.knownSignalIds ?? localSignalIds
|
|
188
|
+
);
|
|
189
|
+
},
|
|
190
|
+
description:
|
|
191
|
+
'Ensure every signal id declared in a trail on: array resolves to a known signal definition.',
|
|
192
|
+
name: 'on-references-exist',
|
|
193
|
+
severity: 'error',
|
|
194
|
+
};
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import {
|
|
2
|
+
collectCrudTableIds,
|
|
3
|
+
collectOnTargetSignalIds,
|
|
4
|
+
findStoreTableDefinitions,
|
|
5
|
+
offsetToLine,
|
|
6
|
+
parse,
|
|
7
|
+
} from './ast.js';
|
|
8
|
+
import type { AstNode } from './ast.js';
|
|
9
|
+
import { isTestFile } from './scan.js';
|
|
10
|
+
import type {
|
|
11
|
+
ProjectAwareWardenRule,
|
|
12
|
+
ProjectContext,
|
|
13
|
+
WardenDiagnostic,
|
|
14
|
+
} from './types.js';
|
|
15
|
+
|
|
16
|
+
const CHANGE_SIGNAL_OPERATIONS = ['created', 'updated', 'removed'] as const;
|
|
17
|
+
|
|
18
|
+
const buildOrphanedSignalDiagnostic = (
|
|
19
|
+
tableId: string,
|
|
20
|
+
missingSignalIds: readonly string[],
|
|
21
|
+
filePath: string,
|
|
22
|
+
line: number
|
|
23
|
+
): WardenDiagnostic => ({
|
|
24
|
+
filePath,
|
|
25
|
+
line,
|
|
26
|
+
message: `Store table "${tableId}" derives change signals with no trail listeners: ${missingSignalIds.join(', ')}. Add trail on: consumers or remove the unused reactive pattern.`,
|
|
27
|
+
rule: 'orphaned-signal',
|
|
28
|
+
severity: 'warn',
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
const getMissingSignalIds = (
|
|
32
|
+
tableId: string,
|
|
33
|
+
onTargetSignalIds: ReadonlySet<string>
|
|
34
|
+
): readonly string[] =>
|
|
35
|
+
CHANGE_SIGNAL_OPERATIONS.map((operation) => `${tableId}.${operation}`).filter(
|
|
36
|
+
(signalId) =>
|
|
37
|
+
!onTargetSignalIds.has(signalId) &&
|
|
38
|
+
// Bare-name fallback: string-literal `on:` consumers store the signal
|
|
39
|
+
// without the composite `${storeBinding}:` prefix.
|
|
40
|
+
!onTargetSignalIds.has(signalId.replace(/^[^:]+:/, ''))
|
|
41
|
+
);
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Strip the `${storeBinding}:` prefix from a composite signal id for display.
|
|
45
|
+
* Keeps diagnostic messages readable while keeping keys composite internally.
|
|
46
|
+
*/
|
|
47
|
+
const stripStoreBinding = (
|
|
48
|
+
signalId: string,
|
|
49
|
+
storeBinding: string | null
|
|
50
|
+
): string => {
|
|
51
|
+
if (!storeBinding) {
|
|
52
|
+
return signalId;
|
|
53
|
+
}
|
|
54
|
+
const prefix = `${storeBinding}:`;
|
|
55
|
+
return signalId.startsWith(prefix) ? signalId.slice(prefix.length) : signalId;
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
const buildDefinitionDiagnostic = (
|
|
59
|
+
definition: ReturnType<typeof findStoreTableDefinitions>[number],
|
|
60
|
+
sourceCode: string,
|
|
61
|
+
filePath: string,
|
|
62
|
+
crudTableIds: ReadonlySet<string>,
|
|
63
|
+
onTargetSignalIds: ReadonlySet<string>
|
|
64
|
+
): WardenDiagnostic | null => {
|
|
65
|
+
if (!crudTableIds.has(definition.key)) {
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const missingSignalIds = getMissingSignalIds(
|
|
70
|
+
definition.key,
|
|
71
|
+
onTargetSignalIds
|
|
72
|
+
);
|
|
73
|
+
return missingSignalIds.length === 0
|
|
74
|
+
? null
|
|
75
|
+
: buildOrphanedSignalDiagnostic(
|
|
76
|
+
definition.name,
|
|
77
|
+
missingSignalIds.map((id) =>
|
|
78
|
+
stripStoreBinding(id, definition.storeBinding)
|
|
79
|
+
),
|
|
80
|
+
filePath,
|
|
81
|
+
offsetToLine(sourceCode, definition.start)
|
|
82
|
+
);
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
const checkOrphanedSignals = (
|
|
86
|
+
ast: AstNode | null,
|
|
87
|
+
sourceCode: string,
|
|
88
|
+
filePath: string,
|
|
89
|
+
crudTableIds: ReadonlySet<string>,
|
|
90
|
+
onTargetSignalIds: ReadonlySet<string>
|
|
91
|
+
): readonly WardenDiagnostic[] => {
|
|
92
|
+
if (isTestFile(filePath) || !ast) {
|
|
93
|
+
return [];
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const diagnostics: WardenDiagnostic[] = [];
|
|
97
|
+
|
|
98
|
+
for (const definition of findStoreTableDefinitions(ast)) {
|
|
99
|
+
const diagnostic = buildDefinitionDiagnostic(
|
|
100
|
+
definition,
|
|
101
|
+
sourceCode,
|
|
102
|
+
filePath,
|
|
103
|
+
crudTableIds,
|
|
104
|
+
onTargetSignalIds
|
|
105
|
+
);
|
|
106
|
+
if (diagnostic) {
|
|
107
|
+
diagnostics.push(diagnostic);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
return diagnostics;
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
export const orphanedSignal: ProjectAwareWardenRule = {
|
|
115
|
+
check(sourceCode: string, filePath: string): readonly WardenDiagnostic[] {
|
|
116
|
+
const ast = parse(filePath, sourceCode);
|
|
117
|
+
return checkOrphanedSignals(
|
|
118
|
+
ast,
|
|
119
|
+
sourceCode,
|
|
120
|
+
filePath,
|
|
121
|
+
ast ? collectCrudTableIds(ast) : new Set<string>(),
|
|
122
|
+
ast ? collectOnTargetSignalIds(ast, sourceCode) : new Set<string>()
|
|
123
|
+
);
|
|
124
|
+
},
|
|
125
|
+
checkWithContext(
|
|
126
|
+
sourceCode: string,
|
|
127
|
+
filePath: string,
|
|
128
|
+
context: ProjectContext
|
|
129
|
+
): readonly WardenDiagnostic[] {
|
|
130
|
+
const ast = parse(filePath, sourceCode);
|
|
131
|
+
const localCrudTableIds = ast
|
|
132
|
+
? collectCrudTableIds(ast)
|
|
133
|
+
: new Set<string>();
|
|
134
|
+
const localOnTargetSignalIds = ast
|
|
135
|
+
? collectOnTargetSignalIds(ast, sourceCode)
|
|
136
|
+
: new Set<string>();
|
|
137
|
+
|
|
138
|
+
return checkOrphanedSignals(
|
|
139
|
+
ast,
|
|
140
|
+
sourceCode,
|
|
141
|
+
filePath,
|
|
142
|
+
context.crudTableIds ?? localCrudTableIds,
|
|
143
|
+
context.onTargetSignalIds ?? localOnTargetSignalIds
|
|
144
|
+
);
|
|
145
|
+
},
|
|
146
|
+
description:
|
|
147
|
+
'Warn when CRUD-backed store change signals are never consumed by trail on: declarations.',
|
|
148
|
+
name: 'orphaned-signal',
|
|
149
|
+
severity: 'warn',
|
|
150
|
+
};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { Trail } from '@ontrails/core';
|
|
2
|
+
import { validatePermits } from '@ontrails/permits';
|
|
3
|
+
|
|
4
|
+
import type { TopoAwareWardenRule, WardenDiagnostic } from './types.js';
|
|
5
|
+
|
|
6
|
+
const toWardenDiagnostic = (
|
|
7
|
+
diagnostic: ReturnType<typeof validatePermits>[number]
|
|
8
|
+
): WardenDiagnostic => ({
|
|
9
|
+
filePath: '<topo>',
|
|
10
|
+
line: 1,
|
|
11
|
+
message: diagnostic.message,
|
|
12
|
+
rule: `permit.${diagnostic.rule}`,
|
|
13
|
+
severity: diagnostic.severity === 'error' ? 'error' : 'warn',
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
export const permitGovernance: TopoAwareWardenRule = {
|
|
17
|
+
checkTopo: (topo) =>
|
|
18
|
+
validatePermits(
|
|
19
|
+
topo.list() as readonly Trail<unknown, unknown, unknown>[]
|
|
20
|
+
).map(toWardenDiagnostic),
|
|
21
|
+
description:
|
|
22
|
+
'Enforces permit declarations and scope hygiene across the compiled topo',
|
|
23
|
+
name: 'permit-governance',
|
|
24
|
+
severity: 'warn',
|
|
25
|
+
};
|