@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
|
@@ -0,0 +1,11 @@
|
|
|
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.
|
|
@@ -2,35 +2,20 @@
|
|
|
2
2
|
"permissions": {
|
|
3
3
|
"allow": [
|
|
4
4
|
"Bash(npm test:*)",
|
|
5
|
-
"Bash(
|
|
6
|
-
"Bash(
|
|
7
|
-
"Bash(
|
|
8
|
-
"Bash(node:*)",
|
|
9
|
-
"Bash(npm install)",
|
|
5
|
+
"Bash(git checkout:*)",
|
|
6
|
+
"Bash(ls:*)",
|
|
7
|
+
"Bash(npm run lint:*)",
|
|
10
8
|
"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:*)",
|
|
14
9
|
"Bash(find:*)",
|
|
15
|
-
"
|
|
16
|
-
"Bash(git
|
|
17
|
-
"Bash(
|
|
18
|
-
"Bash(
|
|
19
|
-
"Bash(
|
|
20
|
-
"Bash(
|
|
21
|
-
"Bash(
|
|
22
|
-
"Bash(
|
|
23
|
-
"Bash(
|
|
24
|
-
|
|
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": []
|
|
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
|
+
]
|
|
35
20
|
}
|
|
36
21
|
}
|
package/lexer/cast.js
CHANGED
|
@@ -23,14 +23,14 @@ export const BoolCastToken = createUniqToken({
|
|
|
23
23
|
// !array
|
|
24
24
|
export const ArrayCastToken = createUniqToken({
|
|
25
25
|
name: "ArrayCast",
|
|
26
|
-
pattern: /!array/,
|
|
26
|
+
pattern: /!array(?!\.)/,
|
|
27
27
|
label: "!array",
|
|
28
28
|
});
|
|
29
29
|
|
|
30
30
|
// !text
|
|
31
31
|
export const TextCastToken = createUniqToken({
|
|
32
32
|
name: "TextCast",
|
|
33
|
-
pattern: /!text/,
|
|
33
|
+
pattern: /!text(?!\.)/,
|
|
34
34
|
label: "!text",
|
|
35
35
|
});
|
|
36
36
|
|
|
@@ -51,7 +51,7 @@ export const DecimalCastToken = createUniqToken({
|
|
|
51
51
|
// !object
|
|
52
52
|
export const ObjectCastToken = createUniqToken({
|
|
53
53
|
name: "ObjectCast",
|
|
54
|
-
pattern: /!object/,
|
|
54
|
+
pattern: /!object(?!\.)/,
|
|
55
55
|
label: "!object",
|
|
56
56
|
});
|
|
57
57
|
|
package/lexer/control.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { Identifier } from "./identifier.js";
|
|
2
|
-
import { IntegerLiteral } from "./literal.js";
|
|
3
2
|
import { createTokenByName, createUniqToken } from "./utils.js";
|
|
4
3
|
|
|
5
4
|
// conditional
|
|
@@ -173,7 +172,6 @@ export const Minus = createUniqToken({
|
|
|
173
172
|
name: "Minus",
|
|
174
173
|
pattern: /-/,
|
|
175
174
|
label: "-",
|
|
176
|
-
longer_alt: IntegerLiteral,
|
|
177
175
|
});
|
|
178
176
|
|
|
179
177
|
// +
|
package/lexer/literal.js
CHANGED
|
@@ -24,13 +24,13 @@ export const SingleQuotedStringLiteral = createUniqToken({
|
|
|
24
24
|
export const FloatLiteral = createUniqToken({
|
|
25
25
|
name: "FloatLiteral",
|
|
26
26
|
label: "floating point number",
|
|
27
|
-
pattern:
|
|
27
|
+
pattern: /\d+\.\d+([eE][+-]?\d+)?/,
|
|
28
28
|
});
|
|
29
29
|
|
|
30
30
|
export const IntegerLiteral = createUniqToken({
|
|
31
31
|
name: "IntegerLiteral",
|
|
32
32
|
label: "integer",
|
|
33
|
-
pattern:
|
|
33
|
+
pattern: /\d+/,
|
|
34
34
|
longer_alt: FloatLiteral,
|
|
35
35
|
});
|
|
36
36
|
|
package/lexer/security.js
CHANGED
|
@@ -51,13 +51,13 @@ export const CreateSecretKeyToken = createTokenByName("create_secret_key", {
|
|
|
51
51
|
|
|
52
52
|
// decrypt
|
|
53
53
|
export const DecryptToken = createTokenByName("decrypt", {
|
|
54
|
-
|
|
54
|
+
longer_alt: Identifier,
|
|
55
55
|
categories: [Identifier],
|
|
56
56
|
});
|
|
57
57
|
|
|
58
58
|
// encrypt
|
|
59
59
|
export const EncryptToken = createTokenByName("encrypt", {
|
|
60
|
-
|
|
60
|
+
longer_alt: Identifier,
|
|
61
61
|
categories: [Identifier],
|
|
62
62
|
});
|
|
63
63
|
|
package/package.json
CHANGED
package/parser/addon_parser.js
CHANGED
|
@@ -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
|
package/parser/agent_parser.js
CHANGED
|
@@ -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));
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { EqualToken } from "../../lexer/control.js";
|
|
1
|
+
import { EqualToken, Minus } from "../../lexer/control.js";
|
|
2
2
|
import {
|
|
3
3
|
FloatLiteral,
|
|
4
4
|
IntegerLiteral,
|
|
@@ -21,8 +21,15 @@ export function columnDefaultValueAttribute($) {
|
|
|
21
21
|
$.CONSUME(EqualToken); // "="
|
|
22
22
|
$.OR([
|
|
23
23
|
{ ALT: () => $.CONSUME(StringLiteral) }, // e.g., "default value"
|
|
24
|
-
{
|
|
25
|
-
|
|
24
|
+
{
|
|
25
|
+
ALT: () => {
|
|
26
|
+
$.OPTION(() => $.CONSUME(Minus));
|
|
27
|
+
return $.OR1([
|
|
28
|
+
{ ALT: () => $.CONSUME(IntegerLiteral) },
|
|
29
|
+
{ ALT: () => $.CONSUME(FloatLiteral) },
|
|
30
|
+
]);
|
|
31
|
+
},
|
|
32
|
+
},
|
|
26
33
|
{ ALT: () => $.CONSUME(TrueToken) }, // true
|
|
27
34
|
{ ALT: () => $.CONSUME(FalseToken) }, // false
|
|
28
35
|
{ ALT: () => $.CONSUME(NowToken) }, // now
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ExclamationToken } from "../../lexer/cast.js";
|
|
2
|
-
import { ColonToken } from "../../lexer/control.js";
|
|
2
|
+
import { ColonToken, Minus } from "../../lexer/control.js";
|
|
3
3
|
import {
|
|
4
4
|
FloatLiteral,
|
|
5
5
|
IntegerLiteral,
|
|
@@ -36,8 +36,15 @@ export function inputFilterFn($) {
|
|
|
36
36
|
{ ALT: () => $.CONSUME2(Identifier) },
|
|
37
37
|
{ ALT: () => $.CONSUME1(StringLiteral) },
|
|
38
38
|
{ ALT: () => $.CONSUME1(RegExpToken) },
|
|
39
|
-
{
|
|
40
|
-
|
|
39
|
+
{
|
|
40
|
+
ALT: () => {
|
|
41
|
+
$.OPTION1(() => $.CONSUME(Minus));
|
|
42
|
+
$.OR1([
|
|
43
|
+
{ ALT: () => $.CONSUME(IntegerLiteral) },
|
|
44
|
+
{ ALT: () => $.CONSUME(FloatLiteral) },
|
|
45
|
+
]);
|
|
46
|
+
},
|
|
47
|
+
},
|
|
41
48
|
{ ALT: () => $.CONSUME1(FalseToken) },
|
|
42
49
|
{ ALT: () => $.CONSUME1(TrueToken) },
|
|
43
50
|
]);
|
|
@@ -52,7 +59,7 @@ export function inputFilterFn($) {
|
|
|
52
59
|
) {
|
|
53
60
|
$.addInvalidValueError(
|
|
54
61
|
filter,
|
|
55
|
-
`Filter '${filter.image}' cannot be applied to input of type '${inputTypeToken.image}'
|
|
62
|
+
`Filter '${filter.image}' cannot be applied to input of type '${inputTypeToken.image}'`,
|
|
56
63
|
);
|
|
57
64
|
}
|
|
58
65
|
},
|
package/parser/base_parser.js
CHANGED
|
@@ -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
|
*
|
package/parser/branch_parser.js
CHANGED
|
@@ -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]" }],
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { EqualToken } from "../../lexer/control.js";
|
|
2
|
-
import { Identifier } from "../../lexer/identifier.js";
|
|
3
2
|
import { StringLiteral } from "../../lexer/literal.js";
|
|
4
3
|
import { MultiLineStringToken } from "../../lexer/multiline.js";
|
|
4
|
+
import { InstructionsToken } from "../../lexer/tool.js";
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* @param {import('../base_parser.js').XanoBaseParser} $
|
|
@@ -14,7 +14,7 @@ export function flexibleStringClause($) {
|
|
|
14
14
|
return (args) => {
|
|
15
15
|
// During parser initialization, args is undefined
|
|
16
16
|
// Use a placeholder token for grammar recording
|
|
17
|
-
const token = args?.[0] ||
|
|
17
|
+
const token = args?.[0] || InstructionsToken;
|
|
18
18
|
const allowEmpty = args?.[1] ?? true;
|
|
19
19
|
|
|
20
20
|
$.sectionStack.push(token.name + "Clause");
|
|
@@ -20,46 +20,53 @@ export function nakedStackFn($) {
|
|
|
20
20
|
$.CONSUME(LCurly); // "{"
|
|
21
21
|
$.MANY(() => {
|
|
22
22
|
$.AT_LEAST_ONE1(() => $.CONSUME1(NewlineToken)); // required new line before each clause
|
|
23
|
-
$.OPTION(() => $.CONSUME(ExclamationToken)); // optional "!" to continue on error
|
|
23
|
+
// $.OPTION(() => $.CONSUME(ExclamationToken)); // optional "!" to continue on error
|
|
24
24
|
$.OR([
|
|
25
25
|
{ ALT: () => $.CONSUME(CommentToken) },
|
|
26
|
-
{ ALT: () => $.SUBRULE($.aiFn) },
|
|
27
|
-
{ ALT: () => $.SUBRULE($.dbFn) },
|
|
28
|
-
{ ALT: () => $.SUBRULE($.varFn) },
|
|
29
|
-
{ ALT: () => $.SUBRULE($.arrayFn) },
|
|
30
|
-
{ ALT: () => $.SUBRULE($.cloudFn) },
|
|
31
|
-
{ ALT: () => $.SUBRULE($.securityFn) },
|
|
32
|
-
{ ALT: () => $.SUBRULE($.mathFn) },
|
|
33
|
-
{ ALT: () => $.SUBRULE($.textFn) },
|
|
34
|
-
{ ALT: () => $.SUBRULE($.utilFn) },
|
|
35
|
-
{ ALT: () => $.SUBRULE($.preconditionFn) },
|
|
36
|
-
{ ALT: () => $.SUBRULE($.zipFn) },
|
|
37
|
-
{ ALT: () => $.SUBRULE($.objectFn) },
|
|
38
|
-
{ ALT: () => $.SUBRULE($.apiFn, { ARGS: [options] }) },
|
|
39
|
-
{ ALT: () => $.SUBRULE($.webflowFn) },
|
|
40
|
-
{ ALT: () => $.SUBRULE($.redisFn) },
|
|
41
|
-
{ ALT: () => $.SUBRULE($.controlFn, { ARGS: [options] }) },
|
|
42
|
-
{ ALT: () => $.SUBRULE($.debugFn) },
|
|
43
|
-
{ ALT: () => $.SUBRULE($.storageFn) },
|
|
44
|
-
{ ALT: () => $.SUBRULE($.streamFn) },
|
|
45
|
-
{ ALT: () => $.SUBRULE($.actionCallFn) },
|
|
46
26
|
{
|
|
47
|
-
|
|
48
|
-
|
|
27
|
+
ALT: () => {
|
|
28
|
+
$.OPTION(() => $.CONSUME(ExclamationToken)); // optional "!" to continue on error
|
|
29
|
+
$.OR1([
|
|
30
|
+
{ ALT: () => $.SUBRULE($.aiFn) },
|
|
31
|
+
{ ALT: () => $.SUBRULE($.dbFn) },
|
|
32
|
+
{ ALT: () => $.SUBRULE($.varFn) },
|
|
33
|
+
{ ALT: () => $.SUBRULE($.arrayFn) },
|
|
34
|
+
{ ALT: () => $.SUBRULE($.cloudFn) },
|
|
35
|
+
{ ALT: () => $.SUBRULE($.securityFn) },
|
|
36
|
+
{ ALT: () => $.SUBRULE($.mathFn) },
|
|
37
|
+
{ ALT: () => $.SUBRULE($.textFn) },
|
|
38
|
+
{ ALT: () => $.SUBRULE($.utilFn) },
|
|
39
|
+
{ ALT: () => $.SUBRULE($.preconditionFn) },
|
|
40
|
+
{ ALT: () => $.SUBRULE($.zipFn) },
|
|
41
|
+
{ ALT: () => $.SUBRULE($.objectFn) },
|
|
42
|
+
{ ALT: () => $.SUBRULE($.apiFn, { ARGS: [options] }) },
|
|
43
|
+
{ ALT: () => $.SUBRULE($.webflowFn) },
|
|
44
|
+
{ ALT: () => $.SUBRULE($.redisFn) },
|
|
45
|
+
{ ALT: () => $.SUBRULE($.controlFn, { ARGS: [options] }) },
|
|
46
|
+
{ ALT: () => $.SUBRULE($.debugFn) },
|
|
47
|
+
{ ALT: () => $.SUBRULE($.storageFn) },
|
|
48
|
+
{ ALT: () => $.SUBRULE($.streamFn) },
|
|
49
|
+
{ ALT: () => $.SUBRULE($.actionCallFn) },
|
|
50
|
+
{
|
|
51
|
+
GATE: () => options.allowCallStatements,
|
|
52
|
+
ALT: () => $.SUBRULE($.toolCallFn),
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
GATE: () => options.allowCallStatements,
|
|
56
|
+
ALT: () => $.SUBRULE($.addonCallFn),
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
GATE: () => options.allowCallStatements,
|
|
60
|
+
ALT: () => $.SUBRULE($.middlewareCallFn),
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
GATE: () => options.allowCallStatements,
|
|
64
|
+
ALT: () => $.SUBRULE($.triggerCallFn),
|
|
65
|
+
},
|
|
66
|
+
{ ALT: () => $.SUBRULE($.workflowExpectFn) },
|
|
67
|
+
]);
|
|
68
|
+
},
|
|
49
69
|
},
|
|
50
|
-
{
|
|
51
|
-
GATE: () => options.allowCallStatements,
|
|
52
|
-
ALT: () => $.SUBRULE($.addonCallFn),
|
|
53
|
-
},
|
|
54
|
-
{
|
|
55
|
-
GATE: () => options.allowCallStatements,
|
|
56
|
-
ALT: () => $.SUBRULE($.middlewareCallFn),
|
|
57
|
-
},
|
|
58
|
-
{
|
|
59
|
-
GATE: () => options.allowCallStatements,
|
|
60
|
-
ALT: () => $.SUBRULE($.triggerCallFn),
|
|
61
|
-
},
|
|
62
|
-
{ ALT: () => $.SUBRULE($.workflowExpectFn) },
|
|
63
70
|
]);
|
|
64
71
|
});
|
|
65
72
|
$.AT_LEAST_ONE2(() => $.CONSUME2(NewlineToken));
|
|
@@ -31,6 +31,16 @@ describe("nakedStack", () => {
|
|
|
31
31
|
expect(parser.errors).to.be.empty;
|
|
32
32
|
});
|
|
33
33
|
|
|
34
|
+
it("nakedStack accepts a disabled array statement", () => {
|
|
35
|
+
const parser = parse(`{
|
|
36
|
+
!array.push $my_array {
|
|
37
|
+
value = 1
|
|
38
|
+
}
|
|
39
|
+
}`);
|
|
40
|
+
console.log(parser.errors);
|
|
41
|
+
expect(parser.errors).to.be.empty;
|
|
42
|
+
});
|
|
43
|
+
|
|
34
44
|
it("nakedStack accepts a multiline statement", () => {
|
|
35
45
|
const parser = parse(`{
|
|
36
46
|
var $some_var {
|
|
@@ -27,6 +27,11 @@ describe("responseClause", () => {
|
|
|
27
27
|
expect(parser.errors).to.be.empty;
|
|
28
28
|
});
|
|
29
29
|
|
|
30
|
+
it("responseClause accepts a string", () => {
|
|
31
|
+
const parser = parse(`response = "no exception"`);
|
|
32
|
+
expect(parser.errors).to.be.empty;
|
|
33
|
+
});
|
|
34
|
+
|
|
30
35
|
it("responseClause accepts an object", () => {
|
|
31
36
|
const parser = parse(`response = {result1: $x1, x2: $x2}`);
|
|
32
37
|
expect(parser.errors).to.be.empty;
|
|
@@ -115,7 +115,11 @@ export function columnDefinition($) {
|
|
|
115
115
|
|
|
116
116
|
// Track table reference from FK column definition
|
|
117
117
|
if (captured.table?.value) {
|
|
118
|
-
$.addReference(
|
|
118
|
+
$.addReference(
|
|
119
|
+
"table",
|
|
120
|
+
getVarName(captured.table.value),
|
|
121
|
+
captured.table.value,
|
|
122
|
+
);
|
|
119
123
|
}
|
|
120
124
|
|
|
121
125
|
if (inputTypeToken.image == "vector" && !captured.size) {
|
|
@@ -126,7 +130,8 @@ export function columnDefinition($) {
|
|
|
126
130
|
}
|
|
127
131
|
|
|
128
132
|
const inTable = $.sectionStack.includes("tableDeclaration");
|
|
129
|
-
|
|
133
|
+
const inIntput = $.sectionStack.includes("inputClause");
|
|
134
|
+
if (captured.visibility && !inTable && !inIntput) {
|
|
130
135
|
$.addInvalidValueError(
|
|
131
136
|
nameToken,
|
|
132
137
|
'The "visibility" attribute is only valid within a table schema definition.',
|
|
@@ -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 {
|
|
@@ -19,6 +19,14 @@ describe("forEachFn", () => {
|
|
|
19
19
|
expect(parser.errors).to.be.empty;
|
|
20
20
|
});
|
|
21
21
|
|
|
22
|
+
it("forEachFn should warn when using a reserved variable name", () => {
|
|
23
|
+
const parser = parse(`foreach ([]|push:1|push:2|push:3|push:4) {
|
|
24
|
+
each as $env
|
|
25
|
+
}`);
|
|
26
|
+
expect(parser.errors).to.be.empty;
|
|
27
|
+
expect(parser.warnings).to.not.be.empty;
|
|
28
|
+
});
|
|
29
|
+
|
|
22
30
|
it("forEachFn can also be foreach.remove", () => {
|
|
23
31
|
const parser = parse(`foreach.remove`);
|
|
24
32
|
expect(parser.errors).to.be.empty;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { LParent, RParent } from "../../../lexer/control.js";
|
|
2
|
+
import { NewlineToken } from "../../../lexer/tokens.js";
|
|
2
3
|
import { PreconditionToken } from "../../../lexer/util.js";
|
|
3
4
|
|
|
4
5
|
/**
|
|
@@ -13,7 +14,9 @@ export function preconditionFn($) {
|
|
|
13
14
|
$.sectionStack.push("preconditionFn");
|
|
14
15
|
const tokenFn = $.CONSUME(PreconditionToken); // "precondition"
|
|
15
16
|
$.CONSUME(LParent);
|
|
17
|
+
$.MANY(() => $.CONSUME(NewlineToken));
|
|
16
18
|
$.SUBRULE($.expressionFn);
|
|
19
|
+
$.MANY1(() => $.CONSUME1(NewlineToken));
|
|
17
20
|
$.CONSUME(RParent);
|
|
18
21
|
$.SUBRULE($.schemaParseAttributeFn, {
|
|
19
22
|
ARGS: [
|
|
@@ -346,4 +346,35 @@ describe("dbQueryFn", () => {
|
|
|
346
346
|
const parser = parse(`query user as $user`);
|
|
347
347
|
expect(parser.errors).to.be.empty;
|
|
348
348
|
});
|
|
349
|
+
|
|
350
|
+
describe("aggregate eval with aggregator pipes", () => {
|
|
351
|
+
const aggregators = [
|
|
352
|
+
"avg",
|
|
353
|
+
"count",
|
|
354
|
+
"count_distinct",
|
|
355
|
+
"max",
|
|
356
|
+
"median",
|
|
357
|
+
"min",
|
|
358
|
+
"sum",
|
|
359
|
+
"to_distinct_list",
|
|
360
|
+
"to_distinct_list_asc",
|
|
361
|
+
"to_distinct_list_desc",
|
|
362
|
+
"to_list",
|
|
363
|
+
"to_list_asc",
|
|
364
|
+
"to_list_desc",
|
|
365
|
+
];
|
|
366
|
+
|
|
367
|
+
for (const agg of aggregators) {
|
|
368
|
+
it(`dbQueryFn return eval accepts |${agg} aggregator`, () => {
|
|
369
|
+
const parser = parse(`query user {
|
|
370
|
+
return = {
|
|
371
|
+
type: "aggregate"
|
|
372
|
+
group: {user_name: $db.user.name}
|
|
373
|
+
eval: {result: $db.user.name|${agg}}
|
|
374
|
+
}
|
|
375
|
+
} as $user`);
|
|
376
|
+
expect(parser.errors).to.be.empty;
|
|
377
|
+
});
|
|
378
|
+
}
|
|
379
|
+
});
|
|
349
380
|
});
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { isArray, some } from "lodash-es";
|
|
2
|
+
import { Minus } from "../../../lexer/control.js";
|
|
2
3
|
import {
|
|
3
4
|
FloatLiteral,
|
|
4
5
|
IntegerLiteral,
|
|
@@ -37,18 +38,18 @@ export function schemaParseConstantFn($) {
|
|
|
37
38
|
return $.CONSUME(SingleQuotedStringLiteral); // e.g., 'field instructions'
|
|
38
39
|
},
|
|
39
40
|
},
|
|
41
|
+
|
|
40
42
|
{
|
|
41
43
|
ALT: () => {
|
|
44
|
+
$.OPTION(() => $.CONSUME(Minus));
|
|
42
45
|
attributeCapturedType = "number";
|
|
43
|
-
return $.
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
ALT: () => {
|
|
48
|
-
attributeCapturedType = "number";
|
|
49
|
-
return $.CONSUME(FloatLiteral); // e.g., 42.54
|
|
46
|
+
return $.OR1([
|
|
47
|
+
{ ALT: () => $.CONSUME(IntegerLiteral) },
|
|
48
|
+
{ ALT: () => $.CONSUME(FloatLiteral) },
|
|
49
|
+
]);
|
|
50
50
|
},
|
|
51
51
|
},
|
|
52
|
+
|
|
52
53
|
{
|
|
53
54
|
ALT: () => {
|
|
54
55
|
attributeCapturedType = "boolean";
|
|
@@ -76,7 +77,7 @@ export function schemaParseConstantFn($) {
|
|
|
76
77
|
const isMatch = matchValueToSchema(
|
|
77
78
|
attributeCapturedType,
|
|
78
79
|
capturedValue.image,
|
|
79
|
-
schema
|
|
80
|
+
schema,
|
|
80
81
|
);
|
|
81
82
|
|
|
82
83
|
if (!isMatch) {
|
|
@@ -92,7 +93,7 @@ function matchValueToSchema(attributeCapturedType, value, schema) {
|
|
|
92
93
|
// schema can be an array of type/values required
|
|
93
94
|
if (isArray(schema)) {
|
|
94
95
|
return some(schema, (s) =>
|
|
95
|
-
matchValueToSchema(attributeCapturedType, value, s)
|
|
96
|
+
matchValueToSchema(attributeCapturedType, value, s),
|
|
96
97
|
);
|
|
97
98
|
}
|
|
98
99
|
|
|
@@ -115,7 +116,7 @@ function matchValueToSchema(attributeCapturedType, value, schema) {
|
|
|
115
116
|
return schemaValue === null;
|
|
116
117
|
default:
|
|
117
118
|
throw new Error(
|
|
118
|
-
`schemaParseConstantFn called with non-constant attribute: ${schema}
|
|
119
|
+
`schemaParseConstantFn called with non-constant attribute: ${schema}`,
|
|
119
120
|
);
|
|
120
121
|
}
|
|
121
122
|
}
|