@ontrails/warden 1.0.0-beta.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/.turbo/turbo-build.log +1 -0
- package/.turbo/turbo-lint.log +3 -0
- package/.turbo/turbo-typecheck.log +1 -0
- package/CHANGELOG.md +21 -0
- package/README.md +132 -0
- package/dist/cli.d.ts +46 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +221 -0
- package/dist/cli.js.map +1 -0
- package/dist/drift.d.ts +26 -0
- package/dist/drift.d.ts.map +1 -0
- package/dist/drift.js +27 -0
- package/dist/drift.js.map +1 -0
- package/dist/formatters.d.ts +29 -0
- package/dist/formatters.d.ts.map +1 -0
- package/dist/formatters.js +87 -0
- package/dist/formatters.js.map +1 -0
- package/dist/index.d.ts +26 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +26 -0
- package/dist/index.js.map +1 -0
- package/dist/rules/ast.d.ts +41 -0
- package/dist/rules/ast.d.ts.map +1 -0
- package/dist/rules/ast.js +163 -0
- package/dist/rules/ast.js.map +1 -0
- package/dist/rules/context-no-surface-types.d.ts +12 -0
- package/dist/rules/context-no-surface-types.d.ts.map +1 -0
- package/dist/rules/context-no-surface-types.js +96 -0
- package/dist/rules/context-no-surface-types.js.map +1 -0
- package/dist/rules/implementation-returns-result.d.ts +13 -0
- package/dist/rules/implementation-returns-result.d.ts.map +1 -0
- package/dist/rules/implementation-returns-result.js +231 -0
- package/dist/rules/implementation-returns-result.js.map +1 -0
- package/dist/rules/index.d.ts +22 -0
- package/dist/rules/index.d.ts.map +1 -0
- package/dist/rules/index.js +41 -0
- package/dist/rules/index.js.map +1 -0
- package/dist/rules/no-direct-impl-in-route.d.ts +12 -0
- package/dist/rules/no-direct-impl-in-route.d.ts.map +1 -0
- package/dist/rules/no-direct-impl-in-route.js +46 -0
- package/dist/rules/no-direct-impl-in-route.js.map +1 -0
- package/dist/rules/no-direct-implementation-call.d.ts +12 -0
- package/dist/rules/no-direct-implementation-call.d.ts.map +1 -0
- package/dist/rules/no-direct-implementation-call.js +39 -0
- package/dist/rules/no-direct-implementation-call.js.map +1 -0
- package/dist/rules/no-sync-result-assumption.d.ts +6 -0
- package/dist/rules/no-sync-result-assumption.d.ts.map +1 -0
- package/dist/rules/no-sync-result-assumption.js +98 -0
- package/dist/rules/no-sync-result-assumption.js.map +1 -0
- package/dist/rules/no-throw-in-detour-target.d.ts +12 -0
- package/dist/rules/no-throw-in-detour-target.d.ts.map +1 -0
- package/dist/rules/no-throw-in-detour-target.js +87 -0
- package/dist/rules/no-throw-in-detour-target.js.map +1 -0
- package/dist/rules/no-throw-in-implementation.d.ts +9 -0
- package/dist/rules/no-throw-in-implementation.d.ts.map +1 -0
- package/dist/rules/no-throw-in-implementation.js +34 -0
- package/dist/rules/no-throw-in-implementation.js.map +1 -0
- package/dist/rules/prefer-schema-inference.d.ts +7 -0
- package/dist/rules/prefer-schema-inference.d.ts.map +1 -0
- package/dist/rules/prefer-schema-inference.js +86 -0
- package/dist/rules/prefer-schema-inference.js.map +1 -0
- package/dist/rules/scan.d.ts +8 -0
- package/dist/rules/scan.d.ts.map +1 -0
- package/dist/rules/scan.js +32 -0
- package/dist/rules/scan.js.map +1 -0
- package/dist/rules/specs.d.ts +29 -0
- package/dist/rules/specs.d.ts.map +1 -0
- package/dist/rules/specs.js +192 -0
- package/dist/rules/specs.js.map +1 -0
- package/dist/rules/structure.d.ts +13 -0
- package/dist/rules/structure.d.ts.map +1 -0
- package/dist/rules/structure.js +142 -0
- package/dist/rules/structure.js.map +1 -0
- package/dist/rules/types.d.ts +52 -0
- package/dist/rules/types.d.ts.map +1 -0
- package/dist/rules/types.js +2 -0
- package/dist/rules/types.js.map +1 -0
- package/dist/rules/valid-describe-refs.d.ts +7 -0
- package/dist/rules/valid-describe-refs.d.ts.map +1 -0
- package/dist/rules/valid-describe-refs.js +51 -0
- package/dist/rules/valid-describe-refs.js.map +1 -0
- package/dist/rules/valid-detour-refs.d.ts +6 -0
- package/dist/rules/valid-detour-refs.d.ts.map +1 -0
- package/dist/rules/valid-detour-refs.js +116 -0
- package/dist/rules/valid-detour-refs.js.map +1 -0
- package/package.json +25 -0
- package/src/__tests__/cli.test.ts +198 -0
- package/src/__tests__/drift.test.ts +74 -0
- package/src/__tests__/formatters.test.ts +157 -0
- package/src/__tests__/implementation-returns-result.test.ts +75 -0
- package/src/__tests__/no-direct-implementation-call.test.ts +83 -0
- package/src/__tests__/no-sync-result-assumption.test.ts +85 -0
- package/src/__tests__/no-throw-in-detour-target.test.ts +78 -0
- package/src/__tests__/prefer-schema-inference.test.ts +84 -0
- package/src/__tests__/rules.test.ts +188 -0
- package/src/__tests__/valid-describe-refs.test.ts +60 -0
- package/src/cli.ts +343 -0
- package/src/drift.ts +50 -0
- package/src/formatters.ts +113 -0
- package/src/index.ts +47 -0
- package/src/rules/ast.ts +217 -0
- package/src/rules/context-no-surface-types.ts +150 -0
- package/src/rules/implementation-returns-result.ts +343 -0
- package/src/rules/index.ts +54 -0
- package/src/rules/no-direct-impl-in-route.ts +77 -0
- package/src/rules/no-direct-implementation-call.ts +47 -0
- package/src/rules/no-sync-result-assumption.ts +156 -0
- package/src/rules/no-throw-in-detour-target.ts +150 -0
- package/src/rules/no-throw-in-implementation.ts +41 -0
- package/src/rules/prefer-schema-inference.ts +141 -0
- package/src/rules/scan.ts +46 -0
- package/src/rules/specs.ts +384 -0
- package/src/rules/structure.ts +234 -0
- package/src/rules/types.ts +62 -0
- package/src/rules/valid-describe-refs.ts +94 -0
- package/src/rules/valid-detour-refs.ts +187 -0
- package/tsconfig.json +9 -0
- package/tsconfig.tsbuildinfo +1 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/rules/index.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAI7C,YAAY,EACV,sBAAsB,EACtB,cAAc,EACd,gBAAgB,EAChB,UAAU,EACV,cAAc,GACf,MAAM,YAAY,CAAC;AAEpB,OAAO,EAAE,uBAAuB,EAAE,MAAM,iCAAiC,CAAC;AAC1E,OAAO,EAAE,qBAAqB,EAAE,MAAM,+BAA+B,CAAC;AACtE,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,EAAE,mBAAmB,EAAE,MAAM,8BAA8B,CAAC;AACnE,OAAO,EAAE,0BAA0B,EAAE,MAAM,oCAAoC,CAAC;AAChF,OAAO,EAAE,sBAAsB,EAAE,MAAM,gCAAgC,CAAC;AACxE,OAAO,EAAE,2BAA2B,EAAE,MAAM,oCAAoC,CAAC;AACjF,OAAO,EAAE,qBAAqB,EAAE,MAAM,gCAAgC,CAAC;AACvE,OAAO,EAAE,qBAAqB,EAAE,MAAM,8BAA8B,CAAC;AACrE,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAE7D;;;;;;;GAOG;AACH,eAAO,MAAM,WAAW,EAAE,WAAW,CAAC,MAAM,EAAE,UAAU,CActD,CAAC"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { contextNoSurfaceTypes } from './context-no-surface-types.js';
|
|
2
|
+
import { implementationReturnsResult } from './implementation-returns-result.js';
|
|
3
|
+
import { noDirectImplInRoute } from './no-direct-impl-in-route.js';
|
|
4
|
+
import { noDirectImplementationCall } from './no-direct-implementation-call.js';
|
|
5
|
+
import { noSyncResultAssumption } from './no-sync-result-assumption.js';
|
|
6
|
+
import { noThrowInDetourTarget } from './no-throw-in-detour-target.js';
|
|
7
|
+
import { noThrowInImplementation } from './no-throw-in-implementation.js';
|
|
8
|
+
import { preferSchemaInference } from './prefer-schema-inference.js';
|
|
9
|
+
import { validDescribeRefs } from './valid-describe-refs.js';
|
|
10
|
+
import { validDetourRefs } from './valid-detour-refs.js';
|
|
11
|
+
export { noThrowInImplementation } from './no-throw-in-implementation.js';
|
|
12
|
+
export { contextNoSurfaceTypes } from './context-no-surface-types.js';
|
|
13
|
+
export { validDetourRefs } from './valid-detour-refs.js';
|
|
14
|
+
export { noDirectImplInRoute } from './no-direct-impl-in-route.js';
|
|
15
|
+
export { noDirectImplementationCall } from './no-direct-implementation-call.js';
|
|
16
|
+
export { noSyncResultAssumption } from './no-sync-result-assumption.js';
|
|
17
|
+
export { implementationReturnsResult } from './implementation-returns-result.js';
|
|
18
|
+
export { noThrowInDetourTarget } from './no-throw-in-detour-target.js';
|
|
19
|
+
export { preferSchemaInference } from './prefer-schema-inference.js';
|
|
20
|
+
export { validDescribeRefs } from './valid-describe-refs.js';
|
|
21
|
+
/**
|
|
22
|
+
* All built-in warden rules, keyed by rule name.
|
|
23
|
+
*
|
|
24
|
+
* Rules that duplicate validateTopo checks (follows-trails-exist,
|
|
25
|
+
* no-recursive-follows, event-origins-exist, examples-match-schema,
|
|
26
|
+
* require-output-schema) and follows-matches-calls (now covered by
|
|
27
|
+
* testExamples follows coverage) have been removed.
|
|
28
|
+
*/
|
|
29
|
+
export const wardenRules = new Map([
|
|
30
|
+
[noThrowInImplementation.name, noThrowInImplementation],
|
|
31
|
+
[contextNoSurfaceTypes.name, contextNoSurfaceTypes],
|
|
32
|
+
[preferSchemaInference.name, preferSchemaInference],
|
|
33
|
+
[validDescribeRefs.name, validDescribeRefs],
|
|
34
|
+
[validDetourRefs.name, validDetourRefs],
|
|
35
|
+
[noDirectImplementationCall.name, noDirectImplementationCall],
|
|
36
|
+
[noSyncResultAssumption.name, noSyncResultAssumption],
|
|
37
|
+
[implementationReturnsResult.name, implementationReturnsResult],
|
|
38
|
+
[noThrowInDetourTarget.name, noThrowInDetourTarget],
|
|
39
|
+
[noDirectImplInRoute.name, noDirectImplInRoute],
|
|
40
|
+
]);
|
|
41
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/rules/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,MAAM,+BAA+B,CAAC;AACtE,OAAO,EAAE,2BAA2B,EAAE,MAAM,oCAAoC,CAAC;AACjF,OAAO,EAAE,mBAAmB,EAAE,MAAM,8BAA8B,CAAC;AACnE,OAAO,EAAE,0BAA0B,EAAE,MAAM,oCAAoC,CAAC;AAChF,OAAO,EAAE,sBAAsB,EAAE,MAAM,gCAAgC,CAAC;AACxE,OAAO,EAAE,qBAAqB,EAAE,MAAM,gCAAgC,CAAC;AACvE,OAAO,EAAE,uBAAuB,EAAE,MAAM,iCAAiC,CAAC;AAC1E,OAAO,EAAE,qBAAqB,EAAE,MAAM,8BAA8B,CAAC;AAErE,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAC7D,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAUzD,OAAO,EAAE,uBAAuB,EAAE,MAAM,iCAAiC,CAAC;AAC1E,OAAO,EAAE,qBAAqB,EAAE,MAAM,+BAA+B,CAAC;AACtE,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,EAAE,mBAAmB,EAAE,MAAM,8BAA8B,CAAC;AACnE,OAAO,EAAE,0BAA0B,EAAE,MAAM,oCAAoC,CAAC;AAChF,OAAO,EAAE,sBAAsB,EAAE,MAAM,gCAAgC,CAAC;AACxE,OAAO,EAAE,2BAA2B,EAAE,MAAM,oCAAoC,CAAC;AACjF,OAAO,EAAE,qBAAqB,EAAE,MAAM,gCAAgC,CAAC;AACvE,OAAO,EAAE,qBAAqB,EAAE,MAAM,8BAA8B,CAAC;AACrE,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAE7D;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,WAAW,GAAoC,IAAI,GAAG,CAGjE;IACA,CAAC,uBAAuB,CAAC,IAAI,EAAE,uBAAuB,CAAC;IACvD,CAAC,qBAAqB,CAAC,IAAI,EAAE,qBAAqB,CAAC;IACnD,CAAC,qBAAqB,CAAC,IAAI,EAAE,qBAAqB,CAAC;IACnD,CAAC,iBAAiB,CAAC,IAAI,EAAE,iBAAiB,CAAC;IAC3C,CAAC,eAAe,CAAC,IAAI,EAAE,eAAe,CAAC;IACvC,CAAC,0BAA0B,CAAC,IAAI,EAAE,0BAA0B,CAAC;IAC7D,CAAC,sBAAsB,CAAC,IAAI,EAAE,sBAAsB,CAAC;IACrD,CAAC,2BAA2B,CAAC,IAAI,EAAE,2BAA2B,CAAC;IAC/D,CAAC,qBAAqB,CAAC,IAAI,EAAE,qBAAqB,CAAC;IACnD,CAAC,mBAAmB,CAAC,IAAI,EAAE,mBAAmB,CAAC;CAChD,CAAC,CAAC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Detects hike implementations that call `.implementation()` directly.
|
|
3
|
+
*
|
|
4
|
+
* Uses AST parsing to find hike definition bodies and check for
|
|
5
|
+
* `.implementation()` call expressions.
|
|
6
|
+
*/
|
|
7
|
+
import type { WardenRule } from './types.js';
|
|
8
|
+
/**
|
|
9
|
+
* Detects routes that call another trail's `.implementation()` directly.
|
|
10
|
+
*/
|
|
11
|
+
export declare const noDirectImplInRoute: WardenRule;
|
|
12
|
+
//# sourceMappingURL=no-direct-impl-in-route.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"no-direct-impl-in-route.d.ts","sourceRoot":"","sources":["../../src/rules/no-direct-impl-in-route.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAUH,OAAO,KAAK,EAAoB,UAAU,EAAE,MAAM,YAAY,CAAC;AA+B/D;;GAEG;AACH,eAAO,MAAM,mBAAmB,EAAE,UA2BjC,CAAC"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Detects hike implementations that call `.implementation()` directly.
|
|
3
|
+
*
|
|
4
|
+
* Uses AST parsing to find hike definition bodies and check for
|
|
5
|
+
* `.implementation()` call expressions.
|
|
6
|
+
*/
|
|
7
|
+
import { findImplementationBodies, findTrailDefinitions, isImplementationCall, offsetToLine, parse, walk, } from './ast.js';
|
|
8
|
+
const findImplCallsInHike = (def, filePath, sourceCode, diagnostics) => {
|
|
9
|
+
for (const body of findImplementationBodies(def.config)) {
|
|
10
|
+
walk(body, (node) => {
|
|
11
|
+
if (isImplementationCall(node)) {
|
|
12
|
+
diagnostics.push({
|
|
13
|
+
filePath,
|
|
14
|
+
line: offsetToLine(sourceCode, node.start),
|
|
15
|
+
message: 'Use ctx.follow("trailId", input) instead of direct .implementation() calls. ctx.follow() validates input and propagates tracing.',
|
|
16
|
+
rule: 'no-direct-impl-in-route',
|
|
17
|
+
severity: 'warn',
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
/**
|
|
24
|
+
* Detects routes that call another trail's `.implementation()` directly.
|
|
25
|
+
*/
|
|
26
|
+
export const noDirectImplInRoute = {
|
|
27
|
+
check(sourceCode, filePath) {
|
|
28
|
+
if (!/\bhike\s*\(/.test(sourceCode)) {
|
|
29
|
+
return [];
|
|
30
|
+
}
|
|
31
|
+
const ast = parse(filePath, sourceCode);
|
|
32
|
+
if (!ast) {
|
|
33
|
+
return [];
|
|
34
|
+
}
|
|
35
|
+
const diagnostics = [];
|
|
36
|
+
const hikeDefs = findTrailDefinitions(ast).filter((d) => d.kind === 'hike');
|
|
37
|
+
for (const def of hikeDefs) {
|
|
38
|
+
findImplCallsInHike(def, filePath, sourceCode, diagnostics);
|
|
39
|
+
}
|
|
40
|
+
return diagnostics;
|
|
41
|
+
},
|
|
42
|
+
description: 'Prefer ctx.follow() over direct .implementation() calls in route bodies.',
|
|
43
|
+
name: 'no-direct-impl-in-route',
|
|
44
|
+
severity: 'warn',
|
|
45
|
+
};
|
|
46
|
+
//# sourceMappingURL=no-direct-impl-in-route.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"no-direct-impl-in-route.js","sourceRoot":"","sources":["../../src/rules/no-direct-impl-in-route.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EACL,wBAAwB,EACxB,oBAAoB,EACpB,oBAAoB,EACpB,YAAY,EACZ,KAAK,EACL,IAAI,GACL,MAAM,UAAU,CAAC;AAUlB,MAAM,mBAAmB,GAAG,CAC1B,GAAiC,EACjC,QAAgB,EAChB,UAAkB,EAClB,WAA+B,EACzB,EAAE;IACR,KAAK,MAAM,IAAI,IAAI,wBAAwB,CAAC,GAAG,CAAC,MAAiB,CAAC,EAAE,CAAC;QACnE,IAAI,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,EAAE;YAClB,IAAI,oBAAoB,CAAC,IAAe,CAAC,EAAE,CAAC;gBAC1C,WAAW,CAAC,IAAI,CAAC;oBACf,QAAQ;oBACR,IAAI,EAAE,YAAY,CAAC,UAAU,EAAE,IAAI,CAAC,KAAK,CAAC;oBAC1C,OAAO,EACL,kIAAkI;oBACpI,IAAI,EAAE,yBAAyB;oBAC/B,QAAQ,EAAE,MAAM;iBACjB,CAAC,CAAC;YACL,CAAC;QACH,CAAC,CAAC,CAAC;IACL,CAAC;AACH,CAAC,CAAC;AAEF;;GAEG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAe;IAC7C,KAAK,CAAC,UAAkB,EAAE,QAAgB;QACxC,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC;YACpC,OAAO,EAAE,CAAC;QACZ,CAAC;QAED,MAAM,GAAG,GAAG,KAAK,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAC;QACxC,IAAI,CAAC,GAAG,EAAE,CAAC;YACT,OAAO,EAAE,CAAC;QACZ,CAAC;QAED,MAAM,WAAW,GAAuB,EAAE,CAAC;QAC3C,MAAM,QAAQ,GAAG,oBAAoB,CAAC,GAAc,CAAC,CAAC,MAAM,CAC1D,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,MAAM,CACzB,CAAC;QAEF,KAAK,MAAM,GAAG,IAAI,QAAQ,EAAE,CAAC;YAC3B,mBAAmB,CAAC,GAAG,EAAE,QAAQ,EAAE,UAAU,EAAE,WAAW,CAAC,CAAC;QAC9D,CAAC;QAED,OAAO,WAAW,CAAC;IACrB,CAAC;IACD,WAAW,EACT,0EAA0E;IAC5E,IAAI,EAAE,yBAAyB;IAE/B,QAAQ,EAAE,MAAM;CACjB,CAAC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Flags direct `.implementation()` calls in application code.
|
|
3
|
+
*
|
|
4
|
+
* Uses AST parsing to find `.implementation()` call expressions,
|
|
5
|
+
* ignoring occurrences in strings and comments.
|
|
6
|
+
*/
|
|
7
|
+
import type { WardenRule } from './types.js';
|
|
8
|
+
/**
|
|
9
|
+
* Flags direct `.implementation()` calls in application code.
|
|
10
|
+
*/
|
|
11
|
+
export declare const noDirectImplementationCall: WardenRule;
|
|
12
|
+
//# sourceMappingURL=no-direct-implementation-call.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"no-direct-implementation-call.d.ts","sourceRoot":"","sources":["../../src/rules/no-direct-implementation-call.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,OAAO,KAAK,EAAoB,UAAU,EAAE,MAAM,YAAY,CAAC;AAE/D;;GAEG;AACH,eAAO,MAAM,0BAA0B,EAAE,UAgCxC,CAAC"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Flags direct `.implementation()` calls in application code.
|
|
3
|
+
*
|
|
4
|
+
* Uses AST parsing to find `.implementation()` call expressions,
|
|
5
|
+
* ignoring occurrences in strings and comments.
|
|
6
|
+
*/
|
|
7
|
+
import { isImplementationCall, offsetToLine, parse, walk } from './ast.js';
|
|
8
|
+
import { isFrameworkInternalFile, isTestFile } from './scan.js';
|
|
9
|
+
/**
|
|
10
|
+
* Flags direct `.implementation()` calls in application code.
|
|
11
|
+
*/
|
|
12
|
+
export const noDirectImplementationCall = {
|
|
13
|
+
check(sourceCode, filePath) {
|
|
14
|
+
if (isTestFile(filePath) || isFrameworkInternalFile(filePath)) {
|
|
15
|
+
return [];
|
|
16
|
+
}
|
|
17
|
+
const ast = parse(filePath, sourceCode);
|
|
18
|
+
if (!ast) {
|
|
19
|
+
return [];
|
|
20
|
+
}
|
|
21
|
+
const diagnostics = [];
|
|
22
|
+
walk(ast, (node) => {
|
|
23
|
+
if (isImplementationCall(node)) {
|
|
24
|
+
diagnostics.push({
|
|
25
|
+
filePath,
|
|
26
|
+
line: offsetToLine(sourceCode, node.start),
|
|
27
|
+
message: 'Use ctx.follow("trailId", input) instead of direct .implementation() calls. Direct implementation access bypasses validation, tracing, and layers.',
|
|
28
|
+
rule: 'no-direct-implementation-call',
|
|
29
|
+
severity: 'warn',
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
return diagnostics;
|
|
34
|
+
},
|
|
35
|
+
description: 'Disallow direct .implementation() calls in application code. Use ctx.follow() instead.',
|
|
36
|
+
name: 'no-direct-implementation-call',
|
|
37
|
+
severity: 'warn',
|
|
38
|
+
};
|
|
39
|
+
//# sourceMappingURL=no-direct-implementation-call.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"no-direct-implementation-call.js","sourceRoot":"","sources":["../../src/rules/no-direct-implementation-call.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,oBAAoB,EAAE,YAAY,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,UAAU,CAAC;AAC3E,OAAO,EAAE,uBAAuB,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAGhE;;GAEG;AACH,MAAM,CAAC,MAAM,0BAA0B,GAAe;IACpD,KAAK,CAAC,UAAkB,EAAE,QAAgB;QACxC,IAAI,UAAU,CAAC,QAAQ,CAAC,IAAI,uBAAuB,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC9D,OAAO,EAAE,CAAC;QACZ,CAAC;QAED,MAAM,GAAG,GAAG,KAAK,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAC;QACxC,IAAI,CAAC,GAAG,EAAE,CAAC;YACT,OAAO,EAAE,CAAC;QACZ,CAAC;QAED,MAAM,WAAW,GAAuB,EAAE,CAAC;QAE3C,IAAI,CAAC,GAAG,EAAE,CAAC,IAAI,EAAE,EAAE;YACjB,IAAI,oBAAoB,CAAC,IAAI,CAAC,EAAE,CAAC;gBAC/B,WAAW,CAAC,IAAI,CAAC;oBACf,QAAQ;oBACR,IAAI,EAAE,YAAY,CAAC,UAAU,EAAE,IAAI,CAAC,KAAK,CAAC;oBAC1C,OAAO,EACL,oJAAoJ;oBACtJ,IAAI,EAAE,+BAA+B;oBACrC,QAAQ,EAAE,MAAM;iBACjB,CAAC,CAAC;YACL,CAAC;QACH,CAAC,CAAC,CAAC;QAEH,OAAO,WAAW,CAAC;IACrB,CAAC;IACD,WAAW,EACT,wFAAwF;IAC1F,IAAI,EAAE,+BAA+B;IACrC,QAAQ,EAAE,MAAM;CACjB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"no-sync-result-assumption.d.ts","sourceRoot":"","sources":["../../src/rules/no-sync-result-assumption.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAoB,UAAU,EAAE,MAAM,YAAY,CAAC;AA6I/D;;GAEG;AACH,eAAO,MAAM,sBAAsB,EAAE,UAWpC,CAAC"}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { isFrameworkInternalFile, isTestFile, stripQuotedContent, } from './scan.js';
|
|
2
|
+
const RESULT_ACCESS_PATTERN = /\.(?:isOk|isErr|match|map)\s*\(|\.(?:value|error)\b/;
|
|
3
|
+
const IMPLEMENTATION_CALL_PATTERN = /\.implementation\s*\(/;
|
|
4
|
+
const isAwaitedImplementationCall = (line) => {
|
|
5
|
+
const callIndex = line.indexOf('.implementation(');
|
|
6
|
+
if (callIndex === -1) {
|
|
7
|
+
return false;
|
|
8
|
+
}
|
|
9
|
+
const awaitIndex = line.indexOf('await');
|
|
10
|
+
return awaitIndex !== -1 && awaitIndex < callIndex;
|
|
11
|
+
};
|
|
12
|
+
const isDirectResultAccess = (line) => IMPLEMENTATION_CALL_PATTERN.test(line) &&
|
|
13
|
+
RESULT_ACCESS_PATTERN.test(line) &&
|
|
14
|
+
!isAwaitedImplementationCall(line);
|
|
15
|
+
const isPendingUse = (line, variableName) => {
|
|
16
|
+
const escaped = variableName.replaceAll(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
17
|
+
const pendingPattern = new RegExp(`\\b${escaped}\\s*(?:\\.(?:isOk|isErr|match|map)\\s*\\(|\\.(?:value|error)\\b)`);
|
|
18
|
+
return pendingPattern.test(line);
|
|
19
|
+
};
|
|
20
|
+
const MISSING_AWAIT_MESSAGE = 'Missing await: .implementation() returns Promise<Result> after normalization. Use `const result = await trail.implementation(input, ctx)`.';
|
|
21
|
+
const createMissingAwaitDiagnostic = (filePath, line) => ({
|
|
22
|
+
filePath,
|
|
23
|
+
line,
|
|
24
|
+
message: MISSING_AWAIT_MESSAGE,
|
|
25
|
+
rule: 'no-sync-result-assumption',
|
|
26
|
+
severity: 'error',
|
|
27
|
+
});
|
|
28
|
+
const trackPendingCall = (line) => {
|
|
29
|
+
const match = line.match(/\b(?:const|let|var)\s+([A-Za-z_$][\w$]*)\s*=\s*([^;]*)/);
|
|
30
|
+
if (!match?.[1] || !match[2] || !IMPLEMENTATION_CALL_PATTERN.test(match[2])) {
|
|
31
|
+
return undefined;
|
|
32
|
+
}
|
|
33
|
+
if (isAwaitedImplementationCall(match[2])) {
|
|
34
|
+
return undefined;
|
|
35
|
+
}
|
|
36
|
+
return match[1];
|
|
37
|
+
};
|
|
38
|
+
const addPendingCall = (pendingCalls, variableName, lineNumber) => {
|
|
39
|
+
pendingCalls.push({
|
|
40
|
+
line: lineNumber,
|
|
41
|
+
remainingLines: 6,
|
|
42
|
+
variableName,
|
|
43
|
+
});
|
|
44
|
+
};
|
|
45
|
+
const advancePendingCalls = (line, filePath, lineNumber, pendingCalls, diagnostics) => {
|
|
46
|
+
for (let j = pendingCalls.length - 1; j >= 0; j -= 1) {
|
|
47
|
+
const pendingCall = pendingCalls[j];
|
|
48
|
+
if (pendingCall && isPendingUse(line, pendingCall.variableName)) {
|
|
49
|
+
diagnostics.push(createMissingAwaitDiagnostic(filePath, lineNumber));
|
|
50
|
+
pendingCalls.splice(j, 1);
|
|
51
|
+
}
|
|
52
|
+
else if (pendingCall) {
|
|
53
|
+
pendingCall.remainingLines -= 1;
|
|
54
|
+
if (pendingCall.remainingLines <= 0) {
|
|
55
|
+
pendingCalls.splice(j, 1);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
const processLine = (line, filePath, lineNumber, pendingCalls, diagnostics) => {
|
|
61
|
+
if (isDirectResultAccess(line)) {
|
|
62
|
+
diagnostics.push(createMissingAwaitDiagnostic(filePath, lineNumber));
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
const variableName = trackPendingCall(line);
|
|
66
|
+
if (variableName) {
|
|
67
|
+
addPendingCall(pendingCalls, variableName, lineNumber);
|
|
68
|
+
}
|
|
69
|
+
advancePendingCalls(line, filePath, lineNumber, pendingCalls, diagnostics);
|
|
70
|
+
};
|
|
71
|
+
const scanSourceCode = (sourceCode, filePath) => {
|
|
72
|
+
const diagnostics = [];
|
|
73
|
+
const lines = sourceCode.split('\n');
|
|
74
|
+
const pendingCalls = [];
|
|
75
|
+
for (let i = 0; i < lines.length; i += 1) {
|
|
76
|
+
const line = lines[i];
|
|
77
|
+
if (!line) {
|
|
78
|
+
continue;
|
|
79
|
+
}
|
|
80
|
+
processLine(line, filePath, i + 1, pendingCalls, diagnostics);
|
|
81
|
+
}
|
|
82
|
+
return diagnostics;
|
|
83
|
+
};
|
|
84
|
+
/**
|
|
85
|
+
* Flags code that assumes `.implementation()` returns a synchronous result.
|
|
86
|
+
*/
|
|
87
|
+
export const noSyncResultAssumption = {
|
|
88
|
+
check(sourceCode, filePath) {
|
|
89
|
+
if (isTestFile(filePath) || isFrameworkInternalFile(filePath)) {
|
|
90
|
+
return [];
|
|
91
|
+
}
|
|
92
|
+
return scanSourceCode(stripQuotedContent(sourceCode), filePath);
|
|
93
|
+
},
|
|
94
|
+
description: 'Disallow treating .implementation() as synchronous after normalization. Always await the returned Promise<Result>.',
|
|
95
|
+
name: 'no-sync-result-assumption',
|
|
96
|
+
severity: 'error',
|
|
97
|
+
};
|
|
98
|
+
//# sourceMappingURL=no-sync-result-assumption.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"no-sync-result-assumption.js","sourceRoot":"","sources":["../../src/rules/no-sync-result-assumption.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Flags throws in implementations that are used as detour targets.
|
|
3
|
+
*
|
|
4
|
+
* Uses AST parsing for accurate detection of detour target IDs and
|
|
5
|
+
* throw statements within those trail implementations.
|
|
6
|
+
*/
|
|
7
|
+
import type { ProjectAwareWardenRule } from './types.js';
|
|
8
|
+
/**
|
|
9
|
+
* Flags throws in implementations that are used as detour targets.
|
|
10
|
+
*/
|
|
11
|
+
export declare const noThrowInDetourTarget: ProjectAwareWardenRule;
|
|
12
|
+
//# sourceMappingURL=no-throw-in-detour-target.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"no-throw-in-detour-target.d.ts","sourceRoot":"","sources":["../../src/rules/no-throw-in-detour-target.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAUH,OAAO,KAAK,EACV,sBAAsB,EAGvB,MAAM,YAAY,CAAC;AAyFpB;;GAEG;AACH,eAAO,MAAM,qBAAqB,EAAE,sBAsCnC,CAAC"}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Flags throws in implementations that are used as detour targets.
|
|
3
|
+
*
|
|
4
|
+
* Uses AST parsing for accurate detection of detour target IDs and
|
|
5
|
+
* throw statements within those trail implementations.
|
|
6
|
+
*/
|
|
7
|
+
import { findImplementationBodies, findTrailDefinitions, offsetToLine, parse, walk, } from './ast.js';
|
|
8
|
+
import { isTestFile } from './scan.js';
|
|
9
|
+
/** Collect all trail IDs referenced as detour targets in the AST. */
|
|
10
|
+
const collectDetourTargets = (ast) => {
|
|
11
|
+
const targets = new Set();
|
|
12
|
+
walk(ast, (node) => {
|
|
13
|
+
if (node.type !== 'Property' ||
|
|
14
|
+
node.key?.name !== 'detours' ||
|
|
15
|
+
!node.value) {
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
walk(node.value, (inner) => {
|
|
19
|
+
if (inner.type === 'Literal' || inner.type === 'StringLiteral') {
|
|
20
|
+
const { value } = inner;
|
|
21
|
+
if (typeof value === 'string' && value.includes('.')) {
|
|
22
|
+
targets.add(value);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
});
|
|
27
|
+
return targets;
|
|
28
|
+
};
|
|
29
|
+
/** Find throws in implementation bodies of targeted trails. */
|
|
30
|
+
const findThrowsInTargetedTrails = (ast, sourceCode, filePath, detourTargets) => {
|
|
31
|
+
const diagnostics = [];
|
|
32
|
+
for (const def of findTrailDefinitions(ast)) {
|
|
33
|
+
if (!detourTargets.has(def.id)) {
|
|
34
|
+
continue;
|
|
35
|
+
}
|
|
36
|
+
for (const body of findImplementationBodies(def.config)) {
|
|
37
|
+
walk(body, (node) => {
|
|
38
|
+
if (node.type === 'ThrowStatement') {
|
|
39
|
+
diagnostics.push({
|
|
40
|
+
filePath,
|
|
41
|
+
line: offsetToLine(sourceCode, node.start),
|
|
42
|
+
message: `Trail "${def.id}" is a detour target and must not throw. Use Result.err() instead.`,
|
|
43
|
+
rule: 'no-throw-in-detour-target',
|
|
44
|
+
severity: 'error',
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return diagnostics;
|
|
51
|
+
};
|
|
52
|
+
const checkThrowInDetourTargets = (sourceCode, filePath, detourTargets) => {
|
|
53
|
+
if (isTestFile(filePath)) {
|
|
54
|
+
return [];
|
|
55
|
+
}
|
|
56
|
+
const ast = parse(filePath, sourceCode);
|
|
57
|
+
if (!ast) {
|
|
58
|
+
return [];
|
|
59
|
+
}
|
|
60
|
+
return findThrowsInTargetedTrails(ast, sourceCode, filePath, detourTargets);
|
|
61
|
+
};
|
|
62
|
+
/**
|
|
63
|
+
* Flags throws in implementations that are used as detour targets.
|
|
64
|
+
*/
|
|
65
|
+
export const noThrowInDetourTarget = {
|
|
66
|
+
check(sourceCode, filePath) {
|
|
67
|
+
const ast = parse(filePath, sourceCode);
|
|
68
|
+
if (!ast) {
|
|
69
|
+
return [];
|
|
70
|
+
}
|
|
71
|
+
return checkThrowInDetourTargets(sourceCode, filePath, collectDetourTargets(ast));
|
|
72
|
+
},
|
|
73
|
+
checkWithContext(sourceCode, filePath, context) {
|
|
74
|
+
if (context.detourTargetTrailIds) {
|
|
75
|
+
return checkThrowInDetourTargets(sourceCode, filePath, context.detourTargetTrailIds);
|
|
76
|
+
}
|
|
77
|
+
const ast = parse(filePath, sourceCode);
|
|
78
|
+
if (!ast) {
|
|
79
|
+
return [];
|
|
80
|
+
}
|
|
81
|
+
return checkThrowInDetourTargets(sourceCode, filePath, collectDetourTargets(ast));
|
|
82
|
+
},
|
|
83
|
+
description: 'Disallow throw statements inside implementations that are referenced as detour targets.',
|
|
84
|
+
name: 'no-throw-in-detour-target',
|
|
85
|
+
severity: 'error',
|
|
86
|
+
};
|
|
87
|
+
//# sourceMappingURL=no-throw-in-detour-target.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"no-throw-in-detour-target.js","sourceRoot":"","sources":["../../src/rules/no-throw-in-detour-target.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Finds `throw` statements inside `implementation:` function bodies.
|
|
3
|
+
*
|
|
4
|
+
* Uses AST parsing for accurate detection — no false positives from
|
|
5
|
+
* throw in comments, strings, or nested non-implementation functions.
|
|
6
|
+
*/
|
|
7
|
+
import type { WardenRule } from './types.js';
|
|
8
|
+
export declare const noThrowInImplementation: WardenRule;
|
|
9
|
+
//# sourceMappingURL=no-throw-in-implementation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"no-throw-in-implementation.d.ts","sourceRoot":"","sources":["../../src/rules/no-throw-in-implementation.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,KAAK,EAAoB,UAAU,EAAE,MAAM,YAAY,CAAC;AAE/D,eAAO,MAAM,uBAAuB,EAAE,UA8BrC,CAAC"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Finds `throw` statements inside `implementation:` function bodies.
|
|
3
|
+
*
|
|
4
|
+
* Uses AST parsing for accurate detection — no false positives from
|
|
5
|
+
* throw in comments, strings, or nested non-implementation functions.
|
|
6
|
+
*/
|
|
7
|
+
import { findImplementationBodies, offsetToLine, parse, walk } from './ast.js';
|
|
8
|
+
export const noThrowInImplementation = {
|
|
9
|
+
check(sourceCode, filePath) {
|
|
10
|
+
const ast = parse(filePath, sourceCode);
|
|
11
|
+
if (!ast) {
|
|
12
|
+
return [];
|
|
13
|
+
}
|
|
14
|
+
const diagnostics = [];
|
|
15
|
+
for (const body of findImplementationBodies(ast)) {
|
|
16
|
+
walk(body, (node) => {
|
|
17
|
+
if (node.type === 'ThrowStatement') {
|
|
18
|
+
diagnostics.push({
|
|
19
|
+
filePath,
|
|
20
|
+
line: offsetToLine(sourceCode, node.start),
|
|
21
|
+
message: 'Do not throw inside implementation. Use Result.err() instead.',
|
|
22
|
+
rule: 'no-throw-in-implementation',
|
|
23
|
+
severity: 'error',
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
return diagnostics;
|
|
29
|
+
},
|
|
30
|
+
description: 'Disallow throw statements inside trail/route implementation bodies. Use Result.err() instead.',
|
|
31
|
+
name: 'no-throw-in-implementation',
|
|
32
|
+
severity: 'error',
|
|
33
|
+
};
|
|
34
|
+
//# sourceMappingURL=no-throw-in-implementation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"no-throw-in-implementation.js","sourceRoot":"","sources":["../../src/rules/no-throw-in-implementation.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,wBAAwB,EAAE,YAAY,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,UAAU,CAAC;AAG/E,MAAM,CAAC,MAAM,uBAAuB,GAAe;IACjD,KAAK,CAAC,UAAkB,EAAE,QAAgB;QACxC,MAAM,GAAG,GAAG,KAAK,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAC;QACxC,IAAI,CAAC,GAAG,EAAE,CAAC;YACT,OAAO,EAAE,CAAC;QACZ,CAAC;QAED,MAAM,WAAW,GAAuB,EAAE,CAAC;QAE3C,KAAK,MAAM,IAAI,IAAI,wBAAwB,CAAC,GAAG,CAAC,EAAE,CAAC;YACjD,IAAI,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,EAAE;gBAClB,IAAI,IAAI,CAAC,IAAI,KAAK,gBAAgB,EAAE,CAAC;oBACnC,WAAW,CAAC,IAAI,CAAC;wBACf,QAAQ;wBACR,IAAI,EAAE,YAAY,CAAC,UAAU,EAAE,IAAI,CAAC,KAAK,CAAC;wBAC1C,OAAO,EACL,+DAA+D;wBACjE,IAAI,EAAE,4BAA4B;wBAClC,QAAQ,EAAE,OAAO;qBAClB,CAAC,CAAC;gBACL,CAAC;YACH,CAAC,CAAC,CAAC;QACL,CAAC;QAED,OAAO,WAAW,CAAC;IACrB,CAAC;IACD,WAAW,EACT,+FAA+F;IACjG,IAAI,EAAE,4BAA4B;IAClC,QAAQ,EAAE,OAAO;CAClB,CAAC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { WardenRule } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Warns when a fields override only repeats metadata deriveFields() already gets from
|
|
4
|
+
* the schema.
|
|
5
|
+
*/
|
|
6
|
+
export declare const preferSchemaInference: WardenRule;
|
|
7
|
+
//# sourceMappingURL=prefer-schema-inference.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prefer-schema-inference.d.ts","sourceRoot":"","sources":["../../src/rules/prefer-schema-inference.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAoB,UAAU,EAAE,MAAM,YAAY,CAAC;AA0H/D;;;GAGG;AACH,eAAO,MAAM,qBAAqB,EAAE,UAcnC,CAAC"}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { isTestFile } from './scan.js';
|
|
2
|
+
import { findTrailLikeSpecs, parseArrayEntries, parseObjectProperties, parseStringLiteral, parseZodObjectShape, } from './specs.js';
|
|
3
|
+
const REDUNDANT_OVERRIDE_KEYS = new Set(['label', 'options']);
|
|
4
|
+
const hasOnlyRedundantKeys = (properties) => [...properties.keys()].every((key) => REDUNDANT_OVERRIDE_KEYS.has(key));
|
|
5
|
+
const redundantLabelPart = (derivedLabel, properties) => {
|
|
6
|
+
const label = parseStringLiteral(properties.get('label')?.value ?? '');
|
|
7
|
+
return label !== null && label === derivedLabel ? ['label'] : [];
|
|
8
|
+
};
|
|
9
|
+
const optionsAreDerivedDefaults = (optionsText, schemaOptions) => {
|
|
10
|
+
const entries = parseArrayEntries(optionsText, 0, optionsText);
|
|
11
|
+
if (entries.length !== schemaOptions.length) {
|
|
12
|
+
return false;
|
|
13
|
+
}
|
|
14
|
+
return entries.every((entry, index) => {
|
|
15
|
+
if (!entry.text.startsWith('{')) {
|
|
16
|
+
return false;
|
|
17
|
+
}
|
|
18
|
+
const properties = parseObjectProperties(entry.text, 0, entry.text);
|
|
19
|
+
if (properties.size !== 1) {
|
|
20
|
+
return false;
|
|
21
|
+
}
|
|
22
|
+
const value = parseStringLiteral(properties.get('value')?.value ?? '');
|
|
23
|
+
return value !== null && value === schemaOptions[index];
|
|
24
|
+
});
|
|
25
|
+
};
|
|
26
|
+
const redundantOptionsPart = (options, schemaOptions) => options !== undefined &&
|
|
27
|
+
schemaOptions !== undefined &&
|
|
28
|
+
optionsAreDerivedDefaults(options.value, schemaOptions)
|
|
29
|
+
? ['options']
|
|
30
|
+
: [];
|
|
31
|
+
const findRedundantParts = (fieldKey, fieldOverride, schemaText) => {
|
|
32
|
+
const fieldInfo = parseZodObjectShape(schemaText).get(fieldKey);
|
|
33
|
+
if (!fieldInfo) {
|
|
34
|
+
return [];
|
|
35
|
+
}
|
|
36
|
+
const properties = parseObjectProperties(fieldOverride, 0, fieldOverride);
|
|
37
|
+
if (properties.size === 0) {
|
|
38
|
+
return ['field metadata'];
|
|
39
|
+
}
|
|
40
|
+
if (!hasOnlyRedundantKeys(properties)) {
|
|
41
|
+
return [];
|
|
42
|
+
}
|
|
43
|
+
const redundant = [
|
|
44
|
+
...redundantLabelPart(fieldInfo.derivedLabel, properties),
|
|
45
|
+
...redundantOptionsPart(properties.get('options'), fieldInfo.options),
|
|
46
|
+
];
|
|
47
|
+
return redundant.length === properties.size ? redundant : [];
|
|
48
|
+
};
|
|
49
|
+
const diagnosticsForSpec = (sourceCode, filePath, spec) => {
|
|
50
|
+
const input = spec.properties.get('input');
|
|
51
|
+
const fields = spec.properties.get('fields');
|
|
52
|
+
if (!input || !fields) {
|
|
53
|
+
return [];
|
|
54
|
+
}
|
|
55
|
+
const fieldEntries = parseObjectProperties(fields.value, fields.start, sourceCode);
|
|
56
|
+
return [...fieldEntries.entries()]
|
|
57
|
+
.map(([fieldKey, fieldEntry]) => ({
|
|
58
|
+
fieldEntry,
|
|
59
|
+
fieldKey,
|
|
60
|
+
redundantParts: findRedundantParts(fieldKey, fieldEntry.value, input.value),
|
|
61
|
+
}))
|
|
62
|
+
.filter(({ redundantParts }) => redundantParts.length > 0)
|
|
63
|
+
.map(({ fieldEntry, fieldKey, redundantParts }) => ({
|
|
64
|
+
filePath,
|
|
65
|
+
line: fieldEntry.line,
|
|
66
|
+
message: `Trail "${spec.id}" field "${fieldKey}" only repeats schema-derived ${redundantParts.join(' and ')}. Remove the override and let deriveFields() infer it.`,
|
|
67
|
+
rule: 'prefer-schema-inference',
|
|
68
|
+
severity: 'warn',
|
|
69
|
+
}));
|
|
70
|
+
};
|
|
71
|
+
/**
|
|
72
|
+
* Warns when a fields override only repeats metadata deriveFields() already gets from
|
|
73
|
+
* the schema.
|
|
74
|
+
*/
|
|
75
|
+
export const preferSchemaInference = {
|
|
76
|
+
check(sourceCode, filePath) {
|
|
77
|
+
if (isTestFile(filePath)) {
|
|
78
|
+
return [];
|
|
79
|
+
}
|
|
80
|
+
return findTrailLikeSpecs(sourceCode).flatMap((spec) => diagnosticsForSpec(sourceCode, filePath, spec));
|
|
81
|
+
},
|
|
82
|
+
description: 'Warn when fields overrides only restate labels or enum options deriveFields() already infers from the Zod schema.',
|
|
83
|
+
name: 'prefer-schema-inference',
|
|
84
|
+
severity: 'warn',
|
|
85
|
+
};
|
|
86
|
+
//# sourceMappingURL=prefer-schema-inference.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prefer-schema-inference.js","sourceRoot":"","sources":["../../src/rules/prefer-schema-inference.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export declare const isTestFile: (filePath: string) => boolean;
|
|
2
|
+
export declare const isFrameworkInternalFile: (filePath: string) => boolean;
|
|
3
|
+
/**
|
|
4
|
+
* Replace quoted content and comments with whitespace while preserving line
|
|
5
|
+
* breaks so simple line-based scanners do not match examples or messages.
|
|
6
|
+
*/
|
|
7
|
+
export declare const stripQuotedContent: (sourceCode: string) => string;
|
|
8
|
+
//# sourceMappingURL=scan.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scan.d.ts","sourceRoot":"","sources":["../../src/rules/scan.ts"],"names":[],"mappings":"AAgBA,eAAO,MAAM,UAAU,GAAI,UAAU,MAAM,KAAG,OACO,CAAC;AAEtD,eAAO,MAAM,uBAAuB,GAAI,UAAU,MAAM,KAAG,OAK1D,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,kBAAkB,GAAI,YAAY,MAAM,KAAG,MAevD,CAAC"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
const TEST_FILE_PATTERN = /(?:^|\/)__tests__(?:\/|$)|(?:\.test|\.spec)\.[cm]?[jt]sx?$/;
|
|
2
|
+
const FRAMEWORK_INTERNAL_SEGMENTS = [
|
|
3
|
+
'/packages/testing/',
|
|
4
|
+
'/packages/warden/',
|
|
5
|
+
];
|
|
6
|
+
const normalizeFilePath = (filePath) => filePath.replaceAll('\\', '/');
|
|
7
|
+
const maskText = (text) => text.replaceAll(/[^\n]/g, ' ');
|
|
8
|
+
const stripPattern = (sourceCode, pattern) => sourceCode.replaceAll(pattern, (match) => maskText(match));
|
|
9
|
+
export const isTestFile = (filePath) => TEST_FILE_PATTERN.test(normalizeFilePath(filePath));
|
|
10
|
+
export const isFrameworkInternalFile = (filePath) => {
|
|
11
|
+
const normalized = normalizeFilePath(filePath);
|
|
12
|
+
return FRAMEWORK_INTERNAL_SEGMENTS.some((segment) => normalized.includes(segment));
|
|
13
|
+
};
|
|
14
|
+
/**
|
|
15
|
+
* Replace quoted content and comments with whitespace while preserving line
|
|
16
|
+
* breaks so simple line-based scanners do not match examples or messages.
|
|
17
|
+
*/
|
|
18
|
+
export const stripQuotedContent = (sourceCode) => {
|
|
19
|
+
let sanitized = sourceCode;
|
|
20
|
+
const patterns = [
|
|
21
|
+
/\/\/[^\n]*/g,
|
|
22
|
+
/\/\*[\s\S]*?\*\//g,
|
|
23
|
+
/'[^'\\\n]*(?:\\.[^'\\\n]*)*'/g,
|
|
24
|
+
/"[^"\\\n]*(?:\\.[^"\\\n]*)*"/g,
|
|
25
|
+
/`[\s\S]*?`/g,
|
|
26
|
+
];
|
|
27
|
+
for (const pattern of patterns) {
|
|
28
|
+
sanitized = stripPattern(sanitized, pattern);
|
|
29
|
+
}
|
|
30
|
+
return sanitized;
|
|
31
|
+
};
|
|
32
|
+
//# sourceMappingURL=scan.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scan.js","sourceRoot":"","sources":["../../src/rules/scan.ts"],"names":[],"mappings":"AAAA,MAAM,iBAAiB,GACrB,4DAA4D,CAAC;AAE/D,MAAM,2BAA2B,GAAG;IAClC,oBAAoB;IACpB,mBAAmB;CACX,CAAC;AAEX,MAAM,iBAAiB,GAAG,CAAC,QAAgB,EAAU,EAAE,CACrD,QAAQ,CAAC,UAAU,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;AAEjC,MAAM,QAAQ,GAAG,CAAC,IAAY,EAAU,EAAE,CAAC,IAAI,CAAC,UAAU,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC;AAE1E,MAAM,YAAY,GAAG,CAAC,UAAkB,EAAE,OAAe,EAAU,EAAE,CACnE,UAAU,CAAC,UAAU,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC;AAE7D,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC,QAAgB,EAAW,EAAE,CACtD,iBAAiB,CAAC,IAAI,CAAC,iBAAiB,CAAC,QAAQ,CAAC,CAAC,CAAC;AAEtD,MAAM,CAAC,MAAM,uBAAuB,GAAG,CAAC,QAAgB,EAAW,EAAE;IACnE,MAAM,UAAU,GAAG,iBAAiB,CAAC,QAAQ,CAAC,CAAC;IAC/C,OAAO,2BAA2B,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE,CAClD,UAAU,CAAC,QAAQ,CAAC,OAAO,CAAC,CAC7B,CAAC;AACJ,CAAC,CAAC;AAEF;;;GAGG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,UAAkB,EAAU,EAAE;IAC/D,IAAI,SAAS,GAAG,UAAU,CAAC;IAC3B,MAAM,QAAQ,GAAG;QACf,aAAa;QACb,mBAAmB;QACnB,+BAA+B;QAC/B,+BAA+B;QAC/B,aAAa;KACd,CAAC;IAEF,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;QAC/B,SAAS,GAAG,YAAY,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;IAC/C,CAAC;IAED,OAAO,SAAS,CAAC;AACnB,CAAC,CAAC"}
|