@xano/xanoscript-language-server 11.10.1 → 11.12.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.
@@ -2,20 +2,35 @@
2
2
  "permissions": {
3
3
  "allow": [
4
4
  "Bash(npm test:*)",
5
- "Bash(git checkout:*)",
6
- "Bash(ls:*)",
7
- "Bash(npm run lint:*)",
5
+ "Bash(npm run lint)",
6
+ "Bash(npm run test:*)",
7
+ "Bash(npx eslint:*)",
8
+ "Bash(node:*)",
9
+ "Bash(npm install)",
8
10
  "Bash(grep:*)",
11
+ "Bash(git -C /Users/justinalbrecht/git/xs-language-server log --all --pretty=format:\"%h %s\")",
12
+ "Bash(git -C /Users/justinalbrecht/git/xs-language-server log --all -S \"password\\\\|secret\\\\|api_key\\\\|token\" --oneline)",
13
+ "Bash(xargs file:*)",
9
14
  "Bash(find:*)",
10
- "Read(//tmp/**)",
11
- "Bash(git stash:*)",
12
- "Bash(do)",
13
- "Bash(if ! grep -q \"guidFieldAttribute\" \"$f\")",
14
- "Bash(then)",
15
- "Bash(echo:*)",
16
- "Bash(fi)",
17
- "Bash(done)",
18
- "Bash(npx eslint:*)"
19
- ]
15
+ "Bash(git -C /Users/justinalbrecht/git/xs-language-server log -p --all -S \"webhook\\\\|Bearer\\\\|discord\\\\|slack\\\\|xano_insiders\" -- \"*.xs\" \"*.js\")",
16
+ "Bash(git -C /Users/justinalbrecht/git/xs-language-server ls-files:*)",
17
+ "Bash(git -C /Users/justinalbrecht/git/xs-language-server status)",
18
+ "Bash(git -C /Users/justinalbrecht/git/xs-language-server diff --stat)",
19
+ "Bash(git -C /Users/justinalbrecht/git/xs-language-server log --oneline -5)",
20
+ "Bash(git -C /Users/justinalbrecht/git/xs-language-server add README.md LICENSE package.json lexer/tests/query/valid_sources/basic_query.xs parser/functions/stream/streamFromRequestFn.js parser/functions/stream/streamFromRequestFn.spec.js parser/tests/function/valid_sources/discord_poll_send_to_slack.xs parser/tests/query/valid_sources/all_basics.xs)",
21
+ "Bash(git -C /Users/justinalbrecht/git/xs-language-server commit -m \"$\\(cat <<''EOF''\nPrepare package for public npm release as @xano/xanoscript-language-server\n\n- Rename package to @xano/xanoscript-language-server\n- Add MIT LICENSE file\n- Add npm metadata \\(description, repository, author, license, bugs, homepage\\)\n- Remove private flag to allow publishing\n- Sanitize test files: replace internal URLs, IDs, and env var names with placeholders\n- Simplify README maintainer section\n\nCo-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>\nEOF\n\\)\")",
22
+ "Bash(git -C /Users/justinalbrecht/git/xs-language-server log -1 --oneline)",
23
+ "Bash(git add:*)",
24
+ "Bash(git commit:*)",
25
+ "Bash(git push)",
26
+ "Bash(npm whoami:*)",
27
+ "Bash(npm view:*)",
28
+ "Bash(npm version:*)",
29
+ "Bash(npm publish:*)",
30
+ "Bash(wc:*)",
31
+ "Bash(git push:*)"
32
+ ],
33
+ "deny": [],
34
+ "ask": []
20
35
  }
21
36
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xano/xanoscript-language-server",
3
- "version": "11.10.1",
3
+ "version": "11.12.0",
4
4
  "description": "Language Server Protocol implementation for XanoScript",
5
5
  "type": "module",
6
6
  "main": "server.js",
