@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,164 @@
1
+ //#region src/utils/ast-utils.ts
2
+ const BINARY_OPS = new Set([
3
+ "==",
4
+ "!=",
5
+ "<",
6
+ "<=",
7
+ ">",
8
+ ">=",
9
+ "+",
10
+ "-",
11
+ "*",
12
+ "/",
13
+ "%",
14
+ "in",
15
+ "||",
16
+ "&&",
17
+ "[]",
18
+ "[?]"
19
+ ]);
20
+ /**
21
+ * Compute the source span {from, to} of an AST node.
22
+ *
23
+ * Uses the AST structure and `node.input` (original source) to calculate
24
+ * positions. Does NOT use `serialize()` which normalizes whitespace,
25
+ * quotes, and parentheses.
26
+ *
27
+ * Note: for binary operators, `node.pos` points to the operator token,
28
+ * not the start of the expression. We compute `from` by finding the
29
+ * leftmost descendant leaf position instead.
30
+ */
31
+ const nodeSpan = (node) => {
32
+ const src = node.input;
33
+ if (node.op === "id") return {
34
+ from: node.pos,
35
+ to: node.pos + node.args.length
36
+ };
37
+ if (node.op === "value") return valueSpan(src, node.pos);
38
+ const children = collectChildren(node);
39
+ let minFrom = node.pos;
40
+ let maxTo = node.pos;
41
+ for (const child of children) {
42
+ const s = nodeSpan(child);
43
+ if (s.from < minFrom) minFrom = s.from;
44
+ if (s.to > maxTo) maxTo = s.to;
45
+ }
46
+ if (node.op === "!_" || node.op === "-_") minFrom = node.pos;
47
+ if (node.op === "." || node.op === ".?") {
48
+ const fieldName = node.args[1];
49
+ let i = maxTo;
50
+ while (i < src.length && src[i] !== ".") i++;
51
+ i++;
52
+ if (node.op === ".?" && i < src.length && src[i] === "?") i++;
53
+ i += fieldName.length;
54
+ return {
55
+ from: minFrom,
56
+ to: Math.min(i, src.length)
57
+ };
58
+ }
59
+ if (node.op === "call" || node.op === "rcall") return {
60
+ from: minFrom,
61
+ to: scanTo(src, maxTo, ")")
62
+ };
63
+ if (node.op === "list" || node.op === "[]" || node.op === "[?]") return {
64
+ from: minFrom,
65
+ to: scanTo(src, maxTo, "]")
66
+ };
67
+ if (node.op === "map") return {
68
+ from: minFrom,
69
+ to: scanTo(src, maxTo, "}")
70
+ };
71
+ return {
72
+ from: minFrom,
73
+ to: maxTo
74
+ };
75
+ };
76
+ /**
77
+ * Depth-first walk of the AST, calling visitor on every node.
78
+ */
79
+ const walkAST = (node, visitor, parent = null) => {
80
+ visitor(node, parent);
81
+ for (const child of collectChildren(node)) walkAST(child, visitor, node);
82
+ };
83
+ /**
84
+ * Collect all ASTNode children of a node, regardless of op shape.
85
+ */
86
+ const collectChildren = (node) => {
87
+ if (node.op === "value" || node.op === "id") return [];
88
+ if (node.op === "!_" || node.op === "-_") return [node.args];
89
+ if (BINARY_OPS.has(node.op)) {
90
+ const [left, right] = node.args;
91
+ return [left, right];
92
+ }
93
+ switch (node.op) {
94
+ case ".":
95
+ case ".?": {
96
+ const [receiver] = node.args;
97
+ return [receiver];
98
+ }
99
+ case "call": {
100
+ const [, callArgs] = node.args;
101
+ return callArgs;
102
+ }
103
+ case "rcall": {
104
+ const [, receiver, callArgs] = node.args;
105
+ return [receiver, ...callArgs];
106
+ }
107
+ case "list": return node.args;
108
+ case "map": return node.args.flat();
109
+ case "?:": {
110
+ const [cond, then, els] = node.args;
111
+ return [
112
+ cond,
113
+ then,
114
+ els
115
+ ];
116
+ }
117
+ default: return [];
118
+ }
119
+ };
120
+ const valueSpan = (src, from) => {
121
+ const ch = src[from];
122
+ if (ch === "\"" || ch === "'") {
123
+ let i = from + 1;
124
+ while (i < src.length && src[i] !== ch) {
125
+ if (src[i] === "\\") i++;
126
+ i++;
127
+ }
128
+ return {
129
+ from,
130
+ to: i < src.length ? i + 1 : i
131
+ };
132
+ }
133
+ let i = from;
134
+ while (i < src.length && /[\w.]/.test(src[i] ?? "")) i++;
135
+ return {
136
+ from,
137
+ to: i
138
+ };
139
+ };
140
+ const scanTo = (src, from, ch) => {
141
+ let i = from;
142
+ while (i < src.length && src[i] !== ch) i++;
143
+ return i < src.length ? i + 1 : from;
144
+ };
145
+ const findNodeWithParentAt = (root, offset, parent = null) => {
146
+ const rootSpan = nodeSpan(root);
147
+ if (offset < rootSpan.from || offset >= rootSpan.to) return;
148
+ const children = collectChildren(root);
149
+ for (const child of children) {
150
+ const childSpan = nodeSpan(child);
151
+ if (offset >= childSpan.from && offset < childSpan.to) return findNodeWithParentAt(child, offset, root) ?? {
152
+ node: child,
153
+ parent: root
154
+ };
155
+ }
156
+ return {
157
+ node: root,
158
+ parent
159
+ };
160
+ };
161
+ //#endregion
162
+ exports.findNodeWithParentAt = findNodeWithParentAt;
163
+ exports.nodeSpan = nodeSpan;
164
+ exports.walkAST = walkAST;
@@ -0,0 +1,162 @@
1
+ //#region src/utils/ast-utils.ts
2
+ const BINARY_OPS = new Set([
3
+ "==",
4
+ "!=",
5
+ "<",
6
+ "<=",
7
+ ">",
8
+ ">=",
9
+ "+",
10
+ "-",
11
+ "*",
12
+ "/",
13
+ "%",
14
+ "in",
15
+ "||",
16
+ "&&",
17
+ "[]",
18
+ "[?]"
19
+ ]);
20
+ /**
21
+ * Compute the source span {from, to} of an AST node.
22
+ *
23
+ * Uses the AST structure and `node.input` (original source) to calculate
24
+ * positions. Does NOT use `serialize()` which normalizes whitespace,
25
+ * quotes, and parentheses.
26
+ *
27
+ * Note: for binary operators, `node.pos` points to the operator token,
28
+ * not the start of the expression. We compute `from` by finding the
29
+ * leftmost descendant leaf position instead.
30
+ */
31
+ const nodeSpan = (node) => {
32
+ const src = node.input;
33
+ if (node.op === "id") return {
34
+ from: node.pos,
35
+ to: node.pos + node.args.length
36
+ };
37
+ if (node.op === "value") return valueSpan(src, node.pos);
38
+ const children = collectChildren(node);
39
+ let minFrom = node.pos;
40
+ let maxTo = node.pos;
41
+ for (const child of children) {
42
+ const s = nodeSpan(child);
43
+ if (s.from < minFrom) minFrom = s.from;
44
+ if (s.to > maxTo) maxTo = s.to;
45
+ }
46
+ if (node.op === "!_" || node.op === "-_") minFrom = node.pos;
47
+ if (node.op === "." || node.op === ".?") {
48
+ const fieldName = node.args[1];
49
+ let i = maxTo;
50
+ while (i < src.length && src[i] !== ".") i++;
51
+ i++;
52
+ if (node.op === ".?" && i < src.length && src[i] === "?") i++;
53
+ i += fieldName.length;
54
+ return {
55
+ from: minFrom,
56
+ to: Math.min(i, src.length)
57
+ };
58
+ }
59
+ if (node.op === "call" || node.op === "rcall") return {
60
+ from: minFrom,
61
+ to: scanTo(src, maxTo, ")")
62
+ };
63
+ if (node.op === "list" || node.op === "[]" || node.op === "[?]") return {
64
+ from: minFrom,
65
+ to: scanTo(src, maxTo, "]")
66
+ };
67
+ if (node.op === "map") return {
68
+ from: minFrom,
69
+ to: scanTo(src, maxTo, "}")
70
+ };
71
+ return {
72
+ from: minFrom,
73
+ to: maxTo
74
+ };
75
+ };
76
+ /**
77
+ * Depth-first walk of the AST, calling visitor on every node.
78
+ */
79
+ const walkAST = (node, visitor, parent = null) => {
80
+ visitor(node, parent);
81
+ for (const child of collectChildren(node)) walkAST(child, visitor, node);
82
+ };
83
+ /**
84
+ * Collect all ASTNode children of a node, regardless of op shape.
85
+ */
86
+ const collectChildren = (node) => {
87
+ if (node.op === "value" || node.op === "id") return [];
88
+ if (node.op === "!_" || node.op === "-_") return [node.args];
89
+ if (BINARY_OPS.has(node.op)) {
90
+ const [left, right] = node.args;
91
+ return [left, right];
92
+ }
93
+ switch (node.op) {
94
+ case ".":
95
+ case ".?": {
96
+ const [receiver] = node.args;
97
+ return [receiver];
98
+ }
99
+ case "call": {
100
+ const [, callArgs] = node.args;
101
+ return callArgs;
102
+ }
103
+ case "rcall": {
104
+ const [, receiver, callArgs] = node.args;
105
+ return [receiver, ...callArgs];
106
+ }
107
+ case "list": return node.args;
108
+ case "map": return node.args.flat();
109
+ case "?:": {
110
+ const [cond, then, els] = node.args;
111
+ return [
112
+ cond,
113
+ then,
114
+ els
115
+ ];
116
+ }
117
+ default: return [];
118
+ }
119
+ };
120
+ const valueSpan = (src, from) => {
121
+ const ch = src[from];
122
+ if (ch === "\"" || ch === "'") {
123
+ let i = from + 1;
124
+ while (i < src.length && src[i] !== ch) {
125
+ if (src[i] === "\\") i++;
126
+ i++;
127
+ }
128
+ return {
129
+ from,
130
+ to: i < src.length ? i + 1 : i
131
+ };
132
+ }
133
+ let i = from;
134
+ while (i < src.length && /[\w.]/.test(src[i] ?? "")) i++;
135
+ return {
136
+ from,
137
+ to: i
138
+ };
139
+ };
140
+ const scanTo = (src, from, ch) => {
141
+ let i = from;
142
+ while (i < src.length && src[i] !== ch) i++;
143
+ return i < src.length ? i + 1 : from;
144
+ };
145
+ const findNodeWithParentAt = (root, offset, parent = null) => {
146
+ const rootSpan = nodeSpan(root);
147
+ if (offset < rootSpan.from || offset >= rootSpan.to) return;
148
+ const children = collectChildren(root);
149
+ for (const child of children) {
150
+ const childSpan = nodeSpan(child);
151
+ if (offset >= childSpan.from && offset < childSpan.to) return findNodeWithParentAt(child, offset, root) ?? {
152
+ node: child,
153
+ parent: root
154
+ };
155
+ }
156
+ return {
157
+ node: root,
158
+ parent
159
+ };
160
+ };
161
+ //#endregion
162
+ export { findNodeWithParentAt, nodeSpan, walkAST };
package/package.json CHANGED
@@ -1,30 +1,36 @@
1
1
  {
2
2
  "name": "@seljs/checker",
3
- "version": "1.0.0",
4
- "type": "module",
3
+ "version": "1.0.1",
4
+ "repository": {
5
+ "url": "https://github.com/abinnovision/seljs"
6
+ },
5
7
  "license": "Apache-2.0",
6
8
  "author": {
7
9
  "name": "abi group GmbH",
8
10
  "email": "info@abigroup.io",
9
11
  "url": "https://abigroup.io/"
10
12
  },
11
- "repository": {
12
- "url": "https://github.com/abinnovision/seljs"
13
- },
13
+ "type": "module",
14
14
  "exports": {
15
15
  ".": {
16
- "import": "./dist/index.js",
17
- "types": "./dist/index.d.ts"
16
+ "import": {
17
+ "types": "./dist/index.d.mts",
18
+ "default": "./dist/index.mjs"
19
+ },
20
+ "require": {
21
+ "types": "./dist/index.d.cts",
22
+ "default": "./dist/index.cjs"
23
+ }
18
24
  }
19
25
  },
20
- "main": "./dist/index.js",
21
- "types": "./dist/index.d.ts",
26
+ "main": "./dist/index.cjs",
27
+ "types": "./dist/index.d.cts",
22
28
  "files": [
23
29
  "dist",
24
30
  "LICENSE.md"
25
31
  ],
26
32
  "scripts": {
27
- "build": "tsc -p tsconfig.build.json",
33
+ "build": "tsdown",
28
34
  "format:check": "prettier --check '{{src,test}/**/*,*}.{{t,j}s{,x},json{,5},md,y{,a}ml}'",
29
35
  "format:fix": "prettier --write '{{src,test}/**/*,*}.{{t,j}s{,x},json{,5},md,y{,a}ml}'",
30
36
  "lint:check": "eslint '{{src,test}/**/*,*}.{t,j}s{,x}'",
@@ -43,15 +49,12 @@
43
49
  ]
44
50
  },
