@seljs/checker 1.0.0 → 1.0.1

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 (124) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/dist/_virtual/_rolldown/runtime.cjs +23 -0
  3. package/dist/checker/checker.cjs +482 -0
  4. package/dist/checker/checker.d.cts +176 -0
  5. package/dist/checker/checker.d.mts +176 -0
  6. package/dist/checker/checker.mjs +481 -0
  7. package/dist/checker/diagnostics.cjs +66 -0
  8. package/dist/checker/diagnostics.mjs +66 -0
  9. package/dist/checker/index.cjs +2 -0
  10. package/dist/checker/index.d.mts +2 -0
  11. package/dist/checker/index.mjs +3 -0
  12. package/dist/checker/type-compatibility.cjs +70 -0
  13. package/dist/checker/type-compatibility.d.cts +8 -0
  14. package/dist/checker/type-compatibility.d.mts +8 -0
  15. package/dist/checker/type-compatibility.mjs +69 -0
  16. package/dist/constants.cjs +14 -0
  17. package/dist/constants.d.cts +7 -0
  18. package/dist/constants.d.mts +7 -0
  19. package/dist/constants.mjs +13 -0
  20. package/dist/debug.cjs +7 -0
  21. package/dist/debug.mjs +5 -0
  22. package/dist/environment/codec-registry.cjs +109 -0
  23. package/dist/environment/codec-registry.d.cts +45 -0
  24. package/dist/environment/codec-registry.d.mts +45 -0
  25. package/dist/environment/codec-registry.mjs +108 -0
  26. package/dist/environment/hydrate.cjs +163 -0
  27. package/dist/environment/hydrate.d.cts +52 -0
  28. package/dist/environment/hydrate.d.mts +52 -0
  29. package/dist/environment/hydrate.mjs +160 -0
  30. package/dist/environment/index.cjs +4 -0
  31. package/dist/environment/index.d.mts +4 -0
  32. package/dist/environment/index.mjs +5 -0
  33. package/dist/environment/register-types.cjs +107 -0
  34. package/dist/environment/register-types.d.cts +15 -0
  35. package/dist/environment/register-types.d.mts +15 -0
  36. package/dist/environment/register-types.mjs +106 -0
  37. package/dist/environment/value-wrappers.cjs +44 -0
  38. package/dist/environment/value-wrappers.d.cts +20 -0
  39. package/dist/environment/value-wrappers.d.mts +20 -0
  40. package/dist/environment/value-wrappers.mjs +41 -0
  41. package/dist/index.cjs +27 -0
  42. package/dist/index.d.cts +11 -0
  43. package/dist/index.d.mts +11 -0
  44. package/dist/index.mjs +13 -0
  45. package/dist/rules/defaults/deferred-call.cjs +107 -0
  46. package/dist/rules/defaults/deferred-call.mjs +106 -0
  47. package/dist/rules/defaults/index.cjs +6 -0
  48. package/dist/rules/defaults/index.mjs +7 -0
  49. package/dist/rules/defaults/no-constant-condition.cjs +31 -0
  50. package/dist/rules/defaults/no-constant-condition.mjs +31 -0
  51. package/dist/rules/defaults/no-mixed-operators.cjs +39 -0
  52. package/dist/rules/defaults/no-mixed-operators.mjs +39 -0
  53. package/dist/rules/defaults/no-redundant-bool.cjs +26 -0
  54. package/dist/rules/defaults/no-redundant-bool.mjs +26 -0
  55. package/dist/rules/defaults/no-self-comparison.cjs +44 -0
  56. package/dist/rules/defaults/no-self-comparison.mjs +43 -0
  57. package/dist/rules/defaults/require-type.cjs +18 -0
  58. package/dist/rules/defaults/require-type.mjs +18 -0
  59. package/dist/rules/facade.cjs +31 -0
  60. package/dist/rules/facade.d.cts +20 -0
  61. package/dist/rules/facade.d.mts +20 -0
  62. package/dist/rules/facade.mjs +31 -0
  63. package/dist/rules/index.cjs +2 -0
  64. package/dist/rules/index.d.mts +3 -0
  65. package/dist/rules/index.mjs +3 -0
  66. package/dist/rules/runner.cjs +40 -0
  67. package/dist/rules/runner.d.cts +27 -0
  68. package/dist/rules/runner.d.mts +27 -0
  69. package/dist/rules/runner.mjs +40 -0
  70. package/dist/rules/types.d.cts +77 -0
  71. package/dist/rules/types.d.mts +77 -0
  72. package/dist/utils/ast-utils.cjs +164 -0
  73. package/dist/utils/ast-utils.mjs +162 -0
  74. package/package.json +24 -17
  75. package/dist/checker/checker.d.ts +0 -173
  76. package/dist/checker/checker.js +0 -567
  77. package/dist/checker/diagnostics.d.ts +0 -10
  78. package/dist/checker/diagnostics.js +0 -80
  79. package/dist/checker/index.d.ts +0 -2
  80. package/dist/checker/index.js +0 -2
  81. package/dist/checker/type-compatibility.d.ts +0 -16
  82. package/dist/checker/type-compatibility.js +0 -59
  83. package/dist/constants.d.ts +0 -4
  84. package/dist/constants.js +0 -10
  85. package/dist/debug.d.ts +0 -2
  86. package/dist/debug.js +0 -2
  87. package/dist/environment/codec-registry.d.ts +0 -42
  88. package/dist/environment/codec-registry.js +0 -146
  89. package/dist/environment/hydrate.d.ts +0 -48
  90. package/dist/environment/hydrate.js +0 -198
  91. package/dist/environment/index.d.ts +0 -4
  92. package/dist/environment/index.js +0 -4
  93. package/dist/environment/register-types.d.ts +0 -14
  94. package/dist/environment/register-types.js +0 -154
  95. package/dist/environment/value-wrappers.d.ts +0 -17
  96. package/dist/environment/value-wrappers.js +0 -65
  97. package/dist/index.d.ts +0 -4
  98. package/dist/index.js +0 -4
  99. package/dist/rules/defaults/deferred-call.d.ts +0 -13
  100. package/dist/rules/defaults/deferred-call.js +0 -162
  101. package/dist/rules/defaults/index.d.ts +0 -6
  102. package/dist/rules/defaults/index.js +0 -6
  103. package/dist/rules/defaults/no-constant-condition.d.ts +0 -7
  104. package/dist/rules/defaults/no-constant-condition.js +0 -36
  105. package/dist/rules/defaults/no-mixed-operators.d.ts +0 -9
  106. package/dist/rules/defaults/no-mixed-operators.js +0 -44
  107. package/dist/rules/defaults/no-redundant-bool.d.ts +0 -5
  108. package/dist/rules/defaults/no-redundant-bool.js +0 -27
  109. package/dist/rules/defaults/no-self-comparison.d.ts +0 -9
  110. package/dist/rules/defaults/no-self-comparison.js +0 -31
  111. package/dist/rules/defaults/require-type.d.ts +0 -7
  112. package/dist/rules/defaults/require-type.js +0 -19
  113. package/dist/rules/facade.d.ts +0 -22
  114. package/dist/rules/facade.js +0 -29
  115. package/dist/rules/index.d.ts +0 -3
  116. package/dist/rules/index.js +0 -3
  117. package/dist/rules/runner.d.ts +0 -16
  118. package/dist/rules/runner.js +0 -30
  119. package/dist/rules/types.d.ts +0 -73
  120. package/dist/rules/types.js +0 -1
  121. package/dist/utils/ast-utils.d.ts +0 -55
  122. package/dist/utils/ast-utils.js +0 -255
  123. package/dist/utils/index.d.ts +0 -1
  124. package/dist/utils/index.js +0 -1