@@ -26,10 +26,11 @@ export function addonDeclaration($) {
26
26
  // Allow leading comments and newlines before the addon declaration
27
27
  $.SUBRULE($.optionalCommentBlockFn);
28
28
  const parent = $.CONSUME(AddonToken);
29
- $.OR([
29
+ const nameToken = $.OR([
30
30
  { ALT: () => $.CONSUME(StringLiteral) },
31
31
  { ALT: () => $.CONSUME(Identifier) },
32
32
  ]);
33
+ $.validateDeclarationName(nameToken, "addon");
33
34
  $.CONSUME(LCurly); // "{"
34
35
  $.MANY(() => {
35
36
  $.AT_LEAST_ONE(() => $.CONSUME(NewlineToken)); // at least one new line
@@ -21,10 +21,11 @@ export function agentDeclaration($) {
21
21
  // Allow leading comments and newlines before the agent declaration
22
22
  $.SUBRULE($.optionalCommentBlockFn);
23
23
  const parent = $.CONSUME(AgentToken); // agent
24
- $.OR([
24
+ const nameToken = $.OR([
25
25
  { ALT: () => $.CONSUME(StringLiteral) },
26
26
  { ALT: () => $.CONSUME(Identifier) },
27
27
  ]);
28
+ $.validateDeclarationName(nameToken, "agent");
28
29
  $.CONSUME(LCurly); // "{"
29
30
  $.MANY(() => {
30
31
  $.AT_LEAST_ONE(() => $.CONSUME(NewlineToken)); // at least one new line
@@ -26,10 +26,11 @@ export function agentTriggerDeclaration($) {
26
26
  // Allow leading comments and newlines before the agent_trigger declaration
27
27
  $.SUBRULE($.optionalCommentBlockFn);
28
28
  const parent = $.CONSUME(AgentTriggerToken); // agent_trigger
29
- $.OR([
29
+ const nameToken = $.OR([
30
30
  { ALT: () => $.CONSUME(StringLiteral) },
31
31
  { ALT: () => $.CONSUME(Identifier) },
32
32
  ]);
33
+ $.validateDeclarationName(nameToken, "agent_trigger");
33
34
  $.CONSUME(LCurly); // "{"
34
35
  $.MANY(() => {
35
36
  $.AT_LEAST_ONE(() => $.CONSUME(NewlineToken)); // at least one new line
@@ -27,10 +27,11 @@ export function apiGroupDeclaration($) {
27
27
  // Allow leading comments and newlines before the api_group declaration
28
28
  $.SUBRULE($.optionalCommentBlockFn);
29
29
  $.CONSUME(ApiGroupToken); // api_group
30
- $.OR([
30
+ const nameToken = $.OR([
31
31
  { ALT: () => $.CONSUME(StringLiteral) },
32
32
  { ALT: () => $.CONSUME(Identifier) },
33
33
  ]);
34
+ $.validateDeclarationName(nameToken, "api_group");
34
35
  $.CONSUME(LCurly); // "{"
35
36
  $.MANY(() => {
36
37
  $.AT_LEAST_ONE(() => $.CONSUME(NewlineToken));
@@ -270,6 +270,17 @@ export class XanoBaseParser extends CstParser {
270
270
  this.SAVE_ERROR(new MismatchedTokenException(message, token));
271
271
  }
272
272
 
273
+ /**
274
+ * Validate that a declaration name token is not an empty string
275
+ * @param {import('chevrotain').IToken} nameToken
276
+ * @param {string} typeName the declaration type (e.g. "query", "function")
277
+ */
278
+ validateDeclarationName(nameToken, typeName) {
279
+ if (nameToken?.image === '""') {
280
+ this.addInvalidValueError(nameToken, `${typeName} name must not be empty`);
281
+ }
282
+ }
283
+
273
284
  /**
274
285
  * Setup all the required rules for the parser but without creating duplicates
275
286
  *
@@ -9,10 +9,11 @@ export function branchDeclaration($) {
9
9
  $.SUBRULE($.optionalCommentBlockFn);
10
10
 
11
11
  const parent = $.CONSUME(BranchToken); // branch
12
- $.OR([
12
+ const nameToken = $.OR([
13
13
  { ALT: () => $.CONSUME(StringLiteral) },
14
14
  { ALT: () => $.CONSUME(Identifier) },
15
15
  ]);
16
+ $.validateDeclarationName(nameToken, "branch");
16
17
 
17
18
  const middlewareSchema = {
18
19
  pre: [{ name: "[string]" }],
@@ -29,6 +29,7 @@ export function functionDeclaration($) {
29
29
  { ALT: () => $.CONSUME(StringLiteral) },
30
30
  ]);
31
31
 
32
+ $.validateDeclarationName(nameToken, "function");
32
33
  if (nameToken?.image && getVarName(nameToken).startsWith("/")) {
33
34
  $.addInvalidValueError(
34
35
  nameToken,
@@ -16,6 +16,20 @@ describe("function_parser", () => {
16
16
  expect(parser.errors).to.be.empty;
17
17
  });
18
18
 
19
+ it("should raise an error when a function name is empty", () => {
20
+ const parser = xanoscriptParser(`function "" {
21
+ input {
22
+ }
23
+
24
+ stack {
25
+ }
26
+
27
+ response = null
28
+ }`);
29
+ expect(parser.errors).to.not.be.empty;
30
+ expect(parser.errors[0].message).to.include("must not be empty");
31
+ });
32
+
19
33
  it("should raise an error when a function name starts with /", () => {
20
34
  const parser = xanoscriptParser(`function "/foo" {
21
35
  input {
@@ -46,6 +46,11 @@ describe("dbDelFn", () => {
46
46
  expect(parser.errors).to.be.empty;
47
47
  });
48
48
 
49
+ it("dbDelFn rejects comma separated attributes", () => {
50
+ const parser = parse(`del account {field_name = "id", field_value = "a"}`);
51
+ expect(parser.errors).to.not.be.empty;
52
+ });
53
+
49
54
  it("dbDelFn requires a field_name", () => {
50
55
  const parser = parse(`del user {
51
56
  field_value = $input.email
@@ -1,11 +1,6 @@
1
1
  import { get, isPlainObject, keys, some } from "lodash-es";
2
2
  import { ExclamationToken } from "../../../lexer/cast.js";
3
- import {
4
- CommaToken,
5
- EqualToken,
6
- LCurly,
7
- RCurly,
8
- } from "../../../lexer/control.js";
3
+ import { EqualToken, LCurly, RCurly } from "../../../lexer/control.js";
9
4
  import { StringLiteral } from "../../../lexer/literal.js";
10
5
  import { Identifier, NewlineToken } from "../../../lexer/tokens.js";
11
6
  import {
@@ -156,10 +151,7 @@ function _schemaParseAttributeFn($) {
156
151
  $.CONSUME1(NewlineToken);
157
152
  });
158
153
 
159
- $.OPTION(() => {
160
- needSeparator = false;
161
- $.CONSUME1(CommaToken);
162
- });
154
+ lastToken = objectKeyToken;
163
155
  });
164
156
  $.CONSUME(RCurly); // "}"
165
157
 
@@ -127,15 +127,17 @@ describe("schemaParseAttributeFn", () => {
127
127
  expect(parser.errors).to.be.empty;
128
128
 
129
129
  parse(`value = {
130
- something = 123.2,
130
+ something = 123.2
131
131
  other_thing = 44
132
132
  }`).schemaParseAttributeFn_flat();
133
133
  expect(parser.errors).to.be.empty;
134
+ });
134
135
 
136
+ it("schemaParseAttributeFn rejects comma separated attributes", () => {
135
137
  parse(
136
138
  `value = { something = 123.2, other_thing = 44 }`,
137
139
  ).schemaParseAttributeFn_flat();
138
- expect(parser.errors).to.be.empty;
140
+ expect(parser.errors).to.not.be.empty;
139
141
  });
140
142
 
141
143
  it("schemaParseAttributeFn immutable restricts to valid token", () => {
@@ -24,10 +24,11 @@ export function mcpServerDeclaration($) {
24
24
  // Allow leading comments and newlines before the mcp_server declaration
25
25
  $.SUBRULE($.optionalCommentBlockFn);
26
26
  const parent = $.CONSUME(McpServerToken); // mcp_server
27
- $.OR([
27
+ const nameToken = $.OR([
28
28
  { ALT: () => $.CONSUME(StringLiteral) },
29
29
  { ALT: () => $.CONSUME(Identifier) },
30
30
  ]);
31
+ $.validateDeclarationName(nameToken, "mcp_server");
31
32
  $.CONSUME(LCurly); // "{"
32
33
  $.MANY(() => {
33
34
  $.AT_LEAST_ONE(() => $.CONSUME(NewlineToken)); // at least one new line
@@ -25,10 +25,11 @@ export function mcpServerTriggerDeclaration($) {
25
25
  $.SUBRULE($.optionalCommentBlockFn);
26
26
 
27
27
  const parent = $.CONSUME(McpServerTriggerToken); // mcp_server_trigger
28
- $.OR([
28
+ const nameToken = $.OR([
29
29
  { ALT: () => $.CONSUME(StringLiteral) },
30
30
  { ALT: () => $.CONSUME(Identifier) },
31
31
  ]);
32
+ $.validateDeclarationName(nameToken, "mcp_server_trigger");
32
33
  $.CONSUME(LCurly); // "{"
33
34
  $.MANY(() => {
34
35
  $.AT_LEAST_ONE(() => $.CONSUME(NewlineToken)); // at least one new line
@@ -28,10 +28,11 @@ export function middlewareDeclaration($) {
28
28
  $.SUBRULE($.optionalCommentBlockFn);
29
29
 
30
30
  const parent = $.CONSUME(MiddlewareToken);
31
- $.OR([
31
+ const nameToken = $.OR([
32
32
  { ALT: () => $.CONSUME(Identifier) },
33
33
  { ALT: () => $.CONSUME(StringLiteral) },
34
34
  ]);
35
+ $.validateDeclarationName(nameToken, "middleware");
35
36
  $.CONSUME(LCurly); // "{"
36
37
  $.MANY(() => {
37
38
  $.AT_LEAST_ONE(() => $.CONSUME(NewlineToken)); // at least one new line
@@ -43,6 +43,7 @@ export function queryDeclaration($) {
43
43
  { ALT: () => $.CONSUME(Identifier) }, // foo
44
44
  ]);
45
45
 
46
+ $.validateDeclarationName(nameToken, "query");
46
47
  if (nameToken?.image && getVarName(nameToken).startsWith("/")) {
47
48
  $.addInvalidValueError(nameToken, "query name must not start with '/'");
48
49
  }
@@ -16,6 +16,20 @@ describe("query_parser", () => {
16
16
  expect(parser.errors).to.be.empty;
17
17
  });
18
18
 
19
+ it("should raise an error when a query name is empty", () => {
20
+ const parser = xanoscriptParser(`query "" verb=GET {
21
+ input {
22
+ }
23
+
24
+ stack {
25
+ }
26
+
27
+ response = null
28
+ }`);
29
+ expect(parser.errors).to.not.be.empty;
30
+ expect(parser.errors[0].message).to.include("must not be empty");
31
+ });
32
+
19
33
  it("should raise an error when a query starts with /", () => {
20
34
  const parser = xanoscriptParser(`query "/foo" verb=GET {
21
35
  input {
@@ -9,10 +9,11 @@ export function realtimeChannelDeclaration($) {
9
9
  $.SUBRULE($.optionalCommentBlockFn);
10
10
 
11
11
  const parent = $.CONSUME(RealtimeChannelToken); // realtime_channel
12
- $.OR([
12
+ const nameToken = $.OR([
13
13
  { ALT: () => $.CONSUME(StringLiteral) },
14
14
  { ALT: () => $.CONSUME(Identifier) },
15
15
  ]);
16
+ $.validateDeclarationName(nameToken, "realtime_channel");
16
17
 
17
18
  $.SUBRULE($.schemaParseAttributeFn, {
18
19
  ARGS: [
@@ -25,10 +25,11 @@ export function realtimeTriggerDeclaration($) {
25
25
  $.SUBRULE($.optionalCommentBlockFn);
26
26
 
27
27
  const parent = $.CONSUME(RealtimeTriggerToken); // realtime_trigger
28
- $.OR([
28
+ const nameToken = $.OR([
29
29
  { ALT: () => $.CONSUME(StringLiteral) },
30
30
  { ALT: () => $.CONSUME(Identifier) },
31
31
  ]);
32
+ $.validateDeclarationName(nameToken, "realtime_trigger");
32
33
  $.CONSUME(LCurly); // "{"
33
34
  $.MANY(() => {
34
35
  $.AT_LEAST_ONE(() => $.CONSUME(NewlineToken)); // at least one new line
@@ -25,10 +25,11 @@ export function runJobClause($) {
25
25
  return () => {
26
26
  const parent = $.CONSUME(JobToken); // "job"
27
27
 
28
- $.OR([
28
+ const nameToken = $.OR([
29
29
  { ALT: () => $.CONSUME(StringLiteral) },
30
30
  { ALT: () => $.CONSUME(Identifier) },
31
31
  ]);
32
+ $.validateDeclarationName(nameToken, "job");
32
33
 
33
34
  $.SUBRULE($.schemaParseAttributeFn, {
34
35
  ARGS: [
@@ -49,10 +50,11 @@ export function runServiceClause($) {
49
50
  return () => {
50
51
  const parent = $.CONSUME(ServiceToken); // "service"
51
52
 
52
- $.OR([
53
+ const nameToken = $.OR([
53
54
  { ALT: () => $.CONSUME(StringLiteral) },
54
55
  { ALT: () => $.CONSUME(Identifier) },
55
56
  ]);
57
+ $.validateDeclarationName(nameToken, "service");
56
58
 
57
59
  $.SUBRULE($.schemaParseAttributeFn, {
58
60
  ARGS: [
@@ -25,7 +25,7 @@ export function tableDeclaration($) {
25
25
  $.SUBRULE($.optionalCommentBlockFn);
26
26
 
27
27
  $.CONSUME(TableToken); // "table"
28
- $.OR1([
28
+ const nameToken = $.OR1([
29
29
  {
30
30
  ALT: () =>
31
31
  $.CONSUME(StringLiteral, {
@@ -39,6 +39,7 @@ export function tableDeclaration($) {
39
39
  }),
40
40
  },
41
41
  ]);
42
+ $.validateDeclarationName(nameToken, "table");
42
43
 
43
44
  let hasSchema = false;
44
45
  let hasView = false;
@@ -30,10 +30,11 @@ export function tableTriggerDeclaration($) {
30
30
  $.SUBRULE($.optionalCommentBlockFn);
31
31
 
32
32
  const parent = $.CONSUME(TableTriggerToken); // table_trigger
33
- $.OR([
33
+ const nameToken = $.OR([
34
34
  { ALT: () => $.CONSUME(StringLiteral) },
35
35
  { ALT: () => $.CONSUME(Identifier) },
36
36
  ]);
37
+ $.validateDeclarationName(nameToken, "table_trigger");
37
38
  $.CONSUME(LCurly); // "{"
38
39
  $.MANY(() => {
39
40
  $.AT_LEAST_ONE(() => $.CONSUME(NewlineToken)); // at least one new line
@@ -25,10 +25,11 @@ export function taskDeclaration($) {
25
25
  $.SUBRULE($.optionalCommentBlockFn);
26
26
 
27
27
  $.CONSUME(TaskToken);
28
- $.OR([
28
+ const nameToken = $.OR([
29
29
  { ALT: () => $.CONSUME(StringLiteral) },
30
30
  { ALT: () => $.CONSUME(Identifier) },
31
31
  ]);
32
+ $.validateDeclarationName(nameToken, "task");
32
33
  $.CONSUME(LCurly); // "{"
33
34
  $.MANY(() => {
34
35
  $.AT_LEAST_ONE(() => $.CONSUME(NewlineToken)); // at least one new line
@@ -13,6 +13,17 @@ describe("task_parser", () => {
13
13
  expect(parser.errors).to.be.empty;
14
14
  });
15
15
 
16
+ it("should raise an error when a task name is empty", () => {
17
+ const parser = xanoscriptParser(`task "" {
18
+ stack {
19
+ }
20
+
21
+ schedule = []
22
+ }`);
23
+ expect(parser.errors).to.not.be.empty;
24
+ expect(parser.errors[0].message).to.include("must not be empty");
25
+ });
26
+
16
27
  it("should parse an active task", () => {
17
28
  const parser = xanoscriptParser(`task task_for_listing {
18
29
  active = true
@@ -21,10 +21,11 @@ export function toolDeclaration($) {
21
21
  $.SUBRULE($.optionalCommentBlockFn);
22
22
 
23
23
  const parent = $.CONSUME(ToolToken);
24
- $.OR([
24
+ const nameToken = $.OR([
25
25
  { ALT: () => $.CONSUME(StringLiteral) },
26
26
  { ALT: () => $.CONSUME(Identifier) },
27
27
  ]);
28
+ $.validateDeclarationName(nameToken, "tool");
28
29
  $.CONSUME(LCurly); // "{"
29
30
  $.MANY(() => {
30
31
  $.AT_LEAST_ONE(() => $.CONSUME(NewlineToken)); // at least one new line
@@ -16,10 +16,11 @@ export function workflowTestDeclaration($) {
16
16
  $.SUBRULE($.optionalCommentBlockFn);
17
17
 
18
18
  const parent = $.CONSUME(WorkflowTestToken);
19
- $.OR([
19
+ const nameToken = $.OR([
20
20
  { ALT: () => $.CONSUME(StringLiteral) },
21
21
  { ALT: () => $.CONSUME(Identifier) },
22
22
  ]);
23
+ $.validateDeclarationName(nameToken, "workflow_test");
23
24
  $.CONSUME(LCurly); // "{"
24
25
  $.MANY(() => {
25
26
  $.AT_LEAST_ONE(() => $.CONSUME(NewlineToken)); // at least one new line
@@ -9,10 +9,11 @@ export function workspaceDeclaration($) {
9
9
  $.SUBRULE($.optionalCommentBlockFn);
10
10
 
11
11
  const parent = $.CONSUME(WorkspaceToken); // workspace
12
- $.OR([
12
+ const nameToken = $.OR([
13
13
  { ALT: () => $.CONSUME(StringLiteral) },
14
14
  { ALT: () => $.CONSUME(Identifier) },
15
15
  ]);
16
+ $.validateDeclarationName(nameToken, "workspace");
16
17
 
17
18
  $.SUBRULE($.schemaParseAttributeFn, {
18
19
  ARGS: [
@@ -23,10 +23,11 @@ export function workspaceTriggerDeclaration($) {
23
23
  $.SUBRULE($.optionalCommentBlockFn);
24
24
 
25
25
  const parent = $.CONSUME(WorkspaceTriggerToken); // workspace_trigger
26
- $.OR([
26
+ const nameToken = $.OR([
27
27
  { ALT: () => $.CONSUME(StringLiteral) },
28
28
  { ALT: () => $.CONSUME(Identifier) },
29
29
  ]);
30
+ $.validateDeclarationName(nameToken, "workspace_trigger");
30
31
  $.CONSUME(LCurly); // "{"
31
32
  $.MANY(() => {
32
33
  $.AT_LEAST_ONE(() => $.CONSUME(NewlineToken)); // at least one new line
@@ -1,11 +0,0 @@
1
- ---
2
- name: no-git-interactions
3
- description: User does not want Claude to interact with git (no commits, no staging, no git commands)
4
- type: feedback
5
- ---
6
-
7
- Do not interact with git — no commits, no staging, no git commands. Focus purely on implementation and testing.
8
-
9
- **Why:** User manages their own git workflow.
10
-
11
- **How to apply:** Skip all git-related steps in plans. When dispatching subagents, explicitly tell them not to run any git commands. Remove commit steps from task instructions.