@xano/xanoscript-language-server 12.15.0 → 12.16.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xano/xanoscript-language-server",
3
- "version": "12.15.0",
3
+ "version": "12.16.1",
4
4
  "description": "Language Server Protocol implementation for XanoScript",
5
5
  "type": "module",
6
6
  "main": "server.js",
@@ -0,0 +1,20 @@
1
+ import { EqualToken } from "../../lexer/control.js";
2
+ import { ActiveToken } from "../../lexer/realtime_trigger.js";
3
+ import { FalseToken, TrueToken } from "../../lexer/tokens.js";
4
+
5
+ /**
6
+ * @param {import('../base_parser.js').XanoBaseParser} $
7
+ * @returns
8
+ */
9
+ export function activeFieldAttribute($) {
10
+ return () => {
11
+ $.sectionStack.push("activeFieldAttribute");
12
+ $.CONSUME(ActiveToken); // "active"
13
+ $.CONSUME(EqualToken); // "="
14
+ $.OR([
15
+ { ALT: () => $.CONSUME(TrueToken) }, // e.g., true,
16
+ { ALT: () => $.CONSUME(FalseToken) }, // e.g., false
17
+ ]);
18
+ $.sectionStack.pop();
19
+ };
20
+ }
@@ -1,3 +1,4 @@
1
+ import { activeFieldAttribute } from "./activeFieldAttribute.js";
1
2
  import { columnDefaultValueAttribute } from "./columnDefaultValueAttribute.js";
2
3
  import { descriptionFieldAttribute } from "./descriptionFieldAttribute.js";
3
4
  import { disabledFieldAttribute } from "./disabledFieldAttribute.js";
@@ -14,6 +15,10 @@ import { visibilityFieldAttribute } from "./visibilityFieldAttribute.js";
14
15
  * @param {import('../base_parser.js').XanoBaseParser} $
15
16
  */
16
17
  export const register = ($) => {
18
+ $.activeFieldAttribute = $.RULE(
19
+ "activeFieldAttribute",
20
+ activeFieldAttribute($),
21
+ );
17
22
  $.columnDefaultValueAttribute = $.RULE(
18
23
  "columnDefaultValueAttribute",
19
24
  columnDefaultValueAttribute($),
@@ -23,6 +23,7 @@ export function channelDeclaration($) {
23
23
  let hasDescription = false;
24
24
  let hasGuid = false;
25
25
  let hasTags = false;
26
+ let hasActive = false;
26
27
 
27
28
  $.sectionStack.push("channelDeclaration");
28
29
  // Allow leading comments and newlines before the channel declaration
@@ -168,6 +169,13 @@ export function channelDeclaration($) {
168
169
  $.SUBRULE($.tagsAttribute);
169
170
  },
170
171
  },
172
+ {
173
+ GATE: () => !hasActive,
174
+ ALT: () => {
175
+ hasActive = true;
176
+ $.SUBRULE($.activeFieldAttribute);
177
+ },
178
+ },
171
179
  ]);
172
180
  });
173
181
 
@@ -1,8 +1,22 @@
1
1
  import { EqualToken } from "../../lexer/control.js";
2
+ import { IntegerLiteral } from "../../lexer/literal.js";
2
3
  import { HistoryToken } from "../../lexer/tokens.js";
3
4
 
4
5
  /**
5
6
  * Parses the history clause.
7
+ *
8
+ * `history` mirrors the backend serializer (cloud-client
9
+ * helper/script/transform via `Transform::onSchemaRequestHistory`): the value is
10
+ * `false` (disabled), `"inherit"` (hand-authored sentinel), `"all"` (unbounded),
11
+ * or ANY non-negative integer message limit — not a fixed set of magic numbers.
12
+ * It is split across two OR branches because `schemaParseEnumFn` refuses an enum
13
+ * that mixes constants (`false`/`"inherit"`/`"all"`) with a generic numeric type:
14
+ * an integer literal is consumed directly, every other value by the sentinel
15
+ * enum (which keeps emitting the existing "must be one of …" error). The limit
16
+ * is an integer message count, so a float is intentionally NOT accepted.
17
+ * `IGNORE_AMBIGUITIES` steers an integer literal to the number branch, since both
18
+ * branches can begin with one.
19
+ *
6
20
  * @param {import('../base_parser.js').XanoBaseParser} $
7
21
  */
