@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,160 @@
|
|
|
1
|
+
import { isDraftId } from '@ontrails/core';
|
|
2
|
+
|
|
3
|
+
import { isDraftMarkedFile } from '../draft.js';
|
|
4
|
+
import {
|
|
5
|
+
collectFrameworkDraftPrefixConstantOffsets,
|
|
6
|
+
findStringLiterals,
|
|
7
|
+
hasIgnoreCommentOnLine,
|
|
8
|
+
offsetToLine,
|
|
9
|
+
parse,
|
|
10
|
+
splitSourceLines,
|
|
11
|
+
} from './ast.js';
|
|
12
|
+
import type { StringLiteralMatch } from './ast.js';
|
|
13
|
+
import type { WardenDiagnostic, WardenRule } from './types.js';
|
|
14
|
+
|
|
15
|
+
const messageForMissingMarker = (draftId: string): string =>
|
|
16
|
+
`Draft id "${draftId}" appears in source, but the file is not draft-marked. ` +
|
|
17
|
+
'Rename it with an _draft. prefix or a .draft. trailing segment.';
|
|
18
|
+
|
|
19
|
+
const makeDiagnostic = (
|
|
20
|
+
sourceCode: string,
|
|
21
|
+
filePath: string,
|
|
22
|
+
start: number,
|
|
23
|
+
message: string,
|
|
24
|
+
severity: WardenDiagnostic['severity']
|
|
25
|
+
): WardenDiagnostic => ({
|
|
26
|
+
filePath,
|
|
27
|
+
line: offsetToLine(sourceCode, start),
|
|
28
|
+
message,
|
|
29
|
+
rule: 'draft-file-marking',
|
|
30
|
+
severity,
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
const collectDraftMatches = (
|
|
34
|
+
sourceCode: string,
|
|
35
|
+
filePath: string,
|
|
36
|
+
ast: NonNullable<ReturnType<typeof parse>>
|
|
37
|
+
): StringLiteralMatch[] => {
|
|
38
|
+
const frameworkConstantOffsets = collectFrameworkDraftPrefixConstantOffsets(
|
|
39
|
+
ast,
|
|
40
|
+
filePath
|
|
41
|
+
);
|
|
42
|
+
const lines = splitSourceLines(sourceCode);
|
|
43
|
+
return findStringLiterals(ast, (value) => isDraftId(value)).filter(
|
|
44
|
+
(match) => {
|
|
45
|
+
if (frameworkConstantOffsets.has(match.start)) {
|
|
46
|
+
return false;
|
|
47
|
+
}
|
|
48
|
+
if (
|
|
49
|
+
hasIgnoreCommentOnLine(lines, offsetToLine(sourceCode, match.start))
|
|
50
|
+
) {
|
|
51
|
+
return false;
|
|
52
|
+
}
|
|
53
|
+
return true;
|
|
54
|
+
}
|
|
55
|
+
);
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
const draftMissingMarkerDiagnostic = (
|
|
59
|
+
sourceCode: string,
|
|
60
|
+
filePath: string,
|
|
61
|
+
ast: NonNullable<ReturnType<typeof parse>>
|
|
62
|
+
): WardenDiagnostic | null => {
|
|
63
|
+
const draftMatches = collectDraftMatches(sourceCode, filePath, ast);
|
|
64
|
+
if (!draftMatches.length || isDraftMarkedFile(filePath)) {
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const [first] = draftMatches;
|
|
69
|
+
if (!first) {
|
|
70
|
+
return null;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return makeDiagnostic(
|
|
74
|
+
sourceCode,
|
|
75
|
+
filePath,
|
|
76
|
+
first.start,
|
|
77
|
+
messageForMissingMarker(first.value),
|
|
78
|
+
'error'
|
|
79
|
+
);
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
const draftMarkedWithoutIdsDiagnostic = (
|
|
83
|
+
filePath: string,
|
|
84
|
+
ast: NonNullable<ReturnType<typeof parse>>
|
|
85
|
+
): WardenDiagnostic | null => {
|
|
86
|
+
// Deciding whether the file's `_draft.` marker is still warranted is a
|
|
87
|
+
// question about *all* draft ids present in source, not just the unsuppressed
|
|
88
|
+
// ones. Pragma-suppressed ids still justify a draft-marked filename — a user
|
|
89
|
+
// intentionally silencing them has not removed the draft content. We
|
|
90
|
+
// therefore filter only the framework-constant declarations (which are not
|
|
91
|
+
// draft ids at all) and bypass the pragma filter that `collectDraftMatches`
|
|
92
|
+
// applies.
|
|
93
|
+
const frameworkConstantOffsets = collectFrameworkDraftPrefixConstantOffsets(
|
|
94
|
+
ast,
|
|
95
|
+
filePath
|
|
96
|
+
);
|
|
97
|
+
const unsuppressedDraftIds = findStringLiterals(ast, (value) =>
|
|
98
|
+
isDraftId(value)
|
|
99
|
+
).filter((match) => !frameworkConstantOffsets.has(match.start));
|
|
100
|
+
|
|
101
|
+
if (unsuppressedDraftIds.length > 0) {
|
|
102
|
+
return null;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
if (!isDraftMarkedFile(filePath)) {
|
|
106
|
+
return null;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
return {
|
|
110
|
+
filePath,
|
|
111
|
+
line: 1,
|
|
112
|
+
message:
|
|
113
|
+
'File is draft-marked but no longer contains draft ids. Remove the draft filename marker or finish the promotion cleanup.',
|
|
114
|
+
rule: 'draft-file-marking',
|
|
115
|
+
severity: 'warn',
|
|
116
|
+
};
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
const collectDraftFileMarkingDiagnostics = (
|
|
120
|
+
sourceCode: string,
|
|
121
|
+
filePath: string,
|
|
122
|
+
ast: NonNullable<ReturnType<typeof parse>>
|
|
123
|
+
): WardenDiagnostic[] => {
|
|
124
|
+
const missingMarkerDiagnostic = draftMissingMarkerDiagnostic(
|
|
125
|
+
sourceCode,
|
|
126
|
+
filePath,
|
|
127
|
+
ast
|
|
128
|
+
);
|
|
129
|
+
if (missingMarkerDiagnostic) {
|
|
130
|
+
return [missingMarkerDiagnostic];
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
const markedWithoutIdsDiagnostic = draftMarkedWithoutIdsDiagnostic(
|
|
134
|
+
filePath,
|
|
135
|
+
ast
|
|
136
|
+
);
|
|
137
|
+
if (markedWithoutIdsDiagnostic) {
|
|
138
|
+
return [markedWithoutIdsDiagnostic];
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
return [];
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Ensures files containing draft ids are visibly marked as draft-bearing files.
|
|
146
|
+
*/
|
|
147
|
+
export const draftFileMarking: WardenRule = {
|
|
148
|
+
check(sourceCode: string, filePath: string): readonly WardenDiagnostic[] {
|
|
149
|
+
const ast = parse(filePath, sourceCode);
|
|
150
|
+
if (!ast) {
|
|
151
|
+
return [];
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
return collectDraftFileMarkingDiagnostics(sourceCode, filePath, ast);
|
|
155
|
+
},
|
|
156
|
+
description:
|
|
157
|
+
'Require draft-bearing files to use _draft.* or *.draft.* filename markers.',
|
|
158
|
+
name: 'draft-file-marking',
|
|
159
|
+
severity: 'error',
|
|
160
|
+
};
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { isDraftId } from '@ontrails/core';
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
collectFrameworkDraftPrefixConstantOffsets,
|
|
5
|
+
findStringLiterals,
|
|
6
|
+
hasIgnoreCommentOnLine,
|
|
7
|
+
offsetToLine,
|
|
8
|
+
parse,
|
|
9
|
+
splitSourceLines,
|
|
10
|
+
} from './ast.js';
|
|
11
|
+
import type { WardenDiagnostic, WardenRule } from './types.js';
|
|
12
|
+
|
|
13
|
+
const createDiagnostic = (
|
|
14
|
+
sourceCode: string,
|
|
15
|
+
filePath: string,
|
|
16
|
+
match: { start: number; value: string }
|
|
17
|
+
): WardenDiagnostic => ({
|
|
18
|
+
filePath,
|
|
19
|
+
line: offsetToLine(sourceCode, match.start),
|
|
20
|
+
message:
|
|
21
|
+
`Draft id "${match.value}" is still visible debt. ` +
|
|
22
|
+
'Established trailheads, lock export, and OpenAPI generation will reject it until it is promoted.',
|
|
23
|
+
rule: 'draft-visible-debt',
|
|
24
|
+
severity: 'warn',
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
const isSuppressedMatch = (
|
|
28
|
+
match: { start: number },
|
|
29
|
+
sourceCode: string,
|
|
30
|
+
lines: readonly string[],
|
|
31
|
+
frameworkConstantOffsets: ReadonlySet<number>
|
|
32
|
+
): boolean =>
|
|
33
|
+
frameworkConstantOffsets.has(match.start) ||
|
|
34
|
+
hasIgnoreCommentOnLine(lines, offsetToLine(sourceCode, match.start));
|
|
35
|
+
|
|
36
|
+
const collectDraftVisibleDebtDiagnostics = (
|
|
37
|
+
sourceCode: string,
|
|
38
|
+
filePath: string,
|
|
39
|
+
ast: NonNullable<ReturnType<typeof parse>>
|
|
40
|
+
): WardenDiagnostic[] => {
|
|
41
|
+
const frameworkConstantOffsets = collectFrameworkDraftPrefixConstantOffsets(
|
|
42
|
+
ast,
|
|
43
|
+
filePath
|
|
44
|
+
);
|
|
45
|
+
const lines = splitSourceLines(sourceCode);
|
|
46
|
+
const seen = new Set<string>();
|
|
47
|
+
|
|
48
|
+
return findStringLiterals(ast, (value) => isDraftId(value)).flatMap(
|
|
49
|
+
(match) => {
|
|
50
|
+
if (
|
|
51
|
+
isSuppressedMatch(match, sourceCode, lines, frameworkConstantOffsets)
|
|
52
|
+
) {
|
|
53
|
+
return [];
|
|
54
|
+
}
|
|
55
|
+
const key = `${match.value}:${String(match.start)}`;
|
|
56
|
+
if (seen.has(key)) {
|
|
57
|
+
return [];
|
|
58
|
+
}
|
|
59
|
+
seen.add(key);
|
|
60
|
+
return [createDiagnostic(sourceCode, filePath, match)];
|
|
61
|
+
}
|
|
62
|
+
);
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Warns when draft ids are still present so the debt stays visible during
|
|
67
|
+
* review even when the file is correctly marked.
|
|
68
|
+
*
|
|
69
|
+
* Severity is intentionally `warn`, not `error`. The hard rejection layer for
|
|
70
|
+
* draft state leaking into established outputs is `validateEstablishedTopo` at
|
|
71
|
+
* runtime — it blocks topo export, trailhead projection, and lockfile writes.
|
|
72
|
+
* This rule surfaces the debt for human reviewers without duplicating that layer.
|
|
73
|
+
*/
|
|
74
|
+
export const draftVisibleDebt: WardenRule = {
|
|
75
|
+
check(sourceCode: string, filePath: string): readonly WardenDiagnostic[] {
|
|
76
|
+
const ast = parse(filePath, sourceCode);
|
|
77
|
+
if (!ast) {
|
|
78
|
+
return [];
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return collectDraftVisibleDebtDiagnostics(sourceCode, filePath, ast);
|
|
82
|
+
},
|
|
83
|
+
description:
|
|
84
|
+
'Warn when draft ids remain in source so the debt stays visible during review.',
|
|
85
|
+
name: 'draft-visible-debt',
|
|
86
|
+
severity: 'warn',
|
|
87
|
+
};
|
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Validates that registered transport error mappers cover every error category.
|
|
3
|
+
*
|
|
4
|
+
* Scans `createTransportErrorMapper(...)` calls and resolves simple object
|
|
5
|
+
* literals, identifier bindings, and object-property references in the same
|
|
6
|
+
* file so incomplete mapper registrations are caught before they ship.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { errorCategories } from '@ontrails/core';
|
|
10
|
+
|
|
11
|
+
import {
|
|
12
|
+
getStringValue,
|
|
13
|
+
identifierName,
|
|
14
|
+
isStringLiteral,
|
|
15
|
+
offsetToLine,
|
|
16
|
+
parse,
|
|
17
|
+
walk,
|
|
18
|
+
} from './ast.js';
|
|
19
|
+
import type { AstNode } from './ast.js';
|
|
20
|
+
import { isTestFile } from './scan.js';
|
|
21
|
+
import type { WardenDiagnostic, WardenRule } from './types.js';
|
|
22
|
+
|
|
23
|
+
const MEMBER_EXPRESSION_TYPES = new Set([
|
|
24
|
+
'MemberExpression',
|
|
25
|
+
'StaticMemberExpression',
|
|
26
|
+
]);
|
|
27
|
+
|
|
28
|
+
const getPropertyName = (node: AstNode | undefined): string | null => {
|
|
29
|
+
if (!node) {
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return (
|
|
34
|
+
identifierName(node) ??
|
|
35
|
+
(isStringLiteral(node) ? getStringValue(node) : null)
|
|
36
|
+
);
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
const collectObjectBindings = (ast: AstNode): ReadonlyMap<string, AstNode> => {
|
|
40
|
+
const bindings = new Map<string, AstNode>();
|
|
41
|
+
|
|
42
|
+
walk(ast, (node) => {
|
|
43
|
+
if (node.type !== 'VariableDeclarator') {
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const { id, init } = node as { id?: AstNode; init?: AstNode };
|
|
48
|
+
const bindingName = identifierName(id);
|
|
49
|
+
|
|
50
|
+
if (bindingName && init?.type === 'ObjectExpression') {
|
|
51
|
+
bindings.set(bindingName, init);
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
return bindings;
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
const getObjectProperties = (objectNode: AstNode): readonly AstNode[] =>
|
|
59
|
+
objectNode.type === 'ObjectExpression'
|
|
60
|
+
? ((objectNode['properties'] as readonly AstNode[] | undefined) ?? [])
|
|
61
|
+
: [];
|
|
62
|
+
|
|
63
|
+
const findObjectPropertyValue = (
|
|
64
|
+
objectNode: AstNode,
|
|
65
|
+
propertyName: string
|
|
66
|
+
): AstNode | null => {
|
|
67
|
+
for (const property of getObjectProperties(objectNode)) {
|
|
68
|
+
if (property.type !== 'Property') {
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const key = getPropertyName((property as unknown as { key?: AstNode }).key);
|
|
73
|
+
if (key === propertyName) {
|
|
74
|
+
return (property as unknown as { value?: AstNode }).value ?? null;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return null;
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
const resolveIdentifierObject = (
|
|
82
|
+
node: AstNode,
|
|
83
|
+
bindings: ReadonlyMap<string, AstNode>
|
|
84
|
+
): AstNode | null =>
|
|
85
|
+
bindings.get((node as { name?: string }).name ?? '') ?? null;
|
|
86
|
+
|
|
87
|
+
const resolveMemberObject = (
|
|
88
|
+
node: AstNode,
|
|
89
|
+
bindings: ReadonlyMap<string, AstNode>,
|
|
90
|
+
depth: number,
|
|
91
|
+
resolve: (
|
|
92
|
+
node: AstNode | undefined,
|
|
93
|
+
bindings: ReadonlyMap<string, AstNode>,
|
|
94
|
+
depth?: number
|
|
95
|
+
) => AstNode | null
|
|
96
|
+
): AstNode | null => {
|
|
97
|
+
const { object, property } = node as { object?: AstNode; property?: AstNode };
|
|
98
|
+
const propertyName = getPropertyName(property);
|
|
99
|
+
if (!propertyName) {
|
|
100
|
+
return null;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const objectNode = resolve(object, bindings, depth + 1);
|
|
104
|
+
return objectNode
|
|
105
|
+
? resolve(
|
|
106
|
+
findObjectPropertyValue(objectNode, propertyName) ?? undefined,
|
|
107
|
+
bindings,
|
|
108
|
+
depth + 1
|
|
109
|
+
)
|
|
110
|
+
: null;
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
const resolveObjectExpression = function resolveObjectExpression(
|
|
114
|
+
node: AstNode | undefined,
|
|
115
|
+
bindings: ReadonlyMap<string, AstNode>,
|
|
116
|
+
depth = 0
|
|
117
|
+
): AstNode | null {
|
|
118
|
+
if (!node || depth > 4) {
|
|
119
|
+
return null;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
if (node.type === 'ObjectExpression') {
|
|
123
|
+
return node;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
if (node.type === 'Identifier') {
|
|
127
|
+
return resolveIdentifierObject(node, bindings);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
return MEMBER_EXPRESSION_TYPES.has(node.type)
|
|
131
|
+
? resolveMemberObject(node, bindings, depth, resolveObjectExpression)
|
|
132
|
+
: null;
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
const addMappedCategory = (
|
|
136
|
+
categories: Set<string>,
|
|
137
|
+
property: AstNode
|
|
138
|
+
): boolean => {
|
|
139
|
+
if (property.type === 'SpreadElement') {
|
|
140
|
+
return false;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
if (property.type !== 'Property') {
|
|
144
|
+
return true;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
const key = getPropertyName((property as unknown as { key?: AstNode }).key);
|
|
148
|
+
if (key) {
|
|
149
|
+
categories.add(key);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
return true;
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
const collectMappedCategories = (
|
|
156
|
+
mapperObject: AstNode
|
|
157
|
+
): ReadonlySet<string> | null => {
|
|
158
|
+
if (mapperObject.type !== 'ObjectExpression') {
|
|
159
|
+
return null;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
const categories = new Set<string>();
|
|
163
|
+
for (const property of getObjectProperties(mapperObject)) {
|
|
164
|
+
if (!addMappedCategory(categories, property)) {
|
|
165
|
+
return null;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
return categories;
|
|
170
|
+
};
|
|
171
|
+
|
|
172
|
+
const createDiagnostic = (
|
|
173
|
+
filePath: string,
|
|
174
|
+
line: number,
|
|
175
|
+
missingCategories: readonly string[]
|
|
176
|
+
): WardenDiagnostic => ({
|
|
177
|
+
filePath,
|
|
178
|
+
line,
|
|
179
|
+
message: `Transport error mapper is missing mappings for: ${missingCategories.join(', ')}. Registered createTransportErrorMapper() calls must cover every ErrorCategory.`,
|
|
180
|
+
rule: 'error-mapping-completeness',
|
|
181
|
+
severity: 'error',
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
const getCallArgs = (node: AstNode): readonly AstNode[] =>
|
|
185
|
+
(node as { arguments?: readonly AstNode[] }).arguments ?? [];
|
|
186
|
+
|
|
187
|
+
const getCallCallee = (node: AstNode): AstNode | undefined =>
|
|
188
|
+
(node as { callee?: AstNode }).callee;
|
|
189
|
+
|
|
190
|
+
const isMapperFactoryCall = (node: AstNode): boolean =>
|
|
191
|
+
node.type === 'CallExpression' &&
|
|
192
|
+
identifierName(getCallCallee(node)) === 'createTransportErrorMapper';
|
|
193
|
+
|
|
194
|
+
const findMissingCategories = (
|
|
195
|
+
mappedCategories: ReadonlySet<string>
|
|
196
|
+
): readonly string[] =>
|
|
197
|
+
errorCategories.filter((category) => !mappedCategories.has(category));
|
|
198
|
+
|
|
199
|
+
const resolveMappedCategories = (
|
|
200
|
+
node: AstNode,
|
|
201
|
+
bindings: ReadonlyMap<string, AstNode>
|
|
202
|
+
): ReadonlySet<string> | null => {
|
|
203
|
+
const [firstArg] = getCallArgs(node);
|
|
204
|
+
const mapperObject = resolveObjectExpression(firstArg, bindings);
|
|
205
|
+
return mapperObject ? collectMappedCategories(mapperObject) : null;
|
|
206
|
+
};
|
|
207
|
+
|
|
208
|
+
const inspectMapperCall = (
|
|
209
|
+
node: AstNode,
|
|
210
|
+
bindings: ReadonlyMap<string, AstNode>,
|
|
211
|
+
filePath: string,
|
|
212
|
+
sourceCode: string
|
|
213
|
+
): WardenDiagnostic | null => {
|
|
214
|
+
if (!isMapperFactoryCall(node)) {
|
|
215
|
+
return null;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
const mappedCategories = resolveMappedCategories(node, bindings);
|
|
219
|
+
if (!mappedCategories) {
|
|
220
|
+
return null;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
const missingCategories = findMissingCategories(mappedCategories);
|
|
224
|
+
if (missingCategories.length === 0) {
|
|
225
|
+
return null;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
return createDiagnostic(
|
|
229
|
+
filePath,
|
|
230
|
+
offsetToLine(sourceCode, node.start),
|
|
231
|
+
missingCategories
|
|
232
|
+
);
|
|
233
|
+
};
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* Flags `createTransportErrorMapper()` registrations that omit error categories.
|
|
237
|
+
*/
|
|
238
|
+
export const errorMappingCompleteness: WardenRule = {
|
|
239
|
+
check(sourceCode: string, filePath: string): readonly WardenDiagnostic[] {
|
|
240
|
+
if (
|
|
241
|
+
isTestFile(filePath) ||
|
|
242
|
+
!sourceCode.includes('createTransportErrorMapper')
|
|
243
|
+
) {
|
|
244
|
+
return [];
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
const ast = parse(filePath, sourceCode);
|
|
248
|
+
if (!ast) {
|
|
249
|
+
return [];
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
const bindings = collectObjectBindings(ast);
|
|
253
|
+
const diagnostics: WardenDiagnostic[] = [];
|
|
254
|
+
|
|
255
|
+
walk(ast, (node) => {
|
|
256
|
+
const diagnostic = inspectMapperCall(
|
|
257
|
+
node,
|
|
258
|
+
bindings,
|
|
259
|
+
filePath,
|
|
260
|
+
sourceCode
|
|
261
|
+
);
|
|
262
|
+
if (diagnostic) {
|
|
263
|
+
diagnostics.push(diagnostic);
|
|
264
|
+
}
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
return diagnostics;
|
|
268
|
+
},
|
|
269
|
+
description:
|
|
270
|
+
'Require registered transport error mappers to cover every ErrorCategory.',
|
|
271
|
+
name: 'error-mapping-completeness',
|
|
272
|
+
severity: 'error',
|
|
273
|
+
};
|