@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.
Files changed (50) hide show
  1. package/LICENSE +21 -0
  2. package/dist/index.d.ts +89 -0
  3. package/dist/index.d.ts.map +1 -0
  4. package/dist/index.js +49 -0
  5. package/dist/index.js.map +1 -0
  6. package/dist/rules/chunk-size-bounded.d.ts +4 -0
  7. package/dist/rules/chunk-size-bounded.d.ts.map +1 -0
  8. package/dist/rules/chunk-size-bounded.js +114 -0
  9. package/dist/rules/chunk-size-bounded.js.map +1 -0
  10. package/dist/rules/gate-is-reproducible.d.ts +4 -0
  11. package/dist/rules/gate-is-reproducible.d.ts.map +1 -0
  12. package/dist/rules/gate-is-reproducible.js +49 -0
  13. package/dist/rules/gate-is-reproducible.js.map +1 -0
  14. package/dist/rules/naming-is-design.d.ts +4 -0
  15. package/dist/rules/naming-is-design.d.ts.map +1 -0
  16. package/dist/rules/naming-is-design.js +44 -0
  17. package/dist/rules/naming-is-design.js.map +1 -0
  18. package/dist/rules/no-untyped-seam-fetch.d.ts +4 -0
  19. package/dist/rules/no-untyped-seam-fetch.d.ts.map +1 -0
  20. package/dist/rules/no-untyped-seam-fetch.js +167 -0
  21. package/dist/rules/no-untyped-seam-fetch.js.map +1 -0
  22. package/dist/rules/retrieval-confidence-floor.d.ts +4 -0
  23. package/dist/rules/retrieval-confidence-floor.d.ts.map +1 -0
  24. package/dist/rules/retrieval-confidence-floor.js +66 -0
  25. package/dist/rules/retrieval-confidence-floor.js.map +1 -0
  26. package/dist/rules/tests-as-contracts.d.ts +4 -0
  27. package/dist/rules/tests-as-contracts.d.ts.map +1 -0
  28. package/dist/rules/tests-as-contracts.js +61 -0
  29. package/dist/rules/tests-as-contracts.js.map +1 -0
  30. package/dist/rules/tool-failures-are-typed.d.ts +4 -0
  31. package/dist/rules/tool-failures-are-typed.d.ts.map +1 -0
  32. package/dist/rules/tool-failures-are-typed.js +48 -0
  33. package/dist/rules/tool-failures-are-typed.js.map +1 -0
  34. package/dist/rules/tool-schemas-are-load-bearing.d.ts +4 -0
  35. package/dist/rules/tool-schemas-are-load-bearing.d.ts.map +1 -0
  36. package/dist/rules/tool-schemas-are-load-bearing.js +41 -0
  37. package/dist/rules/tool-schemas-are-load-bearing.js.map +1 -0
  38. package/dist/rules/wrong-way-impossible.d.ts +4 -0
  39. package/dist/rules/wrong-way-impossible.d.ts.map +1 -0
  40. package/dist/rules/wrong-way-impossible.js +74 -0
  41. package/dist/rules/wrong-way-impossible.js.map +1 -0
  42. package/dist/tester.d.ts +3 -0
  43. package/dist/tester.d.ts.map +1 -0
  44. package/dist/tester.js +7 -0
  45. package/dist/tester.js.map +1 -0
  46. package/dist/util.d.ts +6 -0
  47. package/dist/util.d.ts.map +1 -0
  48. package/dist/util.js +7 -0
  49. package/dist/util.js.map +1 -0
  50. 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.
@@ -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,4 @@
1
+ export declare const chunkSizeBounded: import("@typescript-eslint/utils/ts-eslint").RuleModule<"noSizeBound", [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
2
+ name: string;
3
+ };
4
+ //# sourceMappingURL=chunk-size-bounded.d.ts.map
@@ -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,4 @@
1
+ export declare const gateIsReproducible: import("@typescript-eslint/utils/ts-eslint").RuleModule<"absolutePath", [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
2
+ name: string;
3
+ };
4
+ //# sourceMappingURL=gate-is-reproducible.d.ts.map
@@ -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,4 @@
1
+ export declare const namingIsDesign: import("@typescript-eslint/utils/ts-eslint").RuleModule<"vague", [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
2
+ name: string;
3
+ };
4
+ //# sourceMappingURL=naming-is-design.d.ts.map
@@ -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,4 @@
1
+ export declare const noUntypedSeamFetch: import("@typescript-eslint/utils/ts-eslint").RuleModule<"untypedSeamFetch", [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
2
+ name: string;
3
+ };
4
+ //# sourceMappingURL=no-untyped-seam-fetch.d.ts.map
@@ -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,4 @@
1
+ export declare const testsAsContracts: import("@typescript-eslint/utils/ts-eslint").RuleModule<"missingTest", [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
2
+ name: string;
3
+ };
4
+ //# sourceMappingURL=tests-as-contracts.d.ts.map
@@ -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,4 @@
1
+ export declare const toolFailuresAreTyped: import("@typescript-eslint/utils/ts-eslint").RuleModule<"unstructuredThrow", [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
2
+ name: string;
3
+ };
4
+ //# sourceMappingURL=tool-failures-are-typed.d.ts.map
@@ -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,4 @@
1
+ export declare const wrongWayImpossible: import("@typescript-eslint/utils/ts-eslint").RuleModule<"anyUsed", [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
2
+ name: string;
3
+ };
4
+ //# sourceMappingURL=wrong-way-impossible.d.ts.map
@@ -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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"}
@@ -0,0 +1,3 @@
1
+ import { RuleTester } from "eslint";
2
+ export { RuleTester };
3
+ //# sourceMappingURL=tester.d.ts.map
@@ -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
@@ -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
+ }