@xano/xanoscript-language-server 12.0.0 → 12.1.0

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xano/xanoscript-language-server",
3
- "version": "12.0.0",
3
+ "version": "12.1.0",
4
4
  "description": "Language Server Protocol implementation for XanoScript",
5
5
  "type": "module",
6
6
  "main": "server.js",
@@ -8,6 +8,7 @@ import {
8
8
  FalseToken,
9
9
  Identifier,
10
10
  NowToken,
11
+ NullToken,
11
12
  TrueToken,
12
13
  } from "../../lexer/tokens.js";
13
14
 
@@ -33,6 +34,7 @@ export function columnDefaultValueAttribute($) {
33
34
  { ALT: () => $.CONSUME(TrueToken) }, // true
34
35
  { ALT: () => $.CONSUME(FalseToken) }, // false
35
36
  { ALT: () => $.CONSUME(NowToken) }, // now
37
+ { ALT: () => $.CONSUME(NullToken) }, // null
36
38
  { ALT: () => $.CONSUME(Identifier) }, // when everything else fails, it's an identifier
37
39
  ]);
38
40
  $.sectionStack.pop();
@@ -19,13 +19,19 @@ export function objectColumnDefinition($) {
19
19
  const parent = $.CONSUME(ObjectToken); // "object"
20
20
 
21
21
  let iterable = false;
22
- const nullable = false; // objects cannot be nullable
22
+ let nullable = false;
23
23
  let optional = false;
24
24
 
25
25
  $.OPTION2(() => {
26
26
  // object[]
27
27
  iterable = true;
28
28
  $.SUBRULE($.arraySlice);
29
+
30
+ // object[]? (nullable array)
31
+ $.OPTION3(() => {
32
+ nullable = true;
33
+ $.CONSUME(Question);
34
+ });
29
35
  });
30
36
 
31
37
  // Column name (e.g., "user_id", "optional_column", "required_column")
@@ -40,7 +46,7 @@ export function objectColumnDefinition($) {
40
46
  // ? (is it an optional field)
41
47
  $.OPTION(() => {
42
48
  optional = true;
43
- $.CONSUME(Question);
49
+ $.CONSUME1(Question);
44
50
  });
45
51
 
46
52
  $.SUBRULE($.objectColumnMetadataDefinition); // Object metadata
@@ -4,8 +4,8 @@ import { LambdaToken } from "../../../lexer/api.js";
4
4
  * @param {import('../../base_parser.js').XanoBaseParser} $
5
5
  */
6
6
  export function apiLambdaFn($) {
7
- const requiredAttrs = ["code", "timeout"];
8
- const optionalAttrs = ["description", "disabled"];
7
+ const requiredAttrs = ["code"];
8
+ const optionalAttrs = ["description", "disabled", "timeout"];
9
9
 
10
10
  return () => {
11
11
  $.sectionStack.push("apiLambdaFn");
@@ -0,0 +1,39 @@
1
+ import { MicroserviceToken } from "../../../lexer/api.js";
2
+
3
+ // api.microservice {
4
+ // host = $host
5
+ // path = "/reload"
6
+ // method = "POST"
7
+ // params = {}
8
+ // headers = []
9
+ // timeout = 60
10
+ // follow_location = true
11
+ // } as $api_1
12
+
13
+ /**
14
+ * @param {import('../../base_parser.js').XanoBaseParser} $
15
+ */
16
+ export function apiMicroserviceFn($) {
17
+ return () => {
18
+ $.sectionStack.push("apiMicroserviceFn");
19
+ const fnToken = $.CONSUME(MicroserviceToken); // "microservice"
20
+ $.SUBRULE($.schemaParseAttributeFn, {
21
+ ARGS: [
22
+ fnToken,
23
+ {
24
+ host: "[expression]",
25
+ path: "[expression]",
26
+ method: "[expression]",
27
+ "description?": "[string]",
28
+ "disabled?": "[boolean]",
29
+ "params?": "[expression]",
30
+ "headers?": "[expression]",
31
+ "timeout?": "[expression]",
32
+ "follow_location?": "[expression]",
33
+ },
34
+ ],
35
+ });
36
+ $.SUBRULE($.asVariable, { ARGS: [fnToken] });
37
+ $.sectionStack.pop();
38
+ };
39
+ }
@@ -1,5 +1,6 @@
1
1
  import { apiCallFn } from "./apiCallFn.js";
2
2
  import { apiLambdaFn } from "./apiLambdaFn.js";
3
+ import { apiMicroserviceFn } from "./apiMicroserviceFn.js";
3
4
  import { apiRealtimeEventFn } from "./apiRealtimeEventFn.js";
4
5
  import { apiRequestFn } from "./apiRequestFn.js";
5
6
  import { apiStreamFn } from "./apiStreamFn.js";
@@ -11,6 +12,7 @@ import { webflowRequestFn } from "./webflowRequestFn.js";
11
12
  */
12
13
  export const register = ($) => {
13
14
  $.apiLambdaFn = $.RULE("apiLambdaFn", apiLambdaFn($));
15
+ $.apiMicroserviceFn = $.RULE("apiMicroserviceFn", apiMicroserviceFn($));
14
16
  $.apiRealtimeEventFn = $.RULE("apiRealtimeEventFn", apiRealtimeEventFn($));
15
17
  $.apiRequestFn = $.RULE("apiRequestFn", apiRequestFn($));
16
18
  $.apiStreamFn = $.RULE("apiStreamFn", apiStreamFn($));
@@ -15,6 +15,7 @@ export function apiFn($) {
15
15
  { ALT: () => $.SUBRULE($.apiRequestFn) }, // "api.request"
16
16
  { ALT: () => $.SUBRULE($.apiRealtimeEventFn) }, // "api.realtime_event"
17
17
  { ALT: () => $.SUBRULE($.apiStreamFn) }, // "api.stream"
18
+ { ALT: () => $.SUBRULE($.apiMicroserviceFn) }, // "api.microservice"
18
19
  ]);
19
20
  $.sectionStack.pop();
20
21
  };
@@ -1,4 +1,6 @@
1
- import { ShiftToken } from "../../../lexer/arrays.js";
1
+ import { ArrayToken, ShiftToken } from "../../../lexer/arrays.js";
2
+ import { AsToken } from "../../../lexer/control.js";
3
+ import { DotToken } from "../../../lexer/tokens.js";
2
4
 
3
5
  /**
4
6
  * array.shift $my_array as $test
@@ -7,9 +9,16 @@ import { ShiftToken } from "../../../lexer/arrays.js";
7
9
  export function arrayShiftFn($) {
8
10
  return () => {
9
11
  $.sectionStack.push("arrayShiftFn");
12
+ $.OPTION(() => {
13
+ $.CONSUME(ArrayToken); // "array"
14
+ $.CONSUME(DotToken); // "."
15
+ });
10
16
  $.CONSUME(ShiftToken); // "shift"
11
17
  $.SUBRULE($.variableOnly);
12
- $.SUBRULE($.arrayNoValueAs);
18
+ $.OPTION1(() => {
19
+ $.CONSUME(AsToken);
20
+ $.SUBRULE($.assignableVariableAs);
21
+ });
13
22
  $.sectionStack.pop();
14
23
  };
15
24
  }
@@ -1,3 +1,4 @@
1
+ import { ExclamationToken } from "../../../lexer/cast.js";
1
2
  import { ElseifToken, LParent, RParent } from "../../../lexer/control.js";
2
3
  import { NewlineToken } from "../../../lexer/tokens.js";
3
4
 
@@ -7,6 +8,7 @@ import { NewlineToken } from "../../../lexer/tokens.js";
7
8
  export function conditionalElifFn($) {
8
9
  return () => {
9
10
  $.sectionStack.push("conditionalElifFn");
11
+ $.OPTION1(() => $.CONSUME(ExclamationToken)); // "!" (disabled)
10
12
  $.CONSUME(ElseifToken); // "elseif"
11
13
  $.CONSUME(LParent); // "("
12
14
  $.MANY(() => $.CONSUME(NewlineToken));
@@ -19,6 +19,7 @@ export function forFn($) {
19
19
  $.CONSUME(ForToken); // "for"
20
20
  $.CONSUME(LParent); // "("
21
21
  $.SUBRULE($.expressionFn);
22
+ $.OPTION3(() => $.CONSUME2(NewlineToken));
22
23
  $.CONSUME(RParent); // ")"
23
24
  $.OPTION(() => {
24
25
  $.CONSUME(LCurly); // "{"
@@ -24,7 +24,7 @@ export function dbBulkAddFn($) {
24
24
  fnToken,
25
25
  {
26
26
  "items?": "[expression]",
27
- "allow_id_field?": "[boolean]",
27
+ "allow_id_field?": "[expression]",
28
28
  "description?": "[string]",
29
29
  "disabled?": "[boolean]",
30
30
  },
@@ -7,7 +7,12 @@ import {
7
7
  RCurly,
8
8
  } from "../../../lexer/control.js";
9
9
  import { StringLiteral } from "../../../lexer/literal.js";
10
- import { DotToken, Identifier, NewlineToken } from "../../../lexer/tokens.js";
10
+ import {
11
+ DotToken,
12
+ Identifier,
13
+ NewlineToken,
14
+ NowToken,
15
+ } from "../../../lexer/tokens.js";
11
16
  import { InputVariable, ShortFormVariable } from "../../../lexer/variables.js";
12
17
  import {
13
18
  canBeDisabledAttribute,
@@ -67,7 +72,10 @@ export function schemaParseObjectFn($) {
67
72
  const keyValue = $.OR([
68
73
  {
69
74
  ALT: () => {
70
- objectKeyToken = $.CONSUME(Identifier);
75
+ objectKeyToken = $.OR6([
76
+ { ALT: () => $.CONSUME(Identifier) },
77
+ { ALT: () => $.CONSUME(NowToken) },
78
+ ]);
71
79
  const tokens = [objectKeyToken];
72
80
  $.MANY3(() => {
73
81
  tokens.push($.CONSUME(DotToken));
@@ -13,7 +13,7 @@ export function storageCreateAttachmentFn($) {
13
13
  {
14
14
  value: "[expression]",
15
15
  access: ["public", "private"],
16
- filename: "[expression]",
16
+ "filename?": "[expression]",
17
17
  "description?": "[string]",
18
18
  "disabled?": "[boolean]",
19
19
  },
@@ -115,6 +115,13 @@ export function expressionFn($) {
115
115
  let hasTests = false;
116
116
  let has_ternary = false;
117
117
 
118
+ // Inside any delimited sub-expression — `(...)`, `[...]`, `{...}` — a
119
+ // filter chain is unambiguous and should always be allowed, even when
120
+ // the outer expression has filters disabled (e.g. it is itself a filter
121
+ // argument). See filterFn.js for the rationale on why filter args
122
+ // disable filter parsing.
123
+ const delimitedOptions = { ...options, allowFilter: true };
124
+
118
125
  $.OR([
119
126
  {
120
127
  ALT: () => {
@@ -127,13 +134,17 @@ export function expressionFn($) {
127
134
  $.OR3([
128
135
  {
129
136
  ALT: () =>
130
- $.SUBRULE($.expressionFn, { ARGS: [token, options] }),
137
+ $.SUBRULE($.expressionFn, {
138
+ ARGS: [token, delimitedOptions],
139
+ }),
131
140
  },
132
141
  {
133
142
  // some filters can be used as standalone value (e.g., `(|uuid)`)
134
143
  // bug have to be inside parentheses
135
144
  ALT: () =>
136
- $.SUBRULE($.multilineFilterFn, { ARGS: [options] }),
145
+ $.SUBRULE($.multilineFilterFn, {
146
+ ARGS: [delimitedOptions],
147
+ }),
137
148
  },
138
149
  ]);
139
150
  $.OPTION(() => $.CONSUME1(NewlineToken));
@@ -160,7 +171,7 @@ export function expressionFn($) {
160
171
  token,
161
172
  options.allowQueryExpression ? ["[query]"] : ["[expression]"],
162
173
  {},
163
- options,
174
+ delimitedOptions,
164
175
  ],
165
176
  });
166
177
  },
@@ -181,7 +192,7 @@ export function expressionFn($) {
181
192
  : "[expression]",
182
193
  },
183
194
  {},
184
- options,
195
+ delimitedOptions,
185
196
  ],
186
197
  });
187
198
  $.SUBRULE($.chainedIdentifier);
@@ -199,8 +210,10 @@ export function expressionFn($) {
199
210
  // We use LookAhead to peek at the next tokens without consuming them
200
211
  // to decide if we should close or keep the gate open
201
212
  GATE: () =>
202
- (options.allowFilter && $.LA(1).tokenType === PipeToken) ||
203
- ($.LA(1).tokenType === NewlineToken && $.LA(2).tokenType === PipeToken),
213
+ options.allowFilter &&
214
+ ($.LA(1).tokenType === PipeToken ||
215
+ ($.LA(1).tokenType === NewlineToken &&
216
+ $.LA(2).tokenType === PipeToken)),
204
217
  DEF: () => {
205
218
  hasFilters = true;
206
219
  $.SUBRULE($.filterFn, { ARGS: [options] });
@@ -40,9 +40,22 @@ export function filterFn($) {
40
40
  );
41
41
  }
42
42
 
43
+ // Filter arguments do not absorb subsequent `|filter` tokens — those
44
+ // belong to the outer expression's filter chain. Without this, a long
45
+ // sibling chain like `[]|push:1|push:2|push:3` parses as deep right-
46
+ // nested SUBRULEs and overflows the stack. Explicit nesting still works
47
+ // via parentheses (`|f:(x|g:y)`), since the parens branch in
48
+ // expressionFn restores `allowFilter: true`.
49
+ // allowBareIdentifier lets property-name args like `|get:items:null`
50
+ // parse as Identifier without requiring quotes or backticks.
51
+ const argOptions = {
52
+ ...options,
53
+ allowFilter: false,
54
+ allowBareIdentifier: true,
55
+ };
43
56
  $.MANY1(() => {
44
57
  $.CONSUME(ColonToken);
45
- $.SUBRULE($.expressionFn, { ARGS: [filterToken, options, state] });
58
+ $.SUBRULE($.expressionFn, { ARGS: [filterToken, argOptions, state] });
46
59
  });
47
60
  };
48
61
  }