@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
@@ -1,4 +1,7 @@
1
1
  import { documentCache } from "../cache/documentCache.js";
2
+ import { validateVariables } from "../parser/variableValidator.js";
3
+ import { crossFileValidate } from "../workspace/crossFileValidator.js";
4
+ import { workspaceIndex } from "../workspace/workspaceIndex.js";
2
5
 
3
6
  // Diagnostic severity constants
4
7
  const SEVERITY = {
@@ -71,16 +74,14 @@ export function onDidChangeContent(params, connection) {
71
74
 
72
75
  try {
73
76
  // Parse the XanoScript file using cache
74
- const { parser, scheme } = documentCache.getOrParse(
77
+ const { lexResult, parser, scheme } = documentCache.getOrParse(
75
78
  document.uri,
76
79
  document.version,
77
80
  text,
78
81
  );
79
82
 
80
- if (parser.errors.length === 0) {
81
- // If parsing succeeds with no errors, send an empty diagnostics array
82
- connection.sendDiagnostics({ uri: document.uri, diagnostics: [] });
83
- }
83
+ // Update workspace index with already-parsed data (no re-parse needed)
84
+ workspaceIndex.addParsed(document.uri, text, parser.__symbolTable);
84
85
 
85
86
  for (const error of parser.errors) {
86
87
  console.error(
@@ -91,6 +92,52 @@ export function onDidChangeContent(params, connection) {
91
92
  // Create diagnostics in a single pass
92
93
  const diagnostics = createDiagnostics(parser, document);
93
94
 
95
+ // Run cross-file validation and append as warnings
96
+ if (parser.__symbolTable?.references) {
97
+ const crossFileWarnings = crossFileValidate(
98
+ parser.__symbolTable.references,
99
+ workspaceIndex,
100
+ );
101
+ for (const warning of crossFileWarnings) {
102
+ diagnostics.push({
103
+ severity: SEVERITY.WARNING,
104
+ range: {
105
+ start: document.positionAt(warning.startOffset),
106
+ end: document.positionAt(warning.endOffset + 1),
107
+ },
108
+ message: warning.message,
109
+ });
110
+ }
111
+ }
112
+
113
+ // Run variable validation and append warnings/hints
114
+ if (parser.__symbolTable?.varDeclarations) {
115
+ const varResult = validateVariables(
116
+ parser.__symbolTable,
117
+ lexResult.tokens,
118
+ );
119
+ for (const warning of varResult.warnings) {
120
+ diagnostics.push({
121
+ severity: SEVERITY.WARNING,
122
+ range: {
123
+ start: document.positionAt(warning.startOffset),
124
+ end: document.positionAt(warning.endOffset + 1),
125
+ },
126
+ message: warning.message,
127
+ });
128
+ }
129
+ for (const hint of varResult.hints) {
130
+ diagnostics.push({
131
+ severity: SEVERITY.HINT,
132
+ range: {
133
+ start: document.positionAt(hint.startOffset),
134
+ end: document.positionAt(hint.endOffset + 1),
135
+ },
136
+ message: hint.message,
137
+ });
138
+ }
139
+ }
140
+
94
141
  console.log(
95
142
  `onDidChangeContent(): sending diagnostic (${parser.errors.length} errors) for scheme:`,
96
143
  scheme,
@@ -1538,6 +1538,34 @@ security.jwe_encode {
1538
1538
 
1539
1539
  Encodes a payload as a JSON Web Encryption (JWE) token with `headers`, `claims`, and a `key`. The `key_algorithm` (e.g., `A256KW`) and `content_algorithm` (e.g., `A256GCM`) secure the token, and `ttl` sets its validity (0 for no expiration). The token is stored in the variable defined by `as`, here `$encrypted_token`.
1540
1540
 
1541
+ # security.jwe_encode_legacy
1542
+
1543
+ ```xs
1544
+ security.jwe_encode_legacy {
1545
+ payload = $jwt_payload
1546
+ audience = "LegacyApp"
1547
+ key = $env.magic_jwt_secret
1548
+ key_algorithm = "A256KW"
1549
+ content_algorithm = "A256CBC-HS512"
1550
+ } as $jwt
1551
+ ```
1552
+
1553
+ Encodes a payload as a JSON Web Encryption (JWE) token using legacy algorithms. The `payload` is the data to encrypt, `audience` specifies the intended recipient, and `key` is used for encryption. The `key_algorithm` (e.g., `A256KW`) and `content_algorithm` (e.g., `A256CBC-HS512`) secure the token. The resulting JWE token is stored in the variable defined by `as`, here `$jwt`.
1554
+
1555
+ # security.jwe_decode_legacy
1556
+
1557
+ ```xs
1558
+ security.jwe_decode_legacy {
1559
+ token = $input.magic_token
1560
+ key = $env.magic_jwt_secret
1561
+ audience = "LegacyApp"
1562
+ key_algorithm = "A256KW"
1563
+ content_algorithm = "A256CBC-HS512"
1564
+ } as $decoded_magic_token
1565
+ ```
1566
+
1567
+ Decodes a JSON Web Encryption (JWE) token that was encoded using legacy algorithms. The `token` is the JWE to decode, `key` is used for decryption, and `audience` specifies the expected recipient. The `key_algorithm` (e.g., `A256KW`) and `content_algorithm` (e.g., `A256CBC-HS512`) are used to properly decode the token. The decoded payload is stored in the variable defined by `as`, here `$decoded_magic_token`.
1568
+
1541
1569
  # security.create_secret_key
1542
1570
 
1543
1571
  ```xs
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xano/xanoscript-language-server",
3
- "version": "11.8.4",
3
+ "version": "11.8.5",
4
4
  "description": "Language Server Protocol implementation for XanoScript",
5
5
  "type": "module",
6
6
  "main": "server.js",
@@ -42,10 +42,16 @@ export class XanoBaseParser extends CstParser {
42
42
  * Add a variable to the registry
43
43
  * @param {string} name the name of the variable
44
44
  * @param {string} type the type of the variable (int, decimal, text, bool, etc)
45
- * @param {boolean} nullable whether the variable is nullable
45
+ * @param {import('chevrotain').IToken} [token] the token for position tracking
46
46
  */
47
- addVariable(name, type, value = null) {
48
- this.__symbolTable.var[name] = { type, value };
47
+ addVariable(name, type, token = null) {
48
+ this.__symbolTable.var[name] = { type, value: null };
49
+ this.__symbolTable.varDeclarations.push({
50
+ name,
51
+ type,
52
+ startOffset: token?.startOffset ?? null,
53
+ endOffset: token?.endOffset ?? null,
54
+ });
49
55
  }
50
56
 
51
57
  /**
@@ -58,6 +64,56 @@ export class XanoBaseParser extends CstParser {
58
64
  this.__symbolTable.input[name] = { type, iterable, nullable, optional };
59
65
  }
60
66
 
67
+ /**
68
+ * Record a cross-file reference for validation.
69
+ * @param {string} refType - The type of object referenced ("function", "table", "query", "task")
70
+ * @param {string} name - The referenced object name
71
+ * @param {import('chevrotain').IToken} token - The token containing the name (for diagnostics positioning)
72
+ */
73
+ addReference(refType, name, token) {
74
+ if (name == null || !token) return;
75
+ this.__symbolTable.references.push({
76
+ refType,
77
+ name,
78
+ startOffset: token.startOffset,
79
+ endOffset: token.endOffset,
80
+ });
81
+ }
82
+
83
+ /**
84
+ * Attach argument keys to the most recent reference (for input validation).
85
+ * @param {Object} args - Map of argument names to { value, startOffset, endOffset }
86
+ */
87
+ setReferenceArgs(args) {
88
+ const refs = this.__symbolTable.references;
89
+ if (refs.length === 0) return;
90
+ refs[refs.length - 1].args = args;
91
+ }
92
+
93
+ /**
94
+ * Attach field_name to the most recent reference (for column validation).
95
+ * @param {string} fieldName - The field name value
96
+ * @param {number} startOffset
97
+ * @param {number} endOffset
98
+ */
99
+ setReferenceFieldName(fieldName, startOffset, endOffset) {
100
+ const refs = this.__symbolTable.references;
101
+ if (refs.length === 0) return;
102
+ refs[refs.length - 1].fieldName = fieldName;
103
+ refs[refs.length - 1].fieldNameStartOffset = startOffset;
104
+ refs[refs.length - 1].fieldNameEndOffset = endOffset;
105
+ }
106
+
107
+ /**
108
+ * Attach data keys to the most recent reference (for column validation).
109
+ * @param {Array<{name: string, startOffset: number, endOffset: number}>} dataKeys
110
+ */
111
+ setReferenceDataKeys(dataKeys) {
112
+ const refs = this.__symbolTable.references;
113
+ if (refs.length === 0) return;
114
+ refs[refs.length - 1].dataKeys = dataKeys;
115
+ }
116
+
61
117
  /**
62
118
  * Add a warning message to the parser
63
119
  * @param {*} message
@@ -244,6 +300,8 @@ export class XanoBaseParser extends CstParser {
244
300
  var: {},
245
301
  auth: {},
246
302
  env: {},
303
+ references: [],
304
+ varDeclarations: [],
247
305
  };
248
306
  }
249
307
  }
@@ -1,5 +1,6 @@
1
1
  import { EqualToken } from "../../lexer/control.js";
2
2
  import { MiddlewareToken } from "../../lexer/tokens.js";
3
+ import { getVarName } from "../generic/utils.js";
3
4
 
4
5
  /**
5
6
  * response = $entries
@@ -12,6 +13,7 @@ export function middlewareClause($) {
12
13
 
13
14
  const parent = $.CONSUME(MiddlewareToken); // "middleware"
14
15
  $.CONSUME(EqualToken); // "="
16
+ const captured = {};
15
17
  $.SUBRULE($.schemaParseObjectFn, {
16
18
  ARGS: [
17
19
  parent,
@@ -31,9 +33,23 @@ export function middlewareClause($) {
31
33
  },
32
34
  ],
33
35
  },
36
+ captured,
34
37
  ],
35
38
  });
36
39
 
40
+ $.ACTION(() => {
41
+ for (const phase of ["pre", "post"]) {
42
+ const items = captured[phase]?.value;
43
+ if (!Array.isArray(items)) continue;
44
+ for (const item of items) {
45
+ const nameEntry = item.name;
46
+ if (!nameEntry?.value) continue;
47
+ const token = nameEntry.value;
48
+ $.addReference("middleware", getVarName(token), token);
49
+ }
50
+ }
51
+ });
52
+
37
53
  $.sectionStack.pop();
38
54
  };
39
55
  }
@@ -113,6 +113,11 @@ export function columnDefinition($) {
113
113
  ],
114
114
  });
115
115
 
116
+ // Track table reference from FK column definition
117
+ if (captured.table?.value) {
118
+ $.addReference("table", getVarName(captured.table.value), captured.table.value);
119
+ }
120
+
116
121
  if (inputTypeToken.image == "vector" && !captured.size) {
117
122
  $.addWarning(
118
123
  'Column named "vector" should have a size attribute defining its length.',
@@ -10,6 +10,7 @@ import {
10
10
  VerbToken,
11
11
  } from "../../../lexer/query.js";
12
12
  import { Identifier } from "../../../lexer/tokens.js";
13
+ import { getVarName } from "../../generic/utils.js";
13
14
 
14
15
  /**
15
16
  * @param {import('../../base_parser.js').XanoBaseParser} $
@@ -36,10 +37,11 @@ export function apiCallFn($) {
36
37
  );
37
38
  }
38
39
 
39
- $.OR([
40
- { ALT: () => $.CONSUME(StringLiteral) }, // "foo/bar"
41
- { ALT: () => $.CONSUME(Identifier) }, // foo
40
+ const nameToken = $.OR([
41
+ { ALT: () => $.CONSUME(StringLiteral) },
42
+ { ALT: () => $.CONSUME(Identifier) },
42
43
  ]);
44
+ $.addReference("query", getVarName(nameToken), nameToken);
43
45
  $.CONSUME(VerbToken);
44
46
  $.CONSUME(EqualToken);
45
47
  $.OR1([
@@ -1,6 +1,7 @@
1
1
  import { CallToken, FunctionToken } from "../../../lexer/function.js";
2
2
  import { StringLiteral } from "../../../lexer/literal.js";
3
3
  import { DotToken, Identifier } from "../../../lexer/tokens.js";
4
+ import { getVarName } from "../../generic/utils.js";
4
5
 
5
6
  /**
6
7
  *
@@ -17,10 +18,11 @@ export function functionCallFn($) {
17
18
  $.CONSUME(DotToken); // "."
18
19
  const fnToken = $.CONSUME(CallToken); // "call"
19
20
 
20
- $.OR([
21
- { ALT: () => $.CONSUME(Identifier) }, // user
22
- { ALT: () => $.CONSUME1(StringLiteral) }, // "user/ auth"
21
+ const nameToken = $.OR([
22
+ { ALT: () => $.CONSUME(Identifier) },
23
+ { ALT: () => $.CONSUME1(StringLiteral) },
23
24
  ]);
25
+ $.addReference("function", getVarName(nameToken), nameToken);
24
26
 
25
27
  $.SUBRULE($.functionAttrReq, {
26
28
  ARGS: [
@@ -1,6 +1,21 @@
1
1
  import { FunctionToken, RunToken } from "../../../lexer/function.js";
2
- import { StringLiteral } from "../../../lexer/literal.js";
3
- import { DotToken, Identifier } from "../../../lexer/tokens.js";
2
+ import {
3
+ FloatLiteral,
4
+ IntegerLiteral,
5
+ SingleQuotedStringLiteral,
6
+ StringLiteral,
7
+ } from "../../../lexer/literal.js";
8
+ import { DotToken, FalseToken, Identifier, TrueToken } from "../../../lexer/tokens.js";
9
+ import { getVarName } from "../../generic/utils.js";
10
+
11
+ const literalTypeMap = new Map([
12
+ [IntegerLiteral.name, "int"],
13
+ [FloatLiteral.name, "decimal"],
14
+ [StringLiteral.name, "text"],
15
+ [SingleQuotedStringLiteral.name, "text"],
16
+ [TrueToken.name, "bool"],
17
+ [FalseToken.name, "bool"],
18
+ ]);
4
19
 
5
20
  /**
6
21
  *
@@ -14,11 +29,13 @@ export function functionRunFn($) {
14
29
  $.CONSUME(DotToken); // "."
15
30
  const fnToken = $.CONSUME(RunToken); // "run"
16
31
 
17
- $.OR([
18
- { ALT: () => $.CONSUME(Identifier) }, // user
19
- { ALT: () => $.CONSUME1(StringLiteral) }, // "user/ auth"
32
+ const nameToken = $.OR([
33
+ { ALT: () => $.CONSUME(Identifier) },
34
+ { ALT: () => $.CONSUME1(StringLiteral) },
20
35
  ]);
36
+ $.addReference("function", getVarName(nameToken), nameToken);
21
37
 
38
+ const captured = {};
22
39
  $.OPTION(() => {
23
40
  $.SUBRULE($.schemaParseAttributeFn, {
24
41
  ARGS: [
@@ -30,10 +47,49 @@ export function functionRunFn($) {
30
47
  "runtime_mode?": ["async-shared", "async-dedicated"],
31
48
  "mock?": { "![string]": "[expression]" },
32
49
  },
50
+ captured,
33
51
  ],
34
52
  });
35
53
  });
36
54
 
55
+ $.ACTION(() => {
56
+ if (captured.input) {
57
+ const args = {};
58
+ const tokens = $.input;
59
+ for (const [k, v] of Object.entries(captured.input)) {
60
+ if (k === "key" || k === "value") continue;
61
+ const keyOffset = v?.key?.startOffset;
62
+
63
+ // Find value token: scan from key token, skip colon, grab next token
64
+ let type = null;
65
+ let value = v?.value?.image ?? null;
66
+ if (keyOffset != null) {
67
+ for (let i = 0; i < tokens.length - 2; i++) {
68
+ if (tokens[i].startOffset === keyOffset) {
69
+ // tokens[i] = key, tokens[i+1] = colon, tokens[i+2] = value
70
+ const valueToken = tokens[i + 2];
71
+ if (valueToken) {
72
+ type = literalTypeMap.get(valueToken.tokenType.name) ?? null;
73
+ if (type && !value) value = valueToken.image;
74
+ }
75
+ break;
76
+ }
77
+ }
78
+ }
79
+
80
+ args[k] = {
81
+ value,
82
+ type,
83
+ startOffset: v?.key?.startOffset ?? null,
84
+ endOffset: v?.key?.endOffset ?? null,
85
+ };
86
+ }
87
+ if (Object.keys(args).length > 0) {
88
+ $.setReferenceArgs(args);
89
+ }
90
+ }
91
+ });
92
+
37
93
  $.SUBRULE($.asVariable, { ARGS: [fnToken] });
38
94
  $.sectionStack.pop();
39
95
  };
@@ -1,6 +1,7 @@
1
1
  import { StringLiteral } from "../../../lexer/literal.js";
2
2
  import { CallToken, TaskToken } from "../../../lexer/task.js";
3
3
  import { DotToken, Identifier } from "../../../lexer/tokens.js";
4
+ import { getVarName } from "../../generic/utils.js";
4
5
 
5
6
  /**
6
7
  *
@@ -17,10 +18,11 @@ export function taskCallFn($) {
17
18
  $.CONSUME(DotToken); // "."
18
19
  const fnToken = $.CONSUME(CallToken); // "call"
19
20
 
20
- $.OR([
21
- { ALT: () => $.CONSUME(Identifier) }, // user
22
- { ALT: () => $.CONSUME1(StringLiteral) }, // "user/ auth"
21
+ const nameToken = $.OR([
22
+ { ALT: () => $.CONSUME(Identifier) },
23
+ { ALT: () => $.CONSUME1(StringLiteral) },
23
24
  ]);
25
+ $.addReference("task", getVarName(nameToken), nameToken);
24
26
 
25
27
  $.SUBRULE($.functionAttrReq, {
26
28
  ARGS: [fnToken, requiredAttrs, optionalAttrs],
@@ -0,0 +1,63 @@
1
+ import { SingleQuotedStringLiteral,StringLiteral } from "../../../lexer/literal.js";
2
+ import { Identifier } from "../../../lexer/tokens.js";
3
+ import { getVarName } from "../../generic/utils.js";
4
+
5
+ const identifierLikeTokens = new Set([
6
+ Identifier.name,
7
+ StringLiteral.name,
8
+ SingleQuotedStringLiteral.name,
9
+ ]);
10
+
11
+ /**
12
+ * Extract data object keys from captured schema and attach to last reference.
13
+ * @param {import('../../base_parser.js').XanoBaseParser} $
14
+ * @param {Object} captured
15
+ */
16
+ export function captureDataKeys($, captured) {
17
+ if (!captured.data) return;
18
+
19
+ const dataKeys = [];
20
+ for (const [k, v] of Object.entries(captured.data)) {
21
+ if (k === "key" || k === "value") continue;
22
+ const keyToken = v?.key;
23
+ if (keyToken) {
24
+ dataKeys.push({
25
+ name: k,
26
+ startOffset: keyToken.startOffset,
27
+ endOffset: keyToken.endOffset,
28
+ });
29
+ }
30
+ }
31
+ if (dataKeys.length > 0) {
32
+ $.setReferenceDataKeys(dataKeys);
33
+ }
34
+ }
35
+
36
+ /**
37
+ * Extract field_name value from captured schema object and attach to last reference.
38
+ * Only captures when the value is an identifier or string literal (not a variable expression).
39
+ * @param {import('../../base_parser.js').XanoBaseParser} $
40
+ * @param {Object} captured
41
+ */
42
+ export function captureFieldName($, captured) {
43
+ if (!captured.field_name) return;
44
+
45
+ const keyOffset = captured.field_name.key?.startOffset;
46
+ if (keyOffset == null) return;
47
+
48
+ const tokens = $.input;
49
+ for (let i = 0; i < tokens.length - 2; i++) {
50
+ if (tokens[i].startOffset === keyOffset) {
51
+ // tokens[i] = "field_name" key, tokens[i+1] = "=", tokens[i+2] = value
52
+ const valueToken = tokens[i + 2];
53
+ if (valueToken && identifierLikeTokens.has(valueToken.tokenType.name)) {
54
+ $.setReferenceFieldName(
55
+ getVarName(valueToken),
56
+ valueToken.startOffset,
57
+ valueToken.endOffset
58
+ );
59
+ }
60
+ break;
61
+ }
62
+ }
63
+ }
@@ -1,6 +1,7 @@
1
1
  import { AddToken } from "../../../lexer/db.js";
2
2
  import { StringLiteral } from "../../../lexer/literal.js";
3
3
  import { Identifier } from "../../../lexer/tokens.js";
4
+ import { getVarName } from "../../generic/utils.js";
4
5
  import { addonSchema } from "./schema.js";
5
6
  /**
6
7
  * @param {import('../base_parser.js').XanoBaseParser} $
@@ -10,13 +11,14 @@ export function dbAddFn($) {
10
11
  $.sectionStack.push("dbAddFn");
11
12
  const fnToken = $.CONSUME(AddToken); // "add"
12
13
 
13
- $.OR({
14
+ const nameToken = $.OR({
14
15
  DEF: [
15
- { ALT: () => $.CONSUME(Identifier) }, // user
16
- { ALT: () => $.CONSUME1(StringLiteral) }, // "user table"
16
+ { ALT: () => $.CONSUME(Identifier) },
17
+ { ALT: () => $.CONSUME1(StringLiteral) },
17
18
  ],
18
19
  ERR_MSG: "Expected a table name",
19
20
  });
21
+ $.addReference("table", getVarName(nameToken), nameToken);
20
22
 
21
23
  $.SUBRULE($.schemaParseAttributeFn, {
22
24
  ARGS: [
@@ -1,6 +1,8 @@
1
1
  import { AddOrEditToken } from "../../../lexer/db.js";
2
2
  import { StringLiteral } from "../../../lexer/literal.js";
3
3
  import { Identifier } from "../../../lexer/tokens.js";
4
+ import { getVarName } from "../../generic/utils.js";
5
+ import { captureDataKeys,captureFieldName } from "./captureFieldName.js";
4
6
  import { addonSchema } from "./schema.js";
5
7
 
6
8
  /**
@@ -10,14 +12,16 @@ export function dbAddOrEditFn($) {
10
12
  return () => {
11
13
  $.sectionStack.push("dbAddOrEditFn");
12
14
  const fnToken = $.CONSUME(AddOrEditToken); // "edit"
13
- $.OR({
15
+ const nameToken = $.OR({
14
16
  DEF: [
15
- { ALT: () => $.CONSUME(Identifier) }, // user
16
- { ALT: () => $.CONSUME1(StringLiteral) }, // "user table"
17
+ { ALT: () => $.CONSUME(Identifier) },
18
+ { ALT: () => $.CONSUME1(StringLiteral) },
17
19
  ],
18
20
  ERR_MSG: "Expected a table name",
19
21
  });
22
+ $.addReference("table", getVarName(nameToken), nameToken);
20
23
 
24
+ const captured = {};
21
25
  $.SUBRULE($.schemaParseAttributeFn, {
22
26
  ARGS: [
23
27
  fnToken,
@@ -30,9 +34,15 @@ export function dbAddOrEditFn($) {
30
34
  "addon?": [addonSchema],
31
35
  "mock?": { "![string]": "[expression]" },
32
36
  },
37
+ captured,
33
38
  ],
34
39
  });
35
40
 
41
+ $.ACTION(() => {
42
+ captureFieldName($, captured);
43
+ captureDataKeys($, captured);
44
+ });
45
+
36
46
  $.SUBRULE($.asVariable, { ARGS: [fnToken] });
37
47
  $.sectionStack.pop();
38
48
  };
@@ -1,6 +1,7 @@
1
1
  import { AddToken } from "../../../lexer/db.js";
2
2
  import { StringLiteral } from "../../../lexer/literal.js";
3
3
  import { Identifier } from "../../../lexer/tokens.js";
4
+ import { getVarName } from "../../generic/utils.js";
4
5
 
5
6
  /**
6
7
  * @param {import('../base_parser.js').XanoBaseParser} $
@@ -9,13 +10,14 @@ export function dbBulkAddFn($) {
9
10
  return () => {
10
11
  $.sectionStack.push("dbBulkAddFn");
11
12
  const fnToken = $.CONSUME(AddToken); // "add"
12
- $.OR({
13
+ const nameToken = $.OR({
13
14
  DEF: [
14
- { ALT: () => $.CONSUME(Identifier) }, // user
15
- { ALT: () => $.CONSUME1(StringLiteral) }, // "user table"
15
+ { ALT: () => $.CONSUME(Identifier) },
16
+ { ALT: () => $.CONSUME1(StringLiteral) },
16
17
  ],
17
18
  ERR_MSG: "Expected a table name",
18
19
  });
20
+ $.addReference("table", getVarName(nameToken), nameToken);
19
21
  const defined = {};
20
22
  $.SUBRULE($.schemaParseAttributeFn, {
21
23
  ARGS: [
@@ -1,6 +1,7 @@
1
1
  import { DeleteToken } from "../../../lexer/db.js";
2
2
  import { StringLiteral } from "../../../lexer/literal.js";
3
3
  import { Identifier } from "../../../lexer/tokens.js";
4
+ import { getVarName } from "../../generic/utils.js";
4
5
 
5
6
  /**
6
7
  * @param {import('../base_parser.js').XanoBaseParser} $
@@ -9,13 +10,14 @@ export function dbBulkDeleteFn($) {
9
10
  return () => {
10
11
  $.sectionStack.push("dbBulkDeleteFn");
11
12
  const fnToken = $.CONSUME(DeleteToken); // "delete"
12
- $.OR({
13
+ const nameToken = $.OR({
13
14
  DEF: [
14
- { ALT: () => $.CONSUME(Identifier) }, // user
15
- { ALT: () => $.CONSUME1(StringLiteral) }, // "user table"
15
+ { ALT: () => $.CONSUME(Identifier) },
16
+ { ALT: () => $.CONSUME1(StringLiteral) },
16
17
  ],
17
18
  ERR_MSG: "Expected a table name",
18
19
  });
20
+ $.addReference("table", getVarName(nameToken), nameToken);
19
21
 
20
22
  $.SUBRULE($.schemaParseAttributeFn, {
21
23
  ARGS: [
@@ -1,6 +1,7 @@
1
1
  import { PatchToken } from "../../../lexer/db.js";
2
2
  import { StringLiteral } from "../../../lexer/literal.js";
3
3
  import { Identifier } from "../../../lexer/tokens.js";
4
+ import { getVarName } from "../../generic/utils.js";
4
5
 
5
6
  /**
6
7
  * @param {import('../base_parser.js').XanoBaseParser} $
@@ -9,13 +10,14 @@ export function dbBulkPatchFn($) {
9
10
  return () => {
10
11
  $.sectionStack.push("dbBulkPatchFn");
11
12
  const fnToken = $.CONSUME(PatchToken); // "patch"
12
- $.OR({
13
+ const nameToken = $.OR({
13
14
  DEF: [
14
- { ALT: () => $.CONSUME(Identifier) }, // user
15
- { ALT: () => $.CONSUME1(StringLiteral) }, // "user table"
15
+ { ALT: () => $.CONSUME(Identifier) },
16
+ { ALT: () => $.CONSUME1(StringLiteral) },
16
17
  ],
17
18
  ERR_MSG: "Expected a table name",
18
19
  });
20
+ $.addReference("table", getVarName(nameToken), nameToken);
19
21
 
20
22
  $.SUBRULE($.schemaParseAttributeFn, {
21
23
  ARGS: [
@@ -1,6 +1,7 @@
1
1
  import { UpdateToken } from "../../../lexer/db.js";
2
2
  import { StringLiteral } from "../../../lexer/literal.js";
3
3
  import { Identifier } from "../../../lexer/tokens.js";
4
+ import { getVarName } from "../../generic/utils.js";
4
5
 
5
6
  /**
6
7
  * @param {import('../base_parser.js').XanoBaseParser} $
@@ -9,13 +10,14 @@ export function dbBulkUpdateFn($) {
9
10
  return () => {
10
11
  $.sectionStack.push("dbBulkUpdateFn");
11
12
  const fnToken = $.CONSUME(UpdateToken); // "update"
12
- $.OR({
13
+ const nameToken = $.OR({
13
14
  DEF: [
14
- { ALT: () => $.CONSUME(Identifier) }, // user
15
- { ALT: () => $.CONSUME1(StringLiteral) }, // "user table"
15
+ { ALT: () => $.CONSUME(Identifier) },
16
+ { ALT: () => $.CONSUME1(StringLiteral) },
16
17
  ],
17
18
  ERR_MSG: "Expected a table name",
18
19
  });
20
+ $.addReference("table", getVarName(nameToken), nameToken);
19
21
 
20
22
  $.SUBRULE($.schemaParseAttributeFn, {
21
23
  ARGS: [