@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
|
@@ -3,83 +3,914 @@ import { describe, expect, test } from 'bun:test';
|
|
|
3
3
|
import { noSyncResultAssumption } from '../rules/no-sync-result-assumption.js';
|
|
4
4
|
|
|
5
5
|
describe('no-sync-result-assumption', () => {
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
describe('core behavior', () => {
|
|
7
|
+
test('flags direct result access on implementation calls', () => {
|
|
8
|
+
const code = `
|
|
8
9
|
async function run() {
|
|
9
10
|
const isOk = entityShow.blaze({ id: "1" }, ctx).isOk();
|
|
10
11
|
return isOk;
|
|
11
12
|
}`;
|
|
12
13
|
|
|
13
|
-
|
|
14
|
+
const diagnostics = noSyncResultAssumption.check(code, 'src/app.ts');
|
|
14
15
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
16
|
+
expect(diagnostics).toHaveLength(1);
|
|
17
|
+
expect(diagnostics[0]?.rule).toBe('no-sync-result-assumption');
|
|
18
|
+
expect(diagnostics[0]?.severity).toBe('error');
|
|
19
|
+
expect(diagnostics[0]?.message).toContain('Missing await');
|
|
20
|
+
});
|
|
20
21
|
|
|
21
|
-
|
|
22
|
-
|
|
22
|
+
test('flags a stored implementation result that is used synchronously', () => {
|
|
23
|
+
const code = `
|
|
23
24
|
const result = entityShow.blaze({ id: "1" }, ctx);
|
|
24
25
|
|
|
25
26
|
if (result.isOk()) {
|
|
26
27
|
console.log("ok");
|
|
27
28
|
}`;
|
|
28
29
|
|
|
29
|
-
|
|
30
|
+
const diagnostics = noSyncResultAssumption.check(code, 'src/app.ts');
|
|
30
31
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
32
|
+
expect(diagnostics).toHaveLength(1);
|
|
33
|
+
expect(diagnostics[0]?.line).toBe(4);
|
|
34
|
+
});
|
|
34
35
|
|
|
35
|
-
|
|
36
|
-
|
|
36
|
+
test('allows awaited implementation calls before result access', () => {
|
|
37
|
+
const code = `
|
|
37
38
|
async function run() {
|
|
38
39
|
const result = await entityShow.blaze({ id: "1" }, ctx);
|
|
39
40
|
return result.isOk();
|
|
40
41
|
}`;
|
|
41
42
|
|
|
42
|
-
|
|
43
|
+
const diagnostics = noSyncResultAssumption.check(code, 'src/app.ts');
|
|
43
44
|
|
|
44
|
-
|
|
45
|
-
|
|
45
|
+
expect(diagnostics).toHaveLength(0);
|
|
46
|
+
});
|
|
46
47
|
|
|
47
|
-
|
|
48
|
-
|
|
48
|
+
test('allows awaited implementation calls when the property access is chained', () => {
|
|
49
|
+
const code = `
|
|
49
50
|
async function run() {
|
|
50
51
|
return (await entityShow.blaze({ id: "1" }, ctx)).isOk();
|
|
51
52
|
}`;
|
|
52
53
|
|
|
53
|
-
|
|
54
|
+
const diagnostics = noSyncResultAssumption.check(code, 'src/app.ts');
|
|
54
55
|
|
|
55
|
-
|
|
56
|
-
|
|
56
|
+
expect(diagnostics).toHaveLength(0);
|
|
57
|
+
});
|
|
57
58
|
|
|
58
|
-
|
|
59
|
-
|
|
59
|
+
test('ignores test files', () => {
|
|
60
|
+
const code = `
|
|
60
61
|
const result = entityShow.blaze({ id: "1" }, ctx);
|
|
61
62
|
result.isOk();
|
|
62
63
|
`;
|
|
63
64
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
65
|
+
const diagnostics = noSyncResultAssumption.check(
|
|
66
|
+
code,
|
|
67
|
+
'src/__tests__/app.test.ts'
|
|
68
|
+
);
|
|
68
69
|
|
|
69
|
-
|
|
70
|
-
|
|
70
|
+
expect(diagnostics).toHaveLength(0);
|
|
71
|
+
});
|
|
71
72
|
|
|
72
|
-
|
|
73
|
-
|
|
73
|
+
test('ignores framework internals that intentionally call implementations', () => {
|
|
74
|
+
const code = `
|
|
74
75
|
const result = entityShow.blaze({ id: "1" }, ctx);
|
|
75
76
|
result.isOk();
|
|
76
77
|
`;
|
|
77
78
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
79
|
+
const diagnostics = noSyncResultAssumption.check(
|
|
80
|
+
code,
|
|
81
|
+
'/repo/packages/testing/src/trail.ts'
|
|
82
|
+
);
|
|
83
|
+
|
|
84
|
+
expect(diagnostics).toHaveLength(0);
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
test('ignores .blaze() inside a template-literal string payload', () => {
|
|
88
|
+
const code =
|
|
89
|
+
'const example = `const x = entityShow.blaze(input, ctx).isOk()`;';
|
|
90
|
+
|
|
91
|
+
const diagnostics = noSyncResultAssumption.check(code, 'src/app.ts');
|
|
92
|
+
|
|
93
|
+
expect(diagnostics).toHaveLength(0);
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
test('ignores .blaze() inside a double-quoted string payload', () => {
|
|
97
|
+
const code = 'const example = "entityShow.blaze(input, ctx).isOk()";';
|
|
98
|
+
|
|
99
|
+
const diagnostics = noSyncResultAssumption.check(code, 'src/app.ts');
|
|
100
|
+
|
|
101
|
+
expect(diagnostics).toHaveLength(0);
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
test('allows awaited indirection through destructured call', () => {
|
|
105
|
+
const code = `
|
|
106
|
+
async function run() {
|
|
107
|
+
const result = (await entityShow.blaze({ id: "1" }, ctx));
|
|
108
|
+
return result.isErr();
|
|
109
|
+
}`;
|
|
110
|
+
|
|
111
|
+
const diagnostics = noSyncResultAssumption.check(code, 'src/app.ts');
|
|
112
|
+
|
|
113
|
+
expect(diagnostics).toHaveLength(0);
|
|
114
|
+
});
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
describe('lexical scoping', () => {
|
|
118
|
+
test('does not flag a parameter that shadows a pending binding', () => {
|
|
119
|
+
const code = `
|
|
120
|
+
const result = entityShow.blaze({ id: "1" }, ctx);
|
|
121
|
+
function ok(result) {
|
|
122
|
+
return result.isOk();
|
|
123
|
+
}`;
|
|
124
|
+
|
|
125
|
+
const diagnostics = noSyncResultAssumption.check(code, 'src/app.ts');
|
|
126
|
+
|
|
127
|
+
expect(diagnostics).toHaveLength(0);
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
test('does not flag a local block-scoped shadow of a pending binding', () => {
|
|
131
|
+
const code = `
|
|
132
|
+
const result = entityShow.blaze({ id: "1" }, ctx);
|
|
133
|
+
{
|
|
134
|
+
const result = { isOk: () => true };
|
|
135
|
+
if (result.isOk()) {
|
|
136
|
+
console.log("inner");
|
|
137
|
+
}
|
|
138
|
+
}`;
|
|
139
|
+
|
|
140
|
+
const diagnostics = noSyncResultAssumption.check(code, 'src/app.ts');
|
|
141
|
+
|
|
142
|
+
expect(diagnostics).toHaveLength(0);
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
test('does not flag a local block-scoped class that shadows a pending binding', () => {
|
|
146
|
+
const code = `
|
|
147
|
+
const result = entityShow.blaze({ id: "1" }, ctx);
|
|
148
|
+
{
|
|
149
|
+
class result {
|
|
150
|
+
static isOk() { return true; }
|
|
151
|
+
}
|
|
152
|
+
if (result.isOk()) {
|
|
153
|
+
console.log("inner");
|
|
154
|
+
}
|
|
155
|
+
}`;
|
|
156
|
+
|
|
157
|
+
const diagnostics = noSyncResultAssumption.check(code, 'src/app.ts');
|
|
158
|
+
|
|
159
|
+
expect(diagnostics).toHaveLength(0);
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
test('does not flag an arrow parameter that shadows a pending binding', () => {
|
|
163
|
+
const code = `
|
|
164
|
+
const result = entityShow.blaze({ id: "1" }, ctx);
|
|
165
|
+
const f = (result) => result.isOk();`;
|
|
166
|
+
|
|
167
|
+
const diagnostics = noSyncResultAssumption.check(code, 'src/app.ts');
|
|
168
|
+
|
|
169
|
+
expect(diagnostics).toHaveLength(0);
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
test('does not flag a deeply nested shadowed parameter', () => {
|
|
173
|
+
const code = `
|
|
174
|
+
const result = entityShow.blaze({ id: "1" }, ctx);
|
|
175
|
+
function outer() {
|
|
176
|
+
const result = 1;
|
|
177
|
+
function inner(result) {
|
|
178
|
+
return result.isOk();
|
|
179
|
+
}
|
|
180
|
+
return inner;
|
|
181
|
+
}`;
|
|
182
|
+
|
|
183
|
+
const diagnostics = noSyncResultAssumption.check(code, 'src/app.ts');
|
|
184
|
+
|
|
185
|
+
expect(diagnostics).toHaveLength(0);
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
test('still flags outer pending binding referenced from a non-shadowing nested function', () => {
|
|
189
|
+
const code = `
|
|
190
|
+
const result = entityShow.blaze({ id: "1" }, ctx);
|
|
191
|
+
function ok() {
|
|
192
|
+
return result.isOk();
|
|
193
|
+
}`;
|
|
194
|
+
|
|
195
|
+
const diagnostics = noSyncResultAssumption.check(code, 'src/app.ts');
|
|
196
|
+
|
|
197
|
+
expect(diagnostics).toHaveLength(1);
|
|
198
|
+
expect(diagnostics[0]?.message).toContain('Missing await');
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
test('does not flag a destructured parameter that shadows a pending binding', () => {
|
|
202
|
+
const code = `
|
|
203
|
+
const result = entityShow.blaze({ id: "1" }, ctx);
|
|
204
|
+
function ok({ result }) {
|
|
205
|
+
return result.isOk();
|
|
206
|
+
}`;
|
|
207
|
+
|
|
208
|
+
const diagnostics = noSyncResultAssumption.check(code, 'src/app.ts');
|
|
209
|
+
|
|
210
|
+
expect(diagnostics).toHaveLength(0);
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
test('does not flag a catch binding that shadows a pending binding', () => {
|
|
214
|
+
const code = `
|
|
215
|
+
const result = entityShow.blaze({ id: "1" }, ctx);
|
|
216
|
+
try {
|
|
217
|
+
doThing();
|
|
218
|
+
} catch (result) {
|
|
219
|
+
if (result.isOk) console.log(result.isOk);
|
|
220
|
+
}`;
|
|
221
|
+
|
|
222
|
+
const diagnostics = noSyncResultAssumption.check(code, 'src/app.ts');
|
|
223
|
+
|
|
224
|
+
expect(diagnostics).toHaveLength(0);
|
|
225
|
+
});
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
describe('var hoisting', () => {
|
|
229
|
+
test('var declared inside a block hoists to the enclosing function', () => {
|
|
230
|
+
const code = `
|
|
231
|
+
function run() {
|
|
232
|
+
if (cond) {
|
|
233
|
+
var result = entityShow.blaze({ id: "1" }, ctx);
|
|
234
|
+
}
|
|
235
|
+
result.isOk();
|
|
236
|
+
}`;
|
|
237
|
+
|
|
238
|
+
const diagnostics = noSyncResultAssumption.check(code, 'src/app.ts');
|
|
239
|
+
|
|
240
|
+
expect(diagnostics).toHaveLength(1);
|
|
241
|
+
expect(diagnostics[0]?.message).toContain('Missing await');
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
test('var declared in a for-head hoists to the enclosing function', () => {
|
|
245
|
+
const code = `
|
|
246
|
+
function run() {
|
|
247
|
+
for (var result = entityShow.blaze({ id: "1" }, ctx); ;) { break; }
|
|
248
|
+
result.isOk();
|
|
249
|
+
}`;
|
|
250
|
+
|
|
251
|
+
const diagnostics = noSyncResultAssumption.check(code, 'src/app.ts');
|
|
252
|
+
|
|
253
|
+
expect(diagnostics).toHaveLength(1);
|
|
254
|
+
expect(diagnostics[0]?.message).toContain('Missing await');
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
test('let declared inside a block stays block-scoped', () => {
|
|
258
|
+
const code = `
|
|
259
|
+
function run() {
|
|
260
|
+
if (cond) {
|
|
261
|
+
let result = entityShow.blaze({ id: "1" }, ctx);
|
|
262
|
+
}
|
|
263
|
+
result.isOk();
|
|
264
|
+
}`;
|
|
265
|
+
|
|
266
|
+
const diagnostics = noSyncResultAssumption.check(code, 'src/app.ts');
|
|
267
|
+
|
|
268
|
+
expect(diagnostics).toHaveLength(0);
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
test('var re-init of a same-named parameter registers as a pending binding', () => {
|
|
272
|
+
// `var` and parameters share the function's VariableEnvironment, so
|
|
273
|
+
// `var result = blaze(...)` writes to the parameter's slot. After the
|
|
274
|
+
// declaration runs, `result.isOk()` observes the blaze result and
|
|
275
|
+
// must fire, just as it would in the no-parameter case.
|
|
276
|
+
const code = `
|
|
277
|
+
function run(result) {
|
|
278
|
+
var result = entityShow.blaze({ id: "1" }, ctx);
|
|
279
|
+
result.isOk();
|
|
280
|
+
}`;
|
|
281
|
+
|
|
282
|
+
const diagnostics = noSyncResultAssumption.check(code, 'src/app.ts');
|
|
283
|
+
|
|
284
|
+
expect(diagnostics).toHaveLength(1);
|
|
285
|
+
expect(diagnostics[0]?.message).toContain('Missing await');
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
test('var re-init of a parameter inside a block still hoists to function scope', () => {
|
|
289
|
+
const code = `
|
|
290
|
+
function run(result) {
|
|
291
|
+
if (cond) {
|
|
292
|
+
var result = entityShow.blaze({ id: "1" }, ctx);
|
|
293
|
+
}
|
|
294
|
+
result.isOk();
|
|
295
|
+
}`;
|
|
296
|
+
|
|
297
|
+
const diagnostics = noSyncResultAssumption.check(code, 'src/app.ts');
|
|
298
|
+
|
|
299
|
+
expect(diagnostics).toHaveLength(1);
|
|
300
|
+
expect(diagnostics[0]?.message).toContain('Missing await');
|
|
301
|
+
});
|
|
302
|
+
|
|
303
|
+
test('var re-init of a parameter with no later result-shaped use is not flagged', () => {
|
|
304
|
+
// Registering the pending binding is fine, but nothing consumes it
|
|
305
|
+
// as a sync `Result`, so no diagnostic is emitted.
|
|
306
|
+
const code = `
|
|
307
|
+
function run(result) {
|
|
308
|
+
var result = entityShow.blaze({ id: "1" }, ctx);
|
|
309
|
+
}`;
|
|
310
|
+
|
|
311
|
+
const diagnostics = noSyncResultAssumption.check(code, 'src/app.ts');
|
|
312
|
+
|
|
313
|
+
expect(diagnostics).toHaveLength(0);
|
|
314
|
+
});
|
|
315
|
+
|
|
316
|
+
test('plain `=` re-assignment to a same-named parameter registers as a pending binding', () => {
|
|
317
|
+
// `result = blaze(...)` writes to the parameter's existing slot in
|
|
318
|
+
// the same VariableEnvironment, so the subsequent `result.isOk()`
|
|
319
|
+
// observes the blaze result and must fire.
|
|
320
|
+
const code = `
|
|
321
|
+
function run(result) {
|
|
322
|
+
result = entityShow.blaze({ id: "1" }, ctx);
|
|
323
|
+
result.isOk();
|
|
324
|
+
}`;
|
|
325
|
+
|
|
326
|
+
const diagnostics = noSyncResultAssumption.check(code, 'src/app.ts');
|
|
327
|
+
|
|
328
|
+
expect(diagnostics).toHaveLength(1);
|
|
329
|
+
expect(diagnostics[0]?.message).toContain('Missing await');
|
|
330
|
+
});
|
|
331
|
+
|
|
332
|
+
test('parameter used directly as a Result without re-init is not flagged', () => {
|
|
333
|
+
// The parameter is the real binding; nothing made it a pending
|
|
334
|
+
// `.blaze()` result, so `result.isOk()` is just a call on whatever
|
|
335
|
+
// was passed in.
|
|
336
|
+
const code = `
|
|
337
|
+
function run(result) {
|
|
338
|
+
result.isOk();
|
|
339
|
+
}`;
|
|
340
|
+
|
|
341
|
+
const diagnostics = noSyncResultAssumption.check(code, 'src/app.ts');
|
|
342
|
+
|
|
343
|
+
expect(diagnostics).toHaveLength(0);
|
|
344
|
+
});
|
|
345
|
+
});
|
|
346
|
+
|
|
347
|
+
describe('static blocks', () => {
|
|
348
|
+
test('inner const in static block shadows outer pending binding', () => {
|
|
349
|
+
const code = `
|
|
350
|
+
const result = trail.blaze({ id: "1" }, ctx);
|
|
351
|
+
class Foo {
|
|
352
|
+
static {
|
|
353
|
+
const result = 42;
|
|
354
|
+
result.toString();
|
|
355
|
+
}
|
|
356
|
+
}`;
|
|
357
|
+
|
|
358
|
+
const diagnostics = noSyncResultAssumption.check(code, 'src/app.ts');
|
|
359
|
+
|
|
360
|
+
expect(diagnostics).toHaveLength(0);
|
|
361
|
+
});
|
|
362
|
+
|
|
363
|
+
test('pending binding inside a static block flows to use inside the same static block', () => {
|
|
364
|
+
const code = `
|
|
365
|
+
class Foo {
|
|
366
|
+
static {
|
|
367
|
+
const result = trail.blaze({ id: "1" }, ctx);
|
|
368
|
+
result.isOk();
|
|
369
|
+
}
|
|
370
|
+
}`;
|
|
371
|
+
|
|
372
|
+
const diagnostics = noSyncResultAssumption.check(code, 'src/app.ts');
|
|
373
|
+
|
|
374
|
+
expect(diagnostics).toHaveLength(1);
|
|
375
|
+
expect(diagnostics[0]?.message).toContain('Missing await');
|
|
376
|
+
});
|
|
377
|
+
|
|
378
|
+
test('static block bindings do not leak out to enclosing scope', () => {
|
|
379
|
+
const code = `
|
|
380
|
+
class Foo {
|
|
381
|
+
static {
|
|
382
|
+
const result = trail.blaze({ id: "1" }, ctx);
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
result.isOk();`;
|
|
386
|
+
|
|
387
|
+
const diagnostics = noSyncResultAssumption.check(code, 'src/app.ts');
|
|
388
|
+
|
|
389
|
+
expect(diagnostics).toHaveLength(0);
|
|
390
|
+
});
|
|
391
|
+
|
|
392
|
+
test('flags var-declared blaze result used inside the same static block', () => {
|
|
393
|
+
const code = `
|
|
394
|
+
class Foo {
|
|
395
|
+
static {
|
|
396
|
+
var result = entityShow.blaze({ id: "1" }, ctx);
|
|
397
|
+
result.isOk();
|
|
398
|
+
}
|
|
399
|
+
}`;
|
|
400
|
+
|
|
401
|
+
const diagnostics = noSyncResultAssumption.check(code, 'src/app.ts');
|
|
402
|
+
|
|
403
|
+
expect(diagnostics).toHaveLength(1);
|
|
404
|
+
expect(diagnostics[0]?.message).toContain('Missing await');
|
|
405
|
+
});
|
|
406
|
+
|
|
407
|
+
test('does not let a static-block var leak to the enclosing scope', () => {
|
|
408
|
+
const code = `
|
|
409
|
+
class Foo {
|
|
410
|
+
static {
|
|
411
|
+
var result = entityShow.blaze({ id: "1" }, ctx);
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
function outer() {
|
|
416
|
+
result.isOk();
|
|
417
|
+
}`;
|
|
418
|
+
|
|
419
|
+
const diagnostics = noSyncResultAssumption.check(code, 'src/app.ts');
|
|
420
|
+
|
|
421
|
+
expect(diagnostics).toHaveLength(0);
|
|
422
|
+
});
|
|
423
|
+
});
|
|
424
|
+
|
|
425
|
+
describe('parenthesized blaze calls', () => {
|
|
426
|
+
test('flags member access on a parens-wrapped blaze call', () => {
|
|
427
|
+
const code = `
|
|
428
|
+
function run() {
|
|
429
|
+
return (entityShow.blaze({ id: "1" }, ctx)).isOk();
|
|
430
|
+
}`;
|
|
431
|
+
|
|
432
|
+
const diagnostics = noSyncResultAssumption.check(code, 'src/app.ts');
|
|
433
|
+
|
|
434
|
+
expect(diagnostics).toHaveLength(1);
|
|
435
|
+
expect(diagnostics[0]?.message).toContain('Missing await');
|
|
436
|
+
});
|
|
437
|
+
|
|
438
|
+
test('flags .value access on a parens-wrapped blaze call', () => {
|
|
439
|
+
const code = `
|
|
440
|
+
function run() {
|
|
441
|
+
return (entityShow.blaze({ id: "1" }, ctx)).value;
|
|
442
|
+
}`;
|
|
443
|
+
|
|
444
|
+
const diagnostics = noSyncResultAssumption.check(code, 'src/app.ts');
|
|
445
|
+
|
|
446
|
+
expect(diagnostics).toHaveLength(1);
|
|
447
|
+
expect(diagnostics[0]?.message).toContain('Missing await');
|
|
448
|
+
});
|
|
449
|
+
|
|
450
|
+
test('flags member access through double-wrapped parens', () => {
|
|
451
|
+
const code = `
|
|
452
|
+
function run() {
|
|
453
|
+
return ((entityShow.blaze({ id: "1" }, ctx))).isOk();
|
|
454
|
+
}`;
|
|
455
|
+
|
|
456
|
+
const diagnostics = noSyncResultAssumption.check(code, 'src/app.ts');
|
|
457
|
+
|
|
458
|
+
expect(diagnostics).toHaveLength(1);
|
|
459
|
+
expect(diagnostics[0]?.message).toContain('Missing await');
|
|
460
|
+
});
|
|
461
|
+
|
|
462
|
+
test('flags accessor use on a binding whose init is a parens-wrapped blaze call', () => {
|
|
463
|
+
const code = `
|
|
464
|
+
function run() {
|
|
465
|
+
const result = (entityShow.blaze({ id: "1" }, ctx));
|
|
466
|
+
return result.isOk();
|
|
467
|
+
}`;
|
|
468
|
+
|
|
469
|
+
const diagnostics = noSyncResultAssumption.check(code, 'src/app.ts');
|
|
470
|
+
|
|
471
|
+
expect(diagnostics).toHaveLength(1);
|
|
472
|
+
expect(diagnostics[0]?.message).toContain('Missing await');
|
|
473
|
+
});
|
|
474
|
+
|
|
475
|
+
test('flags accessor use on a binding whose init is a double-parens blaze call', () => {
|
|
476
|
+
const code = `
|
|
477
|
+
function run() {
|
|
478
|
+
const result = ((entityShow.blaze({ id: "1" }, ctx)));
|
|
479
|
+
return result.isOk();
|
|
480
|
+
}`;
|
|
481
|
+
|
|
482
|
+
const diagnostics = noSyncResultAssumption.check(code, 'src/app.ts');
|
|
483
|
+
|
|
484
|
+
expect(diagnostics).toHaveLength(1);
|
|
485
|
+
expect(diagnostics[0]?.message).toContain('Missing await');
|
|
486
|
+
});
|
|
487
|
+
|
|
488
|
+
test('flags destructuring of a Result accessor from a parens-wrapped blaze call', () => {
|
|
489
|
+
const code = `
|
|
490
|
+
function run() {
|
|
491
|
+
const { isOk } = (entityShow.blaze({ id: "1" }, ctx));
|
|
492
|
+
return isOk();
|
|
493
|
+
}`;
|
|
494
|
+
|
|
495
|
+
const diagnostics = noSyncResultAssumption.check(code, 'src/app.ts');
|
|
496
|
+
|
|
497
|
+
expect(diagnostics).toHaveLength(1);
|
|
498
|
+
expect(diagnostics[0]?.message).toContain('Missing await');
|
|
499
|
+
});
|
|
500
|
+
|
|
501
|
+
test('flags accessor use on a binding whose init is a TSAsExpression-wrapped blaze call', () => {
|
|
502
|
+
const code = `
|
|
503
|
+
function run() {
|
|
504
|
+
const result = entityShow.blaze({ id: "1" }, ctx) as any;
|
|
505
|
+
return result.isOk();
|
|
506
|
+
}`;
|
|
507
|
+
|
|
508
|
+
const diagnostics = noSyncResultAssumption.check(code, 'src/app.ts');
|
|
509
|
+
|
|
510
|
+
expect(diagnostics).toHaveLength(1);
|
|
511
|
+
expect(diagnostics[0]?.message).toContain('Missing await');
|
|
512
|
+
});
|
|
513
|
+
|
|
514
|
+
test('flags accessor use on a binding whose init is a TSSatisfiesExpression-wrapped blaze call', () => {
|
|
515
|
+
const code = `
|
|
516
|
+
function run() {
|
|
517
|
+
const result = entityShow.blaze({ id: "1" }, ctx) satisfies unknown;
|
|
518
|
+
return result.isOk();
|
|
519
|
+
}`;
|
|
520
|
+
|
|
521
|
+
const diagnostics = noSyncResultAssumption.check(code, 'src/app.ts');
|
|
522
|
+
|
|
523
|
+
expect(diagnostics).toHaveLength(1);
|
|
524
|
+
expect(diagnostics[0]?.message).toContain('Missing await');
|
|
525
|
+
});
|
|
526
|
+
|
|
527
|
+
test('flags accessor use on a binding whose init is a TSNonNullExpression-wrapped blaze call', () => {
|
|
528
|
+
const code = `
|
|
529
|
+
function run() {
|
|
530
|
+
const result = entityShow.blaze({ id: "1" }, ctx)!;
|
|
531
|
+
return result.isOk();
|
|
532
|
+
}`;
|
|
533
|
+
|
|
534
|
+
const diagnostics = noSyncResultAssumption.check(code, 'src/app.ts');
|
|
535
|
+
|
|
536
|
+
expect(diagnostics).toHaveLength(1);
|
|
537
|
+
expect(diagnostics[0]?.message).toContain('Missing await');
|
|
538
|
+
});
|
|
539
|
+
});
|
|
540
|
+
|
|
541
|
+
describe('destructured bindings', () => {
|
|
542
|
+
test('flags object destructuring of a Result accessor from an unawaited blaze call', () => {
|
|
543
|
+
const code = `
|
|
544
|
+
function run() {
|
|
545
|
+
const { isOk } = entityShow.blaze({ id: "1" }, ctx);
|
|
546
|
+
return isOk();
|
|
547
|
+
}`;
|
|
548
|
+
|
|
549
|
+
const diagnostics = noSyncResultAssumption.check(code, 'src/app.ts');
|
|
550
|
+
|
|
551
|
+
expect(diagnostics).toHaveLength(1);
|
|
552
|
+
expect(diagnostics[0]?.message).toContain('Missing await');
|
|
553
|
+
});
|
|
554
|
+
|
|
555
|
+
test('flags destructuring of value from an unawaited blaze call', () => {
|
|
556
|
+
const code = `
|
|
557
|
+
function run() {
|
|
558
|
+
const { value } = entityShow.blaze({ id: "1" }, ctx);
|
|
559
|
+
return value;
|
|
560
|
+
}`;
|
|
561
|
+
|
|
562
|
+
const diagnostics = noSyncResultAssumption.check(code, 'src/app.ts');
|
|
563
|
+
|
|
564
|
+
expect(diagnostics).toHaveLength(1);
|
|
565
|
+
expect(diagnostics[0]?.message).toContain('Missing await');
|
|
566
|
+
});
|
|
567
|
+
|
|
568
|
+
test('does not flag destructuring of a Result accessor from an awaited blaze call', () => {
|
|
569
|
+
const code = `
|
|
570
|
+
async function run() {
|
|
571
|
+
const { isOk } = await entityShow.blaze({ id: "1" }, ctx);
|
|
572
|
+
return isOk();
|
|
573
|
+
}`;
|
|
574
|
+
|
|
575
|
+
const diagnostics = noSyncResultAssumption.check(code, 'src/app.ts');
|
|
576
|
+
|
|
577
|
+
expect(diagnostics).toHaveLength(0);
|
|
578
|
+
});
|
|
579
|
+
});
|
|
580
|
+
|
|
581
|
+
test('flags .value access on unawaited blaze call', () => {
|
|
582
|
+
const code = `
|
|
583
|
+
function run() {
|
|
584
|
+
return entityShow.blaze({ id: "1" }, ctx).value;
|
|
585
|
+
}`;
|
|
586
|
+
|
|
587
|
+
const diagnostics = noSyncResultAssumption.check(code, 'src/app.ts');
|
|
588
|
+
|
|
589
|
+
expect(diagnostics).toHaveLength(1);
|
|
590
|
+
expect(diagnostics[0]?.message).toContain('Missing await');
|
|
591
|
+
});
|
|
592
|
+
|
|
593
|
+
describe('conditional-expression inits', () => {
|
|
594
|
+
test('flags accessor use when blaze call is the consequent branch', () => {
|
|
595
|
+
const code = `
|
|
596
|
+
function run(cond, fallback) {
|
|
597
|
+
const result = cond ? entityShow.blaze({ id: "1" }, ctx) : fallback;
|
|
598
|
+
return result.isOk();
|
|
599
|
+
}`;
|
|
600
|
+
|
|
601
|
+
const diagnostics = noSyncResultAssumption.check(code, 'src/app.ts');
|
|
602
|
+
|
|
603
|
+
expect(diagnostics).toHaveLength(1);
|
|
604
|
+
expect(diagnostics[0]?.message).toContain('Missing await');
|
|
605
|
+
});
|
|
606
|
+
|
|
607
|
+
test('flags accessor use when blaze call is the alternate branch', () => {
|
|
608
|
+
const code = `
|
|
609
|
+
function run(cond, fallback) {
|
|
610
|
+
const result = cond ? fallback : entityShow.blaze({ id: "1" }, ctx);
|
|
611
|
+
return result.isOk();
|
|
612
|
+
}`;
|
|
613
|
+
|
|
614
|
+
const diagnostics = noSyncResultAssumption.check(code, 'src/app.ts');
|
|
615
|
+
|
|
616
|
+
expect(diagnostics).toHaveLength(1);
|
|
617
|
+
expect(diagnostics[0]?.message).toContain('Missing await');
|
|
618
|
+
});
|
|
619
|
+
|
|
620
|
+
test('flags accessor use when both branches are blaze calls', () => {
|
|
621
|
+
const code = `
|
|
622
|
+
function run(cond) {
|
|
623
|
+
const result = cond
|
|
624
|
+
? entityShow.blaze({ id: "1" }, ctx)
|
|
625
|
+
: entityEdit.blaze({ id: "1" }, ctx);
|
|
626
|
+
return result.isOk();
|
|
627
|
+
}`;
|
|
628
|
+
|
|
629
|
+
const diagnostics = noSyncResultAssumption.check(code, 'src/app.ts');
|
|
630
|
+
|
|
631
|
+
expect(diagnostics.length).toBeGreaterThanOrEqual(1);
|
|
632
|
+
expect(diagnostics[0]?.message).toContain('Missing await');
|
|
633
|
+
});
|
|
634
|
+
|
|
635
|
+
test('does not flag when the conditional is awaited as a whole', () => {
|
|
636
|
+
const code = `
|
|
637
|
+
async function run(cond, fallback) {
|
|
638
|
+
const result = await (cond ? entityShow.blaze({ id: "1" }, ctx) : fallback);
|
|
639
|
+
return result.isOk();
|
|
640
|
+
}`;
|
|
641
|
+
|
|
642
|
+
const diagnostics = noSyncResultAssumption.check(code, 'src/app.ts');
|
|
643
|
+
|
|
644
|
+
expect(diagnostics).toHaveLength(0);
|
|
645
|
+
});
|
|
646
|
+
|
|
647
|
+
test('flags destructuring of a Result accessor from a conditional branch', () => {
|
|
648
|
+
const code = `
|
|
649
|
+
function run(cond, fallback) {
|
|
650
|
+
const { isOk } = cond ? entityShow.blaze({ id: "1" }, ctx) : fallback;
|
|
651
|
+
return isOk();
|
|
652
|
+
}`;
|
|
653
|
+
|
|
654
|
+
const diagnostics = noSyncResultAssumption.check(code, 'src/app.ts');
|
|
655
|
+
|
|
656
|
+
expect(diagnostics).toHaveLength(1);
|
|
657
|
+
expect(diagnostics[0]?.message).toContain('Missing await');
|
|
658
|
+
});
|
|
659
|
+
|
|
660
|
+
test('flags direct accessor call through a conditional wrapper', () => {
|
|
661
|
+
const code = `
|
|
662
|
+
function run(cond, fallback) {
|
|
663
|
+
return (cond ? entityShow.blaze({ id: "1" }, ctx) : fallback).isOk();
|
|
664
|
+
}`;
|
|
665
|
+
|
|
666
|
+
const diagnostics = noSyncResultAssumption.check(code, 'src/app.ts');
|
|
667
|
+
|
|
668
|
+
expect(diagnostics).toHaveLength(1);
|
|
669
|
+
expect(diagnostics[0]?.message).toContain('Missing await');
|
|
670
|
+
});
|
|
671
|
+
});
|
|
672
|
+
|
|
673
|
+
describe('logical-expression inits', () => {
|
|
674
|
+
test('flags accessor use when blaze is the right operand of &&', () => {
|
|
675
|
+
const code = `
|
|
676
|
+
function run(cond) {
|
|
677
|
+
const result = cond && entityShow.blaze({ id: "1" }, ctx);
|
|
678
|
+
return result.isOk();
|
|
679
|
+
}`;
|
|
680
|
+
|
|
681
|
+
const diagnostics = noSyncResultAssumption.check(code, 'src/app.ts');
|
|
682
|
+
|
|
683
|
+
expect(diagnostics).toHaveLength(1);
|
|
684
|
+
expect(diagnostics[0]?.message).toContain('Missing await');
|
|
685
|
+
});
|
|
686
|
+
|
|
687
|
+
test('flags accessor use when blaze is the right operand of ??', () => {
|
|
688
|
+
const code = `
|
|
689
|
+
function run(maybe) {
|
|
690
|
+
const result = maybe ?? entityShow.blaze({ id: "1" }, ctx);
|
|
691
|
+
return result.isOk();
|
|
692
|
+
}`;
|
|
693
|
+
|
|
694
|
+
const diagnostics = noSyncResultAssumption.check(code, 'src/app.ts');
|
|
695
|
+
|
|
696
|
+
expect(diagnostics).toHaveLength(1);
|
|
697
|
+
expect(diagnostics[0]?.message).toContain('Missing await');
|
|
698
|
+
});
|
|
699
|
+
|
|
700
|
+
test('flags accessor use when blaze is the right operand of ||', () => {
|
|
701
|
+
const code = `
|
|
702
|
+
function run(fallback) {
|
|
703
|
+
const result = fallback || entityShow.blaze({ id: "1" }, ctx);
|
|
704
|
+
return result.isOk();
|
|
705
|
+
}`;
|
|
706
|
+
|
|
707
|
+
const diagnostics = noSyncResultAssumption.check(code, 'src/app.ts');
|
|
708
|
+
|
|
709
|
+
expect(diagnostics).toHaveLength(1);
|
|
710
|
+
expect(diagnostics[0]?.message).toContain('Missing await');
|
|
711
|
+
});
|
|
712
|
+
|
|
713
|
+
test('flags direct accessor call through a logical wrapper', () => {
|
|
714
|
+
const code = `
|
|
715
|
+
function run(cond) {
|
|
716
|
+
return (cond && entityShow.blaze({ id: "1" }, ctx)).isOk();
|
|
717
|
+
}`;
|
|
718
|
+
|
|
719
|
+
const diagnostics = noSyncResultAssumption.check(code, 'src/app.ts');
|
|
720
|
+
|
|
721
|
+
expect(diagnostics).toHaveLength(1);
|
|
722
|
+
expect(diagnostics[0]?.message).toContain('Missing await');
|
|
723
|
+
});
|
|
724
|
+
|
|
725
|
+
test('does not flag when the logical expression is awaited as a whole', () => {
|
|
726
|
+
const code = `
|
|
727
|
+
async function run(cond) {
|
|
728
|
+
const result = await (cond && entityShow.blaze({ id: "1" }, ctx));
|
|
729
|
+
return result?.isOk();
|
|
730
|
+
}`;
|
|
731
|
+
|
|
732
|
+
const diagnostics = noSyncResultAssumption.check(code, 'src/app.ts');
|
|
733
|
+
|
|
734
|
+
expect(diagnostics).toHaveLength(0);
|
|
735
|
+
});
|
|
736
|
+
});
|
|
737
|
+
|
|
738
|
+
describe('assignment to pre-declared variable', () => {
|
|
739
|
+
test('flags bare identifier assignment followed by result access', () => {
|
|
740
|
+
const code = `
|
|
741
|
+
async function run(ctx) {
|
|
742
|
+
let result;
|
|
743
|
+
result = entityShow.blaze({ id: "1" }, ctx);
|
|
744
|
+
result.isOk();
|
|
745
|
+
}`;
|
|
746
|
+
|
|
747
|
+
const diagnostics = noSyncResultAssumption.check(code, 'src/app.ts');
|
|
748
|
+
|
|
749
|
+
expect(diagnostics).toHaveLength(1);
|
|
750
|
+
expect(diagnostics[0]?.message).toContain('Missing await');
|
|
751
|
+
});
|
|
752
|
+
|
|
753
|
+
test('flags logical-wrapped assignment to pre-declared variable', () => {
|
|
754
|
+
const code = `
|
|
755
|
+
async function run(cond, ctx) {
|
|
756
|
+
let result;
|
|
757
|
+
result = cond && entityShow.blaze({ id: "1" }, ctx);
|
|
758
|
+
result.isOk();
|
|
759
|
+
}`;
|
|
760
|
+
|
|
761
|
+
const diagnostics = noSyncResultAssumption.check(code, 'src/app.ts');
|
|
762
|
+
|
|
763
|
+
expect(diagnostics).toHaveLength(1);
|
|
764
|
+
expect(diagnostics[0]?.message).toContain('Missing await');
|
|
765
|
+
});
|
|
766
|
+
|
|
767
|
+
test('ignores member-expression assignment', () => {
|
|
768
|
+
const code = `
|
|
769
|
+
async function run(obj, ctx) {
|
|
770
|
+
obj.result = entityShow.blaze({ id: "1" }, ctx);
|
|
771
|
+
obj.result.isOk();
|
|
772
|
+
}`;
|
|
773
|
+
|
|
774
|
+
const diagnostics = noSyncResultAssumption.check(code, 'src/app.ts');
|
|
775
|
+
|
|
776
|
+
expect(diagnostics).toHaveLength(0);
|
|
777
|
+
});
|
|
778
|
+
|
|
779
|
+
test('ignores plain assignments unrelated to blaze', () => {
|
|
780
|
+
const code = `
|
|
781
|
+
async function run() {
|
|
782
|
+
let result = 0;
|
|
783
|
+
result = 42;
|
|
784
|
+
return result;
|
|
785
|
+
}`;
|
|
786
|
+
|
|
787
|
+
const diagnostics = noSyncResultAssumption.check(code, 'src/app.ts');
|
|
788
|
+
|
|
789
|
+
expect(diagnostics).toHaveLength(0);
|
|
790
|
+
});
|
|
791
|
+
});
|
|
792
|
+
});
|
|
793
|
+
|
|
794
|
+
describe('no-sync-result-assumption — pending re-assignment', () => {
|
|
795
|
+
describe('pending binding re-assignment', () => {
|
|
796
|
+
test('clears pending after re-assignment to a non-blaze value', () => {
|
|
797
|
+
const code = `
|
|
798
|
+
async function run(ctx) {
|
|
799
|
+
let result = entityShow.blaze({ id: "1" }, ctx);
|
|
800
|
+
result = 42;
|
|
801
|
+
result.isOk();
|
|
802
|
+
}`;
|
|
803
|
+
|
|
804
|
+
const diagnostics = noSyncResultAssumption.check(code, 'src/app.ts');
|
|
805
|
+
|
|
806
|
+
expect(diagnostics).toHaveLength(0);
|
|
807
|
+
});
|
|
808
|
+
|
|
809
|
+
test('keeps pending after re-assignment to another blaze call', () => {
|
|
810
|
+
const code = `
|
|
811
|
+
async function run(ctx) {
|
|
812
|
+
let result = entityShow.blaze({ id: "1" }, ctx);
|
|
813
|
+
result = entityShow.blaze({ id: "2" }, ctx);
|
|
814
|
+
result.isOk();
|
|
815
|
+
}`;
|
|
816
|
+
|
|
817
|
+
const diagnostics = noSyncResultAssumption.check(code, 'src/app.ts');
|
|
818
|
+
|
|
819
|
+
expect(diagnostics).toHaveLength(1);
|
|
820
|
+
expect(diagnostics[0]?.message).toContain('Missing await');
|
|
821
|
+
});
|
|
822
|
+
|
|
823
|
+
test('does not clear pending when a member-expression LHS is written', () => {
|
|
824
|
+
const code = `
|
|
825
|
+
async function run(ctx, obj) {
|
|
826
|
+
let result = entityShow.blaze({ id: "1" }, ctx);
|
|
827
|
+
obj.result = 42;
|
|
828
|
+
result.isOk();
|
|
829
|
+
}`;
|
|
830
|
+
|
|
831
|
+
const diagnostics = noSyncResultAssumption.check(code, 'src/app.ts');
|
|
832
|
+
|
|
833
|
+
expect(diagnostics).toHaveLength(1);
|
|
834
|
+
expect(diagnostics[0]?.message).toContain('Missing await');
|
|
835
|
+
});
|
|
836
|
+
|
|
837
|
+
test('clears pending after a mathematical compound assignment', () => {
|
|
838
|
+
const code = `
|
|
839
|
+
async function run(ctx) {
|
|
840
|
+
let result = entityShow.blaze({ id: "1" }, ctx);
|
|
841
|
+
result += 1;
|
|
842
|
+
result.isOk();
|
|
843
|
+
}`;
|
|
844
|
+
|
|
845
|
+
const diagnostics = noSyncResultAssumption.check(code, 'src/app.ts');
|
|
846
|
+
|
|
847
|
+
expect(diagnostics).toHaveLength(0);
|
|
848
|
+
});
|
|
849
|
+
|
|
850
|
+
test('keeps pending after a logical compound assignment (??=)', () => {
|
|
851
|
+
const code = `
|
|
852
|
+
async function run(ctx, fallback) {
|
|
853
|
+
let result = entityShow.blaze({ id: "1" }, ctx);
|
|
854
|
+
result ??= fallback;
|
|
855
|
+
result.isOk();
|
|
856
|
+
}`;
|
|
857
|
+
|
|
858
|
+
const diagnostics = noSyncResultAssumption.check(code, 'src/app.ts');
|
|
859
|
+
|
|
860
|
+
expect(diagnostics).toHaveLength(1);
|
|
861
|
+
expect(diagnostics[0]?.message).toContain('Missing await');
|
|
862
|
+
});
|
|
863
|
+
|
|
864
|
+
test('keeps pending after a logical compound assignment (||=)', () => {
|
|
865
|
+
// Mirrors the `??=` case: `||=` only writes when the LHS is falsy, and
|
|
866
|
+
// a pending `Promise<Result>` is truthy, so the pending binding must
|
|
867
|
+
// survive.
|
|
868
|
+
const code = `
|
|
869
|
+
async function run(ctx, fallback) {
|
|
870
|
+
let result = entityShow.blaze({ id: "1" }, ctx);
|
|
871
|
+
result ||= fallback;
|
|
872
|
+
result.isOk();
|
|
873
|
+
}`;
|
|
874
|
+
|
|
875
|
+
const diagnostics = noSyncResultAssumption.check(code, 'src/app.ts');
|
|
876
|
+
|
|
877
|
+
expect(diagnostics).toHaveLength(1);
|
|
878
|
+
expect(diagnostics[0]?.message).toContain('Missing await');
|
|
879
|
+
});
|
|
880
|
+
|
|
881
|
+
test('flags self-referential re-assignment that accesses Result members on RHS', () => {
|
|
882
|
+
// Regression for the pre-order-clear bug. `result = result.value` must
|
|
883
|
+
// fire on the RHS `result.value` BEFORE the assignment clears the
|
|
884
|
+
// pending binding — otherwise the missing-await diagnostic would
|
|
885
|
+
// silently disappear even though the RHS reads a Result accessor from
|
|
886
|
+
// the same pending slot.
|
|
887
|
+
const code = `
|
|
888
|
+
async function run(ctx) {
|
|
889
|
+
let result = entityShow.blaze({ id: "1" }, ctx);
|
|
890
|
+
result = result.value;
|
|
891
|
+
}`;
|
|
892
|
+
|
|
893
|
+
const diagnostics = noSyncResultAssumption.check(code, 'src/app.ts');
|
|
894
|
+
|
|
895
|
+
expect(diagnostics).toHaveLength(1);
|
|
896
|
+
expect(diagnostics[0]?.message).toContain('Missing await');
|
|
897
|
+
});
|
|
898
|
+
|
|
899
|
+
test('clears pending after a logical-AND compound assignment (&&=)', () => {
|
|
900
|
+
// `&&=` writes the RHS when the LHS is truthy. A pending
|
|
901
|
+
// Promise<Result> is truthy, so the RHS always runs and the pending
|
|
902
|
+
// slot is overwritten — the subsequent `result.isOk()` observes the
|
|
903
|
+
// fallback, not the blaze result, so no diagnostic should fire.
|
|
904
|
+
const code = `
|
|
905
|
+
async function run(ctx, fallback) {
|
|
906
|
+
let result = entityShow.blaze({ id: "1" }, ctx);
|
|
907
|
+
result &&= fallback;
|
|
908
|
+
result.isOk();
|
|
909
|
+
}`;
|
|
910
|
+
|
|
911
|
+
const diagnostics = noSyncResultAssumption.check(code, 'src/app.ts');
|
|
82
912
|
|
|
83
|
-
|
|
913
|
+
expect(diagnostics).toHaveLength(0);
|
|
914
|
+
});
|
|
84
915
|
});
|
|
85
916
|
});
|