45
51
  "dependencies": {
46
- "@marcbachmann/cel-js": "^7.5.2",
47
- "@seljs/common": "1.0.0",
48
- "@seljs/schema": "1.0.0",
49
- "@seljs/types": "1.0.0",
52
+ "@marcbachmann/cel-js": "^7.5.3",
53
+ "@seljs/common": "1.0.1",
54
+ "@seljs/schema": "1.0.1",
55
+ "@seljs/types": "1.0.1",
50
56
  "debug": "^4.4.3"
51
57
  },
52
- "peerDependencies": {
53
- "zod": "^4.0.0"
54
- },
55
58
  "devDependencies": {
56
59
  "@abinnovision/eslint-config-base": "^3.2.0",
57
60
  "@abinnovision/prettier-config": "^2.1.5",
@@ -59,10 +62,14 @@
59
62
  "@types/debug": "^4.1.12",
60
63
  "eslint": "^9.39.4",
61
64
  "prettier": "^3.8.1",
65
+ "tsdown": "^0.21.3",
62
66
  "typescript": "^5.9.3",
63
67
  "vitest": "^4.0.18",
64
68
  "zod": "^4.3.6"
65
69
  },
70
+ "peerDependencies": {
71
+ "zod": "^4.0.0"
72
+ },
66
73
  "publishConfig": {
67
74
  "npm": true,
68
75
  "npmAccess": "public"
@@ -1,173 +0,0 @@
1
- import type { SELRule } from "../rules/index.js";
2
- import type { SELSchema } from "@seljs/schema";
3
- interface SELCheckResult {
4
- valid: boolean;
5
- type?: string;
6
- diagnostics: SELDiagnostic[];
7
- }
8
- interface TypeAtResult {
9
- type: string;
10
- from: number;
11
- to: number;
12
- }
13
- interface ExpectedTypeInfo {
14
- /**
15
- * The CEL type expected at this position.
16
- */
17
- expectedType: string;
18
- /**
19
- * How the expectation was inferred.
20
- */
21
- context: "operator" | "function-argument";
22
- /**
23
- * For function args: which parameter index.
24
- */
25
- paramIndex?: number;
26
- /**
27
- * For function args: the function/method name.
28
- */
29
- functionName?: string;
30
- }
31
- interface CompletionInfo {
32
- kind: "top-level" | "dot-access" | "explicit";
33
- receiverType?: string;
34
- items: CompletionItem[];
35
- /**
36
- * Inferred expected type at cursor, if determinable.
37
- */
38
- expectedType?: string;
39
- }
40
- export interface CompletionItem {
41
- label: string;
42
- type: string;
43
- detail?: string;
44
- description?: string;
45
- }
46
- /**
47
- * A diagnostic message with optional position info. Used for both parse/type errors
48
- * and rule violations. Positions are optional because some errors (e.g. from cel-js)
49
- * may not include reliable spans, and rules may choose to report non-positional issues.
50
- */
51
- export interface SELDiagnostic {
52
- message: string;
53
- severity: "error" | "warning" | "info";
54
- from?: number;
55
- to?: number;
56
- }
57
- /**
58
- * Options for configuring the SELChecker.
59
- */
60
- export interface SELCheckerOptions {
61
- /**
62
- * Lint rules to enable. Defaults to none (backward compatible).
63
- */
64
- rules?: readonly SELRule[];
65
- }
66
- /**
67
- * SEL expression checker.
68
- *
69
- * Wraps a hydrated cel-js Environment built from a SELSchema and exposes
70
- * parse/type-check, type inference, cursor-position type lookups, and
71
- * type-aware completions.
72
- */
73
- export declare class SELChecker {
74
- private readonly rules;
75
- private env;
76
- private schema;
77
- private structTypeMap;
78
- constructor(schema: SELSchema, options?: SELCheckerOptions);
79
- /**
80
- * Parse and type-check an expression, returning position-aware diagnostics.
81
- *
82
- * Uses a single `env.parse()` call, then `.check()` on the result
83
- * to avoid double-parsing. Rules run against the AST from the same parse:
84
- * - Structural rules run after successful parse (even if type-check fails)
85
- * - Type-aware rules run only after both parse and type-check succeed
86
- */
87
- check(expression: string): SELCheckResult;
88
- /**
89
- * Get the inferred CEL type of the full expression.
90
- * Returns undefined if the expression is invalid.
91
- */
92
- typeOf(expression: string): string | undefined;
93
- /**
94
- * Get the inferred type at a cursor position (for hover info).
95
- *
96
- * Attempts to identify the sub-expression under the cursor and infer its
97
- * type. Falls back to the full expression type when sub-expression
98
- * isolation is not possible.
99
- */
100
- typeAt(expression: string, offset: number): TypeAtResult | undefined;
101
- /**
102
- * Get type-aware completions for a cursor context.
103
- *
104
- * Supports:
105
- * - **dot-access**: after `contract.` — lists methods of that contract
106
- * - **top-level**: at the start or after operators — lists variables,
107
- * contracts, and functions
108
- */
109
- completionsAt(expression: string, offset: number): CompletionInfo;
110
- /**
111
- * Infer the expected type at a cursor position from surrounding context.
112
- *
113
- * Supports two contexts:
114
- * - **Operator**: `expr > |` — infers the left operand type and derives the
115
- * expected right operand type from the operator.
116
- * - **Function argument**: `contract.method(arg, |)` — looks up the
117
- * parameter type from the schema.
118
- *
119
- * Returns undefined when context cannot be determined, signaling that
120
- * no narrowing should occur (safe fallback).
121
- */
122
- expectedTypeAt(expression: string, offset: number): ExpectedTypeInfo | undefined;
123
- /**
124
- * Resolve type and available members for a dot-access receiver expression.
125
- */
126
- dotCompletions(receiverExpression: string): CompletionInfo;
127
- /**
128
- * Resolve expected type from a structured context.
129
- */
130
- expectedTypeFor(context: {
131
- kind: "operator";
132
- leftExpression: string;
133
- operator: string;
134
- } | {
135
- kind: "function-arg";
136
- receiverName?: string;
137
- functionName: string;
138
- paramIndex: number;
139
- }): string | undefined;
140
- /**
141
- * Rebuild the internal environment from an updated schema.
142
- */
143
- updateSchema(schema: SELSchema): void;
144
- private buildStructTypeMap;
145
- private structFieldsFor;
146
- /**
147
- * Scan backwards from a dot position to extract the receiver expression.
148
- * Handles balanced parentheses so that `foo(erc20.name().` correctly
149
- * identifies `erc20.name()` as the receiver, not `foo(erc20.name()`.
150
- */
151
- private extractReceiverBefore;
152
- /**
153
- * Find the largest call/dot-access chain node that contains the offset.
154
- */
155
- private findContainingChain;
156
- /**
157
- * Scan backwards from the end of text to find a trailing binary operator.
158
- * Tries multi-character operators first (==, !=, etc.) to avoid partial matches.
159
- */
160
- private findTrailingOperator;
161
- /**
162
- * Find the enclosing function/method call around the cursor by scanning
163
- * backwards for an unclosed `(`, then extracting the function name and
164
- * counting commas to determine the parameter index.
165
- */
166
- private findEnclosingCall;
167
- /**
168
- * Look up the expected parameter type from the schema for a function or
169
- * method call at the given parameter index.
170
- */
171
- private resolveRawParamType;
172
- }
173
- export {};