8
22
  export function historyClause($) {
@@ -10,8 +24,19 @@ export function historyClause($) {
10
24
  $.sectionStack.push("historyClause");
11
25
  const parent = $.CONSUME(HistoryToken); // "history"
12
26
  $.CONSUME(EqualToken); // "="
13
- $.SUBRULE($.schemaParseEnumFn, {
14
- ARGS: [parent, [false, "inherit", 0, 10, 100, 1000, 10000, "all"]],
27
+ $.OR({
28
+ IGNORE_AMBIGUITIES: true,
29
+ DEF: [
30
+ // any non-negative integer message limit (e.g. 50, 100, 250)
31
+ { ALT: () => $.CONSUME(IntegerLiteral) },
32
+ // the non-numeric sentinels: false | "inherit" | "all"
33
+ {
34
+ ALT: () =>
35
+ $.SUBRULE($.schemaParseEnumFn, {
36
+ ARGS: [parent, [false, "inherit", "all"]],
37
+ }),
38
+ },
39
+ ],
15
40
  });
16
41
 
17
42
  $.sectionStack.pop();
@@ -5,6 +5,7 @@ import { SetToken } from "../../../lexer/redis.js";
5
5
  * key = "name"
6
6
  * data = "xano"
7
7
  * ttl = 0
8
+ * create_only = true
8
9
  * }
9
10
  * @param {import('../../base_parser.js').XanoBaseParser} $
10
11
  */
@@ -19,6 +20,7 @@ export function redisSetFn($) {
19
20
  key: "[expression]",
20
21
  data: "[expression]",
21
22
  ttl: "[expression]",
23
+ "create_only?": "[boolean]",
22
24
  "description?": "[string]",
23
25
  "disabled?": "[boolean]",
24
26
  },
@@ -10,6 +10,8 @@ export function messageDeclaration($) {
10
10
  let hasChannel = false;
11
11
  let hasDeliverTo = false;
12
12
  let hasDescription = false;
13
+ let hasDocs = false;
14
+ let hasActive = false;
13
15
  let hasGuid = false;
14
16
  let hasHistory = false;
15
17
  let hasInput = false;
@@ -79,6 +81,24 @@ export function messageDeclaration($) {
79
81
  $.SUBRULE($.descriptionFieldAttribute);
80
82
  },
81
83
  },
84
+ {
85
+ // backend Message.php `encode()` emits `docs = "..."` for a
86
+ // documented message; round-tripped by `decode()`. See DEV-7620.
87
+ GATE: () => !hasDocs,
88
+ ALT: () => {
89
+ hasDocs = true;
90
+ $.SUBRULE($.docsFieldAttribute);
91
+ },
92
+ },
93
+ {
94
+ // backend Message.php `encode()` emits `active = false` for a
95
+ // disabled message; mirrors the sibling `channel` declaration.
96
+ GATE: () => !hasActive,
97
+ ALT: () => {
98
+ hasActive = true;
99
+ $.SUBRULE($.activeFieldAttribute);
100
+ },
101
+ },
82
102
  {
83
103
  GATE: () => !hasGuid,
84
104
  ALT: () => {
@@ -147,26 +167,12 @@ export function messageDeclaration($) {
147
167
  );
148
168
  }
149
169
 
150
- if (!hasInput) {
151
- $.addMissingError(
152
- parent,
153
- `{} ${nameToken.image} is missing an input clause`,
154
- );
155
- }
156
-
157
- if (!hasStack) {
158
- $.addMissingError(
159
- parent,
160
- `{} ${nameToken.image} is missing a stack clause`,
161
- );
162
- }
163
-
164
- if (!hasResponse) {
165
- $.addMissingError(
166
- parent,
167
- `{} ${nameToken.image} is missing a response clause`,
168
- );
169
- }
170
+ // input/stack/response are OPTIONAL on a message: a realtime v2 message is
171
+ // created config-only (its Stack is generated server-side after save), so a
172
+ // scaffold carrying only realtime_server/channel/deliver_to/description/history
173
+ // must be diagnostic-clean. Mirrors channel_parser.js (requires realtime_server
174
+ // only). When present, these clauses are still fully parsed + validated by the
175
+ // OR2 alternatives above.
170
176
 
171
177
  $.MANY1(() => $.CONSUME1(NewlineToken)); // optional new line
172
178
  $.CONSUME(RCurly); // "}"
@@ -32,7 +32,11 @@ export function realtimeChannelDeclaration($) {
32
32
  "settings?": {
33
33
  anonymous_clients: "[boolean]",
34
34
  nested_channels: "[boolean]",
35
- message_history: [0, 25, 50, 100, 250, 1000],
35
+ // The CLI pull emits `message_history: history ?? 0` (an arbitrary
36
+ // integer — see cloud-client RealtimeChannel.php `onSettings`), so
37
+ // accept any numeric value rather than a fixed enum. Kept typed
38
+ // ("[number]") so a non-numeric value still surfaces a diagnostic.
39
+ message_history: "[number]",
36
40
  auth_channel: "[boolean]",
37
41
  presence: "[boolean]",
38
42
  },