@xano/xanoscript-language-server 11.8.3 → 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 (58) hide show
  1. package/.claude/settings.local.json +2 -1
  2. package/cache/documentCache.js +58 -10
  3. package/lexer/db.js +9 -1
  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/table_trigger_parser.js +21 -0
  46. package/parser/table_trigger_parser.spec.js +29 -0
  47. package/parser/tests/variable_test/coverage_check.xs +293 -0
  48. package/parser/variableScanner.js +64 -0
  49. package/parser/variableValidator.js +44 -0
  50. package/parser/variableValidator.spec.js +179 -0
  51. package/server.js +164 -10
  52. package/utils.js +32 -0
  53. package/utils.spec.js +93 -1
  54. package/workspace/crossFileValidator.js +166 -0
  55. package/workspace/crossFileValidator.spec.js +654 -0
  56. package/workspace/referenceTracking.spec.js +420 -0
  57. package/workspace/workspaceIndex.js +149 -0
  58. package/workspace/workspaceIndex.spec.js +189 -0
@@ -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: [
@@ -1,6 +1,8 @@
1
1
  import { DelToken } 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 { captureFieldName } from "./captureFieldName.js";
4
6
 
5
7
  /**
6
8
  * @param {import('../base_parser.js').XanoBaseParser} $
@@ -9,14 +11,16 @@ export function dbDelFn($) {
9
11
  return () => {
10
12
  $.sectionStack.push("dbDelFn");
11
13
  const fnToken = $.CONSUME(DelToken); // "delete"
12
- $.OR({
14
+ const nameToken = $.OR({
13
15
  DEF: [
14
- { ALT: () => $.CONSUME(Identifier) }, // user
15
- { ALT: () => $.CONSUME1(StringLiteral) }, // "user table"
16
+ { ALT: () => $.CONSUME(Identifier) },
17
+ { ALT: () => $.CONSUME1(StringLiteral) },
16
18
  ],
17
19
  ERR_MSG: "Expected a table name",
18
20
  });
21
+ $.addReference("table", getVarName(nameToken), nameToken);
19
22
 
23
+ const captured = {};
20
24
  $.SUBRULE($.schemaParseAttributeFn, {
21
25
  ARGS: [
22
26
  fnToken,
@@ -26,9 +30,12 @@ export function dbDelFn($) {
26
30
  "description?": "[string]",
27
31
  "disabled?": "[boolean]",
28
32
  },
33
+ captured,
29
34
  ],
30
35
  });
31
36
 
37
+ $.ACTION(() => captureFieldName($, captured));
38
+
32
39
  $.sectionStack.pop();
33
40
  };
34
41
  }
@@ -1,6 +1,8 @@
1
1
  import { EditToken } 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 dbEditFn($) {
10
12
  return () => {
11
13
  $.sectionStack.push("dbEditFn");
12
14
  const fnToken = $.CONSUME(EditToken); // "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,
@@ -31,9 +35,15 @@ export function dbEditFn($) {
31
35
  "addon?": [addonSchema],
32
36
  "mock?": { "![string]": "[expression]" },
33
37
  },
38
+ captured,
34
39
  ],
35
40
  });
36
41
 
42
+ $.ACTION(() => {
43
+ captureFieldName($, captured);
44
+ captureDataKeys($, captured);
45
+ });
46
+
37
47
  $.SUBRULE($.asVariable, { ARGS: [fnToken] });
38
48
  $.sectionStack.pop();
39
49
  };
@@ -1,6 +1,8 @@
1
1
  import { GetToken } 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 { captureFieldName } from "./captureFieldName.js";
4
6
  import { addonSchema } from "./schema.js";
5
7
 
6
8
  /**
@@ -10,14 +12,16 @@ export function dbGetFn($) {
10
12
  return () => {
11
13
  $.sectionStack.push("dbGetFn");
12
14
  const fnToken = $.CONSUME(GetToken); // "get"
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,
@@ -31,9 +35,12 @@ export function dbGetFn($) {
31
35
  "addon?": [addonSchema],
32
36
  "mock?": { "![string]": "[expression]" },
33
37
  },
38
+ captured,
34
39
  ],
35
40
  });
36
41
 
42
+ $.ACTION(() => captureFieldName($, captured));
43
+
37
44
  $.SUBRULE($.asVariable, { ARGS: [fnToken] });
38
45
  $.sectionStack.pop();
39
46
  };
@@ -1,6 +1,8 @@
1
1
  import { HasToken } 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 { captureFieldName } from "./captureFieldName.js";
4
6
 
5
7
  /**
6
8
  * @param {import('../base_parser.js').XanoBaseParser} $
@@ -9,13 +11,15 @@ export function dbHasFn($) {
9
11
  return () => {
10
12
  $.sectionStack.push("dbHasFn");
11
13
  const fnToken = $.CONSUME(HasToken); // "has"
12
- $.OR({
14
+ const nameToken = $.OR({
13
15
  DEF: [
14
- { ALT: () => $.CONSUME(Identifier) }, // user
15
- { ALT: () => $.CONSUME1(StringLiteral) }, // "user table"
16
+ { ALT: () => $.CONSUME(Identifier) },
17
+ { ALT: () => $.CONSUME1(StringLiteral) },
16
18
  ],
17
19
  ERR_MSG: "Expected a table name",
18
20
  });
21
+ $.addReference("table", getVarName(nameToken), nameToken);
22
+ const captured = {};
19
23
  $.SUBRULE($.schemaParseAttributeFn, {
20
24
  ARGS: [
21
25
  fnToken,
@@ -26,8 +30,10 @@ export function dbHasFn($) {
26
30
  "disabled?": "[boolean]",
27
31
  "mock?": { "![string]": "[expression]" },
28
32
  },
33
+ captured,
29
34
  ],
30
35
  });
36
+ $.ACTION(() => captureFieldName($, captured));
31
37
  $.SUBRULE($.asVariable, { ARGS: [fnToken] });
32
38
  $.sectionStack.pop();
33
39
  };
@@ -1,6 +1,8 @@
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";
5
+ import { captureFieldName } from "./captureFieldName.js";
4
6
  import { addonSchema } from "./schema.js";
5
7
 
6
8
  /**
@@ -10,14 +12,16 @@ export function dbPatchFn($) {
10
12
  return () => {
11
13
  $.sectionStack.push("dbPatchFn");
12
14
  const fnToken = $.CONSUME(PatchToken); // "patch"
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,12 @@ export function dbPatchFn($) {
30
34
  "addon?": [addonSchema],
31
35
  "mock?": { "![string]": "[expression]" },
32
36
  },
37
+ captured,
33
38
  ],
34
39
  });
35
40
 
41
+ $.ACTION(() => captureFieldName($, captured));
42
+
36
43
  $.SUBRULE($.asVariable, { ARGS: [fnToken] });
37
44
  $.sectionStack.pop();
38
45
  };
@@ -1,6 +1,7 @@
1
1
  import { QueryToken } 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
  /**
@@ -10,11 +11,13 @@ export function dbQueryFn($) {
10
11
  return () => {
11
12
  $.sectionStack.push("dbQueryFn");
12
13
  const fnToken = $.CONSUME(QueryToken); // "query"
13
- $.OR([
14
- { ALT: () => $.CONSUME(Identifier) }, // user
15
- { ALT: () => $.CONSUME1(StringLiteral) }, // "user table"
14
+ const nameToken = $.OR([
15
+ { ALT: () => $.CONSUME(Identifier) },
16
+ { ALT: () => $.CONSUME1(StringLiteral) },
16
17
  ]);
18
+ $.addReference("table", getVarName(nameToken), nameToken);
17
19
 
20
+ const captured = {};
18
21
  $.SUBRULE($.schemaParseAttributeFn, {
19
22
  ARGS: [
20
23
  fnToken,
@@ -51,9 +54,32 @@ export function dbQueryFn($) {
51
54
  "where?": "[query]",
52
55
  "additional_where?": "[query]",
53
56
  },
57
+ captured,
54
58
  ],
55
59
  });
56
60
 
61
+ $.ACTION(() => {
62
+ // Extract join table references
63
+ if (captured.join) {
64
+ for (const [k, v] of Object.entries(captured.join)) {
65
+ if (k === "key" || k === "value") continue;
66
+ const tableEntry = v?.table;
67
+ if (tableEntry?.value) {
68
+ $.addReference("table", getVarName(tableEntry.value), tableEntry.value);
69
+ }
70
+ }
71
+ }
72
+ // Extract addon name references
73
+ if (captured.addon?.value && Array.isArray(captured.addon.value)) {
74
+ for (const item of captured.addon.value) {
75
+ const nameEntry = item.name;
76
+ if (nameEntry?.value) {
77
+ $.addReference("function", getVarName(nameEntry.value), nameEntry.value);
78
+ }
79
+ }
80
+ }
81
+ });
82
+
57
83
  // db.query rule is used within an addon declaration, but
58
84
  // in this context we don't allow "as" variable assignment
59
85
  const isAddonContext = $.sectionStack.includes("addonDeclaration");
@@ -1,6 +1,7 @@
1
1
  import { SchemaToken } 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} $
@@ -12,10 +13,11 @@ export function dbSchemaFn($) {
12
13
 
13
14
  $.sectionStack.push("dbSchemaFn");
14
15
  const fnToken = $.CONSUME(SchemaToken); // "schema"
15
- $.OR([
16
- { ALT: () => $.CONSUME(Identifier) }, // user
17
- { ALT: () => $.CONSUME1(StringLiteral) }, // "user table"
16
+ const nameToken = $.OR([
17
+ { ALT: () => $.CONSUME(Identifier) },
18
+ { ALT: () => $.CONSUME1(StringLiteral) },
18
19
  ]);
20
+ $.addReference("table", getVarName(nameToken), nameToken);
19
21
  $.SUBRULE($.functionAttrReq, {
20
22
  ARGS: [fnToken, requiredAttrs, optionalAttrs],
21
23
  });
@@ -1,6 +1,7 @@
1
1
  import { TruncateToken } 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 dbTruncateFn($) {
9
10
  return () => {
10
11
  $.sectionStack.push("dbTruncateFn");
11
12
  const fnToken = $.CONSUME(TruncateToken); // "truncate"
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: [