@xano/xanoscript-language-server 11.8.4 → 11.8.5

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 (56) hide show
  1. package/.claude/settings.local.json +2 -1
  2. package/cache/documentCache.js +58 -10
  3. package/lexer/db.js +1 -2
  4. package/lexer/security.js +16 -0
  5. package/onCompletion/onCompletion.js +61 -1
  6. package/onDefinition/onDefinition.js +150 -0
  7. package/onDefinition/onDefinition.spec.js +313 -0
  8. package/onDidChangeContent/onDidChangeContent.js +52 -5
  9. package/onHover/functions.md +28 -0
  10. package/package.json +1 -1
  11. package/parser/base_parser.js +61 -3
  12. package/parser/clauses/middlewareClause.js +16 -0
  13. package/parser/definitions/columnDefinition.js +5 -0
  14. package/parser/functions/api/apiCallFn.js +5 -3
  15. package/parser/functions/controls/functionCallFn.js +5 -3
  16. package/parser/functions/controls/functionRunFn.js +61 -5
  17. package/parser/functions/controls/taskCallFn.js +5 -3
  18. package/parser/functions/db/captureFieldName.js +63 -0
  19. package/parser/functions/db/dbAddFn.js +5 -3
  20. package/parser/functions/db/dbAddOrEditFn.js +13 -3
  21. package/parser/functions/db/dbBulkAddFn.js +5 -3
  22. package/parser/functions/db/dbBulkDeleteFn.js +5 -3
  23. package/parser/functions/db/dbBulkPatchFn.js +5 -3
  24. package/parser/functions/db/dbBulkUpdateFn.js +5 -3
  25. package/parser/functions/db/dbDelFn.js +10 -3
  26. package/parser/functions/db/dbEditFn.js +13 -3
  27. package/parser/functions/db/dbGetFn.js +10 -3
  28. package/parser/functions/db/dbHasFn.js +9 -3
  29. package/parser/functions/db/dbPatchFn.js +10 -3
  30. package/parser/functions/db/dbQueryFn.js +29 -3
  31. package/parser/functions/db/dbSchemaFn.js +5 -3
  32. package/parser/functions/db/dbTruncateFn.js +5 -3
  33. package/parser/functions/middlewareCallFn.js +3 -1
  34. package/parser/functions/security/register.js +19 -9
  35. package/parser/functions/security/securityCreateAuthTokenFn.js +22 -0
  36. package/parser/functions/security/securityJweDecodeLegacyFn.js +24 -0
  37. package/parser/functions/security/securityJweDecodeLegacyFn.spec.js +26 -0
  38. package/parser/functions/security/securityJweEncodeLegacyFn.js +24 -0
  39. package/parser/functions/security/securityJweEncodeLegacyFn.spec.js +25 -0
  40. package/parser/functions/securityFn.js +2 -0
  41. package/parser/functions/varFn.js +1 -1
  42. package/parser/generic/asVariable.js +2 -0
  43. package/parser/generic/assignableVariableAs.js +1 -0
  44. package/parser/generic/assignableVariableProperty.js +5 -2
  45. package/parser/tests/variable_test/coverage_check.xs +293 -0
  46. package/parser/variableScanner.js +64 -0
  47. package/parser/variableValidator.js +44 -0
  48. package/parser/variableValidator.spec.js +179 -0
  49. package/server.js +164 -10
  50. package/utils.js +32 -0
  51. package/utils.spec.js +93 -1
  52. package/workspace/crossFileValidator.js +166 -0
  53. package/workspace/crossFileValidator.spec.js +654 -0
  54. package/workspace/referenceTracking.spec.js +420 -0
  55. package/workspace/workspaceIndex.js +149 -0
  56. package/workspace/workspaceIndex.spec.js +189 -0
@@ -19,7 +19,8 @@
19
19
  "Bash(then)",
20
20
  "Bash(echo:*)",
21
21
  "Bash(fi)",
22
- "Bash(done)"
22
+ "Bash(done)",
23
+ "Bash(npx eslint:*)"
23
24
  ]
