@nielspeter/eess-ts 0.1.0
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/LICENSE +21 -0
- package/README.md +364 -0
- package/dist/builders/call-rule-builder.d.ts +199 -0
- package/dist/builders/call-rule-builder.d.ts.map +1 -0
- package/dist/builders/call-rule-builder.js +254 -0
- package/dist/builders/call-rule-builder.js.map +1 -0
- package/dist/builders/class-rule-builder.d.ts +136 -0
- package/dist/builders/class-rule-builder.d.ts.map +1 -0
- package/dist/builders/class-rule-builder.js +253 -0
- package/dist/builders/class-rule-builder.js.map +1 -0
- package/dist/builders/cross-layer-builder.d.ts +96 -0
- package/dist/builders/cross-layer-builder.d.ts.map +1 -0
- package/dist/builders/cross-layer-builder.js +172 -0
- package/dist/builders/cross-layer-builder.js.map +1 -0
- package/dist/builders/function-rule-builder.d.ts +198 -0
- package/dist/builders/function-rule-builder.d.ts.map +1 -0
- package/dist/builders/function-rule-builder.js +287 -0
- package/dist/builders/function-rule-builder.js.map +1 -0
- package/dist/builders/jsx-rule-builder.d.ts +113 -0
- package/dist/builders/jsx-rule-builder.d.ts.map +1 -0
- package/dist/builders/jsx-rule-builder.js +150 -0
- package/dist/builders/jsx-rule-builder.js.map +1 -0
- package/dist/builders/module-rule-builder.d.ts +159 -0
- package/dist/builders/module-rule-builder.d.ts.map +1 -0
- package/dist/builders/module-rule-builder.js +237 -0
- package/dist/builders/module-rule-builder.js.map +1 -0
- package/dist/builders/scoped-function-rule-builder.d.ts +21 -0
- package/dist/builders/scoped-function-rule-builder.d.ts.map +1 -0
- package/dist/builders/scoped-function-rule-builder.js +26 -0
- package/dist/builders/scoped-function-rule-builder.js.map +1 -0
- package/dist/builders/slice-rule-builder.d.ts +85 -0
- package/dist/builders/slice-rule-builder.d.ts.map +1 -0
- package/dist/builders/slice-rule-builder.js +134 -0
- package/dist/builders/slice-rule-builder.js.map +1 -0
- package/dist/builders/type-rule-builder.d.ts +103 -0
- package/dist/builders/type-rule-builder.d.ts.map +1 -0
- package/dist/builders/type-rule-builder.js +170 -0
- package/dist/builders/type-rule-builder.js.map +1 -0
- package/dist/cli/bin.d.ts +3 -0
- package/dist/cli/bin.d.ts.map +1 -0
- package/dist/cli/bin.js +4 -0
- package/dist/cli/bin.js.map +1 -0
- package/dist/cli/commands/baseline.d.ts +11 -0
- package/dist/cli/commands/baseline.d.ts.map +1 -0
- package/dist/cli/commands/baseline.js +16 -0
- package/dist/cli/commands/baseline.js.map +1 -0
- package/dist/cli/commands/check.d.ts +21 -0
- package/dist/cli/commands/check.d.ts.map +1 -0
- package/dist/cli/commands/check.js +94 -0
- package/dist/cli/commands/check.js.map +1 -0
- package/dist/cli/commands/explain.d.ts +9 -0
- package/dist/cli/commands/explain.d.ts.map +1 -0
- package/dist/cli/commands/explain.js +49 -0
- package/dist/cli/commands/explain.js.map +1 -0
- package/dist/cli/config.d.ts +31 -0
- package/dist/cli/config.d.ts.map +1 -0
- package/dist/cli/config.js +20 -0
- package/dist/cli/config.js.map +1 -0
- package/dist/cli/index.d.ts +21 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +168 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/load-rules.d.ts +20 -0
- package/dist/cli/load-rules.d.ts.map +1 -0
- package/dist/cli/load-rules.js +97 -0
- package/dist/cli/load-rules.js.map +1 -0
- package/dist/cli/resolve-config.d.ts +11 -0
- package/dist/cli/resolve-config.d.ts.map +1 -0
- package/dist/cli/resolve-config.js +61 -0
- package/dist/cli/resolve-config.js.map +1 -0
- package/dist/cli/watch.d.ts +49 -0
- package/dist/cli/watch.d.ts.map +1 -0
- package/dist/cli/watch.js +138 -0
- package/dist/cli/watch.js.map +1 -0
- package/dist/conditions/body-analysis-function.d.ts +23 -0
- package/dist/conditions/body-analysis-function.d.ts.map +1 -0
- package/dist/conditions/body-analysis-function.js +103 -0
- package/dist/conditions/body-analysis-function.js.map +1 -0
- package/dist/conditions/body-analysis-module.d.ts +27 -0
- package/dist/conditions/body-analysis-module.d.ts.map +1 -0
- package/dist/conditions/body-analysis-module.js +99 -0
- package/dist/conditions/body-analysis-module.js.map +1 -0
- package/dist/conditions/body-analysis.d.ts +34 -0
- package/dist/conditions/body-analysis.d.ts.map +1 -0
- package/dist/conditions/body-analysis.js +95 -0
- package/dist/conditions/body-analysis.js.map +1 -0
- package/dist/conditions/call.d.ts +62 -0
- package/dist/conditions/call.d.ts.map +1 -0
- package/dist/conditions/call.js +267 -0
- package/dist/conditions/call.js.map +1 -0
- package/dist/conditions/catch-analysis.d.ts +31 -0
- package/dist/conditions/catch-analysis.d.ts.map +1 -0
- package/dist/conditions/catch-analysis.js +78 -0
- package/dist/conditions/catch-analysis.js.map +1 -0
- package/dist/conditions/class.d.ts +33 -0
- package/dist/conditions/class.d.ts.map +1 -0
- package/dist/conditions/class.js +110 -0
- package/dist/conditions/class.js.map +1 -0
- package/dist/conditions/cross-layer.d.ts +28 -0
- package/dist/conditions/cross-layer.d.ts.map +1 -0
- package/dist/conditions/cross-layer.js +105 -0
- package/dist/conditions/cross-layer.js.map +1 -0
- package/dist/conditions/dependency.d.ts +73 -0
- package/dist/conditions/dependency.d.ts.map +1 -0
- package/dist/conditions/dependency.js +177 -0
- package/dist/conditions/dependency.js.map +1 -0
- package/dist/conditions/exports.d.ts +33 -0
- package/dist/conditions/exports.d.ts.map +1 -0
- package/dist/conditions/exports.js +101 -0
- package/dist/conditions/exports.js.map +1 -0
- package/dist/conditions/function.d.ts +79 -0
- package/dist/conditions/function.d.ts.map +1 -0
- package/dist/conditions/function.js +167 -0
- package/dist/conditions/function.js.map +1 -0
- package/dist/conditions/helpers.d.ts +14 -0
- package/dist/conditions/helpers.d.ts.map +1 -0
- package/dist/conditions/helpers.js +26 -0
- package/dist/conditions/helpers.js.map +1 -0
- package/dist/conditions/jsx.d.ts +40 -0
- package/dist/conditions/jsx.d.ts.map +1 -0
- package/dist/conditions/jsx.js +133 -0
- package/dist/conditions/jsx.js.map +1 -0
- package/dist/conditions/members.d.ts +74 -0
- package/dist/conditions/members.d.ts.map +1 -0
- package/dist/conditions/members.js +224 -0
- package/dist/conditions/members.js.map +1 -0
- package/dist/conditions/pattern.d.ts +20 -0
- package/dist/conditions/pattern.d.ts.map +1 -0
- package/dist/conditions/pattern.js +108 -0
- package/dist/conditions/pattern.js.map +1 -0
- package/dist/conditions/reverse-dependency.d.ts +28 -0
- package/dist/conditions/reverse-dependency.d.ts.map +1 -0
- package/dist/conditions/reverse-dependency.js +275 -0
- package/dist/conditions/reverse-dependency.js.map +1 -0
- package/dist/conditions/slice.d.ts +49 -0
- package/dist/conditions/slice.d.ts.map +1 -0
- package/dist/conditions/slice.js +153 -0
- package/dist/conditions/slice.js.map +1 -0
- package/dist/conditions/structural.d.ts +58 -0
- package/dist/conditions/structural.d.ts.map +1 -0
- package/dist/conditions/structural.js +102 -0
- package/dist/conditions/structural.js.map +1 -0
- package/dist/conditions/type-level.d.ts +29 -0
- package/dist/conditions/type-level.d.ts.map +1 -0
- package/dist/conditions/type-level.js +48 -0
- package/dist/conditions/type-level.js.map +1 -0
- package/dist/core/import-options.d.ts +32 -0
- package/dist/core/import-options.d.ts.map +1 -0
- package/dist/core/import-options.js +29 -0
- package/dist/core/import-options.js.map +1 -0
- package/dist/core/module-dependencies.d.ts +28 -0
- package/dist/core/module-dependencies.d.ts.map +1 -0
- package/dist/core/module-dependencies.js +35 -0
- package/dist/core/module-dependencies.js.map +1 -0
- package/dist/core/pair-condition.d.ts +10 -0
- package/dist/core/pair-condition.d.ts.map +1 -0
- package/dist/core/pair-condition.js +2 -0
- package/dist/core/pair-condition.js.map +1 -0
- package/dist/core/project.d.ts +68 -0
- package/dist/core/project.d.ts.map +1 -0
- package/dist/core/project.js +127 -0
- package/dist/core/project.js.map +1 -0
- package/dist/core/type-matcher.d.ts +10 -0
- package/dist/core/type-matcher.d.ts.map +1 -0
- package/dist/core/type-matcher.js +2 -0
- package/dist/core/type-matcher.js.map +1 -0
- package/dist/core/violation.d.ts +38 -0
- package/dist/core/violation.d.ts.map +1 -0
- package/dist/core/violation.js +130 -0
- package/dist/core/violation.js.map +1 -0
- package/dist/graphql/index.d.ts +76 -0
- package/dist/graphql/index.d.ts.map +1 -0
- package/dist/graphql/index.js +108 -0
- package/dist/graphql/index.js.map +1 -0
- package/dist/graphql/resolver-rule-builder.d.ts +75 -0
- package/dist/graphql/resolver-rule-builder.d.ts.map +1 -0
- package/dist/graphql/resolver-rule-builder.js +146 -0
- package/dist/graphql/resolver-rule-builder.js.map +1 -0
- package/dist/graphql/schema-conditions.d.ts +31 -0
- package/dist/graphql/schema-conditions.d.ts.map +1 -0
- package/dist/graphql/schema-conditions.js +119 -0
- package/dist/graphql/schema-conditions.js.map +1 -0
- package/dist/graphql/schema-loader.d.ts +75 -0
- package/dist/graphql/schema-loader.d.ts.map +1 -0
- package/dist/graphql/schema-loader.js +113 -0
- package/dist/graphql/schema-loader.js.map +1 -0
- package/dist/graphql/schema-predicates.d.ts +42 -0
- package/dist/graphql/schema-predicates.d.ts.map +1 -0
- package/dist/graphql/schema-predicates.js +74 -0
- package/dist/graphql/schema-predicates.js.map +1 -0
- package/dist/graphql/schema-rule-builder.d.ts +74 -0
- package/dist/graphql/schema-rule-builder.d.ts.map +1 -0
- package/dist/graphql/schema-rule-builder.js +188 -0
- package/dist/graphql/schema-rule-builder.js.map +1 -0
- package/dist/helpers/body-traversal.d.ts +58 -0
- package/dist/helpers/body-traversal.d.ts.map +1 -0
- package/dist/helpers/body-traversal.js +180 -0
- package/dist/helpers/body-traversal.js.map +1 -0
- package/dist/helpers/callback-extractor.d.ts +28 -0
- package/dist/helpers/callback-extractor.d.ts.map +1 -0
- package/dist/helpers/callback-extractor.js +159 -0
- package/dist/helpers/callback-extractor.js.map +1 -0
- package/dist/helpers/complexity.d.ts +26 -0
- package/dist/helpers/complexity.d.ts.map +1 -0
- package/dist/helpers/complexity.js +64 -0
- package/dist/helpers/complexity.js.map +1 -0
- package/dist/helpers/matchers.d.ts +206 -0
- package/dist/helpers/matchers.d.ts.map +1 -0
- package/dist/helpers/matchers.js +470 -0
- package/dist/helpers/matchers.js.map +1 -0
- package/dist/helpers/pattern.d.ts +37 -0
- package/dist/helpers/pattern.d.ts.map +1 -0
- package/dist/helpers/pattern.js +22 -0
- package/dist/helpers/pattern.js.map +1 -0
- package/dist/helpers/slice-graph.d.ts +32 -0
- package/dist/helpers/slice-graph.d.ts.map +1 -0
- package/dist/helpers/slice-graph.js +89 -0
- package/dist/helpers/slice-graph.js.map +1 -0
- package/dist/helpers/tarjan.d.ts +21 -0
- package/dist/helpers/tarjan.d.ts.map +1 -0
- package/dist/helpers/tarjan.js +71 -0
- package/dist/helpers/tarjan.js.map +1 -0
- package/dist/helpers/type-matchers.d.ts +67 -0
- package/dist/helpers/type-matchers.d.ts.map +1 -0
- package/dist/helpers/type-matchers.js +108 -0
- package/dist/helpers/type-matchers.js.map +1 -0
- package/dist/helpers/within.d.ts +41 -0
- package/dist/helpers/within.d.ts.map +1 -0
- package/dist/helpers/within.js +32 -0
- package/dist/helpers/within.js.map +1 -0
- package/dist/index.d.ts +101 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +116 -0
- package/dist/index.js.map +1 -0
- package/dist/models/arch-call.d.ts +83 -0
- package/dist/models/arch-call.d.ts.map +1 -0
- package/dist/models/arch-call.js +90 -0
- package/dist/models/arch-call.js.map +1 -0
- package/dist/models/arch-function.d.ts +76 -0
- package/dist/models/arch-function.d.ts.map +1 -0
- package/dist/models/arch-function.js +122 -0
- package/dist/models/arch-function.js.map +1 -0
- package/dist/models/arch-jsx-element.d.ts +75 -0
- package/dist/models/arch-jsx-element.d.ts.map +1 -0
- package/dist/models/arch-jsx-element.js +231 -0
- package/dist/models/arch-jsx-element.js.map +1 -0
- package/dist/models/cross-layer.d.ts +15 -0
- package/dist/models/cross-layer.d.ts.map +1 -0
- package/dist/models/cross-layer.js +2 -0
- package/dist/models/cross-layer.js.map +1 -0
- package/dist/models/slice.d.ts +60 -0
- package/dist/models/slice.d.ts.map +1 -0
- package/dist/models/slice.js +89 -0
- package/dist/models/slice.js.map +1 -0
- package/dist/predicates/call.d.ts +49 -0
- package/dist/predicates/call.d.ts.map +1 -0
- package/dist/predicates/call.js +115 -0
- package/dist/predicates/call.js.map +1 -0
- package/dist/predicates/class.d.ts +42 -0
- package/dist/predicates/class.d.ts.map +1 -0
- package/dist/predicates/class.js +83 -0
- package/dist/predicates/class.js.map +1 -0
- package/dist/predicates/function.d.ts +81 -0
- package/dist/predicates/function.d.ts.map +1 -0
- package/dist/predicates/function.js +157 -0
- package/dist/predicates/function.js.map +1 -0
- package/dist/predicates/identity.d.ts +55 -0
- package/dist/predicates/identity.d.ts.map +1 -0
- package/dist/predicates/identity.js +96 -0
- package/dist/predicates/identity.js.map +1 -0
- package/dist/predicates/index.d.ts +4 -0
- package/dist/predicates/index.d.ts.map +1 -0
- package/dist/predicates/index.js +3 -0
- package/dist/predicates/index.js.map +1 -0
- package/dist/predicates/jsx.d.ts +41 -0
- package/dist/predicates/jsx.d.ts.map +1 -0
- package/dist/predicates/jsx.js +84 -0
- package/dist/predicates/jsx.js.map +1 -0
- package/dist/predicates/metrics.d.ts +30 -0
- package/dist/predicates/metrics.d.ts.map +1 -0
- package/dist/predicates/metrics.js +65 -0
- package/dist/predicates/metrics.js.map +1 -0
- package/dist/predicates/module.d.ts +44 -0
- package/dist/predicates/module.d.ts.map +1 -0
- package/dist/predicates/module.js +70 -0
- package/dist/predicates/module.js.map +1 -0
- package/dist/predicates/type.d.ts +48 -0
- package/dist/predicates/type.d.ts.map +1 -0
- package/dist/predicates/type.js +107 -0
- package/dist/predicates/type.js.map +1 -0
- package/dist/presets/boundaries.d.ts +19 -0
- package/dist/presets/boundaries.d.ts.map +1 -0
- package/dist/presets/boundaries.js +93 -0
- package/dist/presets/boundaries.js.map +1 -0
- package/dist/presets/data-layer.d.ts +20 -0
- package/dist/presets/data-layer.d.ts.map +1 -0
- package/dist/presets/data-layer.js +30 -0
- package/dist/presets/data-layer.js.map +1 -0
- package/dist/presets/index.d.ts +9 -0
- package/dist/presets/index.d.ts.map +1 -0
- package/dist/presets/index.js +5 -0
- package/dist/presets/index.js.map +1 -0
- package/dist/presets/layered.d.ts +21 -0
- package/dist/presets/layered.d.ts.map +1 -0
- package/dist/presets/layered.js +103 -0
- package/dist/presets/layered.js.map +1 -0
- package/dist/presets/shared.d.ts +3 -0
- package/dist/presets/shared.d.ts.map +1 -0
- package/dist/presets/shared.js +5 -0
- package/dist/presets/shared.js.map +1 -0
- package/dist/rules/architecture.d.ts +10 -0
- package/dist/rules/architecture.d.ts.map +1 -0
- package/dist/rules/architecture.js +14 -0
- package/dist/rules/architecture.js.map +1 -0
- package/dist/rules/code-quality.d.ts +55 -0
- package/dist/rules/code-quality.d.ts.map +1 -0
- package/dist/rules/code-quality.js +116 -0
- package/dist/rules/code-quality.js.map +1 -0
- package/dist/rules/dependencies.d.ts +32 -0
- package/dist/rules/dependencies.d.ts.map +1 -0
- package/dist/rules/dependencies.js +37 -0
- package/dist/rules/dependencies.js.map +1 -0
- package/dist/rules/errors.d.ts +31 -0
- package/dist/rules/errors.d.ts.map +1 -0
- package/dist/rules/errors.js +97 -0
- package/dist/rules/errors.js.map +1 -0
- package/dist/rules/hygiene.d.ts +54 -0
- package/dist/rules/hygiene.d.ts.map +1 -0
- package/dist/rules/hygiene.js +62 -0
- package/dist/rules/hygiene.js.map +1 -0
- package/dist/rules/metrics-function.d.ts +45 -0
- package/dist/rules/metrics-function.d.ts.map +1 -0
- package/dist/rules/metrics-function.js +87 -0
- package/dist/rules/metrics-function.js.map +1 -0
- package/dist/rules/metrics.d.ts +64 -0
- package/dist/rules/metrics.d.ts.map +1 -0
- package/dist/rules/metrics.js +157 -0
- package/dist/rules/metrics.js.map +1 -0
- package/dist/rules/naming.d.ts +24 -0
- package/dist/rules/naming.d.ts.map +1 -0
- package/dist/rules/naming.js +51 -0
- package/dist/rules/naming.js.map +1 -0
- package/dist/rules/security.d.ts +59 -0
- package/dist/rules/security.d.ts.map +1 -0
- package/dist/rules/security.js +92 -0
- package/dist/rules/security.js.map +1 -0
- package/dist/rules/typescript.d.ts +73 -0
- package/dist/rules/typescript.d.ts.map +1 -0
- package/dist/rules/typescript.js +105 -0
- package/dist/rules/typescript.js.map +1 -0
- package/dist/smells/duplicate-bodies.d.ts +24 -0
- package/dist/smells/duplicate-bodies.d.ts.map +1 -0
- package/dist/smells/duplicate-bodies.js +131 -0
- package/dist/smells/duplicate-bodies.js.map +1 -0
- package/dist/smells/fingerprint.d.ts +27 -0
- package/dist/smells/fingerprint.d.ts.map +1 -0
- package/dist/smells/fingerprint.js +50 -0
- package/dist/smells/fingerprint.js.map +1 -0
- package/dist/smells/inconsistent-siblings.d.ts +21 -0
- package/dist/smells/inconsistent-siblings.d.ts.map +1 -0
- package/dist/smells/inconsistent-siblings.js +135 -0
- package/dist/smells/inconsistent-siblings.js.map +1 -0
- package/dist/smells/index.d.ts +12 -0
- package/dist/smells/index.d.ts.map +1 -0
- package/dist/smells/index.js +15 -0
- package/dist/smells/index.js.map +1 -0
- package/dist/smells/smell-builder.d.ts +37 -0
- package/dist/smells/smell-builder.d.ts.map +1 -0
- package/dist/smells/smell-builder.js +51 -0
- package/dist/smells/smell-builder.js.map +1 -0
- package/package.json +121 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Niels Peter
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,364 @@
|
|
|
1
|
+
# @nielspeter/eess-ts
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/@nielspeter/eess-ts)
|
|
4
|
+
[](https://github.com/nielspeter/ts-archunit/actions/workflows/ci.yml)
|
|
5
|
+
[](https://opensource.org/licenses/MIT)
|
|
6
|
+
[](https://nodejs.org/)
|
|
7
|
+
|
|
8
|
+
**Architecture guardrails for AI coding agents.** Executable rules that catch structural violations in CI — before they reach your codebase.
|
|
9
|
+
|
|
10
|
+
Inspired by Java's [ArchUnit](https://www.archunit.org/). Powered by [ts-morph](https://ts-morph.com/).
|
|
11
|
+
|
|
12
|
+
> **The TypeScript dialect of the [eess](../../README.md) family.** Formerly published as `@nielspeter/ts-archunit` — that name still works as an alias, so existing installs are unaffected. New projects should prefer `@nielspeter/eess-ts`.
|
|
13
|
+
|
|
14
|
+
[Documentation](https://nielspeter.github.io/ts-archunit/) · [Getting Started](https://nielspeter.github.io/ts-archunit/getting-started) · [What Can It Check?](https://nielspeter.github.io/ts-archunit/what-to-check)
|
|
15
|
+
|
|
16
|
+
## The Problem
|
|
17
|
+
|
|
18
|
+
AI coding agents don't know your architecture. They generate code that compiles, passes type checks, and looks correct in isolation — but violates the structural decisions your team spent months establishing.
|
|
19
|
+
|
|
20
|
+
An agent will:
|
|
21
|
+
|
|
22
|
+
- Call `parseInt` instead of the shared `extractCount()` helper
|
|
23
|
+
- Throw `new Error()` instead of your typed `NotFoundError`
|
|
24
|
+
- Import the database driver directly from a service instead of going through the repository
|
|
25
|
+
- Copy-paste a parser function instead of using the shared utility
|
|
26
|
+
- Skip validation in a route handler
|
|
27
|
+
|
|
28
|
+
Code review catches some of this. But at scale — with multiple agents generating PRs across a large codebase — review becomes the bottleneck. You need automated enforcement.
|
|
29
|
+
|
|
30
|
+
## The Solution
|
|
31
|
+
|
|
32
|
+
ts-archunit turns your architecture decisions into executable tests. They run in CI. Violations show up inline on the PR with clear messages explaining **what's wrong, why it matters, and how to fix it** — exactly the context an agent needs to self-correct.
|
|
33
|
+
|
|
34
|
+
```typescript
|
|
35
|
+
classes(p)
|
|
36
|
+
.that()
|
|
37
|
+
.extend('BaseRepository')
|
|
38
|
+
.should()
|
|
39
|
+
.notContain(newExpr('Error'))
|
|
40
|
+
.rule({
|
|
41
|
+
id: 'repo/typed-errors',
|
|
42
|
+
because: 'Generic Error loses context and prevents consistent error handling',
|
|
43
|
+
suggestion: 'Use NotFoundError, ValidationError, or DomainError instead',
|
|
44
|
+
})
|
|
45
|
+
.check()
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
When an agent violates this rule, it sees:
|
|
49
|
+
|
|
50
|
+
```
|
|
51
|
+
Architecture Violation [repo/typed-errors]
|
|
52
|
+
|
|
53
|
+
WebhookRepository.findById contains new 'Error' at line 42
|
|
54
|
+
at src/repositories/webhook.repository.ts:42
|
|
55
|
+
|
|
56
|
+
41 | if (!result) {
|
|
57
|
+
> 42 | throw new Error(`Webhook '${id}' not found`)
|
|
58
|
+
43 | }
|
|
59
|
+
|
|
60
|
+
Why: Generic Error loses context and prevents consistent error handling
|
|
61
|
+
Fix: Use NotFoundError, ValidationError, or DomainError instead
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
The `because` and `suggestion` fields give the agent everything it needs to fix the violation without human intervention.
|
|
65
|
+
|
|
66
|
+
## Why Not Just Import Rules?
|
|
67
|
+
|
|
68
|
+
Every other tool (dependency-cruiser, eslint-plugin-boundaries, ts-arch) only checks which files import which. That's necessary but insufficient.
|
|
69
|
+
|
|
70
|
+
AI agents don't violate architecture by importing wrong files. They violate it by **writing the wrong code in the right place** — inlining logic instead of delegating, using raw APIs instead of abstractions, skipping validation, throwing generic errors.
|
|
71
|
+
|
|
72
|
+
ts-archunit checks what happens **inside** your functions:
|
|
73
|
+
|
|
74
|
+
```typescript
|
|
75
|
+
// "Services must delegate to repositories, not hardcode data"
|
|
76
|
+
functions(p)
|
|
77
|
+
.that()
|
|
78
|
+
.resideInFolder('**/services/**')
|
|
79
|
+
.should()
|
|
80
|
+
.satisfy(mustCall(/Repository/))
|
|
81
|
+
.check()
|
|
82
|
+
|
|
83
|
+
// "No eval anywhere in production code"
|
|
84
|
+
modules(p).that().resideInFolder('src/**').should().satisfy(moduleNoEval()).check()
|
|
85
|
+
|
|
86
|
+
// "Route handlers must validate input"
|
|
87
|
+
functions(p)
|
|
88
|
+
.that()
|
|
89
|
+
.resideInFolder('**/handlers/**')
|
|
90
|
+
.should()
|
|
91
|
+
.satisfy(mustCall(/validate|parse/))
|
|
92
|
+
.check()
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
| Capability | ts-archunit | [dependency-cruiser](https://github.com/sverweij/dependency-cruiser) | [eslint-plugin-boundaries](https://github.com/javierbrea/eslint-plugin-boundaries) | [Biome](https://biomejs.dev) | [ts-arch](https://github.com/ts-arch/ts-arch) |
|
|
96
|
+
| -------------------------------------------------- | ----------- | -------------------------------------------------------------------- | ---------------------------------------------------------------------------------- | ---------------------------- | --------------------------------------------- |
|
|
97
|
+
| Import path rules | Yes | Yes | Yes | Yes | Yes |
|
|
98
|
+
| **Body analysis** (what's called inside functions) | Yes | No | No | No | No |
|
|
99
|
+
| **Type checking** (string vs typed union) | Yes | No | No | No | No |
|
|
100
|
+
| JSX element rules | Yes | No | No | No | No |
|
|
101
|
+
| Cross-layer rules | Yes | No | No | No | Partial |
|
|
102
|
+
| Cycle detection | Yes | Yes | No | No | Yes |
|
|
103
|
+
| Code smell detection (duplicate bodies) | Yes | No | No | No | No |
|
|
104
|
+
| Code metrics (cyclomatic, LOC) | Yes | No | No | No | No |
|
|
105
|
+
| Pattern matching (reusable rule templates) | Yes | No | No | No | No |
|
|
106
|
+
| Baseline (gradual adoption) | Yes | No | No | No | No |
|
|
107
|
+
| Diff-aware / PR-only checking | Yes | No | No | No | No |
|
|
108
|
+
| Watch mode | Yes | No | No | Yes | No |
|
|
109
|
+
| Inline exclusion comments | Yes | No | Via ESLint | Yes | No |
|
|
110
|
+
| GitHub PR annotations | Yes | No | Via ESLint | No | No |
|
|
111
|
+
|
|
112
|
+
## Quick Start with Presets
|
|
113
|
+
|
|
114
|
+
One function call enforces an entire architecture pattern — layer ordering, cycles, import direction, package restrictions:
|
|
115
|
+
|
|
116
|
+
```typescript
|
|
117
|
+
import { project } from '@nielspeter/eess-ts'
|
|
118
|
+
import { layeredArchitecture } from '@nielspeter/eess-ts/presets'
|
|
119
|
+
|
|
120
|
+
const p = project('tsconfig.json')
|
|
121
|
+
|
|
122
|
+
layeredArchitecture(p, {
|
|
123
|
+
layers: {
|
|
124
|
+
routes: 'src/routes/**',
|
|
125
|
+
services: 'src/services/**',
|
|
126
|
+
repositories: 'src/repositories/**',
|
|
127
|
+
},
|
|
128
|
+
shared: ['src/shared/**'],
|
|
129
|
+
strict: true,
|
|
130
|
+
})
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
This generates 5 coordinated rules. Override individual rules without disabling the preset:
|
|
134
|
+
|
|
135
|
+
```typescript
|
|
136
|
+
layeredArchitecture(p, {
|
|
137
|
+
layers: { ... },
|
|
138
|
+
overrides: {
|
|
139
|
+
'preset/layered/type-imports-only': 'off',
|
|
140
|
+
},
|
|
141
|
+
})
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
Three presets available: [`layeredArchitecture`](https://nielspeter.github.io/ts-archunit/presets), [`dataLayerIsolation`](https://nielspeter.github.io/ts-archunit/presets#datalayerisolation), [`strictBoundaries`](https://nielspeter.github.io/ts-archunit/presets#strictboundaries).
|
|
145
|
+
|
|
146
|
+
## Feed Your Architecture to the Agent
|
|
147
|
+
|
|
148
|
+
The `explain` command dumps all active rules as structured JSON — pipe it into your agent's system prompt so it knows the constraints before writing code:
|
|
149
|
+
|
|
150
|
+
```bash
|
|
151
|
+
npx ts-archunit explain arch.rules.ts
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
```json
|
|
155
|
+
{
|
|
156
|
+
"rules": [
|
|
157
|
+
{
|
|
158
|
+
"id": "repo/typed-errors",
|
|
159
|
+
"rule": "that extend 'BaseRepository' should not contain new 'Error'",
|
|
160
|
+
"because": "Generic Error loses context and prevents consistent error handling",
|
|
161
|
+
"suggestion": "Use NotFoundError, ValidationError, or DomainError instead"
|
|
162
|
+
}
|
|
163
|
+
]
|
|
164
|
+
}
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
The agent reads the rules, understands the constraints, and generates compliant code from the start. When it doesn't, CI catches it with actionable violation messages.
|
|
168
|
+
|
|
169
|
+
## Custom Rules
|
|
170
|
+
|
|
171
|
+
The fluent API reads like English:
|
|
172
|
+
|
|
173
|
+
```typescript
|
|
174
|
+
// Select → Filter → Assert → Execute
|
|
175
|
+
classes(p).that().extend('BaseRepository').should().notContain(call('parseInt')).check()
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
### Body Analysis
|
|
179
|
+
|
|
180
|
+
Inspect what happens inside functions — the differentiator:
|
|
181
|
+
|
|
182
|
+
```typescript
|
|
183
|
+
// Ban inline parseInt — use the shared helper
|
|
184
|
+
classes(p)
|
|
185
|
+
.that()
|
|
186
|
+
.extend('BaseRepository')
|
|
187
|
+
.should()
|
|
188
|
+
.useInsteadOf(call('parseInt'), call('this.extractCount'))
|
|
189
|
+
.check()
|
|
190
|
+
|
|
191
|
+
// Services must delegate to repositories
|
|
192
|
+
functions(p)
|
|
193
|
+
.that()
|
|
194
|
+
.resideInFolder('**/services/**')
|
|
195
|
+
.should()
|
|
196
|
+
.satisfy(mustCall(/Repository/))
|
|
197
|
+
.check()
|
|
198
|
+
|
|
199
|
+
// No process.env in domain — use dependency injection
|
|
200
|
+
functions(p).that().resideInFolder('**/domain/**').should().satisfy(functionNoProcessEnv()).check()
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
### Layer Enforcement
|
|
204
|
+
|
|
205
|
+
```typescript
|
|
206
|
+
slices(p)
|
|
207
|
+
.assignedFrom({
|
|
208
|
+
controllers: 'src/controllers/**',
|
|
209
|
+
services: 'src/services/**',
|
|
210
|
+
repositories: 'src/repositories/**',
|
|
211
|
+
})
|
|
212
|
+
.should()
|
|
213
|
+
.respectLayerOrder('controllers', 'services', 'repositories')
|
|
214
|
+
.check()
|
|
215
|
+
|
|
216
|
+
slices(p).matching('src/features/*/').should().beFreeOfCycles().check()
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
### Type-Level Rules
|
|
220
|
+
|
|
221
|
+
Check property types using the TypeScript type checker:
|
|
222
|
+
|
|
223
|
+
```typescript
|
|
224
|
+
types(p)
|
|
225
|
+
.that()
|
|
226
|
+
.haveProperty('orderBy')
|
|
227
|
+
.should()
|
|
228
|
+
.havePropertyType('orderBy', not(isString()))
|
|
229
|
+
.rule({
|
|
230
|
+
because: 'Bare string orderBy is a SQL injection surface',
|
|
231
|
+
suggestion: "Use a union type: orderBy?: 'created_at' | 'updated_at'",
|
|
232
|
+
})
|
|
233
|
+
.check()
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
### Standard Rules Library
|
|
237
|
+
|
|
238
|
+
25+ ready-to-use rules across 8 categories:
|
|
239
|
+
|
|
240
|
+
```typescript
|
|
241
|
+
import {
|
|
242
|
+
functionNoEval,
|
|
243
|
+
functionNoConsole,
|
|
244
|
+
functionNoJsonParse,
|
|
245
|
+
} from '@nielspeter/eess-ts/rules/security'
|
|
246
|
+
import { functionNoGenericErrors } from '@nielspeter/eess-ts/rules/errors'
|
|
247
|
+
import { mustCall } from '@nielspeter/eess-ts/rules/architecture'
|
|
248
|
+
import { noDeadModules, noStubComments, noEmptyBodies } from '@nielspeter/eess-ts/rules/hygiene'
|
|
249
|
+
|
|
250
|
+
functions(p).that().resideInFolder('src/**').should().satisfy(functionNoEval()).check()
|
|
251
|
+
functions(p).that().resideInFolder('src/**').should().satisfy(noEmptyBodies()).check()
|
|
252
|
+
functions(p).that().resideInFolder('src/**').should().satisfy(noStubComments()).check()
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
Categories: `rules/typescript`, `rules/security`, `rules/errors`, `rules/naming`, `rules/dependencies`, `rules/code-quality`, `rules/metrics`, `rules/architecture`, `rules/hygiene`.
|
|
256
|
+
|
|
257
|
+
### Baseline Mode
|
|
258
|
+
|
|
259
|
+
Adopt rules in existing codebases without fixing every pre-existing violation:
|
|
260
|
+
|
|
261
|
+
```typescript
|
|
262
|
+
const baseline = withBaseline('arch-baseline.json')
|
|
263
|
+
|
|
264
|
+
// Only NEW violations fail — existing ones are recorded
|
|
265
|
+
classes(p).should().notContain(call('parseInt')).check({ baseline })
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
### GitHub Actions Annotations
|
|
269
|
+
|
|
270
|
+
Violations appear inline on PR diffs — automatically detected in GitHub Actions:
|
|
271
|
+
|
|
272
|
+
```typescript
|
|
273
|
+
classes(p).should().notContain(call('eval')).check({ format: detectFormat() })
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
### Smell Detection
|
|
277
|
+
|
|
278
|
+
Find code drift — duplicate function bodies and inconsistent patterns:
|
|
279
|
+
|
|
280
|
+
```typescript
|
|
281
|
+
smells.duplicateBodies(p).inFolder('src/routes/**').withMinSimilarity(0.9).warn()
|
|
282
|
+
|
|
283
|
+
smells
|
|
284
|
+
.inconsistentSiblings(p)
|
|
285
|
+
.inFolder('src/repositories/**')
|
|
286
|
+
.forPattern(call('this.extractCount'))
|
|
287
|
+
.warn()
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
### More Features
|
|
291
|
+
|
|
292
|
+
- **[Call matching](https://nielspeter.github.io/ts-archunit/calls)** — framework-agnostic route/handler inspection (Express, Fastify, Hono)
|
|
293
|
+
- **[Scoped rules](https://nielspeter.github.io/ts-archunit/body-analysis)** — `within(routes).functions()` for callback-level rules
|
|
294
|
+
- **[Pattern templates](https://nielspeter.github.io/ts-archunit/patterns)** — enforce return type shapes (`{ items, total, skip, limit }`)
|
|
295
|
+
- **[GraphQL rules](https://nielspeter.github.io/ts-archunit/graphql)** — schema and resolver conventions
|
|
296
|
+
- **[Cross-layer validation](https://nielspeter.github.io/ts-archunit/cross-layer)** — route/schema/SDK consistency
|
|
297
|
+
- **[Custom predicates and conditions](https://nielspeter.github.io/ts-archunit/custom-rules)** — `definePredicate()`, `defineCondition()`, `and`/`or`/`not` combinators
|
|
298
|
+
- **[Metrics](https://nielspeter.github.io/ts-archunit/metrics)** — cyclomatic complexity, lines of code, method count limits
|
|
299
|
+
- **[CLI](https://nielspeter.github.io/ts-archunit/cli)** — `check`, `baseline`, `explain`, `--watch` mode
|
|
300
|
+
|
|
301
|
+
## Entry Points
|
|
302
|
+
|
|
303
|
+
| Function | Operates on | Use case |
|
|
304
|
+
| -------------- | ----------------------------------------- | ----------------------------------------------- |
|
|
305
|
+
| `modules(p)` | Source files | Import/dependency rules |
|
|
306
|
+
| `classes(p)` | Class declarations | Inheritance, decorators, methods, body analysis |
|
|
307
|
+
| `functions(p)` | Functions, arrow functions, class methods | Naming, parameters, body analysis |
|
|
308
|
+
| `types(p)` | Interfaces + type aliases | Property types, type safety |
|
|
309
|
+
| `slices(p)` | Groups of files | Cycles, layer ordering |
|
|
310
|
+
| `calls(p)` | Call expressions | Framework-agnostic route/handler matching |
|
|
311
|
+
| `within(sel)` | Scoped callbacks | Rules inside matched call callbacks |
|
|
312
|
+
|
|
313
|
+
## Compared to Other Tools
|
|
314
|
+
|
|
315
|
+
| Capability | ts-archunit | [dependency-cruiser](https://github.com/sverweij/dependency-cruiser) | [ArchUnitTS](https://github.com/LukasNiessen/ArchUnitTS) | [Biome](https://biomejs.dev) | [ts-arch](https://github.com/ts-arch/ts-arch) |
|
|
316
|
+
| ------------------------------------------------- | --------------- | -------------------------------------------------------------------- | -------------------------------------------------------- | ---------------------------- | --------------------------------------------- |
|
|
317
|
+
| Import path rules | Yes | Yes | Yes | Yes (`noRestrictedImports`) | Yes |
|
|
318
|
+
| **Body analysis** (calls, access, constructors) | Yes | No | No | No | No |
|
|
319
|
+
| **Type checking** (resolved types via ts-morph) | Yes | No | No | No | No |
|
|
320
|
+
| Class rules (inheritance, decorators, members) | Yes | No | No | No | No |
|
|
321
|
+
| Function rules (params, return types, async) | Yes | No | No | No | No |
|
|
322
|
+
| JSX element rules | Yes | No | No | No | No |
|
|
323
|
+
| Cross-layer rules (pair-condition per layer) | Yes | No | No | No | Partial (layered slices) |
|
|
324
|
+
| Cycle detection | Yes | Yes | Yes | No | Yes |
|
|
325
|
+
| Parameterized presets | Yes | Flat config | No | No | No |
|
|
326
|
+
| Pattern matching (reusable rule templates) | Yes | No | No | No | No |
|
|
327
|
+
| Code smell detection (duplicate bodies, siblings) | Yes | No | No | No | No |
|
|
328
|
+
| Code metrics | Cyclomatic, LOC | No | LCOM, coupling, instability | No | No |
|
|
329
|
+
| Baseline / gradual adoption | Yes | No | No | No | No |
|
|
330
|
+
| Diff-aware / PR-only checking | Yes | No | No | No | No |
|
|
331
|
+
| Watch mode | Yes | No | No | Yes | No |
|
|
332
|
+
| Inline exclusion comments | Yes | No | No | Yes (`biome-ignore`) | No |
|
|
333
|
+
| JSON output format | Yes | Yes | Via vitest | Yes | Via test runner |
|
|
334
|
+
| GitHub PR annotations | Yes | No | No | No | No |
|
|
335
|
+
| Violation messages with fix suggestions | Yes | No | No | Partial (autofixes) | No |
|
|
336
|
+
| `explain` command (dump rules as JSON for agents) | Yes | No | No | No | No |
|
|
337
|
+
| PlantUML diagram compliance | No | No | Yes | No | No |
|
|
338
|
+
| Dependency graph visualization | No | Yes (dot, HTML) | No | No | No |
|
|
339
|
+
| License checking | No | Yes | No | No | No |
|
|
340
|
+
| Nx monorepo support | No | No | Yes | No | No |
|
|
341
|
+
|
|
342
|
+
**Use ts-archunit** when you need to enforce what happens _inside_ functions — call patterns, error types, missing delegation, stub comments — and when AI agents are generating code that needs architectural guardrails. This is the only tool that catches "service calls parseInt instead of extractCount()", flags duplicate function bodies as smells, and can restrict checking to only the files changed in a PR.
|
|
343
|
+
|
|
344
|
+
**Use dependency-cruiser** when you only need import direction rules and want fast graph visualization, license compliance checking, or stability metrics. It's faster (no ts-morph project load) and has mature HTML/dot reporting.
|
|
345
|
+
|
|
346
|
+
**Use ArchUnitTS** when you need OO metrics (LCOM cohesion, coupling factor, distance from main sequence), PlantUML diagram validation, or Nx monorepo project-graph awareness.
|
|
347
|
+
|
|
348
|
+
**Use Biome** when you want a fast Rust-based lint + format toolchain and only need lint-level import restrictions (`noRestrictedImports`) — not architecture rules over class hierarchies, function bodies, or slice cycles. Pair it with ts-archunit if you want both.
|
|
349
|
+
|
|
350
|
+
**Use ts-arch** when you want the classic ArchUnit-style fluent API scoped to file/folder dependency rules and layered slices, and don't need body analysis, type-checker rules, or CI integrations like baselines, diff-aware runs, or GitHub annotations.
|
|
351
|
+
|
|
352
|
+
**Use ts-archunit + dependency-cruiser together** if you want both body-level enforcement and dependency graph visualization.
|
|
353
|
+
|
|
354
|
+
## Install
|
|
355
|
+
|
|
356
|
+
```bash
|
|
357
|
+
npm install -D @nielspeter/eess-ts
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
Requires Node.js >= 24 and a `tsconfig.json`. Works with vitest (recommended) or jest.
|
|
361
|
+
|
|
362
|
+
## License
|
|
363
|
+
|
|
364
|
+
MIT
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
import type { ArchProject } from '../core/project.js';
|
|
2
|
+
import { RuleBuilder } from '@nielspeter/eess';
|
|
3
|
+
import type { ConditionContext } from '@nielspeter/eess';
|
|
4
|
+
import type { ExpressionMatcher } from '../helpers/matchers.js';
|
|
5
|
+
import type { ArchCall } from '../models/arch-call.js';
|
|
6
|
+
/**
|
|
7
|
+
* Rule builder for call-expression-level architecture rules.
|
|
8
|
+
*
|
|
9
|
+
* Operates on CallExpression nodes across all source files,
|
|
10
|
+
* wrapped in the ArchCall model for uniform predicate access.
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ```typescript
|
|
14
|
+
* // All Express route handlers must call handleError()
|
|
15
|
+
* calls(project)
|
|
16
|
+
* .that().onObject('app')
|
|
17
|
+
* .and().withMethod(/^(get|post|put|delete|patch)$/)
|
|
18
|
+
* .should().haveCallbackContaining(call('handleError'))
|
|
19
|
+
* .because('unhandled errors crash the server')
|
|
20
|
+
* .check()
|
|
21
|
+
*
|
|
22
|
+
* // No route may call db.query() directly
|
|
23
|
+
* calls(project)
|
|
24
|
+
* .that().onObject('app')
|
|
25
|
+
* .and().withMethod(/^(get|post|put|delete|patch)$/)
|
|
26
|
+
* .should().notHaveCallbackContaining(call('db.query'))
|
|
27
|
+
* .because('use repository methods instead')
|
|
28
|
+
* .check()
|
|
29
|
+
*
|
|
30
|
+
* // Select specific routes by path pattern
|
|
31
|
+
* calls(project)
|
|
32
|
+
* .that().onObject('router')
|
|
33
|
+
* .and().withMethod('get')
|
|
34
|
+
* .and().withStringArg(0, '/api/users/**')
|
|
35
|
+
* .should().haveCallbackContaining(call('authenticate'))
|
|
36
|
+
* .check()
|
|
37
|
+
* ```
|
|
38
|
+
*/
|
|
39
|
+
export declare class CallRuleBuilder extends RuleBuilder<ArchCall, ArchProject> {
|
|
40
|
+
/**
|
|
41
|
+
* Argument index to fold into the violation element/message. Set by
|
|
42
|
+
* `.identifiedByArg(index)`. Threaded into `ConditionContext.identifyByArgument`
|
|
43
|
+
* so the eight `archCall.getName()` sites in `src/conditions/call.ts`
|
|
44
|
+
* can build identity-keyed violation strings.
|
|
45
|
+
*
|
|
46
|
+
* Survives `fork()` via `Object.assign(fork, this)` in the base class
|
|
47
|
+
* (primitive field; no explicit copy needed). See plan 0057.
|
|
48
|
+
*/
|
|
49
|
+
protected _identifyByArgument?: number;
|
|
50
|
+
protected getElements(): ArchCall[];
|
|
51
|
+
protected buildConditionContext(): ConditionContext;
|
|
52
|
+
/**
|
|
53
|
+
* Filter to calls whose callee name matches the pattern.
|
|
54
|
+
* A string is compiled to a RegExp. For `app.get(...)` the name is `app.get`.
|
|
55
|
+
*/
|
|
56
|
+
haveNameMatching(pattern: RegExp | string): this;
|
|
57
|
+
/** Filter to calls whose callee name starts with the given prefix. */
|
|
58
|
+
haveNameStartingWith(prefix: string): this;
|
|
59
|
+
/** Filter to calls whose callee name ends with the given suffix. */
|
|
60
|
+
haveNameEndingWith(suffix: string): this;
|
|
61
|
+
/** Filter to calls in a file whose absolute path matches the glob. */
|
|
62
|
+
resideInFile(glob: string): this;
|
|
63
|
+
/** Filter to calls in a folder whose path matches the glob. */
|
|
64
|
+
resideInFolder(glob: string): this;
|
|
65
|
+
/**
|
|
66
|
+
* Filter to method calls on the object with the exact given name.
|
|
67
|
+
* For `app.get(...)` use `onObject('app')`; for `router.route.get(...)`
|
|
68
|
+
* use `onObject('router.route')`. Optional chaining is normalized.
|
|
69
|
+
*/
|
|
70
|
+
onObject(name: string): this;
|
|
71
|
+
/**
|
|
72
|
+
* Filter to calls whose method name matches. Accepts an exact string
|
|
73
|
+
* or a regex; also matches bare function calls (`handleError(...)`).
|
|
74
|
+
*/
|
|
75
|
+
withMethod(nameOrRegex: string | RegExp): this;
|
|
76
|
+
/**
|
|
77
|
+
* Filter to calls whose argument at `index` matches the pattern, tested
|
|
78
|
+
* against the argument's source text (so it matches identifiers and
|
|
79
|
+
* expressions, not just string literals).
|
|
80
|
+
*/
|
|
81
|
+
withArgMatching(index: number, pattern: string | RegExp): this;
|
|
82
|
+
/**
|
|
83
|
+
* Filter to calls whose argument at `index` is a string literal whose
|
|
84
|
+
* value matches the glob. Non-literal arguments (variables, expressions)
|
|
85
|
+
* never match.
|
|
86
|
+
*/
|
|
87
|
+
withStringArg(index: number, glob: string): this;
|
|
88
|
+
/**
|
|
89
|
+
* Fold the indexed argument's source text into the violation
|
|
90
|
+
* `element` and `message`, so identity-keyed registrations (HTTP
|
|
91
|
+
* routes, event handlers, registry entries, etc.) can be excluded
|
|
92
|
+
* individually rather than only by file.
|
|
93
|
+
*
|
|
94
|
+
* When the indexed argument is a `StringLiteral` or
|
|
95
|
+
* `NoSubstitutionTemplateLiteral`, the violation reads
|
|
96
|
+
* `` `${callee}(${rawText})` `` (e.g. `app.post("/auth/token")`).
|
|
97
|
+
* Otherwise (template with substitutions, `as const`, parenthesized,
|
|
98
|
+
* identifier, spread, out-of-bounds, non-string literal) it degrades
|
|
99
|
+
* gracefully to the bare `${callee}` form.
|
|
100
|
+
*
|
|
101
|
+
* **Identity scope — predicates see the bare callee.** This method
|
|
102
|
+
* affects violation output and `.excluding()` matching only;
|
|
103
|
+
* predicates that read `archCall.getName()` (e.g. `haveNameMatching`,
|
|
104
|
+
* `haveNameStartingWith`) continue to see the bare `app.post`. To
|
|
105
|
+
* filter by argument value, use `.withStringArg(i, glob)` or
|
|
106
|
+
* `.withArgMatching(i, pattern)`.
|
|
107
|
+
*
|
|
108
|
+
* @example Filtering by path AND naming each violation by path:
|
|
109
|
+
* ```ts
|
|
110
|
+
* calls(p).that()
|
|
111
|
+
* .onObject('app').withMethod(/^(get|post)$/)
|
|
112
|
+
* .withStringArg(0, '/auth/**') // filter: only /auth routes
|
|
113
|
+
* .identifiedByArg(0) // identity: name violations by path
|
|
114
|
+
* .should().haveArgumentWithProperty('preHandler')
|
|
115
|
+
* .excluding(/"\/auth\/(login|register)"/)
|
|
116
|
+
* .check()
|
|
117
|
+
* ```
|
|
118
|
+
*
|
|
119
|
+
* @example Footgun — predicates do NOT see the enriched name:
|
|
120
|
+
* ```ts
|
|
121
|
+
* // This produces ZERO violations regardless of source — the
|
|
122
|
+
* // predicate sees bare "app.post", which never matches the regex.
|
|
123
|
+
* calls(p).that()
|
|
124
|
+
* .haveNameMatching(/app\.post\("\/auth/) // <-- never matches
|
|
125
|
+
* .identifiedByArg(0)
|
|
126
|
+
* .should()...
|
|
127
|
+
* ```
|
|
128
|
+
*
|
|
129
|
+
* See proposal 011 / plan 0057 for the full design, the 8-case
|
|
130
|
+
* generic-pattern table, and the edge-case behavior matrix.
|
|
131
|
+
*
|
|
132
|
+
* @param index — zero-based argument index to fold into the identity.
|
|
133
|
+
*/
|
|
134
|
+
identifiedByArg(index: number): this;
|
|
135
|
+
/**
|
|
136
|
+
* Assert that the call's callback argument(s) contain a match.
|
|
137
|
+
*/
|
|
138
|
+
haveCallbackContaining(matcher: ExpressionMatcher): this;
|
|
139
|
+
/**
|
|
140
|
+
* Assert that the call's callback argument(s) do NOT contain a match.
|
|
141
|
+
*/
|
|
142
|
+
notHaveCallbackContaining(matcher: ExpressionMatcher): this;
|
|
143
|
+
/**
|
|
144
|
+
* The filtered call set must be empty.
|
|
145
|
+
*/
|
|
146
|
+
notExist(): this;
|
|
147
|
+
/**
|
|
148
|
+
* Assert that at least one object literal argument has ALL named properties.
|
|
149
|
+
*/
|
|
150
|
+
haveArgumentWithProperty(...names: string[]): this;
|
|
151
|
+
/**
|
|
152
|
+
* Assert that NO object literal argument has ANY of the named properties.
|
|
153
|
+
*/
|
|
154
|
+
notHaveArgumentWithProperty(...names: string[]): this;
|
|
155
|
+
/**
|
|
156
|
+
* Assert that at least one argument subtree contains a match.
|
|
157
|
+
*
|
|
158
|
+
* Searches ALL arguments recursively — a superset of `haveCallbackContaining`.
|
|
159
|
+
* Use `haveCallbackContaining` when you only want to search callback bodies.
|
|
160
|
+
*/
|
|
161
|
+
haveArgumentContaining(matcher: ExpressionMatcher): this;
|
|
162
|
+
/**
|
|
163
|
+
* Assert that NO argument subtree contains a match.
|
|
164
|
+
*
|
|
165
|
+
* Searches ALL arguments recursively — a superset of `notHaveCallbackContaining`.
|
|
166
|
+
* Use `notHaveCallbackContaining` when you only want to search callback bodies.
|
|
167
|
+
*/
|
|
168
|
+
notHaveArgumentContaining(matcher: ExpressionMatcher): this;
|
|
169
|
+
/**
|
|
170
|
+
* Get the underlying ArchProject.
|
|
171
|
+
* Used by within() to create scoped builders.
|
|
172
|
+
*/
|
|
173
|
+
getProject(): ArchProject;
|
|
174
|
+
/**
|
|
175
|
+
* Get the ArchCall elements that match the current predicate chain.
|
|
176
|
+
* Used by within() to extract callbacks from matched calls.
|
|
177
|
+
*/
|
|
178
|
+
getMatchedCalls(): ArchCall[];
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* Entry point for call-expression architecture rules.
|
|
182
|
+
*
|
|
183
|
+
* Scans all source files in the project for CallExpression nodes
|
|
184
|
+
* and wraps them in ArchCall for predicate/condition evaluation.
|
|
185
|
+
*
|
|
186
|
+
* @example
|
|
187
|
+
* ```typescript
|
|
188
|
+
* import { project, calls, call } from '@nielspeter/eess-ts'
|
|
189
|
+
*
|
|
190
|
+
* const p = project('tsconfig.json')
|
|
191
|
+
*
|
|
192
|
+
* calls(p)
|
|
193
|
+
* .that().onObject('app').and().withMethod('get')
|
|
194
|
+
* .should().haveCallbackContaining(call('authenticate'))
|
|
195
|
+
* .check()
|
|
196
|
+
* ```
|
|
197
|
+
*/
|
|
198
|
+
export declare function calls(p: ArchProject): CallRuleBuilder;
|
|
199
|
+
//# sourceMappingURL=call-rule-builder.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"call-rule-builder.d.ts","sourceRoot":"","sources":["../../src/builders/call-rule-builder.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAA;AACrD,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAA;AAC9C,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAA;AACxD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAA;AAC/D,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAA;AAyBtD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,qBAAa,eAAgB,SAAQ,WAAW,CAAC,QAAQ,EAAE,WAAW,CAAC;IACrE;;;;;;;;OAQG;IACH,SAAS,CAAC,mBAAmB,CAAC,EAAE,MAAM,CAAA;IAEtC,SAAS,CAAC,WAAW,IAAI,QAAQ,EAAE;cAIhB,qBAAqB,IAAI,gBAAgB;IAS5D;;;OAGG;IACH,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;IAIhD,sEAAsE;IACtE,oBAAoB,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAI1C,oEAAoE;IACpE,kBAAkB,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAIxC,sEAAsE;IACtE,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAIhC,+DAA+D;IAC/D,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IASlC;;;;OAIG;IACH,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAI5B;;;OAGG;IACH,UAAU,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;IAI9C;;;;OAIG;IACH,eAAe,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;IAI9D;;;;OAIG;IACH,aAAa,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI;IAMhD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA6CG;IACH,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAOpC;;OAEG;IACH,sBAAsB,CAAC,OAAO,EAAE,iBAAiB,GAAG,IAAI;IAIxD;;OAEG;IACH,yBAAyB,CAAC,OAAO,EAAE,iBAAiB,GAAG,IAAI;IAI3D;;OAEG;IACH,QAAQ,IAAI,IAAI;IAIhB;;OAEG;IACH,wBAAwB,CAAC,GAAG,KAAK,EAAE,MAAM,EAAE,GAAG,IAAI;IAIlD;;OAEG;IACH,2BAA2B,CAAC,GAAG,KAAK,EAAE,MAAM,EAAE,GAAG,IAAI;IAIrD;;;;;OAKG;IACH,sBAAsB,CAAC,OAAO,EAAE,iBAAiB,GAAG,IAAI;IAIxD;;;;;OAKG;IACH,yBAAyB,CAAC,OAAO,EAAE,iBAAiB,GAAG,IAAI;IAM3D;;;OAGG;IACH,UAAU,IAAI,WAAW;IAIzB;;;OAGG;IACH,eAAe,IAAI,QAAQ,EAAE;CAG9B;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,KAAK,CAAC,CAAC,EAAE,WAAW,GAAG,eAAe,CAErD"}
|