@@ -0,0 +1,39 @@
1
+ import { nodeSpan, walkAST } from "../../utils/ast-utils.mjs";
2
+ //#region src/rules/defaults/no-mixed-operators.ts
3
+ /**
4
+ * Check whether a child expression has explicit parentheses in the source.
5
+ * Scans backwards from the child's start position for a `(`.
6
+ */
7
+ const hasExplicitParens = (src, child) => {
8
+ let i = nodeSpan(child).from - 1;
9
+ while (i >= 0 && (src[i] === " " || src[i] === " ")) i--;
10
+ return i >= 0 && src[i] === "(";
11
+ };
12
+ /**
13
+ * Flags mixed `&&` and `||` without explicit parentheses.
14
+ *
15
+ * The cel-js parser discards parentheses from the AST, so this rule
16
+ * inspects the original source text via `node.input` to determine
17
+ * whether parentheses were explicitly written.
18
+ */
19
+ const noMixedOperators = {
20
+ name: "no-mixed-operators",
21
+ description: "Require parentheses when mixing logical operators (&& and ||).",
22
+ defaultSeverity: "info",
23
+ tier: "structural",
24
+ run(context) {
25
+ const diagnostics = [];
26
+ walkAST(context.ast, (node) => {
27
+ if (node.op !== "&&" && node.op !== "||") return;
28
+ const opposite = node.op === "&&" ? "||" : "&&";
29
+ const [left, right] = node.args;
30
+ for (const child of [left, right]) if (child.op === opposite && !hasExplicitParens(node.input, child)) {
31
+ diagnostics.push(context.report(node, "Mixed logical operators without parentheses. Add explicit grouping to clarify precedence."));
32
+ break;
33
+ }
34
+ });
35
+ return diagnostics;
36
+ }
37
+ };
38
+ //#endregion
39
+ export { noMixedOperators };
@@ -0,0 +1,26 @@
1
+ const require_ast_utils = require("../../utils/ast-utils.cjs");
2
+ //#region src/rules/defaults/no-redundant-bool.ts
3
+ /**
4
+ * Flags redundant boolean comparisons: `x == true`, `x != false`, etc.
5
+ */
6
+ const noRedundantBool = {
7
+ name: "no-redundant-bool",
8
+ description: "Disallow redundant comparisons to boolean literals (true/false).",
9
+ defaultSeverity: "warning",
10
+ tier: "structural",
11
+ run(context) {
12
+ const diagnostics = [];
13
+ require_ast_utils.walkAST(context.ast, (node) => {
14
+ if (node.op !== "==" && node.op !== "!=") return;
15
+ const [left, right] = node.args;
16
+ const leftIsBool = left.op === "value" && typeof left.args === "boolean";
17
+ const rightIsBool = right.op === "value" && typeof right.args === "boolean";
18
+ if (!leftIsBool && !rightIsBool) return;
19
+ const boolValue = leftIsBool ? left.args : right.args;
20
+ diagnostics.push(context.report(node, `Redundant comparison to \`${String(boolValue)}\`. Simplify the expression.`));
21
+ });
22
+ return diagnostics;
23
+ }
24
+ };
25
+ //#endregion
26
+ exports.noRedundantBool = noRedundantBool;
@@ -0,0 +1,26 @@
1
+ import { walkAST } from "../../utils/ast-utils.mjs";
2
+ //#region src/rules/defaults/no-redundant-bool.ts
3
+ /**
4
+ * Flags redundant boolean comparisons: `x == true`, `x != false`, etc.
5
+ */
6
+ const noRedundantBool = {
7
+ name: "no-redundant-bool",
8
+ description: "Disallow redundant comparisons to boolean literals (true/false).",
9
+ defaultSeverity: "warning",
10
+ tier: "structural",
11
+ run(context) {
12
+ const diagnostics = [];
13
+ walkAST(context.ast, (node) => {
14
+ if (node.op !== "==" && node.op !== "!=") return;
15
+ const [left, right] = node.args;
16
+ const leftIsBool = left.op === "value" && typeof left.args === "boolean";
17
+ const rightIsBool = right.op === "value" && typeof right.args === "boolean";
18
+ if (!leftIsBool && !rightIsBool) return;
19
+ const boolValue = leftIsBool ? left.args : right.args;
20
+ diagnostics.push(context.report(node, `Redundant comparison to \`${String(boolValue)}\`. Simplify the expression.`));
21
+ });
22
+ return diagnostics;
23
+ }
24
+ };
25
+ //#endregion
26
+ export { noRedundantBool };
@@ -0,0 +1,44 @@
1
+ require("../../_virtual/_rolldown/runtime.cjs");
2
+ const require_ast_utils = require("../../utils/ast-utils.cjs");
3
+ let _marcbachmann_cel_js = require("@marcbachmann/cel-js");
4
+ //#region src/rules/defaults/no-self-comparison.ts
5
+ const COMPARISON_OPS = new Set([
6
+ "==",
7
+ "!=",
8
+ "<",
9
+ "<=",
10
+ ">",
11
+ ">="
12
+ ]);
13
+ const ALWAYS_TRUE_OPS = new Set([
14
+ "==",
15
+ "<=",
16
+ ">="
17
+ ]);
18
+ /**
19
+ * Flags comparisons where both sides are the same expression (tautology/contradiction).
20
+ *
21
+ * Uses `serialize()` for identity comparison (safe for this purpose —
22
+ * normalization is acceptable when comparing subtree identity, unlike
23
+ * span calculation where it would produce incorrect positions).
24
+ */
25
+ const noSelfComparison = {
26
+ name: "no-self-comparison",
27
+ description: "Disallow comparing an expression to itself.",
28
+ defaultSeverity: "warning",
29
+ tier: "structural",
30
+ run(context) {
31
+ const diagnostics = [];
32
+ require_ast_utils.walkAST(context.ast, (node) => {
33
+ if (!COMPARISON_OPS.has(node.op)) return;
34
+ const [left, right] = node.args;
35
+ if ((0, _marcbachmann_cel_js.serialize)(left) === (0, _marcbachmann_cel_js.serialize)(right)) {
36
+ const alwaysResult = ALWAYS_TRUE_OPS.has(node.op) ? "true" : "false";
37
+ diagnostics.push(context.report(node, `Both sides of \`${node.op}\` are identical. This is always \`${alwaysResult}\`.`));
38
+ }
39
+ });
40
+ return diagnostics;
41
+ }
42
+ };
43
+ //#endregion
44
+ exports.noSelfComparison = noSelfComparison;
@@ -0,0 +1,43 @@
1
+ import { walkAST } from "../../utils/ast-utils.mjs";
2
+ import { serialize } from "@marcbachmann/cel-js";
3
+ //#region src/rules/defaults/no-self-comparison.ts
4
+ const COMPARISON_OPS = new Set([
5
+ "==",
6
+ "!=",
7
+ "<",
8
+ "<=",
9
+ ">",
10
+ ">="
11
+ ]);
12
+ const ALWAYS_TRUE_OPS = new Set([
13
+ "==",
14
+ "<=",
15
+ ">="
16
+ ]);
17
+ /**
18
+ * Flags comparisons where both sides are the same expression (tautology/contradiction).
19
+ *
20
+ * Uses `serialize()` for identity comparison (safe for this purpose —
21
+ * normalization is acceptable when comparing subtree identity, unlike
22
+ * span calculation where it would produce incorrect positions).
23
+ */
24
+ const noSelfComparison = {
25
+ name: "no-self-comparison",
26
+ description: "Disallow comparing an expression to itself.",
27
+ defaultSeverity: "warning",
28
+ tier: "structural",
29
+ run(context) {
30
+ const diagnostics = [];
31
+ walkAST(context.ast, (node) => {
32
+ if (!COMPARISON_OPS.has(node.op)) return;
33
+ const [left, right] = node.args;
34
+ if (serialize(left) === serialize(right)) {
35
+ const alwaysResult = ALWAYS_TRUE_OPS.has(node.op) ? "true" : "false";
36
+ diagnostics.push(context.report(node, `Both sides of \`${node.op}\` are identical. This is always \`${alwaysResult}\`.`));
37
+ }
38
+ });
39
+ return diagnostics;
40
+ }
41
+ };
42
+ //#endregion
43
+ export { noSelfComparison };
@@ -0,0 +1,18 @@
1
+ //#region src/rules/defaults/require-type.ts
2
+ /**
3
+ * Rule factory that enforces an expression evaluates to the expected CEL type.
4
+ *
5
+ * @param expected - The CEL type name the expression must resolve to (e.g. "bool", "uint256")
6
+ */
7
+ const requireType = (expected) => ({
8
+ name: `require-type-${expected}`,
9
+ description: `Expression must evaluate to ${expected}.`,
10
+ defaultSeverity: "error",
11
+ tier: "type-aware",
12
+ run(context) {
13
+ if (context.resolvedType === expected) return [];
14
+ return [context.reportAt(0, context.expression.length, `Expected expression to evaluate to "${expected}", but got "${String(context.resolvedType)}".`)];
15
+ }
16
+ });
17
+ //#endregion
18
+ exports.requireType = requireType;
@@ -0,0 +1,18 @@
1
+ //#region src/rules/defaults/require-type.ts
2
+ /**
3
+ * Rule factory that enforces an expression evaluates to the expected CEL type.
4
+ *
5
+ * @param expected - The CEL type name the expression must resolve to (e.g. "bool", "uint256")
6
+ */
7
+ const requireType = (expected) => ({
8
+ name: `require-type-${expected}`,
9
+ description: `Expression must evaluate to ${expected}.`,
10
+ defaultSeverity: "error",
11
+ tier: "type-aware",
12
+ run(context) {
13
+ if (context.resolvedType === expected) return [];
14
+ return [context.reportAt(0, context.expression.length, `Expected expression to evaluate to "${expected}", but got "${String(context.resolvedType)}".`)];
15
+ }
16
+ });
17
+ //#endregion
18
+ export { requireType };
@@ -0,0 +1,31 @@
1
+ const require_deferred_call = require("./defaults/deferred-call.cjs");
2
+ const require_no_constant_condition = require("./defaults/no-constant-condition.cjs");
3
+ const require_no_mixed_operators = require("./defaults/no-mixed-operators.cjs");
4
+ const require_no_redundant_bool = require("./defaults/no-redundant-bool.cjs");
5
+ const require_no_self_comparison = require("./defaults/no-self-comparison.cjs");
6
+ const require_require_type = require("./defaults/require-type.cjs");
7
+ require("./defaults/index.cjs");
8
+ //#region src/rules/facade.ts
9
+ /**
10
+ * Unified export for rules.
11
+ *
12
+ * Provides access to individual built-in rules, a convenience array of all
13
+ * built-in rules, and rule factories for custom enforcement.
14
+ */
15
+ const rules = {
16
+ builtIn: [
17
+ require_no_redundant_bool.noRedundantBool,
18
+ require_no_constant_condition.noConstantCondition,
19
+ require_no_mixed_operators.noMixedOperators,
20
+ require_no_self_comparison.noSelfComparison,
21
+ require_deferred_call.deferredCall
22
+ ],
23
+ noRedundantBool: require_no_redundant_bool.noRedundantBool,
24
+ noConstantCondition: require_no_constant_condition.noConstantCondition,
25
+ noMixedOperators: require_no_mixed_operators.noMixedOperators,
26
+ noSelfComparison: require_no_self_comparison.noSelfComparison,
27
+ deferredCall: require_deferred_call.deferredCall,
28
+ requireType: require_require_type.requireType
29
+ };
30
+ //#endregion
31
+ exports.rules = rules;
@@ -0,0 +1,20 @@
1
+ import { SELRule } from "./types.cjs";
2
+
3
+ //#region src/rules/facade.d.ts
4
+ /**
5
+ * Unified export for rules.
6
+ *
7
+ * Provides access to individual built-in rules, a convenience array of all
8
+ * built-in rules, and rule factories for custom enforcement.
9
+ */
10
+ declare const rules: {
11
+ /** All built-in lint rules. */readonly builtIn: readonly [SELRule, SELRule, SELRule, SELRule, SELRule]; /** Flags redundant comparisons to boolean literals (true/false). */
12
+ readonly noRedundantBool: SELRule; /** Flags constant conditions (e.g. `true && x`, `1 == 1`). */
13
+ readonly noConstantCondition: SELRule; /** Requires parentheses when mixing `&&` and `||`. */
14
+ readonly noMixedOperators: SELRule; /** Flags self-comparisons (e.g. `x == x`). */
15
+ readonly noSelfComparison: SELRule; /** Flags contract calls with dynamic arguments that will execute as live RPC calls. */
16
+ readonly deferredCall: SELRule; /** Rule factory that enforces an expression evaluates to the expected CEL type. */
17
+ readonly requireType: (expected: string) => SELRule;
18
+ };
19
+ //#endregion
20
+ export { rules };
@@ -0,0 +1,20 @@
1
+ import { SELRule } from "./types.mjs";
2
+
3
+ //#region src/rules/facade.d.ts
4
+ /**
5
+ * Unified export for rules.
6
+ *
7
+ * Provides access to individual built-in rules, a convenience array of all
8
+ * built-in rules, and rule factories for custom enforcement.
9
+ */
10
+ declare const rules: {
11
+ /** All built-in lint rules. */readonly builtIn: readonly [SELRule, SELRule, SELRule, SELRule, SELRule]; /** Flags redundant comparisons to boolean literals (true/false). */
12
+ readonly noRedundantBool: SELRule; /** Flags constant conditions (e.g. `true && x`, `1 == 1`). */
13
+ readonly noConstantCondition: SELRule; /** Requires parentheses when mixing `&&` and `||`. */
14
+ readonly noMixedOperators: SELRule; /** Flags self-comparisons (e.g. `x == x`). */
15
+ readonly noSelfComparison: SELRule; /** Flags contract calls with dynamic arguments that will execute as live RPC calls. */
16
+ readonly deferredCall: SELRule; /** Rule factory that enforces an expression evaluates to the expected CEL type. */
17
+ readonly requireType: (expected: string) => SELRule;
18
+ };
19
+ //#endregion
20
+ export { rules };
@@ -0,0 +1,31 @@
1
+ import { deferredCall } from "./defaults/deferred-call.mjs";
2
+ import { noConstantCondition } from "./defaults/no-constant-condition.mjs";
3
+ import { noMixedOperators } from "./defaults/no-mixed-operators.mjs";
4
+ import { noRedundantBool } from "./defaults/no-redundant-bool.mjs";
5
+ import { noSelfComparison } from "./defaults/no-self-comparison.mjs";
6
+ import { requireType } from "./defaults/require-type.mjs";
7
+ import "./defaults/index.mjs";
8
+ //#region src/rules/facade.ts
9
+ /**
10
+ * Unified export for rules.
11
+ *
12
+ * Provides access to individual built-in rules, a convenience array of all
13
+ * built-in rules, and rule factories for custom enforcement.
14
+ */
15
+ const rules = {
16
+ builtIn: [
17
+ noRedundantBool,
18
+ noConstantCondition,
19
+ noMixedOperators,
20
+ noSelfComparison,
21
+ deferredCall
22
+ ],
23
+ noRedundantBool,
24
+ noConstantCondition,
25
+ noMixedOperators,
26
+ noSelfComparison,
27
+ deferredCall,
28
+ requireType
29
+ };
30
+ //#endregion
31
+ export { rules };
@@ -0,0 +1,2 @@
1
+ require("./facade.cjs");
2
+ require("./runner.cjs");
@@ -0,0 +1,3 @@
1
+ import { RuleContext, RuleSeverity, RuleTier, SELRule } from "./types.mjs";
2
+ import { rules } from "./facade.mjs";
3
+ import { RunRulesOptions, runRules } from "./runner.mjs";
@@ -0,0 +1,3 @@
1
+ import "./facade.mjs";
2
+ import "./runner.mjs";
3
+ export {};
@@ -0,0 +1,40 @@
1
+ const require_ast_utils = require("../utils/ast-utils.cjs");
2
+ //#region src/rules/runner.ts
3
+ /**
4
+ * Run enabled rules of the specified tier against a parsed AST.
5
+ */
6
+ const runRules = ({ expression, ast, schema, rules, tier, resolvedType }) => {
7
+ const diagnostics = [];
8
+ for (const rule of rules) {
9
+ if ((rule.tier ?? "structural") !== tier) continue;
10
+ const severity = rule.defaultSeverity;
11
+ const context = {
12
+ expression,
13
+ ast,
14
+ schema,
15
+ severity,
16
+ resolvedType,
17
+ report(node, message) {
18
+ const span = require_ast_utils.nodeSpan(node);
19
+ return {
20
+ message,
21
+ severity,
22
+ from: span.from,
23
+ to: span.to
24
+ };
25
+ },
26
+ reportAt(from, to, message) {
27
+ return {
28
+ message,
29
+ severity,
30
+ from,
31
+ to
32
+ };
33
+ }
34
+ };
35
+ diagnostics.push(...rule.run(context));
36
+ }
37
+ return diagnostics;
38
+ };
39
+ //#endregion
40
+ exports.runRules = runRules;
@@ -0,0 +1,27 @@
1
+ import { RuleTier, SELRule } from "./types.cjs";
2
+ import { SELDiagnostic } from "../checker/checker.cjs";
3
+ import { ASTNode } from "@marcbachmann/cel-js";
4
+ import { SELSchema } from "@seljs/schema";
5
+
6
+ //#region src/rules/runner.d.ts
7
+ interface RunRulesOptions {
8
+ expression: string;
9
+ ast: ASTNode;
10
+ schema: SELSchema;
11
+ rules: readonly SELRule[];
12
+ tier: RuleTier;
13
+ resolvedType?: string;
14
+ }
15
+ /**
16
+ * Run enabled rules of the specified tier against a parsed AST.
17
+ */
18
+ declare const runRules: ({
19
+ expression,
20
+ ast,
21
+ schema,
22
+ rules,
23
+ tier,
24
+ resolvedType
25
+ }: RunRulesOptions) => SELDiagnostic[];
26
+ //#endregion
27
+ export { RunRulesOptions, runRules };
@@ -0,0 +1,27 @@
1
+ import { RuleTier, SELRule } from "./types.mjs";
2
+ import { SELDiagnostic } from "../checker/checker.mjs";
3
+ import { ASTNode } from "@marcbachmann/cel-js";
4
+ import { SELSchema } from "@seljs/schema";
5
+
6
+ //#region src/rules/runner.d.ts
7
+ interface RunRulesOptions {
8
+ expression: string;
9
+ ast: ASTNode;
10
+ schema: SELSchema;
11
+ rules: readonly SELRule[];
12
+ tier: RuleTier;
13
+ resolvedType?: string;
14
+ }
15
+ /**
16
+ * Run enabled rules of the specified tier against a parsed AST.
17
+ */
18
+ declare const runRules: ({
19
+ expression,
20
+ ast,
21
+ schema,
22
+ rules,
23
+ tier,
24
+ resolvedType
25
+ }: RunRulesOptions) => SELDiagnostic[];
26
+ //#endregion
27
+ export { RunRulesOptions, runRules };
@@ -0,0 +1,40 @@
1
+ import { nodeSpan } from "../utils/ast-utils.mjs";
2
+ //#region src/rules/runner.ts
3
+ /**
4
+ * Run enabled rules of the specified tier against a parsed AST.
5
+ */
6
+ const runRules = ({ expression, ast, schema, rules, tier, resolvedType }) => {
7
+ const diagnostics = [];
8
+ for (const rule of rules) {
9
+ if ((rule.tier ?? "structural") !== tier) continue;
10
+ const severity = rule.defaultSeverity;
11
+ const context = {
12
+ expression,
13
+ ast,
14
+ schema,
15
+ severity,
16
+ resolvedType,
17
+ report(node, message) {
18
+ const span = nodeSpan(node);
19
+ return {
20
+ message,
21
+ severity,
22
+ from: span.from,
23
+ to: span.to
24
+ };
25
+ },
26
+ reportAt(from, to, message) {
27
+ return {
28
+ message,
29
+ severity,
30
+ from,
31
+ to
32
+ };
33
+ }
34
+ };
35
+ diagnostics.push(...rule.run(context));
36
+ }
37
+ return diagnostics;
38
+ };
39
+ //#endregion
40
+ export { runRules };
@@ -0,0 +1,77 @@
1
+ import { SELDiagnostic } from "../checker/checker.cjs";
2
+ import { ASTNode } from "@marcbachmann/cel-js";
3
+ import { SELSchema } from "@seljs/schema";
4
+
5
+ //#region src/rules/types.d.ts
6
+ /**
7
+ * Severity levels for rules.
8
+ */
9
+ type RuleSeverity = "error" | "warning" | "info";
10
+ /**
11
+ * Tier determines when a rule runs:
12
+ * - "structural": runs on any successfully-parsed expression, even if type-check fails
13
+ * - "type-aware": runs only when both parse and type-check succeed
14
+ */
15
+ type RuleTier = "structural" | "type-aware";
16
+ /**
17
+ * Context passed to each rule's run function.
18
+ */
19
+ interface RuleContext {
20
+ /**
21
+ * The raw expression string.
22
+ */
23
+ expression: string;
24
+ /**
25
+ * The parsed AST root node.
26
+ */
27
+ ast: ASTNode;
28
+ /**
29
+ * The active schema (contracts, variables, types, functions).
30
+ */
31
+ schema: SELSchema;
32
+ /**
33
+ * The resolved severity for this rule invocation.
34
+ */
35
+ severity: RuleSeverity;
36
+ /**
37
+ * Resolved CEL type of the full expression. Only set for type-aware rules.
38
+ */
39
+ resolvedType?: string;
40
+ /**
41
+ * Create a diagnostic spanning an AST node's source range.
42
+ */
43
+ report: (node: ASTNode, message: string) => SELDiagnostic;
44
+ /**
45
+ * Create a diagnostic at an explicit position range.
46
+ */
47
+ reportAt: (from: number, to: number, message: string) => SELDiagnostic;
48
+ }
49
+ /**
50
+ * A lint rule that analyzes a parsed expression and reports diagnostics.
51
+ */
52
+ interface SELRule {
53
+ /**
54
+ * Unique rule identifier (kebab-case, e.g. "no-redundant-bool").
55
+ */
56
+ name: string;
57
+ /**
58
+ * Human-readable description.
59
+ */
60
+ description: string;
61
+ /**
62
+ * Severity level for diagnostics reported by this rule.
63
+ */
64
+ defaultSeverity: RuleSeverity;
65
+ /**
66
+ * Execution tier. Defaults to "structural" if omitted.
67
+ * - "structural": rule receives AST even when type-check failed
68
+ * - "type-aware": rule only runs after successful type-check
69
+ */
70
+ tier?: RuleTier;
71
+ /**
72
+ * Analyze the expression and return diagnostics.
73
+ */
74
+ run: (context: RuleContext) => SELDiagnostic[];
75
+ }
76
+ //#endregion
77
+ export { RuleContext, RuleSeverity, RuleTier, SELRule };
@@ -0,0 +1,77 @@
1
+ import { SELDiagnostic } from "../checker/checker.mjs";
2
+ import { ASTNode } from "@marcbachmann/cel-js";
3
+ import { SELSchema } from "@seljs/schema";
4
+
5
+ //#region src/rules/types.d.ts
6
+ /**
7
+ * Severity levels for rules.
8
+ */
9
+ type RuleSeverity = "error" | "warning" | "info";
10
+ /**
11
+ * Tier determines when a rule runs:
12
+ * - "structural": runs on any successfully-parsed expression, even if type-check fails
13
+ * - "type-aware": runs only when both parse and type-check succeed
14
+ */
15
+ type RuleTier = "structural" | "type-aware";
16
+ /**
17
+ * Context passed to each rule's run function.
18
+ */
19
+ interface RuleContext {
20
+ /**
21
+ * The raw expression string.
22
+ */
23
+ expression: string;
24
+ /**
25
+ * The parsed AST root node.
26
+ */
27
+ ast: ASTNode;
28
+ /**
29
+ * The active schema (contracts, variables, types, functions).
30
+ */
31
+ schema: SELSchema;
32
+ /**
33
+ * The resolved severity for this rule invocation.
34
+ */
35
+ severity: RuleSeverity;
36
+ /**
37
+ * Resolved CEL type of the full expression. Only set for type-aware rules.
38
+ */
39
+ resolvedType?: string;
40
+ /**
41
+ * Create a diagnostic spanning an AST node's source range.
42
+ */
43
+ report: (node: ASTNode, message: string) => SELDiagnostic;
44
+ /**
45
+ * Create a diagnostic at an explicit position range.
46
+ */
47
+ reportAt: (from: number, to: number, message: string) => SELDiagnostic;
48
+ }
49
+ /**
50
+ * A lint rule that analyzes a parsed expression and reports diagnostics.
51
+ */
52
+ interface SELRule {
53
+ /**
54
+ * Unique rule identifier (kebab-case, e.g. "no-redundant-bool").
55
+ */
56
+ name: string;
57
+ /**
58
+ * Human-readable description.
59
+ */
60
+ description: string;
61
+ /**
62
+ * Severity level for diagnostics reported by this rule.
63
+ */
64
+ defaultSeverity: RuleSeverity;
65
+ /**
66
+ * Execution tier. Defaults to "structural" if omitted.
67
+ * - "structural": rule receives AST even when type-check failed
68
+ * - "type-aware": rule only runs after successful type-check
69
+ */
70
+ tier?: RuleTier;
71
+ /**
72
+ * Analyze the expression and return diagnostics.
73
+ */
74
+ run: (context: RuleContext) => SELDiagnostic[];
75
+ }
76
+ //#endregion
77
+ export { RuleContext, RuleSeverity, RuleTier, SELRule };