@keel_flow/eslint-plugin 0.2.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/dist/index.d.ts +89 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +49 -0
- package/dist/index.js.map +1 -0
- package/dist/rules/chunk-size-bounded.d.ts +4 -0
- package/dist/rules/chunk-size-bounded.d.ts.map +1 -0
- package/dist/rules/chunk-size-bounded.js +114 -0
- package/dist/rules/chunk-size-bounded.js.map +1 -0
- package/dist/rules/gate-is-reproducible.d.ts +4 -0
- package/dist/rules/gate-is-reproducible.d.ts.map +1 -0
- package/dist/rules/gate-is-reproducible.js +49 -0
- package/dist/rules/gate-is-reproducible.js.map +1 -0
- package/dist/rules/naming-is-design.d.ts +4 -0
- package/dist/rules/naming-is-design.d.ts.map +1 -0
- package/dist/rules/naming-is-design.js +44 -0
- package/dist/rules/naming-is-design.js.map +1 -0
- package/dist/rules/no-untyped-seam-fetch.d.ts +4 -0
- package/dist/rules/no-untyped-seam-fetch.d.ts.map +1 -0
- package/dist/rules/no-untyped-seam-fetch.js +167 -0
- package/dist/rules/no-untyped-seam-fetch.js.map +1 -0
- package/dist/rules/retrieval-confidence-floor.d.ts +4 -0
- package/dist/rules/retrieval-confidence-floor.d.ts.map +1 -0
- package/dist/rules/retrieval-confidence-floor.js +66 -0
- package/dist/rules/retrieval-confidence-floor.js.map +1 -0
- package/dist/rules/tests-as-contracts.d.ts +4 -0
- package/dist/rules/tests-as-contracts.d.ts.map +1 -0
- package/dist/rules/tests-as-contracts.js +61 -0
- package/dist/rules/tests-as-contracts.js.map +1 -0
- package/dist/rules/tool-failures-are-typed.d.ts +4 -0
- package/dist/rules/tool-failures-are-typed.d.ts.map +1 -0
- package/dist/rules/tool-failures-are-typed.js +48 -0
- package/dist/rules/tool-failures-are-typed.js.map +1 -0
- package/dist/rules/tool-schemas-are-load-bearing.d.ts +4 -0
- package/dist/rules/tool-schemas-are-load-bearing.d.ts.map +1 -0
- package/dist/rules/tool-schemas-are-load-bearing.js +41 -0
- package/dist/rules/tool-schemas-are-load-bearing.js.map +1 -0
- package/dist/rules/wrong-way-impossible.d.ts +4 -0
- package/dist/rules/wrong-way-impossible.d.ts.map +1 -0
- package/dist/rules/wrong-way-impossible.js +74 -0
- package/dist/rules/wrong-way-impossible.js.map +1 -0
- package/dist/tester.d.ts +3 -0
- package/dist/tester.d.ts.map +1 -0
- package/dist/tester.js +7 -0
- package/dist/tester.js.map +1 -0
- package/dist/util.d.ts +6 -0
- package/dist/util.d.ts.map +1 -0
- package/dist/util.js +7 -0
- package/dist/util.js.map +1 -0
- package/package.json +42 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 jglasskatz
|
|
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/dist/index.d.ts
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
export declare const plugin: {
|
|
2
|
+
meta: {
|
|
3
|
+
name: string;
|
|
4
|
+
version: string;
|
|
5
|
+
};
|
|
6
|
+
rules: {
|
|
7
|
+
readonly "tests-as-contracts": import("@typescript-eslint/utils/ts-eslint").RuleModule<"missingTest", [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
|
|
8
|
+
name: string;
|
|
9
|
+
};
|
|
10
|
+
readonly "wrong-way-impossible": import("@typescript-eslint/utils/ts-eslint").RuleModule<"anyUsed", [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
|
|
11
|
+
name: string;
|
|
12
|
+
};
|
|
13
|
+
readonly "naming-is-design": import("@typescript-eslint/utils/ts-eslint").RuleModule<"vague", [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
|
|
14
|
+
name: string;
|
|
15
|
+
};
|
|
16
|
+
readonly "tool-schemas-are-load-bearing": import("@typescript-eslint/utils/ts-eslint").RuleModule<"missingSchema", [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
|
|
17
|
+
name: string;
|
|
18
|
+
};
|
|
19
|
+
readonly "tool-failures-are-typed": import("@typescript-eslint/utils/ts-eslint").RuleModule<"unstructuredThrow", [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
|
|
20
|
+
name: string;
|
|
21
|
+
};
|
|
22
|
+
readonly "chunk-size-bounded": import("@typescript-eslint/utils/ts-eslint").RuleModule<"noSizeBound", [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
|
|
23
|
+
name: string;
|
|
24
|
+
};
|
|
25
|
+
readonly "retrieval-confidence-floor": import("@typescript-eslint/utils/ts-eslint").RuleModule<"noThreshold" | "optsNoFloor", [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
|
|
26
|
+
name: string;
|
|
27
|
+
};
|
|
28
|
+
readonly "gate-is-reproducible": import("@typescript-eslint/utils/ts-eslint").RuleModule<"absolutePath", [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
|
|
29
|
+
name: string;
|
|
30
|
+
};
|
|
31
|
+
readonly "no-untyped-seam-fetch": import("@typescript-eslint/utils/ts-eslint").RuleModule<"untypedSeamFetch", [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
|
|
32
|
+
name: string;
|
|
33
|
+
};
|
|
34
|
+
};
|
|
35
|
+
configs: Record<string, unknown>;
|
|
36
|
+
};
|
|
37
|
+
export declare const recommended: {
|
|
38
|
+
plugins: {
|
|
39
|
+
"@keel_flow": {
|
|
40
|
+
meta: {
|
|
41
|
+
name: string;
|
|
42
|
+
version: string;
|
|
43
|
+
};
|
|
44
|
+
rules: {
|
|
45
|
+
readonly "tests-as-contracts": import("@typescript-eslint/utils/ts-eslint").RuleModule<"missingTest", [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
|
|
46
|
+
name: string;
|
|
47
|
+
};
|
|
48
|
+
readonly "wrong-way-impossible": import("@typescript-eslint/utils/ts-eslint").RuleModule<"anyUsed", [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
|
|
49
|
+
name: string;
|
|
50
|
+
};
|
|
51
|
+
readonly "naming-is-design": import("@typescript-eslint/utils/ts-eslint").RuleModule<"vague", [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
|
|
52
|
+
name: string;
|
|
53
|
+
};
|
|
54
|
+
readonly "tool-schemas-are-load-bearing": import("@typescript-eslint/utils/ts-eslint").RuleModule<"missingSchema", [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
|
|
55
|
+
name: string;
|
|
56
|
+
};
|
|
57
|
+
readonly "tool-failures-are-typed": import("@typescript-eslint/utils/ts-eslint").RuleModule<"unstructuredThrow", [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
|
|
58
|
+
name: string;
|
|
59
|
+
};
|
|
60
|
+
readonly "chunk-size-bounded": import("@typescript-eslint/utils/ts-eslint").RuleModule<"noSizeBound", [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
|
|
61
|
+
name: string;
|
|
62
|
+
};
|
|
63
|
+
readonly "retrieval-confidence-floor": import("@typescript-eslint/utils/ts-eslint").RuleModule<"noThreshold" | "optsNoFloor", [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
|
|
64
|
+
name: string;
|
|
65
|
+
};
|
|
66
|
+
readonly "gate-is-reproducible": import("@typescript-eslint/utils/ts-eslint").RuleModule<"absolutePath", [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
|
|
67
|
+
name: string;
|
|
68
|
+
};
|
|
69
|
+
readonly "no-untyped-seam-fetch": import("@typescript-eslint/utils/ts-eslint").RuleModule<"untypedSeamFetch", [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
|
|
70
|
+
name: string;
|
|
71
|
+
};
|
|
72
|
+
};
|
|
73
|
+
configs: Record<string, unknown>;
|
|
74
|
+
};
|
|
75
|
+
};
|
|
76
|
+
rules: {
|
|
77
|
+
readonly "@keel_flow/tests-as-contracts": "error";
|
|
78
|
+
readonly "@keel_flow/wrong-way-impossible": "error";
|
|
79
|
+
readonly "@keel_flow/naming-is-design": "warn";
|
|
80
|
+
readonly "@keel_flow/tool-schemas-are-load-bearing": "error";
|
|
81
|
+
readonly "@keel_flow/tool-failures-are-typed": "error";
|
|
82
|
+
readonly "@keel_flow/chunk-size-bounded": "warn";
|
|
83
|
+
readonly "@keel_flow/retrieval-confidence-floor": "error";
|
|
84
|
+
readonly "@keel_flow/gate-is-reproducible": "error";
|
|
85
|
+
readonly "@keel_flow/no-untyped-seam-fetch": "error";
|
|
86
|
+
};
|
|
87
|
+
};
|
|
88
|
+
export default plugin;
|
|
89
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAsBA,eAAO,MAAM,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;aAMF,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;CACvC,CAAC;AAEF,eAAO,MAAM,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;qBAHP,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;;;;;;;;;;;;;;CAoBvC,CAAC;AAIF,eAAe,MAAM,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { testsAsContracts } from "./rules/tests-as-contracts.js";
|
|
2
|
+
import { wrongWayImpossible } from "./rules/wrong-way-impossible.js";
|
|
3
|
+
import { namingIsDesign } from "./rules/naming-is-design.js";
|
|
4
|
+
import { toolSchemasAreLoadBearing } from "./rules/tool-schemas-are-load-bearing.js";
|
|
5
|
+
import { toolFailuresAreTyped } from "./rules/tool-failures-are-typed.js";
|
|
6
|
+
import { chunkSizeBounded } from "./rules/chunk-size-bounded.js";
|
|
7
|
+
import { retrievalConfidenceFloor } from "./rules/retrieval-confidence-floor.js";
|
|
8
|
+
import { gateIsReproducible } from "./rules/gate-is-reproducible.js";
|
|
9
|
+
import { noUntypedSeamFetch } from "./rules/no-untyped-seam-fetch.js";
|
|
10
|
+
const rules = {
|
|
11
|
+
"tests-as-contracts": testsAsContracts,
|
|
12
|
+
"wrong-way-impossible": wrongWayImpossible,
|
|
13
|
+
"naming-is-design": namingIsDesign,
|
|
14
|
+
"tool-schemas-are-load-bearing": toolSchemasAreLoadBearing,
|
|
15
|
+
"tool-failures-are-typed": toolFailuresAreTyped,
|
|
16
|
+
"chunk-size-bounded": chunkSizeBounded,
|
|
17
|
+
"retrieval-confidence-floor": retrievalConfidenceFloor,
|
|
18
|
+
"gate-is-reproducible": gateIsReproducible,
|
|
19
|
+
"no-untyped-seam-fetch": noUntypedSeamFetch,
|
|
20
|
+
};
|
|
21
|
+
export const plugin = {
|
|
22
|
+
meta: {
|
|
23
|
+
name: "@keel_flow/eslint-plugin",
|
|
24
|
+
version: "0.1.0",
|
|
25
|
+
},
|
|
26
|
+
rules,
|
|
27
|
+
configs: {},
|
|
28
|
+
};
|
|
29
|
+
export const recommended = {
|
|
30
|
+
plugins: { "@keel_flow": plugin },
|
|
31
|
+
rules: {
|
|
32
|
+
// Severity mirrors the principle's severity (CLAUDE.md: critical → error,
|
|
33
|
+
// warning → warn) so an editor warning and a `keel verify` violation always
|
|
34
|
+
// agree. tests-as-contracts, tool-failures-are-typed, and
|
|
35
|
+
// retrieval-confidence-floor are critical principles and must be errors.
|
|
36
|
+
"@keel_flow/tests-as-contracts": "error",
|
|
37
|
+
"@keel_flow/wrong-way-impossible": "error",
|
|
38
|
+
"@keel_flow/naming-is-design": "warn",
|
|
39
|
+
"@keel_flow/tool-schemas-are-load-bearing": "error",
|
|
40
|
+
"@keel_flow/tool-failures-are-typed": "error",
|
|
41
|
+
"@keel_flow/chunk-size-bounded": "warn",
|
|
42
|
+
"@keel_flow/retrieval-confidence-floor": "error",
|
|
43
|
+
"@keel_flow/gate-is-reproducible": "error",
|
|
44
|
+
"@keel_flow/no-untyped-seam-fetch": "error",
|
|
45
|
+
},
|
|
46
|
+
};
|
|
47
|
+
plugin.configs["recommended"] = recommended;
|
|
48
|
+
export default plugin;
|
|
49
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AACjE,OAAO,EAAE,kBAAkB,EAAE,MAAM,iCAAiC,CAAC;AACrE,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAC7D,OAAO,EAAE,yBAAyB,EAAE,MAAM,0CAA0C,CAAC;AACrF,OAAO,EAAE,oBAAoB,EAAE,MAAM,oCAAoC,CAAC;AAC1E,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AACjE,OAAO,EAAE,wBAAwB,EAAE,MAAM,uCAAuC,CAAC;AACjF,OAAO,EAAE,kBAAkB,EAAE,MAAM,iCAAiC,CAAC;AACrE,OAAO,EAAE,kBAAkB,EAAE,MAAM,kCAAkC,CAAC;AAEtE,MAAM,KAAK,GAAG;IACZ,oBAAoB,EAAE,gBAAgB;IACtC,sBAAsB,EAAE,kBAAkB;IAC1C,kBAAkB,EAAE,cAAc;IAClC,+BAA+B,EAAE,yBAAyB;IAC1D,yBAAyB,EAAE,oBAAoB;IAC/C,oBAAoB,EAAE,gBAAgB;IACtC,4BAA4B,EAAE,wBAAwB;IACtD,sBAAsB,EAAE,kBAAkB;IAC1C,uBAAuB,EAAE,kBAAkB;CACnC,CAAC;AAEX,MAAM,CAAC,MAAM,MAAM,GAAG;IACpB,IAAI,EAAE;QACJ,IAAI,EAAE,0BAA0B;QAChC,OAAO,EAAE,OAAO;KACjB;IACD,KAAK;IACL,OAAO,EAAE,EAA6B;CACvC,CAAC;AAEF,MAAM,CAAC,MAAM,WAAW,GAAG;IACzB,OAAO,EAAE,EAAE,YAAY,EAAE,MAAM,EAAE;IACjC,KAAK,EAAE;QACL,0EAA0E;QAC1E,4EAA4E;QAC5E,0DAA0D;QAC1D,yEAAyE;QACzE,+BAA+B,EAAE,OAAO;QACxC,iCAAiC,EAAE,OAAO;QAC1C,6BAA6B,EAAE,MAAM;QACrC,0CAA0C,EAAE,OAAO;QACnD,oCAAoC,EAAE,OAAO;QAC7C,+BAA+B,EAAE,MAAM;QACvC,uCAAuC,EAAE,OAAO;QAChD,iCAAiC,EAAE,OAAO;QAC1C,kCAAkC,EAAE,OAAO;KACnC;CACX,CAAC;AAEF,MAAM,CAAC,OAAO,CAAC,aAAa,CAAC,GAAG,WAAW,CAAC;AAE5C,eAAe,MAAM,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chunk-size-bounded.d.ts","sourceRoot":"","sources":["../../src/rules/chunk-size-bounded.ts"],"names":[],"mappings":"AAsCA,eAAO,MAAM,gBAAgB;;CAkG3B,CAAC"}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { AST_NODE_TYPES } from "@typescript-eslint/utils";
|
|
2
|
+
import { createRule, hasIgnoreFileDirective } from "../util.js";
|
|
3
|
+
const SIZE_PARAM = /maxChunkSize|chunkSize|maxTokens|tokenLimit|maxSize|windowSize/i;
|
|
4
|
+
function returnsArray(fn) {
|
|
5
|
+
return fn.returnType?.typeAnnotation.type === AST_NODE_TYPES.TSArrayType;
|
|
6
|
+
}
|
|
7
|
+
function paramDeclaresSize(fn) {
|
|
8
|
+
for (const p of fn.params) {
|
|
9
|
+
if (p.type === AST_NODE_TYPES.Identifier && SIZE_PARAM.test(p.name))
|
|
10
|
+
return true;
|
|
11
|
+
if (p.type === AST_NODE_TYPES.ObjectPattern) {
|
|
12
|
+
for (const prop of p.properties) {
|
|
13
|
+
if (prop.type === AST_NODE_TYPES.Property &&
|
|
14
|
+
prop.key.type === AST_NODE_TYPES.Identifier &&
|
|
15
|
+
SIZE_PARAM.test(prop.key.name)) {
|
|
16
|
+
return true;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
return false;
|
|
22
|
+
}
|
|
23
|
+
export const chunkSizeBounded = createRule({
|
|
24
|
+
name: "chunk-size-bounded",
|
|
25
|
+
meta: {
|
|
26
|
+
type: "suggestion",
|
|
27
|
+
docs: {
|
|
28
|
+
description: "A chunk-producing function (name matches /chunk/i and returns an array) must bound chunk size: take a size parameter, read one from its options object, or pass one into a delegated chunker (maxSize / windowSize / maxChunkSize / chunkSize / maxTokens / tokenLimit).",
|
|
29
|
+
},
|
|
30
|
+
schema: [],
|
|
31
|
+
messages: {
|
|
32
|
+
noSizeBound: 'Chunk-producing function "{{name}}" does not bound chunk size. Accept or read a maxSize/windowSize/tokenLimit value.',
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
defaultOptions: [],
|
|
36
|
+
create(context) {
|
|
37
|
+
const filename = context.filename ?? "";
|
|
38
|
+
if (!filename.includes("kb") && !filename.includes("chunk") && !filename.includes("ingest")) {
|
|
39
|
+
return {};
|
|
40
|
+
}
|
|
41
|
+
if (filename.endsWith(".test.ts"))
|
|
42
|
+
return {};
|
|
43
|
+
const stack = [];
|
|
44
|
+
const producers = [];
|
|
45
|
+
function enter(fn, name) {
|
|
46
|
+
if (name && /chunk/i.test(name) && returnsArray(fn)) {
|
|
47
|
+
const producer = { fn, name, hasSizeBound: paramDeclaresSize(fn) };
|
|
48
|
+
stack.push(producer);
|
|
49
|
+
producers.push(producer);
|
|
50
|
+
}
|
|
51
|
+
else {
|
|
52
|
+
stack.push({ fn, name: name ?? "", hasSizeBound: true });
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
function exit() {
|
|
56
|
+
stack.pop();
|
|
57
|
+
}
|
|
58
|
+
function markSizeRead() {
|
|
59
|
+
const top = stack[stack.length - 1];
|
|
60
|
+
if (top)
|
|
61
|
+
top.hasSizeBound = true;
|
|
62
|
+
}
|
|
63
|
+
function enterExpr(node) {
|
|
64
|
+
const parent = node.parent;
|
|
65
|
+
const name = parent.type === AST_NODE_TYPES.VariableDeclarator &&
|
|
66
|
+
parent.id.type === AST_NODE_TYPES.Identifier
|
|
67
|
+
? parent.id.name
|
|
68
|
+
: undefined;
|
|
69
|
+
enter(node, name);
|
|
70
|
+
}
|
|
71
|
+
return {
|
|
72
|
+
FunctionDeclaration(node) {
|
|
73
|
+
enter(node, node.id?.name);
|
|
74
|
+
},
|
|
75
|
+
"FunctionDeclaration:exit"() {
|
|
76
|
+
exit();
|
|
77
|
+
},
|
|
78
|
+
ArrowFunctionExpression(node) {
|
|
79
|
+
enterExpr(node);
|
|
80
|
+
},
|
|
81
|
+
"ArrowFunctionExpression:exit"() {
|
|
82
|
+
exit();
|
|
83
|
+
},
|
|
84
|
+
FunctionExpression(node) {
|
|
85
|
+
enterExpr(node);
|
|
86
|
+
},
|
|
87
|
+
"FunctionExpression:exit"() {
|
|
88
|
+
exit();
|
|
89
|
+
},
|
|
90
|
+
MemberExpression(node) {
|
|
91
|
+
if (node.property.type === AST_NODE_TYPES.Identifier &&
|
|
92
|
+
SIZE_PARAM.test(node.property.name)) {
|
|
93
|
+
markSizeRead();
|
|
94
|
+
}
|
|
95
|
+
},
|
|
96
|
+
Property(node) {
|
|
97
|
+
if (node.key.type === AST_NODE_TYPES.Identifier && SIZE_PARAM.test(node.key.name)) {
|
|
98
|
+
markSizeRead();
|
|
99
|
+
}
|
|
100
|
+
},
|
|
101
|
+
"Program:exit"() {
|
|
102
|
+
const offending = producers.filter((p) => !p.hasSizeBound);
|
|
103
|
+
if (offending.length === 0)
|
|
104
|
+
return;
|
|
105
|
+
if (hasIgnoreFileDirective(context.sourceCode.getText(), "chunk-size-bounded"))
|
|
106
|
+
return;
|
|
107
|
+
for (const o of offending) {
|
|
108
|
+
context.report({ node: o.fn, messageId: "noSizeBound", data: { name: o.name } });
|
|
109
|
+
}
|
|
110
|
+
},
|
|
111
|
+
};
|
|
112
|
+
},
|
|
113
|
+
});
|
|
114
|
+
//# sourceMappingURL=chunk-size-bounded.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chunk-size-bounded.js","sourceRoot":"","sources":["../../src/rules/chunk-size-bounded.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gate-is-reproducible.d.ts","sourceRoot":"","sources":["../../src/rules/gate-is-reproducible.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,kBAAkB;;CA4C7B,CAAC"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { createRule } from "../util.js";
|
|
2
|
+
const ABSOLUTE_PATH_RE = /\/Users\/[^/\s"'`]+\/|\/home\/[^/\s"'`]+\//;
|
|
3
|
+
export const gateIsReproducible = createRule({
|
|
4
|
+
name: "gate-is-reproducible",
|
|
5
|
+
meta: {
|
|
6
|
+
type: "problem",
|
|
7
|
+
docs: {
|
|
8
|
+
description: "Hardcoded absolute user/home paths (/Users/<name>/ or /home/<name>/) make results machine-specific and break CI reproducibility.",
|
|
9
|
+
},
|
|
10
|
+
schema: [],
|
|
11
|
+
messages: {
|
|
12
|
+
absolutePath: 'Hardcoded absolute user path "{{path}}" detected. Use process.cwd(), os.homedir(), or a relative path instead.',
|
|
13
|
+
},
|
|
14
|
+
},
|
|
15
|
+
defaultOptions: [],
|
|
16
|
+
create(context) {
|
|
17
|
+
const filename = context.filename ?? "";
|
|
18
|
+
if (filename.endsWith("/architecture/data.ts"))
|
|
19
|
+
return {};
|
|
20
|
+
return {
|
|
21
|
+
Literal(node) {
|
|
22
|
+
if (typeof node.value !== "string")
|
|
23
|
+
return;
|
|
24
|
+
const match = ABSOLUTE_PATH_RE.exec(node.value);
|
|
25
|
+
if (!match)
|
|
26
|
+
return;
|
|
27
|
+
context.report({
|
|
28
|
+
node,
|
|
29
|
+
messageId: "absolutePath",
|
|
30
|
+
data: { path: match[0].replace(/\/$/, "") },
|
|
31
|
+
});
|
|
32
|
+
},
|
|
33
|
+
TemplateLiteral(node) {
|
|
34
|
+
for (const quasi of node.quasis) {
|
|
35
|
+
const raw = quasi.value.raw;
|
|
36
|
+
const match = ABSOLUTE_PATH_RE.exec(raw);
|
|
37
|
+
if (!match)
|
|
38
|
+
continue;
|
|
39
|
+
context.report({
|
|
40
|
+
node: quasi,
|
|
41
|
+
messageId: "absolutePath",
|
|
42
|
+
data: { path: match[0].replace(/\/$/, "") },
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
};
|
|
47
|
+
},
|
|
48
|
+
});
|
|
49
|
+
//# sourceMappingURL=gate-is-reproducible.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gate-is-reproducible.js","sourceRoot":"","sources":["../../src/rules/gate-is-reproducible.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAExC,MAAM,gBAAgB,GAAG,4CAA4C,CAAC;AAEtE,MAAM,CAAC,MAAM,kBAAkB,GAAG,UAAU,CAAC;IAC3C,IAAI,EAAE,sBAAsB;IAC5B,IAAI,EAAE;QACJ,IAAI,EAAE,SAAS;QACf,IAAI,EAAE;YACJ,WAAW,EACT,kIAAkI;SACrI;QACD,MAAM,EAAE,EAAE;QACV,QAAQ,EAAE;YACR,YAAY,EACV,gHAAgH;SACnH;KACF;IACD,cAAc,EAAE,EAAE;IAClB,MAAM,CAAC,OAAO;QACZ,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,IAAI,EAAE,CAAC;QACxC,IAAI,QAAQ,CAAC,QAAQ,CAAC,uBAAuB,CAAC;YAAE,OAAO,EAAE,CAAC;QAE1D,OAAO;YACL,OAAO,CAAC,IAAI;gBACV,IAAI,OAAO,IAAI,CAAC,KAAK,KAAK,QAAQ;oBAAE,OAAO;gBAC3C,MAAM,KAAK,GAAG,gBAAgB,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;gBAChD,IAAI,CAAC,KAAK;oBAAE,OAAO;gBACnB,OAAO,CAAC,MAAM,CAAC;oBACb,IAAI;oBACJ,SAAS,EAAE,cAAc;oBACzB,IAAI,EAAE,EAAE,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,EAAE;iBAC5C,CAAC,CAAC;YACL,CAAC;YACD,eAAe,CAAC,IAAI;gBAClB,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;oBAChC,MAAM,GAAG,GAAG,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC;oBAC5B,MAAM,KAAK,GAAG,gBAAgB,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;oBACzC,IAAI,CAAC,KAAK;wBAAE,SAAS;oBACrB,OAAO,CAAC,MAAM,CAAC;wBACb,IAAI,EAAE,KAAK;wBACX,SAAS,EAAE,cAAc;wBACzB,IAAI,EAAE,EAAE,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,EAAE;qBAC5C,CAAC,CAAC;gBACL,CAAC;YACH,CAAC;SACF,CAAC;IACJ,CAAC;CACF,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"naming-is-design.d.ts","sourceRoot":"","sources":["../../src/rules/naming-is-design.ts"],"names":[],"mappings":"AAKA,eAAO,MAAM,cAAc;;CAsCzB,CAAC"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { AST_NODE_TYPES } from "@typescript-eslint/utils";
|
|
2
|
+
import { createRule, hasIgnoreFileDirective } from "../util.js";
|
|
3
|
+
const VAGUE = new Set(["data", "info", "temp", "tmp", "obj", "item", "thing", "stuff", "val"]);
|
|
4
|
+
export const namingIsDesign = createRule({
|
|
5
|
+
name: "naming-is-design",
|
|
6
|
+
meta: {
|
|
7
|
+
type: "suggestion",
|
|
8
|
+
docs: {
|
|
9
|
+
description: "Flag vague identifier names (data, info, temp, obj, item, thing, stuff, val) as variable, parameter, or property names.",
|
|
10
|
+
},
|
|
11
|
+
schema: [],
|
|
12
|
+
messages: {
|
|
13
|
+
vague: 'Vague identifier "{{name}}" — names must communicate intent precisely (Keel principle: naming-is-design).',
|
|
14
|
+
},
|
|
15
|
+
},
|
|
16
|
+
defaultOptions: [],
|
|
17
|
+
create(context) {
|
|
18
|
+
const sourceText = context.sourceCode.getText();
|
|
19
|
+
if (hasIgnoreFileDirective(sourceText, "naming-is-design"))
|
|
20
|
+
return {};
|
|
21
|
+
const filename = context.filename ?? "";
|
|
22
|
+
if (filename.endsWith("/architecture/data.ts"))
|
|
23
|
+
return {};
|
|
24
|
+
if (/packages\/pack-[^/]+\/src\/index\.ts$/.test(filename))
|
|
25
|
+
return {};
|
|
26
|
+
if (/packages\/eslint-plugin\/src\/rules\//.test(filename))
|
|
27
|
+
return {};
|
|
28
|
+
function reportIfVague(node) {
|
|
29
|
+
if (VAGUE.has(node.name)) {
|
|
30
|
+
context.report({ node, messageId: "vague", data: { name: node.name } });
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
return {
|
|
34
|
+
VariableDeclarator(node) {
|
|
35
|
+
if (node.id.type === AST_NODE_TYPES.Identifier)
|
|
36
|
+
reportIfVague(node.id);
|
|
37
|
+
},
|
|
38
|
+
":function > Identifier.params"(node) {
|
|
39
|
+
reportIfVague(node);
|
|
40
|
+
},
|
|
41
|
+
};
|
|
42
|
+
},
|
|
43
|
+
});
|
|
44
|
+
//# sourceMappingURL=naming-is-design.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"naming-is-design.js","sourceRoot":"","sources":["../../src/rules/naming-is-design.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAiB,MAAM,0BAA0B,CAAC;AACzE,OAAO,EAAE,UAAU,EAAE,sBAAsB,EAAE,MAAM,YAAY,CAAC;AAEhE,MAAM,KAAK,GAAG,IAAI,GAAG,CAAC,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,KAAK,CAAC,CAAC,CAAC;AAE/F,MAAM,CAAC,MAAM,cAAc,GAAG,UAAU,CAAC;IACvC,IAAI,EAAE,kBAAkB;IACxB,IAAI,EAAE;QACJ,IAAI,EAAE,YAAY;QAClB,IAAI,EAAE;YACJ,WAAW,EACT,yHAAyH;SAC5H;QACD,MAAM,EAAE,EAAE;QACV,QAAQ,EAAE;YACR,KAAK,EACH,2GAA2G;SAC9G;KACF;IACD,cAAc,EAAE,EAAE;IAClB,MAAM,CAAC,OAAO;QACZ,MAAM,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC,OAAO,EAAE,CAAC;QAChD,IAAI,sBAAsB,CAAC,UAAU,EAAE,kBAAkB,CAAC;YAAE,OAAO,EAAE,CAAC;QACtE,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,IAAI,EAAE,CAAC;QACxC,IAAI,QAAQ,CAAC,QAAQ,CAAC,uBAAuB,CAAC;YAAE,OAAO,EAAE,CAAC;QAC1D,IAAI,uCAAuC,CAAC,IAAI,CAAC,QAAQ,CAAC;YAAE,OAAO,EAAE,CAAC;QACtE,IAAI,uCAAuC,CAAC,IAAI,CAAC,QAAQ,CAAC;YAAE,OAAO,EAAE,CAAC;QAEtE,SAAS,aAAa,CAAC,IAAyB;YAC9C,IAAI,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;gBACzB,OAAO,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;YAC1E,CAAC;QACH,CAAC;QAED,OAAO;YACL,kBAAkB,CAAC,IAAI;gBACrB,IAAI,IAAI,CAAC,EAAE,CAAC,IAAI,KAAK,cAAc,CAAC,UAAU;oBAAE,aAAa,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;YACzE,CAAC;YACD,+BAA+B,CAAC,IAAyB;gBACvD,aAAa,CAAC,IAAI,CAAC,CAAC;YACtB,CAAC;SACF,CAAC;IACJ,CAAC;CACF,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"no-untyped-seam-fetch.d.ts","sourceRoot":"","sources":["../../src/rules/no-untyped-seam-fetch.ts"],"names":[],"mappings":"AAmIA,eAAO,MAAM,kBAAkB;;CAwC7B,CAAC"}
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import { AST_NODE_TYPES } from "@typescript-eslint/utils";
|
|
2
|
+
import { createRule } from "../util.js";
|
|
3
|
+
const API_URL_VARS = new Set([
|
|
4
|
+
"API_URL",
|
|
5
|
+
"KEEL_API_URL",
|
|
6
|
+
"apiUrl",
|
|
7
|
+
"apiURL",
|
|
8
|
+
"baseUrl",
|
|
9
|
+
"baseURL",
|
|
10
|
+
"apiBase",
|
|
11
|
+
"apiBaseUrl",
|
|
12
|
+
"BASE_URL",
|
|
13
|
+
"API_BASE_URL",
|
|
14
|
+
]);
|
|
15
|
+
const KEEL_ENV_KEYS = new Set([
|
|
16
|
+
"KEEL_API_URL",
|
|
17
|
+
"API_URL",
|
|
18
|
+
"API_BASE_URL",
|
|
19
|
+
]);
|
|
20
|
+
const FETCH_HOLDERS = new Set(["globalThis", "window", "self"]);
|
|
21
|
+
function isFetchCallee(callee) {
|
|
22
|
+
if (callee.type === AST_NODE_TYPES.Identifier)
|
|
23
|
+
return callee.name === "fetch";
|
|
24
|
+
return (callee.type === AST_NODE_TYPES.MemberExpression &&
|
|
25
|
+
!callee.computed &&
|
|
26
|
+
callee.property.type === AST_NODE_TYPES.Identifier &&
|
|
27
|
+
callee.property.name === "fetch" &&
|
|
28
|
+
callee.object.type === AST_NODE_TYPES.Identifier &&
|
|
29
|
+
FETCH_HOLDERS.has(callee.object.name));
|
|
30
|
+
}
|
|
31
|
+
const NON_EXTENSION_RE = /^[a-zA-Z][a-zA-Z0-9_]+$/;
|
|
32
|
+
const KNOWN_EXTENSIONS = new Set([
|
|
33
|
+
"png", "jpg", "jpeg", "gif", "svg", "ico", "webp",
|
|
34
|
+
"js", "ts", "mjs", "cjs", "jsx", "tsx",
|
|
35
|
+
"css", "scss", "less",
|
|
36
|
+
"json", "yaml", "yml", "xml", "html", "htm",
|
|
37
|
+
"txt", "md", "pdf", "csv",
|
|
38
|
+
"woff", "woff2", "ttf", "eot",
|
|
39
|
+
"map",
|
|
40
|
+
]);
|
|
41
|
+
function looksLikeTrpcPath(segment) {
|
|
42
|
+
const lastSlashIdx = segment.lastIndexOf("/");
|
|
43
|
+
const lastSegment = lastSlashIdx >= 0 ? segment.slice(lastSlashIdx + 1) : segment;
|
|
44
|
+
const dotIdx = lastSegment.indexOf(".");
|
|
45
|
+
if (dotIdx < 0)
|
|
46
|
+
return false;
|
|
47
|
+
const routerPart = lastSegment.slice(0, dotIdx);
|
|
48
|
+
const procedurePart = lastSegment.slice(dotIdx + 1);
|
|
49
|
+
if (!NON_EXTENSION_RE.test(routerPart))
|
|
50
|
+
return false;
|
|
51
|
+
if (!NON_EXTENSION_RE.test(procedurePart))
|
|
52
|
+
return false;
|
|
53
|
+
if (KNOWN_EXTENSIONS.has(procedurePart.toLowerCase()))
|
|
54
|
+
return false;
|
|
55
|
+
return true;
|
|
56
|
+
}
|
|
57
|
+
function isProcessEnvApiKey(node) {
|
|
58
|
+
if (node.type !== AST_NODE_TYPES.MemberExpression)
|
|
59
|
+
return false;
|
|
60
|
+
if (node.object.type !== AST_NODE_TYPES.MemberExpression ||
|
|
61
|
+
node.object.object.type !== AST_NODE_TYPES.Identifier ||
|
|
62
|
+
node.object.object.name !== "process" ||
|
|
63
|
+
node.object.property.type !== AST_NODE_TYPES.Identifier ||
|
|
64
|
+
node.object.property.name !== "env")
|
|
65
|
+
return false;
|
|
66
|
+
if (!node.computed && node.property.type === AST_NODE_TYPES.Identifier) {
|
|
67
|
+
return KEEL_ENV_KEYS.has(node.property.name);
|
|
68
|
+
}
|
|
69
|
+
if (node.computed && node.property.type === AST_NODE_TYPES.Literal && typeof node.property.value === "string") {
|
|
70
|
+
return KEEL_ENV_KEYS.has(node.property.value);
|
|
71
|
+
}
|
|
72
|
+
return false;
|
|
73
|
+
}
|
|
74
|
+
function isProcessEnvAnyKey(node) {
|
|
75
|
+
return (node.type === AST_NODE_TYPES.MemberExpression &&
|
|
76
|
+
node.object.type === AST_NODE_TYPES.MemberExpression &&
|
|
77
|
+
node.object.object.type === AST_NODE_TYPES.Identifier &&
|
|
78
|
+
node.object.object.name === "process" &&
|
|
79
|
+
node.object.property.type === AST_NODE_TYPES.Identifier &&
|
|
80
|
+
node.object.property.name === "env");
|
|
81
|
+
}
|
|
82
|
+
function isApiUrlReference(node, aliasedVars) {
|
|
83
|
+
if (node.type === AST_NODE_TYPES.Identifier) {
|
|
84
|
+
if (API_URL_VARS.has(node.name))
|
|
85
|
+
return true;
|
|
86
|
+
if (aliasedVars.has(node.name))
|
|
87
|
+
return true;
|
|
88
|
+
}
|
|
89
|
+
if (isProcessEnvAnyKey(node))
|
|
90
|
+
return true;
|
|
91
|
+
return false;
|
|
92
|
+
}
|
|
93
|
+
function templateHasApiSeam(node, aliasedVars) {
|
|
94
|
+
const hasApiRef = node.expressions.some((expr) => isApiUrlReference(expr, aliasedVars));
|
|
95
|
+
if (!hasApiRef)
|
|
96
|
+
return false;
|
|
97
|
+
return node.quasis.some((quasi) => looksLikeTrpcPath(quasi.value.raw));
|
|
98
|
+
}
|
|
99
|
+
const KEEL_API_HOSTS = /^https?:\/\/(localhost|127\.0\.0\.1)(:\d+)?/;
|
|
100
|
+
function stringLiteralHasApiSeam(value) {
|
|
101
|
+
if (!KEEL_API_HOSTS.test(value))
|
|
102
|
+
return false;
|
|
103
|
+
return looksLikeTrpcPath(value);
|
|
104
|
+
}
|
|
105
|
+
function leftmostBinaryOperand(node) {
|
|
106
|
+
if (node.type === AST_NODE_TYPES.BinaryExpression && node.operator === "+") {
|
|
107
|
+
return leftmostBinaryOperand(node.left);
|
|
108
|
+
}
|
|
109
|
+
return node;
|
|
110
|
+
}
|
|
111
|
+
function firstArgIsApiSeam(arg, aliasedVars) {
|
|
112
|
+
if (arg.type === AST_NODE_TYPES.TemplateLiteral)
|
|
113
|
+
return templateHasApiSeam(arg, aliasedVars);
|
|
114
|
+
if (arg.type === AST_NODE_TYPES.Literal && typeof arg.value === "string") {
|
|
115
|
+
return stringLiteralHasApiSeam(arg.value);
|
|
116
|
+
}
|
|
117
|
+
if (arg.type === AST_NODE_TYPES.BinaryExpression && arg.operator === "+") {
|
|
118
|
+
if (isApiUrlReference(leftmostBinaryOperand(arg), aliasedVars)) {
|
|
119
|
+
return true;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
return false;
|
|
123
|
+
}
|
|
124
|
+
export const noUntypedSeamFetch = createRule({
|
|
125
|
+
name: "no-untyped-seam-fetch",
|
|
126
|
+
meta: {
|
|
127
|
+
type: "problem",
|
|
128
|
+
docs: {
|
|
129
|
+
description: "Raw fetch() to the tRPC API base URL bypasses the typed client. Use the server-side tRPC client (createServerClient) so procedure names and input shapes are type-checked end-to-end.",
|
|
130
|
+
},
|
|
131
|
+
schema: [],
|
|
132
|
+
messages: {
|
|
133
|
+
untypedSeamFetch: "Raw fetch() to the API seam is untyped. Import createServerClient from lib/server-client and call the typed procedure instead (e.g. api.workspaces.create.mutate(...)).",
|
|
134
|
+
},
|
|
135
|
+
},
|
|
136
|
+
defaultOptions: [],
|
|
137
|
+
create(context) {
|
|
138
|
+
const filename = context.filename ?? "";
|
|
139
|
+
if (filename.endsWith(".test.ts") || filename.endsWith(".test.tsx"))
|
|
140
|
+
return {};
|
|
141
|
+
if (/packages\/(cli|telemetry)\//.test(filename))
|
|
142
|
+
return {};
|
|
143
|
+
const aliasedVars = new Set();
|
|
144
|
+
return {
|
|
145
|
+
VariableDeclarator(node) {
|
|
146
|
+
if (node.id.type !== AST_NODE_TYPES.Identifier)
|
|
147
|
+
return;
|
|
148
|
+
if (!node.init)
|
|
149
|
+
return;
|
|
150
|
+
if (isProcessEnvApiKey(node.init)) {
|
|
151
|
+
aliasedVars.add(node.id.name);
|
|
152
|
+
}
|
|
153
|
+
},
|
|
154
|
+
CallExpression(node) {
|
|
155
|
+
if (!isFetchCallee(node.callee))
|
|
156
|
+
return;
|
|
157
|
+
const firstArg = node.arguments[0];
|
|
158
|
+
if (!firstArg)
|
|
159
|
+
return;
|
|
160
|
+
if (firstArgIsApiSeam(firstArg, aliasedVars)) {
|
|
161
|
+
context.report({ node, messageId: "untypedSeamFetch" });
|
|
162
|
+
}
|
|
163
|
+
},
|
|
164
|
+
};
|
|
165
|
+
},
|
|
166
|
+
});
|
|
167
|
+
//# sourceMappingURL=no-untyped-seam-fetch.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"no-untyped-seam-fetch.js","sourceRoot":"","sources":["../../src/rules/no-untyped-seam-fetch.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export declare const retrievalConfidenceFloor: import("@typescript-eslint/utils/ts-eslint").RuleModule<"noThreshold" | "optsNoFloor", [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
|
|
2
|
+
name: string;
|
|
3
|
+
};
|
|
4
|
+
//# sourceMappingURL=retrieval-confidence-floor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"retrieval-confidence-floor.d.ts","sourceRoot":"","sources":["../../src/rules/retrieval-confidence-floor.ts"],"names":[],"mappings":"AAuCA,eAAO,MAAM,wBAAwB;;CAmCnC,CAAC"}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { AST_NODE_TYPES } from "@typescript-eslint/utils";
|
|
2
|
+
import { createRule } from "../util.js";
|
|
3
|
+
const THRESHOLD = /threshold|minScore|similarity|floor/i;
|
|
4
|
+
function isRetrieveCallee(callee) {
|
|
5
|
+
if (callee.type === AST_NODE_TYPES.Identifier)
|
|
6
|
+
return callee.name === "retrieve";
|
|
7
|
+
if (callee.type === AST_NODE_TYPES.MemberExpression &&
|
|
8
|
+
callee.property.type === AST_NODE_TYPES.Identifier) {
|
|
9
|
+
return callee.property.name === "retrieve";
|
|
10
|
+
}
|
|
11
|
+
return false;
|
|
12
|
+
}
|
|
13
|
+
function argHasThreshold(arg) {
|
|
14
|
+
if (!arg || arg.type !== AST_NODE_TYPES.ObjectExpression)
|
|
15
|
+
return false;
|
|
16
|
+
return arg.properties.some((p) => p.type === AST_NODE_TYPES.Property &&
|
|
17
|
+
p.key.type === AST_NODE_TYPES.Identifier &&
|
|
18
|
+
THRESHOLD.test(p.key.name));
|
|
19
|
+
}
|
|
20
|
+
function anyArgHasThreshold(args) {
|
|
21
|
+
return args.some(argHasThreshold);
|
|
22
|
+
}
|
|
23
|
+
function interfaceHasFloorMember(node) {
|
|
24
|
+
return node.body.body.some((m) => m.type === AST_NODE_TYPES.TSPropertySignature &&
|
|
25
|
+
m.key.type === AST_NODE_TYPES.Identifier &&
|
|
26
|
+
THRESHOLD.test(m.key.name));
|
|
27
|
+
}
|
|
28
|
+
export const retrievalConfidenceFloor = createRule({
|
|
29
|
+
name: "retrieval-confidence-floor",
|
|
30
|
+
meta: {
|
|
31
|
+
type: "problem",
|
|
32
|
+
docs: {
|
|
33
|
+
description: "The retrieval data path must enforce a confidence floor: every retrieve(...) call passes a threshold (threshold / minScore / similarity / floor) and any RetrieveOpts type declares a floor member so the retriever can drop sub-floor chunks.",
|
|
34
|
+
},
|
|
35
|
+
schema: [],
|
|
36
|
+
messages: {
|
|
37
|
+
noThreshold: "retrieve(...) call does not pass a confidence threshold. Add a minScore/threshold to its options.",
|
|
38
|
+
optsNoFloor: "Retrieval options type has no confidence floor member. Add a minScore/threshold field so the retriever can drop sub-floor chunks.",
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
defaultOptions: [],
|
|
42
|
+
create(context) {
|
|
43
|
+
const filename = context.filename ?? "";
|
|
44
|
+
if (!filename.includes("kb") && !filename.includes("retriev"))
|
|
45
|
+
return {};
|
|
46
|
+
if (filename.endsWith(".test.ts"))
|
|
47
|
+
return {};
|
|
48
|
+
return {
|
|
49
|
+
CallExpression(node) {
|
|
50
|
+
if (!isRetrieveCallee(node.callee))
|
|
51
|
+
return;
|
|
52
|
+
if (anyArgHasThreshold(node.arguments))
|
|
53
|
+
return;
|
|
54
|
+
context.report({ node, messageId: "noThreshold" });
|
|
55
|
+
},
|
|
56
|
+
TSInterfaceDeclaration(node) {
|
|
57
|
+
if (!/Retrieve.*Opts|RetrieveOpts/.test(node.id.name))
|
|
58
|
+
return;
|
|
59
|
+
if (interfaceHasFloorMember(node))
|
|
60
|
+
return;
|
|
61
|
+
context.report({ node, messageId: "optsNoFloor" });
|
|
62
|
+
},
|
|
63
|
+
};
|
|
64
|
+
},
|
|
65
|
+
});
|
|
66
|
+
//# sourceMappingURL=retrieval-confidence-floor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"retrieval-confidence-floor.js","sourceRoot":"","sources":["../../src/rules/retrieval-confidence-floor.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tests-as-contracts.d.ts","sourceRoot":"","sources":["../../src/rules/tests-as-contracts.ts"],"names":[],"mappings":"AAKA,eAAO,MAAM,gBAAgB;;CAmD3B,CAAC"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { existsSync } from "fs";
|
|
2
|
+
import { dirname, basename, join } from "path";
|
|
3
|
+
import { createRule } from "../util.js";
|
|
4
|
+
import { isReExportOnlyBarrel, isTypeOnlyModule } from "@keel_flow/pack-general-se/scope-filters";
|
|
5
|
+
export const testsAsContracts = createRule({
|
|
6
|
+
name: "tests-as-contracts",
|
|
7
|
+
meta: {
|
|
8
|
+
type: "problem",
|
|
9
|
+
docs: {
|
|
10
|
+
description: "Every source file under packages/<pkg>/src must have a co-located test (Keel principle: tests-as-contracts).",
|
|
11
|
+
},
|
|
12
|
+
schema: [],
|
|
13
|
+
messages: {
|
|
14
|
+
missingTest: 'Source file "{{file}}" has no co-located test. Looked for {{candidates}}.',
|
|
15
|
+
},
|
|
16
|
+
},
|
|
17
|
+
defaultOptions: [],
|
|
18
|
+
create(context) {
|
|
19
|
+
return {
|
|
20
|
+
"Program:exit"(node) {
|
|
21
|
+
const filename = context.filename;
|
|
22
|
+
if (!filename)
|
|
23
|
+
return;
|
|
24
|
+
if (!/packages\/[^/]+\/src\/[^/]+\.ts$/.test(filename))
|
|
25
|
+
return;
|
|
26
|
+
if (filename.endsWith(".test.ts"))
|
|
27
|
+
return;
|
|
28
|
+
if (filename.endsWith(".d.ts"))
|
|
29
|
+
return;
|
|
30
|
+
if (/packages\/pack-[^/]+\/src\/index\.ts$/.test(filename))
|
|
31
|
+
return;
|
|
32
|
+
if (/packages\/eslint-plugin\/src\/rules\//.test(filename))
|
|
33
|
+
return;
|
|
34
|
+
if (isReExportOnlyBarrel(node.body))
|
|
35
|
+
return;
|
|
36
|
+
if (isTypeOnlyModule(node.body))
|
|
37
|
+
return;
|
|
38
|
+
const base = basename(filename, ".ts");
|
|
39
|
+
const dir = dirname(filename);
|
|
40
|
+
const packageRoot = filename.replace(/(packages\/[^/]+)\/.*$/, "$1");
|
|
41
|
+
const candidates = [
|
|
42
|
+
join(dir, `${base}.test.ts`),
|
|
43
|
+
join(packageRoot, "src", "__tests__", `${base}.test.ts`),
|
|
44
|
+
join(packageRoot, "__tests__", `${base}.test.ts`),
|
|
45
|
+
join(packageRoot, "src", "index.test.ts"),
|
|
46
|
+
];
|
|
47
|
+
if (candidates.some((c) => existsSync(c)))
|
|
48
|
+
return;
|
|
49
|
+
context.report({
|
|
50
|
+
node,
|
|
51
|
+
messageId: "missingTest",
|
|
52
|
+
data: {
|
|
53
|
+
file: filename,
|
|
54
|
+
candidates: candidates.map((c) => c.replace(dirname(packageRoot) + "/", "")).join(", "),
|
|
55
|
+
},
|
|
56
|
+
});
|
|
57
|
+
},
|
|
58
|
+
};
|
|
59
|
+
},
|
|
60
|
+
});
|
|
61
|
+
//# sourceMappingURL=tests-as-contracts.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tests-as-contracts.js","sourceRoot":"","sources":["../../src/rules/tests-as-contracts.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,IAAI,CAAC;AAChC,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC/C,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AACxC,OAAO,EAAE,oBAAoB,EAAE,gBAAgB,EAAE,MAAM,0CAA0C,CAAC;AAElG,MAAM,CAAC,MAAM,gBAAgB,GAAG,UAAU,CAAC;IACzC,IAAI,EAAE,oBAAoB;IAC1B,IAAI,EAAE;QACJ,IAAI,EAAE,SAAS;QACf,IAAI,EAAE;YACJ,WAAW,EACT,8GAA8G;SACjH;QACD,MAAM,EAAE,EAAE;QACV,QAAQ,EAAE;YACR,WAAW,EACT,2EAA2E;SAC9E;KACF;IACD,cAAc,EAAE,EAAE;IAClB,MAAM,CAAC,OAAO;QACZ,OAAO;YACL,cAAc,CAAC,IAAI;gBACjB,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC;gBAClC,IAAI,CAAC,QAAQ;oBAAE,OAAO;gBACtB,IAAI,CAAC,kCAAkC,CAAC,IAAI,CAAC,QAAQ,CAAC;oBAAE,OAAO;gBAC/D,IAAI,QAAQ,CAAC,QAAQ,CAAC,UAAU,CAAC;oBAAE,OAAO;gBAC1C,IAAI,QAAQ,CAAC,QAAQ,CAAC,OAAO,CAAC;oBAAE,OAAO;gBACvC,IAAI,uCAAuC,CAAC,IAAI,CAAC,QAAQ,CAAC;oBAAE,OAAO;gBACnE,IAAI,uCAAuC,CAAC,IAAI,CAAC,QAAQ,CAAC;oBAAE,OAAO;gBAEnE,IAAI,oBAAoB,CAAC,IAAI,CAAC,IAAI,CAAC;oBAAE,OAAO;gBAC5C,IAAI,gBAAgB,CAAC,IAAI,CAAC,IAAI,CAAC;oBAAE,OAAO;gBAExC,MAAM,IAAI,GAAG,QAAQ,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;gBACvC,MAAM,GAAG,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC;gBAC9B,MAAM,WAAW,GAAG,QAAQ,CAAC,OAAO,CAAC,wBAAwB,EAAE,IAAI,CAAC,CAAC;gBACrE,MAAM,UAAU,GAAG;oBACjB,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,UAAU,CAAC;oBAC5B,IAAI,CAAC,WAAW,EAAE,KAAK,EAAE,WAAW,EAAE,GAAG,IAAI,UAAU,CAAC;oBACxD,IAAI,CAAC,WAAW,EAAE,WAAW,EAAE,GAAG,IAAI,UAAU,CAAC;oBACjD,IAAI,CAAC,WAAW,EAAE,KAAK,EAAE,eAAe,CAAC;iBAC1C,CAAC;gBACF,IAAI,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;oBAAE,OAAO;gBAElD,OAAO,CAAC,MAAM,CAAC;oBACb,IAAI;oBACJ,SAAS,EAAE,aAAa;oBACxB,IAAI,EAAE;wBACJ,IAAI,EAAE,QAAQ;wBACd,UAAU,EAAE,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,WAAW,CAAC,GAAG,GAAG,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC;qBACxF;iBACF,CAAC,CAAC;YACL,CAAC;SACF,CAAC;IACJ,CAAC;CACF,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tool-failures-are-typed.d.ts","sourceRoot":"","sources":["../../src/rules/tool-failures-are-typed.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,oBAAoB;;CA8C/B,CAAC"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { AST_NODE_TYPES } from "@typescript-eslint/utils";
|
|
2
|
+
import { createRule } from "../util.js";
|
|
3
|
+
export const toolFailuresAreTyped = createRule({
|
|
4
|
+
name: "tool-failures-are-typed",
|
|
5
|
+
meta: {
|
|
6
|
+
type: "problem",
|
|
7
|
+
docs: {
|
|
8
|
+
description: "Tool files must not throw unstructured `new Error(...)` unless a typed error variant (errorType / ErrorType / ToolError / ResultError / errorKind) is also referenced.",
|
|
9
|
+
},
|
|
10
|
+
schema: [],
|
|
11
|
+
messages: {
|
|
12
|
+
unstructuredThrow: "Tool throws an unstructured Error. Return a typed error variant instead (Keel principle: tool-failures-are-typed).",
|
|
13
|
+
},
|
|
14
|
+
},
|
|
15
|
+
defaultOptions: [],
|
|
16
|
+
create(context) {
|
|
17
|
+
const filename = context.filename ?? "";
|
|
18
|
+
if (!filename.includes("/tools/") && !filename.endsWith("tool.ts"))
|
|
19
|
+
return {};
|
|
20
|
+
if (filename.endsWith(".test.ts"))
|
|
21
|
+
return {};
|
|
22
|
+
if (/packages\/pack-[^/]+\/src\/index\.ts$/.test(filename))
|
|
23
|
+
return {};
|
|
24
|
+
const offending = [];
|
|
25
|
+
return {
|
|
26
|
+
ThrowStatement(node) {
|
|
27
|
+
const arg = node.argument;
|
|
28
|
+
if (arg.type === AST_NODE_TYPES.NewExpression &&
|
|
29
|
+
arg.callee.type === AST_NODE_TYPES.Identifier &&
|
|
30
|
+
arg.callee.name === "Error") {
|
|
31
|
+
offending.push(node);
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
"Program:exit"() {
|
|
35
|
+
if (offending.length === 0)
|
|
36
|
+
return;
|
|
37
|
+
const src = context.sourceCode.getText();
|
|
38
|
+
const hasTypedErrorReference = /error[Tt]ype|ErrorType|ToolError|ResultError|errorKind/.test(src);
|
|
39
|
+
if (hasTypedErrorReference)
|
|
40
|
+
return;
|
|
41
|
+
for (const node of offending) {
|
|
42
|
+
context.report({ node, messageId: "unstructuredThrow" });
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
};
|
|
46
|
+
},
|
|
47
|
+
});
|
|
48
|
+
//# sourceMappingURL=tool-failures-are-typed.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tool-failures-are-typed.js","sourceRoot":"","sources":["../../src/rules/tool-failures-are-typed.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAiB,MAAM,0BAA0B,CAAC;AACzE,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAExC,MAAM,CAAC,MAAM,oBAAoB,GAAG,UAAU,CAAC;IAC7C,IAAI,EAAE,yBAAyB;IAC/B,IAAI,EAAE;QACJ,IAAI,EAAE,SAAS;QACf,IAAI,EAAE;YACJ,WAAW,EACT,wKAAwK;SAC3K;QACD,MAAM,EAAE,EAAE;QACV,QAAQ,EAAE;YACR,iBAAiB,EACf,oHAAoH;SACvH;KACF;IACD,cAAc,EAAE,EAAE;IAClB,MAAM,CAAC,OAAO;QACZ,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,IAAI,EAAE,CAAC;QACxC,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,SAAS,CAAC,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,SAAS,CAAC;YAAE,OAAO,EAAE,CAAC;QAC9E,IAAI,QAAQ,CAAC,QAAQ,CAAC,UAAU,CAAC;YAAE,OAAO,EAAE,CAAC;QAC7C,IAAI,uCAAuC,CAAC,IAAI,CAAC,QAAQ,CAAC;YAAE,OAAO,EAAE,CAAC;QAEtE,MAAM,SAAS,GAA8B,EAAE,CAAC;QAEhD,OAAO;YACL,cAAc,CAAC,IAAI;gBACjB,MAAM,GAAG,GAAG,IAAI,CAAC,QAAQ,CAAC;gBAC1B,IACE,GAAG,CAAC,IAAI,KAAK,cAAc,CAAC,aAAa;oBACzC,GAAG,CAAC,MAAM,CAAC,IAAI,KAAK,cAAc,CAAC,UAAU;oBAC7C,GAAG,CAAC,MAAM,CAAC,IAAI,KAAK,OAAO,EAC3B,CAAC;oBACD,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;gBACvB,CAAC;YACH,CAAC;YACD,cAAc;gBACZ,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC;oBAAE,OAAO;gBACnC,MAAM,GAAG,GAAG,OAAO,CAAC,UAAU,CAAC,OAAO,EAAE,CAAC;gBACzC,MAAM,sBAAsB,GAC1B,wDAAwD,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;gBACrE,IAAI,sBAAsB;oBAAE,OAAO;gBACnC,KAAK,MAAM,IAAI,IAAI,SAAS,EAAE,CAAC;oBAC7B,OAAO,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,mBAAmB,EAAE,CAAC,CAAC;gBAC3D,CAAC;YACH,CAAC;SACF,CAAC;IACJ,CAAC;CACF,CAAC,CAAC"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export declare const toolSchemasAreLoadBearing: import("@typescript-eslint/utils/ts-eslint").RuleModule<"missingSchema", [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
|
|
2
|
+
name: string;
|
|
3
|
+
};
|
|
4
|
+
//# sourceMappingURL=tool-schemas-are-load-bearing.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tool-schemas-are-load-bearing.d.ts","sourceRoot":"","sources":["../../src/rules/tool-schemas-are-load-bearing.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,yBAAyB;;CAoCpC,CAAC"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { createRule } from "../util.js";
|
|
2
|
+
export const toolSchemasAreLoadBearing = createRule({
|
|
3
|
+
name: "tool-schemas-are-load-bearing",
|
|
4
|
+
meta: {
|
|
5
|
+
type: "problem",
|
|
6
|
+
docs: {
|
|
7
|
+
description: "Files defining tools must declare a typed parameter schema (Zod `inputSchema` / `z.object` or TypeBox `Type.Object`).",
|
|
8
|
+
},
|
|
9
|
+
schema: [],
|
|
10
|
+
messages: {
|
|
11
|
+
missingSchema: 'Tool file "{{file}}" appears to define a tool without a typed parameter schema (Zod inputSchema or TypeBox Type.Object).',
|
|
12
|
+
},
|
|
13
|
+
},
|
|
14
|
+
defaultOptions: [],
|
|
15
|
+
create(context) {
|
|
16
|
+
const filename = context.filename ?? "";
|
|
17
|
+
if (!filename.includes("/tools/") && !filename.endsWith("tool.ts"))
|
|
18
|
+
return {};
|
|
19
|
+
if (filename.endsWith(".test.ts"))
|
|
20
|
+
return {};
|
|
21
|
+
if (/packages\/pack-[^/]+\/src\/index\.ts$/.test(filename))
|
|
22
|
+
return {};
|
|
23
|
+
if (/\/tools\/index\.ts$/.test(filename))
|
|
24
|
+
return {};
|
|
25
|
+
return {
|
|
26
|
+
"Program:exit"(node) {
|
|
27
|
+
const src = context.sourceCode.getText();
|
|
28
|
+
const hasZod = src.includes("inputSchema") || src.includes("z.object");
|
|
29
|
+
const hasTypeBox = src.includes("Type.Object") || /parameters\s*:/.test(src);
|
|
30
|
+
if (hasZod || hasTypeBox)
|
|
31
|
+
return;
|
|
32
|
+
context.report({
|
|
33
|
+
node,
|
|
34
|
+
messageId: "missingSchema",
|
|
35
|
+
data: { file: filename },
|
|
36
|
+
});
|
|
37
|
+
},
|
|
38
|
+
};
|
|
39
|
+
},
|
|
40
|
+
});
|
|
41
|
+
//# sourceMappingURL=tool-schemas-are-load-bearing.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tool-schemas-are-load-bearing.js","sourceRoot":"","sources":["../../src/rules/tool-schemas-are-load-bearing.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAExC,MAAM,CAAC,MAAM,yBAAyB,GAAG,UAAU,CAAC;IAClD,IAAI,EAAE,+BAA+B;IACrC,IAAI,EAAE;QACJ,IAAI,EAAE,SAAS;QACf,IAAI,EAAE;YACJ,WAAW,EACT,uHAAuH;SAC1H;QACD,MAAM,EAAE,EAAE;QACV,QAAQ,EAAE;YACR,aAAa,EACX,0HAA0H;SAC7H;KACF;IACD,cAAc,EAAE,EAAE;IAClB,MAAM,CAAC,OAAO;QACZ,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,IAAI,EAAE,CAAC;QACxC,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,SAAS,CAAC,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,SAAS,CAAC;YAAE,OAAO,EAAE,CAAC;QAC9E,IAAI,QAAQ,CAAC,QAAQ,CAAC,UAAU,CAAC;YAAE,OAAO,EAAE,CAAC;QAC7C,IAAI,uCAAuC,CAAC,IAAI,CAAC,QAAQ,CAAC;YAAE,OAAO,EAAE,CAAC;QACtE,IAAI,qBAAqB,CAAC,IAAI,CAAC,QAAQ,CAAC;YAAE,OAAO,EAAE,CAAC;QAEpD,OAAO;YACL,cAAc,CAAC,IAAI;gBACjB,MAAM,GAAG,GAAG,OAAO,CAAC,UAAU,CAAC,OAAO,EAAE,CAAC;gBACzC,MAAM,MAAM,GAAG,GAAG,CAAC,QAAQ,CAAC,aAAa,CAAC,IAAI,GAAG,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC;gBACvE,MAAM,UAAU,GAAG,GAAG,CAAC,QAAQ,CAAC,aAAa,CAAC,IAAI,gBAAgB,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;gBAC7E,IAAI,MAAM,IAAI,UAAU;oBAAE,OAAO;gBACjC,OAAO,CAAC,MAAM,CAAC;oBACb,IAAI;oBACJ,SAAS,EAAE,eAAe;oBAC1B,IAAI,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;iBACzB,CAAC,CAAC;YACL,CAAC;SACF,CAAC;IACJ,CAAC;CACF,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"wrong-way-impossible.d.ts","sourceRoot":"","sources":["../../src/rules/wrong-way-impossible.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,kBAAkB;;CA6E7B,CAAC"}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { createRule, hasIgnoreFileDirective } from "../util.js";
|
|
2
|
+
export const wrongWayImpossible = createRule({
|
|
3
|
+
name: "wrong-way-impossible",
|
|
4
|
+
meta: {
|
|
5
|
+
type: "problem",
|
|
6
|
+
docs: {
|
|
7
|
+
description: "Flag unjustified `any` (TSAnyKeyword). This rule scopes the wrong-way-impossible principle to the `any` type only. It does NOT cover `as unknown as T` double-casts, `@ts-ignore` / `@ts-expect-error`, or non-null `!` assertions — those defeat type safety without an `any` token and are out of this rule's scope.",
|
|
8
|
+
},
|
|
9
|
+
schema: [],
|
|
10
|
+
messages: {
|
|
11
|
+
anyUsed: "Unjustified `any` — use a precise type, `unknown`, or annotate with eslint-disable-next-line @typescript-eslint/no-explicit-any and a reason.",
|
|
12
|
+
},
|
|
13
|
+
},
|
|
14
|
+
defaultOptions: [],
|
|
15
|
+
create(context) {
|
|
16
|
+
const sourceCode = context.sourceCode;
|
|
17
|
+
if (hasIgnoreFileDirective(sourceCode.getText(), "wrong-way-impossible")) {
|
|
18
|
+
return {};
|
|
19
|
+
}
|
|
20
|
+
const filename = context.filename ?? "";
|
|
21
|
+
if (/packages\/pack-[^/]+\/src\/index\.ts$/.test(filename))
|
|
22
|
+
return {};
|
|
23
|
+
if (/packages\/eslint-plugin\/src\/rules\//.test(filename))
|
|
24
|
+
return {};
|
|
25
|
+
const sourceLines = sourceCode.lines;
|
|
26
|
+
const justifiedDisableEndLines = [];
|
|
27
|
+
const justifiedSameLines = new Set();
|
|
28
|
+
for (const comment of sourceCode.getAllComments()) {
|
|
29
|
+
if (comment.type !== "Line")
|
|
30
|
+
continue;
|
|
31
|
+
if (!comment.loc)
|
|
32
|
+
continue;
|
|
33
|
+
if (/eslint-disable-line\s+(?:[^\n]*\s)?@typescript-eslint\/no-explicit-any\b/.test(comment.value) &&
|
|
34
|
+
/--\s*\S/.test(comment.value)) {
|
|
35
|
+
justifiedSameLines.add(comment.loc.end.line);
|
|
36
|
+
continue;
|
|
37
|
+
}
|
|
38
|
+
if (/^\s*eslint-disable-next-line\s+(?:[^\n]*\s)?@typescript-eslint\/no-explicit-any\b/.test(comment.value) &&
|
|
39
|
+
/--\s*\S/.test(comment.value)) {
|
|
40
|
+
justifiedDisableEndLines.push(comment.loc.end.line);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
const isCommentOrBlank = (text) => {
|
|
44
|
+
const t = text.trim();
|
|
45
|
+
return t === "" || t.startsWith("//") || t.startsWith("*") || t.startsWith("/*");
|
|
46
|
+
};
|
|
47
|
+
const isSuppressed = (anyLine) => {
|
|
48
|
+
if (justifiedSameLines.has(anyLine))
|
|
49
|
+
return true;
|
|
50
|
+
for (const end of justifiedDisableEndLines) {
|
|
51
|
+
if (end >= anyLine)
|
|
52
|
+
continue;
|
|
53
|
+
let contiguous = true;
|
|
54
|
+
for (let l = end + 1; l < anyLine; l++) {
|
|
55
|
+
if (!isCommentOrBlank(sourceLines[l - 1] ?? "")) {
|
|
56
|
+
contiguous = false;
|
|
57
|
+
break;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
if (contiguous)
|
|
61
|
+
return true;
|
|
62
|
+
}
|
|
63
|
+
return false;
|
|
64
|
+
};
|
|
65
|
+
return {
|
|
66
|
+
TSAnyKeyword(node) {
|
|
67
|
+
if (node.loc && isSuppressed(node.loc.start.line))
|
|
68
|
+
return;
|
|
69
|
+
context.report({ node, messageId: "anyUsed" });
|
|
70
|
+
},
|
|
71
|
+
};
|
|
72
|
+
},
|
|
73
|
+
});
|
|
74
|
+
//# sourceMappingURL=wrong-way-impossible.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"wrong-way-impossible.js","sourceRoot":"","sources":["../../src/rules/wrong-way-impossible.ts"],"names":[],"mappings":"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"}
|
package/dist/tester.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tester.d.ts","sourceRoot":"","sources":["../src/tester.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AAOpC,OAAO,EAAE,UAAU,EAAE,CAAC"}
|
package/dist/tester.js
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { RuleTester } from "eslint";
|
|
2
|
+
import { describe, it } from "vitest";
|
|
3
|
+
RuleTester.describe = (text, callback) => describe(text, callback);
|
|
4
|
+
RuleTester.it = (text, callback) => it(text, callback);
|
|
5
|
+
RuleTester.itOnly = (text, callback) => it.only(text, callback);
|
|
6
|
+
export { RuleTester };
|
|
7
|
+
//# sourceMappingURL=tester.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tester.js","sourceRoot":"","sources":["../src/tester.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AACpC,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAEtC,UAAU,CAAC,QAAQ,GAAG,CAAC,IAAY,EAAE,QAAoB,EAAE,EAAE,CAAC,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;AACvF,UAAU,CAAC,EAAE,GAAG,CAAC,IAAY,EAAE,QAAoB,EAAE,EAAE,CAAC,EAAE,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;AAC3E,UAAU,CAAC,MAAM,GAAG,CAAC,IAAY,EAAE,QAAoB,EAAE,EAAE,CAAC,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;AAEpF,OAAO,EAAE,UAAU,EAAE,CAAC"}
|
package/dist/util.d.ts
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { ESLintUtils } from "@typescript-eslint/utils";
|
|
2
|
+
export declare const createRule: <Options extends readonly unknown[], MessageIds extends string>({ meta, name, ...rule }: Readonly<ESLintUtils.RuleWithMetaAndName<Options, MessageIds, unknown>>) => ESLintUtils.RuleModule<MessageIds, Options, unknown, ESLintUtils.RuleListener> & {
|
|
3
|
+
name: string;
|
|
4
|
+
};
|
|
5
|
+
export declare function hasIgnoreFileDirective(src: string, principleId: string): boolean;
|
|
6
|
+
//# sourceMappingURL=util.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"util.d.ts","sourceRoot":"","sources":["../src/util.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAEvD,eAAO,MAAM,UAAU;;CAGtB,CAAC;AAEF,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAGhF"}
|
package/dist/util.js
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { ESLintUtils } from "@typescript-eslint/utils";
|
|
2
|
+
export const createRule = ESLintUtils.RuleCreator((name) => `https://github.com/jglasskatz/keel/blob/main/packages/eslint-plugin/src/rules/${name}.ts`);
|
|
3
|
+
export function hasIgnoreFileDirective(src, principleId) {
|
|
4
|
+
const escaped = principleId.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
5
|
+
return new RegExp(`@keel-ignore-file\\s*:\\s*${escaped}\\b`).test(src);
|
|
6
|
+
}
|
|
7
|
+
//# sourceMappingURL=util.js.map
|
package/dist/util.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"util.js","sourceRoot":"","sources":["../src/util.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAEvD,MAAM,CAAC,MAAM,UAAU,GAAG,WAAW,CAAC,WAAW,CAC/C,CAAC,IAAI,EAAE,EAAE,CACP,iFAAiF,IAAI,KAAK,CAC7F,CAAC;AAEF,MAAM,UAAU,sBAAsB,CAAC,GAAW,EAAE,WAAmB;IACrE,MAAM,OAAO,GAAG,WAAW,CAAC,OAAO,CAAC,qBAAqB,EAAE,MAAM,CAAC,CAAC;IACnE,OAAO,IAAI,MAAM,CAAC,6BAA6B,OAAO,KAAK,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AACzE,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@keel_flow/eslint-plugin",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"private": false,
|
|
5
|
+
"type": "module",
|
|
6
|
+
"publishConfig": {
|
|
7
|
+
"access": "public"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"dist"
|
|
11
|
+
],
|
|
12
|
+
"main": "./dist/index.js",
|
|
13
|
+
"types": "./dist/index.d.ts",
|
|
14
|
+
"exports": {
|
|
15
|
+
".": {
|
|
16
|
+
"import": "./dist/index.js",
|
|
17
|
+
"require": "./dist/index.js",
|
|
18
|
+
"types": "./dist/index.d.ts"
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
"peerDependencies": {
|
|
22
|
+
"eslint": "^9.0.0"
|
|
23
|
+
},
|
|
24
|
+
"dependencies": {
|
|
25
|
+
"@typescript-eslint/utils": "^8.0.0",
|
|
26
|
+
"@keel_flow/pack-general-se": "0.2.0"
|
|
27
|
+
},
|
|
28
|
+
"devDependencies": {
|
|
29
|
+
"@types/node": "^25.9.1",
|
|
30
|
+
"@typescript-eslint/eslint-plugin": "^8.0.0",
|
|
31
|
+
"@typescript-eslint/parser": "^8.0.0",
|
|
32
|
+
"eslint": "^9.0.0",
|
|
33
|
+
"typescript": "^5.5.0",
|
|
34
|
+
"vitest": "^2.0.0"
|
|
35
|
+
},
|
|
36
|
+
"scripts": {
|
|
37
|
+
"build": "tsc",
|
|
38
|
+
"typecheck": "tsc --noEmit",
|
|
39
|
+
"test": "vitest run",
|
|
40
|
+
"lint": "eslint src"
|
|
41
|
+
}
|
|
42
|
+
}
|