@xano/xanoscript-language-server 11.11.0 → 11.12.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/.claude/memory/feedback_no_git.md +11 -0
- package/.claude/settings.local.json +13 -28
- package/lexer/cast.js +3 -3
- package/lexer/control.js +0 -2
- package/lexer/literal.js +2 -2
- package/lexer/security.js +2 -2
- package/package.json +1 -1
- package/parser/addon_parser.js +2 -1
- package/parser/agent_parser.js +2 -1
- package/parser/agent_trigger_parser.js +2 -1
- package/parser/api_group_parser.js +2 -1
- package/parser/attributes/columnDefaultValueAttribute.js +10 -3
- package/parser/attributes/inputFilterFn.js +11 -4
- package/parser/base_parser.js +11 -0
- package/parser/branch_parser.js +2 -1
- package/parser/clauses/flexibleStringClause.js +2 -2
- package/parser/clauses/nakedStackFn.js +43 -36
- package/parser/clauses/nakedStackFn.spec.js +10 -0
- package/parser/clauses/responseClause.spec.js +5 -0
- package/parser/definitions/columnDefinition.js +7 -2
- package/parser/function_parser.js +1 -0
- package/parser/function_parser.spec.js +14 -0
- package/parser/functions/array/arrayNoValueAs.js +1 -1
- package/parser/functions/array/arrayPopFn.js +1 -3
- package/parser/functions/controls/forEachFn.spec.js +8 -0
- package/parser/functions/controls/preconditionFn.js +3 -0
- package/parser/functions/db/dbExternalDirectQueryFn.js +1 -0
- package/parser/functions/db/dbQueryFn.spec.js +31 -0
- package/parser/functions/schema/schemaParseConstantFn.js +11 -10
- package/parser/functions/varFn.js +14 -2
- package/parser/functions/varFn.spec.js +20 -12
- package/parser/generic/asVariable.spec.js +12 -8
- package/parser/generic/assignableVariableAs.js +50 -11
- package/parser/generic/assignableVariableAs.spec.js +29 -7
- package/parser/generic/assignableVariableProperty.js +22 -10
- package/parser/generic/assignableVariableProperty.spec.js +21 -12
- package/parser/generic/expressionFn.js +9 -2
- package/parser/generic/expressionFn.spec.js +5 -0
- package/parser/generic/filterNames.js +9 -0
- package/parser/mcp_server_parser.js +2 -1
- package/parser/mcp_server_trigger_parser.js +2 -1
- package/parser/middleware_parser.js +2 -1
- package/parser/multidoc.js +38 -17
- package/parser/multidoc.spec.js +7 -1
- package/parser/query_parser.js +12 -3
- package/parser/query_parser.spec.js +14 -3
- package/parser/realtime_channel_parser.js +2 -1
- package/parser/realtime_trigger_parser.js +2 -1
- package/parser/run_parser.js +4 -2
- package/parser/table_parser.js +2 -1
- package/parser/table_trigger_parser.js +2 -1
- package/parser/task_parser.js +2 -1
- package/parser/task_parser.spec.js +11 -0
- package/parser/tests/function/function_parser.spec.js +4 -4
- package/parser/tests/multidoc/multidoc_parser.spec.js +55 -0
- package/parser/tests/multidoc/valid_sources/function_stack.xs +130603 -0
- package/parser/tests/query/query_parser.spec.js +3 -3
- package/parser/tool_parser.js +14 -4
- package/parser/tool_parser.spec.js +19 -6
- package/parser/workflow_test_parser.js +2 -1
- package/parser/workspace_parser.js +2 -1
- package/parser/workspace_trigger_parser.js +2 -1
package/parser/multidoc.spec.js
CHANGED
|
@@ -299,6 +299,10 @@ function bar {
|
|
|
299
299
|
var.update $x {
|
|
300
300
|
value = 2
|
|
301
301
|
}
|
|
302
|
+
|
|
303
|
+
var $y {
|
|
304
|
+
value = 2
|
|
305
|
+
}
|
|
302
306
|
}
|
|
303
307
|
|
|
304
308
|
response = null
|
|
@@ -691,7 +695,9 @@ function bar {
|
|
|
691
695
|
// The error must be within seg3's global range
|
|
692
696
|
const error = result.errors[0];
|
|
693
697
|
expect(error.token.startOffset).to.be.at.least(expectedSeg3Start);
|
|
694
|
-
expect(error.token.startOffset).to.be.lessThan(
|
|
698
|
+
expect(error.token.startOffset).to.be.lessThan(
|
|
699
|
+
expectedSeg3Start + seg3.length,
|
|
700
|
+
);
|
|
695
701
|
});
|
|
696
702
|
|
|
697
703
|
it("should isolate errors — bad seg1 should not prevent seg2 from parsing", () => {
|
package/parser/query_parser.js
CHANGED
|
@@ -30,6 +30,7 @@ export function queryDeclaration($) {
|
|
|
30
30
|
let hasTags = false;
|
|
31
31
|
let hasApiGroup = false;
|
|
32
32
|
let hasResponseType = false;
|
|
33
|
+
let responseType = "standard";
|
|
33
34
|
|
|
34
35
|
const testNames = [];
|
|
35
36
|
|
|
@@ -43,6 +44,7 @@ export function queryDeclaration($) {
|
|
|
43
44
|
{ ALT: () => $.CONSUME(Identifier) }, // foo
|
|
44
45
|
]);
|
|
45
46
|
|
|
47
|
+
$.validateDeclarationName(nameToken, "query");
|
|
46
48
|
if (nameToken?.image && getVarName(nameToken).startsWith("/")) {
|
|
47
49
|
$.addInvalidValueError(nameToken, "query name must not start with '/'");
|
|
48
50
|
}
|
|
@@ -79,8 +81,8 @@ export function queryDeclaration($) {
|
|
|
79
81
|
$.CONSUME(ResponseTypeToken);
|
|
80
82
|
$.CONSUME8(EqualToken);
|
|
81
83
|
const valueToken = $.CONSUME2(StringLiteral);
|
|
82
|
-
|
|
83
|
-
if (!["stream", "standard"].includes(
|
|
84
|
+
responseType = getVarName(valueToken);
|
|
85
|
+
if (!["stream", "standard"].includes(responseType)) {
|
|
84
86
|
$.addInvalidValueError(
|
|
85
87
|
valueToken,
|
|
86
88
|
"response_type must be either 'stream' or 'standard'",
|
|
@@ -192,10 +194,17 @@ export function queryDeclaration($) {
|
|
|
192
194
|
$.addMissingError(parent, "{} is missing a stack clause");
|
|
193
195
|
}
|
|
194
196
|
|
|
195
|
-
if (!hasResponse) {
|
|
197
|
+
if (!hasResponse && responseType === "standard") {
|
|
196
198
|
$.addMissingError(parent, "{} is missing a response clause");
|
|
197
199
|
}
|
|
198
200
|
|
|
201
|
+
if (hasResponse && responseType === "stream") {
|
|
202
|
+
$.addInvalidValueError(
|
|
203
|
+
parent,
|
|
204
|
+
"response clause is not allowed when response_type is 'stream'",
|
|
205
|
+
);
|
|
206
|
+
}
|
|
207
|
+
|
|
199
208
|
$.MANY1(() => $.CONSUME1(NewlineToken)); // optional new line
|
|
200
209
|
$.CONSUME(RCurly); // "}"
|
|
201
210
|
$.MANY2(() => $.CONSUME2(NewlineToken)); // optional new line
|
|
@@ -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 {
|
|
@@ -60,7 +74,6 @@ describe("query_parser", () => {
|
|
|
60
74
|
expect(parser.errors).to.be.empty;
|
|
61
75
|
|
|
62
76
|
parser = xanoscriptParser(`query foo verb=GET {
|
|
63
|
-
|
|
64
77
|
response_type = "stream"
|
|
65
78
|
|
|
66
79
|
input {
|
|
@@ -68,8 +81,6 @@ describe("query_parser", () => {
|
|
|
68
81
|
|
|
69
82
|
stack {
|
|
70
83
|
}
|
|
71
|
-
|
|
72
|
-
response = null
|
|
73
84
|
}`);
|
|
74
85
|
expect(parser.errors).to.be.empty;
|
|
75
86
|
});
|
|
@@ -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
|
package/parser/run_parser.js
CHANGED
|
@@ -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: [
|
package/parser/table_parser.js
CHANGED
|
@@ -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
|
package/parser/task_parser.js
CHANGED
|
@@ -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
|
|
@@ -9,12 +9,12 @@ describe("Xanoscript Function Parser", () => {
|
|
|
9
9
|
it("parses all the valid sources", () => {
|
|
10
10
|
const errors = [];
|
|
11
11
|
const files = readdirSync(
|
|
12
|
-
join(dirname("./"), "parser/tests/function/valid_sources")
|
|
12
|
+
join(dirname("./"), "parser/tests/function/valid_sources"),
|
|
13
13
|
);
|
|
14
14
|
files.forEach((file) => {
|
|
15
15
|
const source = readFileSync(
|
|
16
16
|
join(dirname("./"), "parser/tests/function/valid_sources", file),
|
|
17
|
-
"utf8"
|
|
17
|
+
"utf8",
|
|
18
18
|
);
|
|
19
19
|
const parser = xanoscriptParser(source);
|
|
20
20
|
|
|
@@ -26,7 +26,7 @@ describe("Xanoscript Function Parser", () => {
|
|
|
26
26
|
error.previousToken.image,
|
|
27
27
|
{
|
|
28
28
|
length: 20,
|
|
29
|
-
}
|
|
29
|
+
},
|
|
30
30
|
)})`
|
|
31
31
|
: "N/A",
|
|
32
32
|
file: `parser/tests/function/valid_sources/${file}:${error.token.startLine}:${error.token.startColumn}`,
|
|
@@ -36,7 +36,7 @@ describe("Xanoscript Function Parser", () => {
|
|
|
36
36
|
error.token.image,
|
|
37
37
|
{
|
|
38
38
|
length: 20,
|
|
39
|
-
}
|
|
39
|
+
},
|
|
40
40
|
)})`,
|
|
41
41
|
});
|
|
42
42
|
});
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { expect } from "chai";
|
|
2
|
+
import { readdirSync, readFileSync } from "fs";
|
|
3
|
+
import { truncate } from "lodash-es";
|
|
4
|
+
import { describe, it } from "mocha";
|
|
5
|
+
import { dirname, join } from "path";
|
|
6
|
+
import { multidocParser } from "../../multidoc.js";
|
|
7
|
+
|
|
8
|
+
describe("Xanoscript multidoc Parser", () => {
|
|
9
|
+
it("parses all the valid sources", () => {
|
|
10
|
+
const errors = [];
|
|
11
|
+
const files = readdirSync(
|
|
12
|
+
join(dirname("./"), "parser/tests/multidoc/valid_sources"),
|
|
13
|
+
);
|
|
14
|
+
|
|
15
|
+
files.forEach((file) => {
|
|
16
|
+
const source = readFileSync(
|
|
17
|
+
join(dirname("./"), "parser/tests/multidoc/valid_sources", file),
|
|
18
|
+
"utf8",
|
|
19
|
+
);
|
|
20
|
+
const parser = multidocParser(source);
|
|
21
|
+
|
|
22
|
+
if (parser.errors.length > 0) {
|
|
23
|
+
parser.errors.forEach((error) => {
|
|
24
|
+
errors.push({
|
|
25
|
+
previous: error.previousToken
|
|
26
|
+
? `${error.previousToken.tokenTypeName} (${truncate(
|
|
27
|
+
error.previousToken.image,
|
|
28
|
+
{
|
|
29
|
+
length: 20,
|
|
30
|
+
},
|
|
31
|
+
)})`
|
|
32
|
+
: "N/A",
|
|
33
|
+
file: error.token
|
|
34
|
+
? `parser/tests/multidoc/valid_sources/${file}:${error.token.startLine}:${error.token.startColumn}`
|
|
35
|
+
: `parser/tests/multidoc/valid_sources/${file}`,
|
|
36
|
+
stack:
|
|
37
|
+
parser.sectionStack?.[parser.sectionStack.length - 1] ?? "N/A",
|
|
38
|
+
message: truncate(error.message, { length: 50 }),
|
|
39
|
+
token: error.token
|
|
40
|
+
? `${error.token.tokenTypeName} (${truncate(error.token.image, {
|
|
41
|
+
length: 20,
|
|
42
|
+
})})`
|
|
43
|
+
: "N/A",
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
if (errors.length > 0) {
|
|
50
|
+
console.table(errors);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
expect(errors).to.be.empty;
|
|
54
|
+
});
|
|
55
|
+
});
|