24
25
  }
25
26
  }
@@ -7,10 +7,13 @@ import { getSchemeFromContent } from "../utils.js";
7
7
  * Stores parse results keyed by document URI + version.
8
8
  * Note: We cache a snapshot of parser state, not the parser instance itself,
9
9
  * since the parser is a singleton that gets mutated on each parse.
10
+ *
11
+ * lexResult (token arrays) are NOT cached to reduce memory footprint.
12
+ * Lexing is fast (~1ms) and can be repeated when needed.
10
13
  */
11
14
  class DocumentCache {
12
15
  constructor() {
13
- // Map<uri, { version: number, lexResult: Object, parserState: Object, scheme: string }>
16
+ // Map<uri, { version: number, textLength: number, parserState: Object, scheme: string }>
14
17
  this.cache = new Map();
15
18
  }
16
19
 
@@ -31,9 +34,9 @@ class DocumentCache {
31
34
  cached.version === version &&
32
35
  cached.textLength === textLength
33
36
  ) {
34
- // Return cached result with a proxy parser object containing cached state
37
+ // Re-lex (cheap) to provide tokens; return cached parser state
35
38
  return {
36
- lexResult: cached.lexResult,
39
+ lexResult: lexDocument(text),
37
40
  parser: cached.parserState,
38
41
  scheme: cached.scheme,
39
42
  };
@@ -44,21 +47,66 @@ class DocumentCache {
44
47
  const lexResult = lexDocument(text);
45
48
  const parser = xanoscriptParser(text, scheme, lexResult);
46
49
 
47
- // Create a snapshot of the parser's state including symbol table
50
+ // Snapshot only the parser state we need - not the token arrays.
51
+ // Use structured clone to avoid retaining references to parser internals.
48
52
  const parserState = {
49
- errors: [...parser.errors],
50
- warnings: [...parser.warnings],
51
- informations: [...parser.informations],
52
- hints: [...parser.hints],
53
+ errors: parser.errors.map((e) => ({
54
+ message: e.message,
55
+ token: e.token
56
+ ? {
57
+ startOffset: e.token.startOffset,
58
+ endOffset: e.token.endOffset,
59
+ }
60
+ : null,
61
+ })),
62
+ warnings: parser.warnings.map((w) => ({
63
+ message: w.message,
64
+ token: w.token
65
+ ? {
66
+ startOffset: w.token.startOffset,
67
+ endOffset: w.token.endOffset,
68
+ }
69
+ : null,
70
+ })),
71
+ informations: parser.informations.map((i) => ({
72
+ message: i.message,
73
+ token: i.token
74
+ ? {
75
+ startOffset: i.token.startOffset,
76
+ endOffset: i.token.endOffset,
77
+ }
78
+ : null,
79
+ })),
80
+ hints: parser.hints.map((h) => ({
81
+ message: h.message,
82
+ token: h.token
83
+ ? {
84
+ startOffset: h.token.startOffset,
85
+ endOffset: h.token.endOffset,
86
+ }
87
+ : null,
88
+ })),
53
89
  __symbolTable: parser.__symbolTable
54
- ? JSON.parse(JSON.stringify(parser.__symbolTable))
90
+ ? {
91
+ input: { ...parser.__symbolTable.input },
92
+ var: { ...parser.__symbolTable.var },
93
+ auth: { ...parser.__symbolTable.auth },
94
+ env: { ...parser.__symbolTable.env },
95
+ references: parser.__symbolTable.references.map((r) => ({
96
+ ...r,
97
+ args: r.args ? { ...r.args } : undefined,
98
+ dataKeys: r.dataKeys ? [...r.dataKeys] : undefined,
99
+ })),
100
+ varDeclarations: parser.__symbolTable.varDeclarations.map((d) => ({
101
+ ...d,
102
+ })),
103
+ }
55
104
  : null,
56
105
  };
57
106
 
58
107
  const cacheEntry = {
59
108
  version,
60
109
  textLength,
61
- lexResult,
62
110
  parserState,
63
111
  scheme,
64
112
  };
package/lexer/db.js CHANGED
@@ -190,9 +190,8 @@ export function mapTokenToType(token) {
190
190
  case TruncateToken.name:
191
191
  case DirectQueryToken.name:
192
192
  case SetDatasourceToken.name:
193
- return "variable";
194
193
  case WhereToken.name:
195
- return;
194
+ return "variable";
196
195
  default:
197
196
  return null;
198
197
  }
package/lexer/security.js CHANGED
@@ -85,6 +85,18 @@ export const JweEncodeToken = createTokenByName("jwe_encode", {
85
85
  categories: [Identifier],
86
86
  });
87
87
 
88
+ // jwe_decode_legacy
89
+ export const JweDecodeLegacyToken = createTokenByName("jwe_decode_legacy", {
90
+ longer_alt: Identifier,
91
+ categories: [Identifier],
92
+ });
93
+
94
+ // jwe_encode_legacy
95
+ export const JweEncodeLegacyToken = createTokenByName("jwe_encode_legacy", {
96
+ longer_alt: Identifier,
97
+ categories: [Identifier],
98
+ });
99
+
88
100
  // jws_decode
89
101
  export const JwsDecodeToken = createTokenByName("jws_decode", {
90
102
  longer_alt: Identifier,
@@ -121,6 +133,8 @@ export const SecurityTokens = [
121
133
  EncryptToken,
122
134
  GeneratePassToken,
123
135
  GenerateUuidToken,
136
+ JweDecodeLegacyToken,
137
+ JweEncodeLegacyToken,
124
138
  JweDecodeToken,
125
139
  JweEncodeToken,
126
140
  JwsDecodeToken,
@@ -150,6 +164,8 @@ export function mapTokenToType(token) {
150
164
  case GenerateUuidToken.name:
151
165
  case JweDecodeToken.name:
152
166
  case JweEncodeToken.name:
167
+ case JweDecodeLegacyToken.name:
168
+ case JweEncodeLegacyToken.name:
153
169
  case JwsDecodeToken.name:
154
170
  case JwsEncodeToken.name:
155
171
  case RandomBytesToken.name:
@@ -1,5 +1,6 @@
1
1
  import { mapToVirtualJS } from "../embedded/embeddedContent.js";
2
2
  import { getSchemeFromContent } from "../utils.js";
3
+ import { workspaceIndex } from "../workspace/workspaceIndex.js";
3
4
  import { getContentAssistSuggestions } from "./contentAssist.js";
4
5
 
5
6
  /**
@@ -33,6 +34,65 @@ export function onCompletion(params, documents) {
33
34
 
34
35
  // Otherwise, handle as regular XanoScript
35
36
  const scheme = getSchemeFromContent(text);
37
+ const prefix = text.slice(0, offset);
36
38
 
37
- return getContentAssistSuggestions(text.slice(0, offset), scheme);
39
+ const suggestions = getContentAssistSuggestions(prefix, scheme);
40
+
41
+ // Add workspace object name completions
42
+ const workspaceCompletions = getWorkspaceCompletions(prefix);
43
+ if (workspaceCompletions) {
44
+ const existing = suggestions || [];
45
+ return [...existing, ...workspaceCompletions];
46
+ }
47
+
48
+ return suggestions;
49
+ }
50
+
51
+ /**
52
+ * Get completions for cross-file references based on cursor context.
53
+ * @param {string} prefix - Text before cursor
54
+ * @returns {Array|null} Completion items or null if not in a reference context
55
+ */
56
+ function getWorkspaceCompletions(prefix) {
57
+ // function.run " or function.call " context
58
+ if (/function\.(run|call)\s+"[^"]*$/.test(prefix)) {
59
+ return workspaceIndex.getAllNames("function").map((name) => ({
60
+ label: name,
61
+ kind: 3, // Function
62
+ detail: "function",
63
+ }));
64
+ }
65
+
66
+ // task.call " context
67
+ if (/task\.call\s+"[^"]*$/.test(prefix)) {
68
+ return workspaceIndex.getAllNames("task").map((name) => ({
69
+ label: name,
70
+ kind: 3,
71
+ detail: "task",
72
+ }));
73
+ }
74
+
75
+ // db.* table name context
76
+ if (
77
+ /\b(get|add|edit|delete|query|patch|has|truncate|add_or_edit|schema)\s+("?[^"\s{]*$)/.test(
78
+ prefix
79
+ )
80
+ ) {
81
+ return workspaceIndex.getAllNames("table").map((name) => ({
82
+ label: name,
83
+ kind: 7, // Class (for table)
84
+ detail: "table",
85
+ }));
86
+ }
87
+
88
+ // api.call " context
89
+ if (/api\.call\s+"[^"]*$/.test(prefix)) {
90
+ return workspaceIndex.getAllNames("query").map((name) => ({
91
+ label: name,
92
+ kind: 3,
93
+ detail: "query",
94
+ }));
95
+ }
96
+
97
+ return null;
38
98
  }
@@ -0,0 +1,150 @@
1
+ import { lexDocument } from "../lexer/lexer.js";
2
+ import { LongFormVariable, ShortFormVariable } from "../lexer/variables.js";
3
+ import { getVarName } from "../parser/generic/utils.js";
4
+ import { xanoscriptParser } from "../parser/parser.js";
5
+
6
+ // Token names that precede a table name reference
7
+ const DB_OPERATION_TOKENS = new Set([
8
+ "get",
9
+ "add",
10
+ "edit",
11
+ "delete",
12
+ "query",
13
+ "patch",
14
+ "has",
15
+ "truncate",
16
+ "add_or_edit",
17
+ "schema",
18
+ ]);
19
+
20
+ const SHORT_FORM = ShortFormVariable.name;
21
+ const LONG_FORM = LongFormVariable.name;
22
+
23
+ /**
24
+ * Find the definition location for the symbol at the given offset.
25
+ * @param {string} text - Document text
26
+ * @param {number} offset - Cursor offset in document
27
+ * @param {import('../workspace/workspaceIndex.js').WorkspaceIndex} index
28
+ * @param {Object} [symbolTable] - Parsed symbol table (optional, for variable lookups)
29
+ * @returns {{ uri: string, offset?: number } | null}
30
+ */
31
+ export function findDefinition(text, offset, index, symbolTable) {
32
+ const lexResult = lexDocument(text);
33
+ const tokens = lexResult.tokens;
34
+
35
+ // Find the token at the cursor position
36
+ let tokenIndex = -1;
37
+ for (let i = 0; i < tokens.length; i++) {
38
+ if (tokens[i].startOffset <= offset && offset <= tokens[i].endOffset) {
39
+ tokenIndex = i;
40
+ break;
41
+ }
42
+ }
43
+
44
+ if (tokenIndex < 0) return null;
45
+
46
+ const token = tokens[tokenIndex];
47
+ const typeName = token.tokenType.name;
48
+
49
+ // Variable go-to-definition
50
+ if (typeName === SHORT_FORM || typeName === LONG_FORM) {
51
+ // Parse if no symbol table provided
52
+ const st =
53
+ symbolTable ||
54
+ xanoscriptParser(text, undefined, lexResult).__symbolTable;
55
+ return findVariableDefinition(tokens, tokenIndex, st);
56
+ }
57
+
58
+ // For LongFormVariable ($var), check if cursor is on the identifier after $var.
59
+ // e.g., "$var.foo" — cursor on "foo" (an Identifier token after DotToken after LongFormVariable)
60
+ if (
61
+ tokenIndex >= 2 &&
62
+ tokens[tokenIndex - 1].image === "." &&
63
+ tokens[tokenIndex - 2].tokenType.name === LONG_FORM
64
+ ) {
65
+ const st =
66
+ symbolTable ||
67
+ xanoscriptParser(text, undefined, lexResult).__symbolTable;
68
+ return findVariableDefinition(tokens, tokenIndex, st);
69
+ }
70
+
71
+ const name = getVarName(token);
72
+
73
+ // Look backwards to determine context (cross-file references)
74
+ for (let i = tokenIndex - 1; i >= 0 && i >= tokenIndex - 3; i--) {
75
+ const prevImage = tokens[i].image.toLowerCase();
76
+
77
+ if (prevImage === "run" || prevImage === "call") {
78
+ // Check what's before: function. or task. or api.
79
+ if (i >= 2 && tokens[i - 1].image === ".") {
80
+ const prefix = tokens[i - 2].image.toLowerCase();
81
+ if (prefix === "function") {
82
+ const entry = index.get("function", name);
83
+ return entry ? { uri: entry.uri } : null;
84
+ }
85
+ if (prefix === "task") {
86
+ const entry = index.get("task", name);
87
+ return entry ? { uri: entry.uri } : null;
88
+ }
89
+ if (prefix === "api") {
90
+ const entry = index.get("query", name);
91
+ return entry ? { uri: entry.uri } : null;
92
+ }
93
+ }
94
+ }
95
+
96
+ if (DB_OPERATION_TOKENS.has(prevImage)) {
97
+ const entry = index.get("table", name);
98
+ return entry ? { uri: entry.uri } : null;
99
+ }
100
+ }
101
+
102
+ return null;
103
+ }
104
+
105
+ /**
106
+ * Find the declaration offset for a variable token.
107
+ * @param {import('chevrotain').IToken[]} tokens
108
+ * @param {number} tokenIndex - Index of the token the cursor is on
109
+ * @param {Object} symbolTable
110
+ * @returns {{ offset: number } | null}
111
+ */
112
+ function findVariableDefinition(tokens, tokenIndex, symbolTable) {
113
+ const declarations = symbolTable?.varDeclarations;
114
+ if (!declarations || declarations.length === 0) return null;
115
+
116
+ const token = tokens[tokenIndex];
117
+ const typeName = token.tokenType.name;
118
+
119
+ let varName;
120
+ if (typeName === SHORT_FORM) {
121
+ varName = token.image; // "$foo"
122
+ } else if (typeName === LONG_FORM) {
123
+ // $var.foo → resolve to $foo
124
+ if (tokenIndex + 2 < tokens.length) {
125
+ varName = `$${tokens[tokenIndex + 2].image}`;
126
+ } else {
127
+ return null;
128
+ }
129
+ } else {
130
+ // Cursor is on the identifier after "$var." (e.g., "foo" in "$var.foo")
131
+ varName = `$${token.image}`;
132
+ }
133
+
134
+ // Find the first declaration for this variable
135
+ const decl = declarations.find((d) => d.name === varName);
136
+ if (!decl || decl.startOffset == null) return null;
137
+
138
+ // Don't navigate if cursor is already on the declaration
139
+ if (token.startOffset === decl.startOffset) return null;
140
+ // For LongFormVariable, also check if the identifier token matches
141
+ if (
142
+ typeName === LONG_FORM &&
143
+ tokenIndex + 2 < tokens.length &&
144
+ tokens[tokenIndex + 2].startOffset === decl.startOffset
145
+ ) {
146
+ return null;
147
+ }
148
+
149
+ return { offset: decl.startOffset };
150
+ }
@@ -0,0 +1,313 @@
1
+ import { expect } from "chai";
2
+ import { readFileSync } from "fs";
3
+ import { before, beforeEach, describe, it } from "mocha";
4
+ import { lexDocument } from "../lexer/lexer.js";
5
+ import { xanoscriptParser } from "../parser/parser.js";
6
+ import { WorkspaceIndex } from "../workspace/workspaceIndex.js";
7
+ import { findDefinition } from "./onDefinition.js";
8
+
9
+ describe("findDefinition", () => {
10
+ let index;
11
+
12
+ beforeEach(() => {
13
+ index = new WorkspaceIndex();
14
+ index.addFile("file:///ws/users.xs", "table users {\n schema {\n }\n}");
15
+ index.addFile("file:///ws/helper.xs", 'function "helper" {\n}');
16
+ });
17
+
18
+ it("should find definition for function.run reference", () => {
19
+ const text = `function "caller" {
20
+ stack {
21
+ function.run "helper" as $result
22
+ }
23
+ }`;
24
+ const offset = text.indexOf('"helper"') + 1;
25
+ const result = findDefinition(text, offset, index);
26
+ expect(result).to.exist;
27
+ expect(result.uri).to.equal("file:///ws/helper.xs");
28
+ });
29
+
30
+ it("should find definition for db.get reference", () => {
31
+ const text = `function "caller" {
32
+ stack {
33
+ db.get users {
34
+ field_name = "user_id"
35
+ field_value = 1
36
+ } as $user
37
+ }
38
+ }`;
39
+ const offset = text.indexOf("users") + 2;
40
+ const result = findDefinition(text, offset, index);
41
+ expect(result).to.exist;
42
+ expect(result.uri).to.equal("file:///ws/users.xs");
43
+ });
44
+
45
+ it("should return null when cursor is not on a reference", () => {
46
+ const text = `function "caller" {
47
+ stack {
48
+ }
49
+ }`;
50
+ const offset = text.indexOf("stack") + 2;
51
+ const result = findDefinition(text, offset, index);
52
+ expect(result).to.be.null;
53
+ });
54
+
55
+ it("should return null when reference target not in index", () => {
56
+ const text = `function "caller" {
57
+ stack {
58
+ function.run "unknown" as $result
59
+ }
60
+ }`;
61
+ const offset = text.indexOf('"unknown"') + 1;
62
+ const result = findDefinition(text, offset, index);
63
+ expect(result).to.be.null;
64
+ });
65
+ });
66
+
67
+ describe("findDefinition - variable go-to-definition with coverage_check.xs", () => {
68
+ let text;
69
+ let lexResult;
70
+ let st;
71
+ let index;
72
+ let tokens;
73
+
74
+ before(() => {
75
+ text = readFileSync(
76
+ new URL(
77
+ "../parser/tests/variable_test/coverage_check.xs",
78
+ import.meta.url,
79
+ ),
80
+ "utf8",
81
+ );
82
+ lexResult = lexDocument(text);
83
+ const parser = xanoscriptParser(text, undefined, lexResult);
84
+ st = { ...parser.__symbolTable };
85
+ st.varDeclarations = [...parser.__symbolTable.varDeclarations];
86
+ tokens = lexResult.tokens;
87
+ index = new WorkspaceIndex();
88
+ });
89
+
90
+ function lineOf(offset) {
91
+ return text.substring(0, offset).split("\n").length;
92
+ }
93
+
94
+ function findAtLine(line, varImage) {
95
+ // Find token matching varImage on the given line
96
+ for (const t of tokens) {
97
+ if (t.image === varImage && lineOf(t.startOffset) === line) {
98
+ return findDefinition(text, t.startOffset, index, st);
99
+ }
100
+ }
101
+ throw new Error(`Token '${varImage}' not found on line ${line}`);
102
+ }
103
+
104
+ // --- Declarations should return NULL (already at definition) ---
105
+
106
+ it("L26: as $vehicle (declaration) -> null", () => {
107
+ expect(findAtLine(26, "$vehicle")).to.be.null;
108
+ });
109
+
110
+ it("L35: var $unused_variable (declaration) -> null", () => {
111
+ expect(findAtLine(35, "$unused_variable")).to.be.null;
112
+ });
113
+
114
+ it("L63: as $policy (declaration) -> null", () => {
115
+ expect(findAtLine(63, "$policy")).to.be.null;
116
+ });
117
+
118
+ it("L109: each as $pc (declaration) -> null", () => {
119
+ expect(findAtLine(109, "$pc")).to.be.null;
120
+ });
121
+
122
+ it("L160: each as $claim (declaration) -> null", () => {
123
+ expect(findAtLine(160, "$claim")).to.be.null;
124
+ });
125
+
126
+ // --- Built-in $db should return NULL ---
127
+
128
+ it("L19: $db (built-in) -> null", () => {
129
+ expect(findAtLine(19, "$db")).to.be.null;
130
+ });
131
+
132
+ // --- Usages should resolve to correct declaration line ---
133
+
134
+ it("L29: $vehicle in precondition -> decl at L26", () => {
135
+ const r = findAtLine(29, "$vehicle");
136
+ expect(r).to.exist;
137
+ expect(lineOf(r.offset)).to.equal(26);
138
+ });
139
+
140
+ it("L42: $vehicle.vin in var value -> decl at L26", () => {
141
+ const r = findAtLine(42, "$vehicle");
142
+ expect(r).to.exist;
143
+ expect(lineOf(r.offset)).to.equal(26);
144
+ });
145
+
146
+ it("L57: $vehicle.id in db.query where -> decl at L26", () => {
147
+ const r = findAtLine(57, "$vehicle");
148
+ expect(r).to.exist;
149
+ expect(lineOf(r.offset)).to.equal(26);
150
+ });
151
+
152
+ it("L82: $policy in conditional if -> decl at L63", () => {
153
+ const r = findAtLine(82, "$policy");
154
+ expect(r).to.exist;
155
+ expect(lineOf(r.offset)).to.equal(63);
156
+ });
157
+
158
+ it("L87: $policy_response in var.update -> decl at L66", () => {
159
+ const r = findAtLine(87, "$policy_response");
160
+ expect(r).to.exist;
161
+ expect(lineOf(r.offset)).to.equal(66);
162
+ });
163
+
164
+ it("L89: $policy.policy_number in value -> decl at L63", () => {
165
+ const r = findAtLine(89, "$policy");
166
+ expect(r).to.exist;
167
+ expect(lineOf(r.offset)).to.equal(63);
168
+ });
169
+
170
+ it("L108: $policy_coverages in foreach -> decl at L105", () => {
171
+ const r = findAtLine(108, "$policy_coverages");
172
+ expect(r).to.exist;
173
+ expect(lineOf(r.offset)).to.equal(105);
174
+ });
175
+
176
+ it("L112: $pc.coverage_type_id in field_value -> decl at L109", () => {
177
+ const r = findAtLine(112, "$pc");
178
+ expect(r).to.exist;
179
+ expect(lineOf(r.offset)).to.equal(109);
180
+ });
181
+
182
+ it("L118: $ctype.code in value -> decl at L113", () => {
183
+ const r = findAtLine(118, "$ctype");
184
+ expect(r).to.exist;
185
+ expect(lineOf(r.offset)).to.equal(113);
186
+ });
187
+
188
+ it("L127: $coverages_response in array.push -> decl at L70", () => {
189
+ const r = findAtLine(127, "$coverages_response");
190
+ expect(r).to.exist;
191
+ expect(lineOf(r.offset)).to.equal(70);
192
+ });
193
+
194
+ it("L128: $coverage_item in value -> decl at L116", () => {
195
+ const r = findAtLine(128, "$coverage_item");
196
+ expect(r).to.exist;
197
+ expect(lineOf(r.offset)).to.equal(116);
198
+ });
199
+
200
+ it("L146: $all_claims in value -> decl at L142", () => {
201
+ const r = findAtLine(146, "$all_claims");
202
+ expect(r).to.exist;
203
+ expect(lineOf(r.offset)).to.equal(142);
204
+ });
205
+
206
+ it("L163: $claim.incident_date in value -> decl at L160", () => {
207
+ const r = findAtLine(163, "$claim");
208
+ expect(r).to.exist;
209
+ expect(lineOf(r.offset)).to.equal(160);
210
+ });
211
+
212
+ it("L168: $claim_date_ts in conditional if -> decl at L162", () => {
213
+ const r = findAtLine(168, "$claim_date_ts");
214
+ expect(r).to.exist;
215
+ expect(lineOf(r.offset)).to.equal(162);
216
+ });
217
+
218
+ it("L168: $two_years_ago in conditional if -> decl at L150", () => {
219
+ const r = findAtLine(168, "$two_years_ago");
220
+ expect(r).to.exist;
221
+ expect(lineOf(r.offset)).to.equal(150);
222
+ });
223
+
224
+ it("L179: $recent_claims_list in array.push -> decl at L155", () => {
225
+ const r = findAtLine(179, "$recent_claims_list");
226
+ expect(r).to.exist;
227
+ expect(lineOf(r.offset)).to.equal(155);
228
+ });
229
+
230
+ it("L180: $recent_claim_item in value -> decl at L169", () => {
231
+ const r = findAtLine(180, "$recent_claim_item");
232
+ expect(r).to.exist;
233
+ expect(lineOf(r.offset)).to.equal(169);
234
+ });
235
+
236
+ it("L188: $claims_summary in var.update -> decl at L74", () => {
237
+ const r = findAtLine(188, "$claims_summary");
238
+ expect(r).to.exist;
239
+ expect(lineOf(r.offset)).to.equal(74);
240
+ });
241
+
242
+ it("L190: $total_claims_count in value -> decl at L145", () => {
243
+ const r = findAtLine(190, "$total_claims_count");
244
+ expect(r).to.exist;
245
+ expect(lineOf(r.offset)).to.equal(145);
246
+ });
247
+
248
+ it("L216: $shop in conditional if -> decl at L212", () => {
249
+ const r = findAtLine(216, "$shop");
250
+ expect(r).to.exist;
251
+ expect(lineOf(r.offset)).to.equal(212);
252
+ });
253
+
254
+ it("L217: $queried_by in var.update -> decl at L202", () => {
255
+ const r = findAtLine(217, "$queried_by");
256
+ expect(r).to.exist;
257
+ expect(lineOf(r.offset)).to.equal(202);
258
+ });
259
+
260
+ it("L232: $vehicle_response in final result -> decl at L40", () => {
261
+ const r = findAtLine(232, "$vehicle_response");
262
+ expect(r).to.exist;
263
+ expect(lineOf(r.offset)).to.equal(40);
264
+ });
265
+
266
+ it("L233: $policy_response in final result -> decl at L66", () => {
267
+ const r = findAtLine(233, "$policy_response");
268
+ expect(r).to.exist;
269
+ expect(lineOf(r.offset)).to.equal(66);
270
+ });
271
+
272
+ it("L234: $coverages_response in final result -> decl at L70", () => {
273
+ const r = findAtLine(234, "$coverages_response");
274
+ expect(r).to.exist;
275
+ expect(lineOf(r.offset)).to.equal(70);
276
+ });
277
+
278
+ it("L235: $claims_summary in final result -> decl at L74", () => {
279
+ const r = findAtLine(235, "$claims_summary");
280
+ expect(r).to.exist;
281
+ expect(lineOf(r.offset)).to.equal(74);
282
+ });
283
+
284
+ it("L241: $queried_by in conditional if -> decl at L202", () => {
285
+ const r = findAtLine(241, "$queried_by");
286
+ expect(r).to.exist;
287
+ expect(lineOf(r.offset)).to.equal(202);
288
+ });
289
+
290
+ it("L242: $result in var.update -> decl at L230", () => {
291
+ const r = findAtLine(242, "$result");
292
+ expect(r).to.exist;
293
+ expect(lineOf(r.offset)).to.equal(230);
294
+ });
295
+
296
+ it("L243: $result in value expression -> decl at L230", () => {
297
+ const r = findAtLine(243, "$result");
298
+ expect(r).to.exist;
299
+ expect(lineOf(r.offset)).to.equal(230);
300
+ });
301
+
302
+ it("L243: $queried_by in value expression -> decl at L202", () => {
303
+ const r = findAtLine(243, "$queried_by");
304
+ expect(r).to.exist;
305
+ expect(lineOf(r.offset)).to.equal(202);
306
+ });
307
+
308
+ it("L251: $result in response = -> decl at L230", () => {
309
+ const r = findAtLine(251, "$result");
310
+ expect(r).to.exist;
311
+ expect(lineOf(r.offset)).to.equal(230);
312
+ });
313
+ });