@xano/xanoscript-language-server 11.7.0 → 11.7.2
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/settings.local.json +10 -1
- package/onCompletion/contentAssist.js +27 -23
- package/package.json +1 -1
- package/parser/addon_parser.js +8 -0
- package/parser/agent_parser.js +8 -0
- package/parser/agent_trigger_parser.js +8 -0
- package/parser/api_group_parser.js +8 -0
- package/parser/attributes/guidFieldAttribute.js +16 -0
- package/parser/attributes/guidFieldAttribute.spec.js +93 -0
- package/parser/attributes/register.js +5 -0
- package/parser/function_parser.js +8 -0
- package/parser/function_parser.spec.js +8 -5
- package/parser/functions/api/apiCallFn.js +7 -3
- package/parser/functions/expect/unitExpectToBeWithinFn.spec.js +4 -2
- package/parser/functions/expect/unitExpectWithArgumentsFn.js +8 -2
- package/parser/functions/expect/unitExpectWithArgumentsFn.spec.js +7 -0
- package/parser/functions/expect/workflowExpectToBeWithinFn.js +2 -0
- package/parser/functions/expect/workflowExpectWithArgumentsFn.js +2 -0
- package/parser/functions/expect/workflowExpectWithArgumentsFn.spec.js +7 -0
- package/parser/functions/expect/workflowExpectWithoutArgumentsFn.js +2 -0
- package/parser/functions/schema/schemaFn.js +1 -1
- package/parser/functions/schema/schemaParseAttributeFn.spec.js +9 -9
- package/parser/functions/workflowExpectFn.js +2 -2
- package/parser/functions/workflowExpectFn.spec.js +33 -0
- package/parser/generic/chainedIdentifier.js +2 -1
- package/parser/mcp_server_parser.js +8 -0
- package/parser/mcp_server_trigger_parser.js +8 -0
- package/parser/middleware_parser.js +8 -0
- package/parser/parser.js +1 -0
- package/parser/query_parser.js +8 -0
- package/parser/realtime_trigger_parser.js +8 -0
- package/parser/table_parser.js +5 -0
- package/parser/table_trigger_parser.js +8 -0
- package/parser/task_parser.js +8 -0
- package/parser/tool_parser.js +8 -0
- package/parser/workflow_test_parser.js +9 -3
- package/parser/workflow_test_parser.spec.js +13 -2
- package/parser/workspace_trigger_parser.js +8 -0
|
@@ -10,7 +10,16 @@
|
|
|
10
10
|
"Bash(grep:*)",
|
|
11
11
|
"Bash(node --input-type=module:*)",
|
|
12
12
|
"Bash(find:*)",
|
|
13
|
-
"Read(//tmp/**)"
|
|
13
|
+
"Read(//tmp/**)",
|
|
14
|
+
"Bash(git stash:*)",
|
|
15
|
+
"Bash(node:*)",
|
|
16
|
+
"Bash(for:*)",
|
|
17
|
+
"Bash(do)",
|
|
18
|
+
"Bash(if ! grep -q \"guidFieldAttribute\" \"$f\")",
|
|
19
|
+
"Bash(then)",
|
|
20
|
+
"Bash(echo:*)",
|
|
21
|
+
"Bash(fi)",
|
|
22
|
+
"Bash(done)"
|
|
14
23
|
]
|
|
15
24
|
}
|
|
16
25
|
}
|
|
@@ -127,6 +127,11 @@ const filterSuggestions = Object.freeze(
|
|
|
127
127
|
})
|
|
128
128
|
);
|
|
129
129
|
|
|
130
|
+
// Maximum number of tokens to pass to computeContentAssist.
|
|
131
|
+
// Chevrotain's content assist has exponential complexity with complex grammars;
|
|
132
|
+
// beyond this threshold it can consume gigabytes of memory and OOM the process.
|
|
133
|
+
const MAX_CONTENT_ASSIST_TOKENS = 40;
|
|
134
|
+
|
|
130
135
|
export function getContentAssistSuggestions(text, scheme) {
|
|
131
136
|
try {
|
|
132
137
|
const lexResult = lexDocument(text);
|
|
@@ -138,32 +143,31 @@ export function getContentAssistSuggestions(text, scheme) {
|
|
|
138
143
|
return filterSuggestions;
|
|
139
144
|
}
|
|
140
145
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
);
|
|
158
|
-
} else if (scheme === "task") {
|
|
159
|
-
syntacticSuggestions = parser.computeContentAssist(
|
|
160
|
-
"taskDeclaration",
|
|
161
|
-
partialTokenVector
|
|
162
|
-
);
|
|
163
|
-
} else {
|
|
146
|
+
// Skip computeContentAssist for large token vectors to prevent OOM.
|
|
147
|
+
// Chevrotain explores all possible parse paths which grows exponentially
|
|
148
|
+
// with deeply nested grammars (e.g., arrays inside function blocks).
|
|
149
|
+
if (partialTokenVector.length > MAX_CONTENT_ASSIST_TOKENS) {
|
|
150
|
+
return [];
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
const ruleNameByScheme = {
|
|
154
|
+
db: "tableDeclaration",
|
|
155
|
+
api: "queryDeclaration",
|
|
156
|
+
function: "functionDeclaration",
|
|
157
|
+
task: "taskDeclaration",
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
const ruleName = ruleNameByScheme[scheme];
|
|
161
|
+
if (!ruleName) {
|
|
164
162
|
return [];
|
|
165
163
|
}
|
|
166
164
|
|
|
165
|
+
parser.reset();
|
|
166
|
+
const syntacticSuggestions = parser.computeContentAssist(
|
|
167
|
+
ruleName,
|
|
168
|
+
partialTokenVector
|
|
169
|
+
);
|
|
170
|
+
|
|
167
171
|
// The suggestions also include the context, we are only interested
|
|
168
172
|
// in the TokenTypes in this example.
|
|
169
173
|
const tokenTypesSuggestions = syntacticSuggestions
|
package/package.json
CHANGED
package/parser/addon_parser.js
CHANGED
|
@@ -17,6 +17,7 @@ import {
|
|
|
17
17
|
export function addonDeclaration($) {
|
|
18
18
|
return () => {
|
|
19
19
|
let hasDescription = false;
|
|
20
|
+
let hasGuid = false;
|
|
20
21
|
let hasInput = false;
|
|
21
22
|
let hasStack = false;
|
|
22
23
|
let hasTags = false;
|
|
@@ -41,6 +42,13 @@ export function addonDeclaration($) {
|
|
|
41
42
|
$.SUBRULE($.descriptionFieldAttribute);
|
|
42
43
|
},
|
|
43
44
|
},
|
|
45
|
+
{
|
|
46
|
+
GATE: () => !hasGuid,
|
|
47
|
+
ALT: () => {
|
|
48
|
+
hasGuid = true;
|
|
49
|
+
$.SUBRULE($.guidFieldAttribute);
|
|
50
|
+
},
|
|
51
|
+
},
|
|
44
52
|
{
|
|
45
53
|
GATE: () => !hasInput,
|
|
46
54
|
ALT: () => {
|
package/parser/agent_parser.js
CHANGED
|
@@ -9,6 +9,7 @@ export function agentDeclaration($) {
|
|
|
9
9
|
return () => {
|
|
10
10
|
let hasCanonical = false;
|
|
11
11
|
let hasDescription = false;
|
|
12
|
+
let hasGuid = false;
|
|
12
13
|
let hasDocs = false;
|
|
13
14
|
let hasHistory = false;
|
|
14
15
|
let hasLLM = false;
|
|
@@ -48,6 +49,13 @@ export function agentDeclaration($) {
|
|
|
48
49
|
$.SUBRULE($.descriptionFieldAttribute);
|
|
49
50
|
},
|
|
50
51
|
},
|
|
52
|
+
{
|
|
53
|
+
GATE: () => !hasGuid,
|
|
54
|
+
ALT: () => {
|
|
55
|
+
hasGuid = true;
|
|
56
|
+
$.SUBRULE($.guidFieldAttribute);
|
|
57
|
+
},
|
|
58
|
+
},
|
|
51
59
|
{
|
|
52
60
|
GATE: () => !hasDocs,
|
|
53
61
|
ALT: () => {
|
|
@@ -14,6 +14,7 @@ export function agentTriggerDeclaration($) {
|
|
|
14
14
|
let hasActive = false;
|
|
15
15
|
let hasAgent = false;
|
|
16
16
|
let hasDescription = false;
|
|
17
|
+
let hasGuid = false;
|
|
17
18
|
let hasDocs = false;
|
|
18
19
|
let hasHistory = false;
|
|
19
20
|
let hasInput = false;
|
|
@@ -69,6 +70,13 @@ export function agentTriggerDeclaration($) {
|
|
|
69
70
|
$.SUBRULE($.descriptionFieldAttribute);
|
|
70
71
|
},
|
|
71
72
|
},
|
|
73
|
+
{
|
|
74
|
+
GATE: () => !hasGuid,
|
|
75
|
+
ALT: () => {
|
|
76
|
+
hasGuid = true;
|
|
77
|
+
$.SUBRULE($.guidFieldAttribute);
|
|
78
|
+
},
|
|
79
|
+
},
|
|
72
80
|
{
|
|
73
81
|
GATE: () => !hasDocs,
|
|
74
82
|
ALT: () => {
|
|
@@ -16,6 +16,7 @@ export function apiGroupDeclaration($) {
|
|
|
16
16
|
let hasCanonical = false;
|
|
17
17
|
let hasCors = false;
|
|
18
18
|
let hasDescription = false;
|
|
19
|
+
let hasGuid = false;
|
|
19
20
|
let hasDocs = false;
|
|
20
21
|
let hasHistory = false;
|
|
21
22
|
let hasSwagger = false;
|
|
@@ -81,6 +82,13 @@ export function apiGroupDeclaration($) {
|
|
|
81
82
|
$.SUBRULE($.descriptionFieldAttribute);
|
|
82
83
|
},
|
|
83
84
|
},
|
|
85
|
+
{
|
|
86
|
+
GATE: () => !hasGuid,
|
|
87
|
+
ALT: () => {
|
|
88
|
+
hasGuid = true;
|
|
89
|
+
$.SUBRULE($.guidFieldAttribute);
|
|
90
|
+
},
|
|
91
|
+
},
|
|
84
92
|
{
|
|
85
93
|
GATE: () => !hasDocs,
|
|
86
94
|
ALT: () => {
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { EqualToken } from "../../lexer/control.js";
|
|
2
|
+
import { StringLiteral } from "../../lexer/literal.js";
|
|
3
|
+
import { GuidToken } from "../../lexer/tokens.js";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @param {import('../base_parser.js').XanoBaseParser} $
|
|
7
|
+
*/
|
|
8
|
+
export function guidFieldAttribute($) {
|
|
9
|
+
return () => {
|
|
10
|
+
$.sectionStack.push("guidFieldAttribute");
|
|
11
|
+
$.CONSUME(GuidToken); // "guid"
|
|
12
|
+
$.CONSUME(EqualToken); // "="
|
|
13
|
+
$.CONSUME(StringLiteral); // e.g., "tgGYuUFKbBz4_6DIkDR5hUUCRno"
|
|
14
|
+
$.sectionStack.pop();
|
|
15
|
+
};
|
|
16
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { expect } from "chai";
|
|
2
|
+
import { describe, it } from "mocha";
|
|
3
|
+
import { xanoscriptParser } from "../parser.js";
|
|
4
|
+
|
|
5
|
+
describe("guidFieldAttribute", () => {
|
|
6
|
+
it("should parse guid in a table", () => {
|
|
7
|
+
const parser = xanoscriptParser(`table "foo" {
|
|
8
|
+
guid = "tgGYuUFKbBz4_6DIkDR5hUUCRno"
|
|
9
|
+
auth = false
|
|
10
|
+
schema {
|
|
11
|
+
int id
|
|
12
|
+
}
|
|
13
|
+
}`);
|
|
14
|
+
expect(parser.errors).to.be.empty;
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
it("should parse guid in a function", () => {
|
|
18
|
+
const parser = xanoscriptParser(`function "my/function" {
|
|
19
|
+
guid = "dg4egGY_sadclkj231af32"
|
|
20
|
+
input {
|
|
21
|
+
}
|
|
22
|
+
stack {
|
|
23
|
+
}
|
|
24
|
+
response = null
|
|
25
|
+
}`);
|
|
26
|
+
expect(parser.errors).to.be.empty;
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it("should parse guid in a query", () => {
|
|
30
|
+
const parser = xanoscriptParser(`query "test" verb = GET {
|
|
31
|
+
guid = "abc123def456"
|
|
32
|
+
api_group = "default"
|
|
33
|
+
input {
|
|
34
|
+
}
|
|
35
|
+
stack {
|
|
36
|
+
}
|
|
37
|
+
response = null
|
|
38
|
+
}`);
|
|
39
|
+
expect(parser.errors).to.be.empty;
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it("should parse guid in a task", () => {
|
|
43
|
+
const parser = xanoscriptParser(`task "my_task" {
|
|
44
|
+
guid = "task_guid_123"
|
|
45
|
+
stack {
|
|
46
|
+
}
|
|
47
|
+
}`);
|
|
48
|
+
expect(parser.errors).to.be.empty;
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it("should parse guid in an api_group", () => {
|
|
52
|
+
const parser = xanoscriptParser(`api_group "my_group" {
|
|
53
|
+
guid = "api_group_guid_123"
|
|
54
|
+
}`);
|
|
55
|
+
expect(parser.errors).to.be.empty;
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
it("should parse guid in a middleware", () => {
|
|
59
|
+
const parser = xanoscriptParser(`middleware "my_mw" {
|
|
60
|
+
guid = "mw_guid_123"
|
|
61
|
+
input {
|
|
62
|
+
}
|
|
63
|
+
stack {
|
|
64
|
+
}
|
|
65
|
+
response = null
|
|
66
|
+
}`);
|
|
67
|
+
expect(parser.errors).to.be.empty;
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it("should not allow duplicate guid", () => {
|
|
71
|
+
const parser = xanoscriptParser(`function "test" {
|
|
72
|
+
guid = "first"
|
|
73
|
+
guid = "second"
|
|
74
|
+
input {
|
|
75
|
+
}
|
|
76
|
+
stack {
|
|
77
|
+
}
|
|
78
|
+
response = null
|
|
79
|
+
}`);
|
|
80
|
+
expect(parser.errors).to.not.be.empty;
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
it("should work without guid (optional)", () => {
|
|
84
|
+
const parser = xanoscriptParser(`function "test" {
|
|
85
|
+
input {
|
|
86
|
+
}
|
|
87
|
+
stack {
|
|
88
|
+
}
|
|
89
|
+
response = null
|
|
90
|
+
}`);
|
|
91
|
+
expect(parser.errors).to.be.empty;
|
|
92
|
+
});
|
|
93
|
+
});
|
|
@@ -2,6 +2,7 @@ import { columnDefaultValueAttribute } from "./columnDefaultValueAttribute.js";
|
|
|
2
2
|
import { descriptionFieldAttribute } from "./descriptionFieldAttribute.js";
|
|
3
3
|
import { disabledFieldAttribute } from "./disabledFieldAttribute.js";
|
|
4
4
|
import { docsFieldAttribute } from "./docsFieldAttribute.js"
|
|
5
|
+
import { guidFieldAttribute } from "./guidFieldAttribute.js";
|
|
5
6
|
import { inputFilterFn } from "./inputFilterFn.js";
|
|
6
7
|
import { sensitiveFieldAttribute } from "./sensitiveFieldAttribute.js";
|
|
7
8
|
import { valueFieldAttribute } from "./valueFieldAttribute.js";
|
|
@@ -28,6 +29,10 @@ export const register = ($) => {
|
|
|
28
29
|
"docsFieldAttribute",
|
|
29
30
|
docsFieldAttribute($)
|
|
30
31
|
);
|
|
32
|
+
$.guidFieldAttribute = $.RULE(
|
|
33
|
+
"guidFieldAttribute",
|
|
34
|
+
guidFieldAttribute($)
|
|
35
|
+
);
|
|
31
36
|
$.inputFilterFn = $.RULE("inputFilterFn", inputFilterFn($));
|
|
32
37
|
$.sensitiveFieldAttribute = $.RULE(
|
|
33
38
|
"sensitiveFieldAttribute",
|
|
@@ -9,6 +9,7 @@ export function functionDeclaration($) {
|
|
|
9
9
|
return () => {
|
|
10
10
|
let hasCache = false;
|
|
11
11
|
let hasDescription = false;
|
|
12
|
+
let hasGuid = false;
|
|
12
13
|
let hasDocs = false;
|
|
13
14
|
let hasHistory = false;
|
|
14
15
|
let hasInput = false;
|
|
@@ -54,6 +55,13 @@ export function functionDeclaration($) {
|
|
|
54
55
|
$.SUBRULE($.descriptionFieldAttribute);
|
|
55
56
|
},
|
|
56
57
|
},
|
|
58
|
+
{
|
|
59
|
+
GATE: () => !hasGuid,
|
|
60
|
+
ALT: () => {
|
|
61
|
+
hasGuid = true;
|
|
62
|
+
$.SUBRULE($.guidFieldAttribute);
|
|
63
|
+
},
|
|
64
|
+
},
|
|
57
65
|
{
|
|
58
66
|
GATE: () => !hasDocs,
|
|
59
67
|
ALT: () => {
|
|
@@ -100,10 +100,14 @@ describe("function_parser", () => {
|
|
|
100
100
|
}
|
|
101
101
|
|
|
102
102
|
stack {
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
103
|
+
group {
|
|
104
|
+
stack {
|
|
105
|
+
api.call "users/{user_id}" verb=GET {
|
|
106
|
+
api_group = "account"
|
|
107
|
+
input = { user_id: 123 }
|
|
108
|
+
} as $user
|
|
109
|
+
}
|
|
110
|
+
}
|
|
107
111
|
}
|
|
108
112
|
|
|
109
113
|
response = null
|
|
@@ -111,4 +115,3 @@ describe("function_parser", () => {
|
|
|
111
115
|
expect(parser.errors).to.not.be.empty;
|
|
112
116
|
});
|
|
113
117
|
});
|
|
114
|
-
|
|
@@ -20,15 +20,19 @@ export function apiCallFn($) {
|
|
|
20
20
|
const fnToken = $.CONSUME(CallToken); // "call"
|
|
21
21
|
|
|
22
22
|
// Validate that api.call is only used in allowed contexts
|
|
23
|
-
const validContexts = [
|
|
23
|
+
const validContexts = [
|
|
24
|
+
"workflowTestDeclaration",
|
|
25
|
+
"toolDeclaration",
|
|
26
|
+
"testClause",
|
|
27
|
+
];
|
|
24
28
|
const isInValidContext = validContexts.some((context) =>
|
|
25
|
-
$.sectionStack.includes(context)
|
|
29
|
+
$.sectionStack.includes(context),
|
|
26
30
|
);
|
|
27
31
|
|
|
28
32
|
if (!isInValidContext) {
|
|
29
33
|
$.addInvalidValueError(
|
|
30
34
|
fnToken,
|
|
31
|
-
"api.call can only be used within workflow_test, tool, or test contexts"
|
|
35
|
+
"api.call can only be used within workflow_test, tool, or test contexts",
|
|
32
36
|
);
|
|
33
37
|
}
|
|
34
38
|
|
|
@@ -29,8 +29,10 @@ describe("unitExpectToBeWithinFn", () => {
|
|
|
29
29
|
});
|
|
30
30
|
|
|
31
31
|
it("unitExpectToBeWithinFn does not accepts filters on value argument", () => {
|
|
32
|
-
const parser =
|
|
33
|
-
|
|
32
|
+
const parser = parse(`to_be_within (($response|get:foo:"bar")
|
|
33
|
+
|first
|
|
34
|
+
|concat:bar
|
|
35
|
+
) {
|
|
34
36
|
min = 11
|
|
35
37
|
max = 13
|
|
36
38
|
}`);
|
|
@@ -10,7 +10,10 @@ import {
|
|
|
10
10
|
ToStartWithToken,
|
|
11
11
|
} from "../../../lexer/expect.js";
|
|
12
12
|
import { DotToken, Identifier } from "../../../lexer/tokens.js";
|
|
13
|
-
import {
|
|
13
|
+
import {
|
|
14
|
+
ResponseVariable,
|
|
15
|
+
ShortFormVariable,
|
|
16
|
+
} from "../../../lexer/variables.js";
|
|
14
17
|
|
|
15
18
|
/**
|
|
16
19
|
* @param {import('../../base_parser.js').XanoBaseParser} $
|
|
@@ -32,7 +35,10 @@ export function unitExpectWithArgumentsFn($) {
|
|
|
32
35
|
$.CONSUME(ResponseVariable); // "$response"
|
|
33
36
|
$.MANY(() => {
|
|
34
37
|
$.CONSUME(DotToken); // "."
|
|
35
|
-
$.
|
|
38
|
+
$.OR1([
|
|
39
|
+
{ ALT: () => $.CONSUME(Identifier) }, // "x", "users", etc.
|
|
40
|
+
{ ALT: () => $.CONSUME(ShortFormVariable) }, // "$xyz"
|
|
41
|
+
]);
|
|
36
42
|
});
|
|
37
43
|
$.CONSUME(RParent); // ")"
|
|
38
44
|
|
|
@@ -20,6 +20,13 @@ describe("unitExpectWithArgumentsFn", () => {
|
|
|
20
20
|
expect(parser.errors).to.be.empty;
|
|
21
21
|
});
|
|
22
22
|
|
|
23
|
+
it("unitExpectWithArgumentsFn to_equal accepts a $response subpath with $ prefix", () => {
|
|
24
|
+
const parser = parse(`to_equal ($response.$xyz) {
|
|
25
|
+
value = "foo"
|
|
26
|
+
}`);
|
|
27
|
+
expect(parser.errors).to.be.empty;
|
|
28
|
+
});
|
|
29
|
+
|
|
23
30
|
it("unitExpectWithArgumentsFn does not accept a non response variable", () => {
|
|
24
31
|
const parser = parse(`to_equal ($foo) {
|
|
25
32
|
value = 12
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { LParent, RParent } from "../../../lexer/control.js";
|
|
2
2
|
import { ToBeWithinToken } from "../../../lexer/expect.js";
|
|
3
|
+
import { NewlineToken } from "../../../lexer/tokens.js";
|
|
3
4
|
|
|
4
5
|
/**
|
|
5
6
|
* @param {import('../../base_parser.js').XanoBaseParser} $
|
|
@@ -13,6 +14,7 @@ export function workflowExpectToBeWithinFn($) {
|
|
|
13
14
|
const fnToken = $.CONSUME(ToBeWithinToken); // "to_be_within"
|
|
14
15
|
$.CONSUME(LParent); // "("
|
|
15
16
|
$.SUBRULE($.expressionFn); // "$foo|get:bar:null"
|
|
17
|
+
$.MANY(() => $.CONSUME(NewlineToken)); // allow newlines between the expression and the attributes
|
|
16
18
|
$.CONSUME(RParent); // ")"
|
|
17
19
|
$.SUBRULE($.functionAttrReq, {
|
|
18
20
|
ARGS: [fnToken, requiredAttrs, optionalAttrs],
|
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
ToNotEqualToken,
|
|
10
10
|
ToStartWithToken,
|
|
11
11
|
} from "../../../lexer/expect.js";
|
|
12
|
+
import { NewlineToken } from "../../../lexer/tokens.js";
|
|
12
13
|
|
|
13
14
|
/**
|
|
14
15
|
* @param {import('../../base_parser.js').XanoBaseParser} $
|
|
@@ -31,6 +32,7 @@ export function workflowExpectWithArgumentsFn($) {
|
|
|
31
32
|
]);
|
|
32
33
|
$.CONSUME(LParent); // "("
|
|
33
34
|
$.SUBRULE($.expressionFn); // "$foo|get:bar:null"
|
|
35
|
+
$.MANY(() => $.CONSUME(NewlineToken)); // allow newlines between the expression and the attributes
|
|
34
36
|
$.CONSUME(RParent); // ")"
|
|
35
37
|
$.SUBRULE($.functionAttrReq, {
|
|
36
38
|
ARGS: [fnToken, requiredAttrs, optionalAttrs],
|
|
@@ -27,6 +27,13 @@ describe("workflowExpectWithArgumentsFn", () => {
|
|
|
27
27
|
expect(parser.errors).to.be.empty;
|
|
28
28
|
});
|
|
29
29
|
|
|
30
|
+
it("accept string a named attribute", () => {
|
|
31
|
+
const parser = parse(`to_equal ($attr.sub."some value") {
|
|
32
|
+
value = "enabled"
|
|
33
|
+
}`);
|
|
34
|
+
expect(parser.errors).to.be.empty;
|
|
35
|
+
});
|
|
36
|
+
|
|
30
37
|
it("workflowExpectWithArgumentsFn accepts a filtered variable", () => {
|
|
31
38
|
// foo is not in quotes
|
|
32
39
|
let parser = parse(`to_equal ($x|get:foo:"bar") {
|
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
ToNotBeDefinedToken,
|
|
11
11
|
ToNotBeNullToken,
|
|
12
12
|
} from "../../../lexer/expect.js";
|
|
13
|
+
import { NewlineToken } from "../../../lexer/tokens.js";
|
|
13
14
|
|
|
14
15
|
/**
|
|
15
16
|
* @param {import('../../base_parser.js').XanoBaseParser} $
|
|
@@ -30,6 +31,7 @@ export function workflowExpectWithoutArgumentsFn($) {
|
|
|
30
31
|
]);
|
|
31
32
|
$.CONSUME(LParent); // "("
|
|
32
33
|
$.SUBRULE($.expressionFn); // "$foo|get:bar:null"
|
|
34
|
+
$.MANY(() => $.CONSUME(NewlineToken)); // allow newlines between the expression and the attributes
|
|
33
35
|
$.CONSUME(RParent); // ")"
|
|
34
36
|
$.SUBRULE($.functionAttrReq, {
|
|
35
37
|
ARGS: [fnToken, [], ["description", "disabled"]],
|
|
@@ -14,7 +14,7 @@ export function parserExtension() {
|
|
|
14
14
|
const name = this.CONSUME(Identifier);
|
|
15
15
|
this.CONSUME(EqualToken); // "="
|
|
16
16
|
this.SUBRULE(this.schemaParseAttributeFn, { ARGS: [name, schema] });
|
|
17
|
-
}
|
|
17
|
+
},
|
|
18
18
|
);
|
|
19
19
|
|
|
20
20
|
this.schemaParseAttributeFn_expression = this.RULE(
|
|
@@ -26,7 +26,7 @@ export function parserExtension() {
|
|
|
26
26
|
};
|
|
27
27
|
const name = this.CONSUME(Identifier);
|
|
28
28
|
this.SUBRULE(this.schemaParseAttributeFn, { ARGS: [name, schema] });
|
|
29
|
-
}
|
|
29
|
+
},
|
|
30
30
|
);
|
|
31
31
|
|
|
32
32
|
// this rule requires a foo field to be defined
|
|
@@ -36,7 +36,7 @@ export function parserExtension() {
|
|
|
36
36
|
const schema = { "disabled?": "[boolean]", "description?": "[string]" };
|
|
37
37
|
const name = this.CONSUME(Identifier);
|
|
38
38
|
this.SUBRULE(this.schemaParseAttributeFn, { ARGS: [name, schema] });
|
|
39
|
-
}
|
|
39
|
+
},
|
|
40
40
|
);
|
|
41
41
|
|
|
42
42
|
this.schemaParseAttributeFn_deep = this.RULE(
|
|
@@ -52,7 +52,7 @@ export function parserExtension() {
|
|
|
52
52
|
const name = this.CONSUME(Identifier);
|
|
53
53
|
this.CONSUME(EqualToken); // "="
|
|
54
54
|
this.SUBRULE(this.schemaParseAttributeFn, { ARGS: [name, schema] });
|
|
55
|
-
}
|
|
55
|
+
},
|
|
56
56
|
);
|
|
57
57
|
|
|
58
58
|
this.schemaParseAttributeFn_stack = this.RULE(
|
|
@@ -65,7 +65,7 @@ export function parserExtension() {
|
|
|
65
65
|
};
|
|
66
66
|
const name = this.CONSUME(Identifier);
|
|
67
67
|
this.SUBRULE(this.schemaParseAttributeFn, { ARGS: [name, schema] });
|
|
68
|
-
}
|
|
68
|
+
},
|
|
69
69
|
);
|
|
70
70
|
|
|
71
71
|
this.schemaParseAttributeFn_optional = this.RULE(
|
|
@@ -80,7 +80,7 @@ export function parserExtension() {
|
|
|
80
80
|
const name = this.CONSUME(Identifier);
|
|
81
81
|
this.CONSUME(EqualToken); // "="
|
|
82
82
|
this.SUBRULE(this.schemaParseAttributeFn, { ARGS: [name, schema] });
|
|
83
|
-
}
|
|
83
|
+
},
|
|
84
84
|
);
|
|
85
85
|
|
|
86
86
|
this.schemaParseAttributeFn_multiple = this.RULE(
|
|
@@ -93,7 +93,7 @@ export function parserExtension() {
|
|
|
93
93
|
const name = this.CONSUME(Identifier);
|
|
94
94
|
this.CONSUME(EqualToken); // "="
|
|
95
95
|
this.SUBRULE(this.schemaParseAttributeFn, { ARGS: [name, schema] });
|
|
96
|
-
}
|
|
96
|
+
},
|
|
97
97
|
);
|
|
98
98
|
|
|
99
99
|
this.schemaParseAttributeFn_disabled = this.RULE(
|
|
@@ -108,7 +108,7 @@ export function parserExtension() {
|
|
|
108
108
|
const name = this.CONSUME(Identifier);
|
|
109
109
|
this.CONSUME(EqualToken); // "="
|
|
110
110
|
this.SUBRULE(this.schemaParseAttributeFn, { ARGS: [name, schema] });
|
|
111
|
-
}
|
|
111
|
+
},
|
|
112
112
|
);
|
|
113
113
|
}
|
|
114
114
|
|
|
@@ -133,7 +133,7 @@ describe("schemaParseAttributeFn", () => {
|
|
|
133
133
|
expect(parser.errors).to.be.empty;
|
|
134
134
|
|
|
135
135
|
parse(
|
|
136
|
-
`value = { something = 123.2, other_thing = 44 }
|
|
136
|
+
`value = { something = 123.2, other_thing = 44 }`,
|
|
137
137
|
).schemaParseAttributeFn_flat();
|
|
138
138
|
expect(parser.errors).to.be.empty;
|
|
139
139
|
});
|
|
@@ -12,13 +12,13 @@ export function workflowExpectFn($) {
|
|
|
12
12
|
// Validate that expect statements are only used in allowed contexts
|
|
13
13
|
const validContexts = ["workflowTestDeclaration", "testClause"];
|
|
14
14
|
const isInValidContext = validContexts.some((context) =>
|
|
15
|
-
$.sectionStack.includes(context)
|
|
15
|
+
$.sectionStack.includes(context),
|
|
16
16
|
);
|
|
17
17
|
|
|
18
18
|
if (!isInValidContext) {
|
|
19
19
|
$.addInvalidValueError(
|
|
20
20
|
expectToken,
|
|
21
|
-
"expect statements can only be used within workflow_test or test contexts"
|
|
21
|
+
"expect statements can only be used within workflow_test or test contexts",
|
|
22
22
|
);
|
|
23
23
|
}
|
|
24
24
|
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { expect } from "chai";
|
|
2
|
+
import { describe, it } from "mocha";
|
|
3
|
+
import { lexDocument } from "../../lexer/lexer.js";
|
|
4
|
+
import { parser } from "../test_parser.js";
|
|
5
|
+
|
|
6
|
+
function parse(inputText) {
|
|
7
|
+
parser.reset();
|
|
8
|
+
const lexResult = lexDocument(inputText);
|
|
9
|
+
parser.input = lexResult.tokens;
|
|
10
|
+
parser.sectionStack.push("workflowTestDeclaration");
|
|
11
|
+
parser.workflowExpectFn();
|
|
12
|
+
return parser;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
describe("workflowExpectFn", () => {
|
|
16
|
+
it("workflowExpectFn accepts a string literal as value", () => {
|
|
17
|
+
const parser = parse(`expect.to_equal ($response.email) {
|
|
18
|
+
value = "email@example.com"
|
|
19
|
+
}`);
|
|
20
|
+
expect(parser.errors).to.be.empty;
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it("workflowExpectFn accepts filtered values", () => {
|
|
24
|
+
const parser = parse(`expect.to_equal ($total
|
|
25
|
+
|subtract:$tax
|
|
26
|
+
|subtract:$cost
|
|
27
|
+
) {
|
|
28
|
+
value = $som_value
|
|
29
|
+
}`);
|
|
30
|
+
console.log(parser.errors);
|
|
31
|
+
expect(parser.errors).to.be.empty;
|
|
32
|
+
});
|
|
33
|
+
});
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { IntegerLiteral } from "../../lexer/literal.js";
|
|
1
|
+
import { IntegerLiteral, StringLiteral } from "../../lexer/literal.js";
|
|
2
2
|
import { DotToken, Identifier } from "../../lexer/tokens.js";
|
|
3
3
|
import { ShortFormVariable } from "../../lexer/variables.js";
|
|
4
4
|
|
|
@@ -38,6 +38,7 @@ export function singleChainedIdentifier($) {
|
|
|
38
38
|
}, // e.g., .bar
|
|
39
39
|
{ ALT: () => $.CONSUME(Identifier) }, // e.g., .bar
|
|
40
40
|
{ ALT: () => $.CONSUME2(IntegerLiteral) }, // e.g., .3
|
|
41
|
+
{ ALT: () => $.CONSUME(StringLiteral) }, // e.g., ."some value"
|
|
41
42
|
{ ALT: () => $.SUBRULE($.bracketAccessor) }, // e.g., .["key"]
|
|
42
43
|
]);
|
|
43
44
|
},
|
|
@@ -13,6 +13,7 @@ export function mcpServerDeclaration($) {
|
|
|
13
13
|
let hasActive = false;
|
|
14
14
|
let hasCanonical = false;
|
|
15
15
|
let hasDescription = false;
|
|
16
|
+
let hasGuid = false;
|
|
16
17
|
let hasDocs = false;
|
|
17
18
|
let hasHistory = false;
|
|
18
19
|
let hasInstructions = false;
|
|
@@ -58,6 +59,13 @@ export function mcpServerDeclaration($) {
|
|
|
58
59
|
$.SUBRULE($.descriptionFieldAttribute);
|
|
59
60
|
},
|
|
60
61
|
},
|
|
62
|
+
{
|
|
63
|
+
GATE: () => !hasGuid,
|
|
64
|
+
ALT: () => {
|
|
65
|
+
hasGuid = true;
|
|
66
|
+
$.SUBRULE($.guidFieldAttribute);
|
|
67
|
+
},
|
|
68
|
+
},
|
|
61
69
|
{
|
|
62
70
|
GATE: () => !hasDocs,
|
|
63
71
|
ALT: () => {
|
|
@@ -12,6 +12,7 @@ export function mcpServerTriggerDeclaration($) {
|
|
|
12
12
|
let hasActions = false;
|
|
13
13
|
let hasActive = false;
|
|
14
14
|
let hasDescription = false;
|
|
15
|
+
let hasGuid = false;
|
|
15
16
|
let hasHistory = false;
|
|
16
17
|
let hasInput = false;
|
|
17
18
|
let hasMcpServer = false;
|
|
@@ -69,6 +70,13 @@ export function mcpServerTriggerDeclaration($) {
|
|
|
69
70
|
$.SUBRULE($.descriptionFieldAttribute);
|
|
70
71
|
},
|
|
71
72
|
},
|
|
73
|
+
{
|
|
74
|
+
GATE: () => !hasGuid,
|
|
75
|
+
ALT: () => {
|
|
76
|
+
hasGuid = true;
|
|
77
|
+
$.SUBRULE($.guidFieldAttribute);
|
|
78
|
+
},
|
|
79
|
+
},
|
|
72
80
|
{
|
|
73
81
|
GATE: () => !hasHistory,
|
|
74
82
|
ALT: () => {
|
|
@@ -11,6 +11,7 @@ import { Identifier, NewlineToken } from "../lexer/tokens.js";
|
|
|
11
11
|
export function middlewareDeclaration($) {
|
|
12
12
|
return () => {
|
|
13
13
|
let hasDescription = false;
|
|
14
|
+
let hasGuid = false;
|
|
14
15
|
let hasDocs = false;
|
|
15
16
|
let hasExceptionPolicy = false;
|
|
16
17
|
let hasHistory = false;
|
|
@@ -43,6 +44,13 @@ export function middlewareDeclaration($) {
|
|
|
43
44
|
$.SUBRULE($.descriptionFieldAttribute);
|
|
44
45
|
},
|
|
45
46
|
},
|
|
47
|
+
{
|
|
48
|
+
GATE: () => !hasGuid,
|
|
49
|
+
ALT: () => {
|
|
50
|
+
hasGuid = true;
|
|
51
|
+
$.SUBRULE($.guidFieldAttribute);
|
|
52
|
+
},
|
|
53
|
+
},
|
|
46
54
|
{
|
|
47
55
|
GATE: () => !hasDocs,
|
|
48
56
|
ALT: () => {
|
package/parser/parser.js
CHANGED
|
@@ -15,6 +15,7 @@ export function xanoscriptParser(text, scheme, preTokenized = null) {
|
|
|
15
15
|
scheme = getSchemeFromContent(text);
|
|
16
16
|
}
|
|
17
17
|
const lexResult = preTokenized || lexDocument(text);
|
|
18
|
+
parser.reset();
|
|
18
19
|
parser.input = lexResult.tokens;
|
|
19
20
|
switch (scheme.toLowerCase()) {
|
|
20
21
|
case "addon":
|
package/parser/query_parser.js
CHANGED
|
@@ -20,6 +20,7 @@ export function queryDeclaration($) {
|
|
|
20
20
|
let hasAuth = false;
|
|
21
21
|
let hasCache = false;
|
|
22
22
|
let hasDescription = false;
|
|
23
|
+
let hasGuid = false;
|
|
23
24
|
let hasDocs = false;
|
|
24
25
|
let hasHistory = false;
|
|
25
26
|
let hasInput = false;
|
|
@@ -101,6 +102,13 @@ export function queryDeclaration($) {
|
|
|
101
102
|
$.SUBRULE($.descriptionFieldAttribute);
|
|
102
103
|
},
|
|
103
104
|
},
|
|
105
|
+
{
|
|
106
|
+
GATE: () => !hasGuid,
|
|
107
|
+
ALT: () => {
|
|
108
|
+
hasGuid = true;
|
|
109
|
+
$.SUBRULE($.guidFieldAttribute);
|
|
110
|
+
},
|
|
111
|
+
},
|
|
104
112
|
{
|
|
105
113
|
GATE: () => !hasApiGroup,
|
|
106
114
|
ALT: () => {
|
|
@@ -13,6 +13,7 @@ export function realtimeTriggerDeclaration($) {
|
|
|
13
13
|
let hasActive = false;
|
|
14
14
|
let hasChannel = false;
|
|
15
15
|
let hasDescription = false;
|
|
16
|
+
let hasGuid = false;
|
|
16
17
|
let hasHistory = false;
|
|
17
18
|
let hasInput = false;
|
|
18
19
|
let hasResponse = false;
|
|
@@ -75,6 +76,13 @@ export function realtimeTriggerDeclaration($) {
|
|
|
75
76
|
$.SUBRULE($.descriptionFieldAttribute);
|
|
76
77
|
},
|
|
77
78
|
},
|
|
79
|
+
{
|
|
80
|
+
GATE: () => !hasGuid,
|
|
81
|
+
ALT: () => {
|
|
82
|
+
hasGuid = true;
|
|
83
|
+
$.SUBRULE($.guidFieldAttribute);
|
|
84
|
+
},
|
|
85
|
+
},
|
|
78
86
|
{
|
|
79
87
|
GATE: () => !hasHistory,
|
|
80
88
|
ALT: () => {
|
package/parser/table_parser.js
CHANGED
|
@@ -44,6 +44,7 @@ export function tableDeclaration($) {
|
|
|
44
44
|
let hasView = false;
|
|
45
45
|
let hasAuth = false;
|
|
46
46
|
let hasDescription = false;
|
|
47
|
+
let hasGuid = false;
|
|
47
48
|
let hasTags = false;
|
|
48
49
|
let hasAutoComplete = false;
|
|
49
50
|
let hasIndex = false;
|
|
@@ -62,6 +63,10 @@ export function tableDeclaration($) {
|
|
|
62
63
|
GATE: () => !hasDescription,
|
|
63
64
|
ALT: () => $.SUBRULE($.descriptionFieldAttribute),
|
|
64
65
|
},
|
|
66
|
+
{
|
|
67
|
+
GATE: () => !hasGuid,
|
|
68
|
+
ALT: () => $.SUBRULE($.guidFieldAttribute),
|
|
69
|
+
},
|
|
65
70
|
{
|
|
66
71
|
GATE: () => !hasTags,
|
|
67
72
|
ALT: () => $.SUBRULE($.tagsAttribute),
|
|
@@ -15,6 +15,7 @@ export function tableTriggerDeclaration($) {
|
|
|
15
15
|
let hasDatasources = false;
|
|
16
16
|
let hasDbTable = false;
|
|
17
17
|
let hasDescription = false;
|
|
18
|
+
let hasGuid = false;
|
|
18
19
|
let hasHistory = false;
|
|
19
20
|
let hasInput = false;
|
|
20
21
|
let hasStack = false;
|
|
@@ -91,6 +92,13 @@ export function tableTriggerDeclaration($) {
|
|
|
91
92
|
$.SUBRULE($.descriptionFieldAttribute);
|
|
92
93
|
},
|
|
93
94
|
},
|
|
95
|
+
{
|
|
96
|
+
GATE: () => !hasGuid,
|
|
97
|
+
ALT: () => {
|
|
98
|
+
hasGuid = true;
|
|
99
|
+
$.SUBRULE($.guidFieldAttribute);
|
|
100
|
+
},
|
|
101
|
+
},
|
|
94
102
|
{
|
|
95
103
|
GATE: () => !hasHistory,
|
|
96
104
|
ALT: () => {
|
package/parser/task_parser.js
CHANGED
|
@@ -9,6 +9,7 @@ export function taskDeclaration($) {
|
|
|
9
9
|
let hasActive = false;
|
|
10
10
|
let hasDataSource = false;
|
|
11
11
|
let hasDescription = false;
|
|
12
|
+
let hasGuid = false;
|
|
12
13
|
let hasDocs = false;
|
|
13
14
|
let hasHistory = false;
|
|
14
15
|
let hasMiddleware = false;
|
|
@@ -57,6 +58,13 @@ export function taskDeclaration($) {
|
|
|
57
58
|
$.SUBRULE($.descriptionFieldAttribute);
|
|
58
59
|
},
|
|
59
60
|
},
|
|
61
|
+
{
|
|
62
|
+
GATE: () => !hasGuid,
|
|
63
|
+
ALT: () => {
|
|
64
|
+
hasGuid = true;
|
|
65
|
+
$.SUBRULE($.guidFieldAttribute);
|
|
66
|
+
},
|
|
67
|
+
},
|
|
60
68
|
{
|
|
61
69
|
GATE: () => !hasDocs,
|
|
62
70
|
ALT: () => {
|
package/parser/tool_parser.js
CHANGED
|
@@ -6,6 +6,7 @@ import { InstructionsToken, ToolToken } from "../lexer/tool.js";
|
|
|
6
6
|
export function toolDeclaration($) {
|
|
7
7
|
return () => {
|
|
8
8
|
let hasDescription = false;
|
|
9
|
+
let hasGuid = false;
|
|
9
10
|
let hasDocs = false;
|
|
10
11
|
let hasHistory = false;
|
|
11
12
|
let hasInput = false;
|
|
@@ -36,6 +37,13 @@ export function toolDeclaration($) {
|
|
|
36
37
|
$.SUBRULE($.descriptionFieldAttribute);
|
|
37
38
|
},
|
|
38
39
|
},
|
|
40
|
+
{
|
|
41
|
+
GATE: () => !hasGuid,
|
|
42
|
+
ALT: () => {
|
|
43
|
+
hasGuid = true;
|
|
44
|
+
$.SUBRULE($.guidFieldAttribute);
|
|
45
|
+
},
|
|
46
|
+
},
|
|
39
47
|
{
|
|
40
48
|
GATE: () => !hasDocs,
|
|
41
49
|
ALT: () => {
|
|
@@ -7,6 +7,7 @@ export function workflowTestDeclaration($) {
|
|
|
7
7
|
return () => {
|
|
8
8
|
let hasDatasource = false;
|
|
9
9
|
let hasDescription = false;
|
|
10
|
+
let hasGuid = false;
|
|
10
11
|
let hasStack = false;
|
|
11
12
|
let hasTags = false;
|
|
12
13
|
|
|
@@ -40,14 +41,19 @@ export function workflowTestDeclaration($) {
|
|
|
40
41
|
$.SUBRULE($.descriptionFieldAttribute);
|
|
41
42
|
},
|
|
42
43
|
},
|
|
44
|
+
{
|
|
45
|
+
GATE: () => !hasGuid,
|
|
46
|
+
ALT: () => {
|
|
47
|
+
hasGuid = true;
|
|
48
|
+
$.SUBRULE($.guidFieldAttribute);
|
|
49
|
+
},
|
|
50
|
+
},
|
|
43
51
|
{
|
|
44
52
|
GATE: () => !hasStack,
|
|
45
53
|
ALT: () => {
|
|
46
54
|
hasStack = true;
|
|
47
55
|
$.SUBRULE($.stackClause, {
|
|
48
|
-
ARGS: [
|
|
49
|
-
{ allowCallStatements: true },
|
|
50
|
-
],
|
|
56
|
+
ARGS: [{ allowCallStatements: true }],
|
|
51
57
|
});
|
|
52
58
|
},
|
|
53
59
|
},
|
|
@@ -39,8 +39,10 @@ describe("workflow_test_parser", () => {
|
|
|
39
39
|
input = {a: 5, b: 10}
|
|
40
40
|
} as $sums
|
|
41
41
|
|
|
42
|
-
expect.to_equal ($sum
|
|
43
|
-
|
|
42
|
+
expect.to_equal ($sum
|
|
43
|
+
|subtract:5
|
|
44
|
+
) {
|
|
45
|
+
value = 10
|
|
44
46
|
}
|
|
45
47
|
}
|
|
46
48
|
}`);
|
|
@@ -95,6 +97,10 @@ describe("workflow_test_parser", () => {
|
|
|
95
97
|
value = 42
|
|
96
98
|
}
|
|
97
99
|
|
|
100
|
+
function.run woot {
|
|
101
|
+
input = {scope: 12}
|
|
102
|
+
} as $my_val
|
|
103
|
+
|
|
98
104
|
group {
|
|
99
105
|
stack {
|
|
100
106
|
function.call "add" {
|
|
@@ -113,6 +119,11 @@ describe("workflow_test_parser", () => {
|
|
|
113
119
|
input = {a: 20, b: 22}
|
|
114
120
|
} as $sum
|
|
115
121
|
|
|
122
|
+
api.call "users/{user_id}" verb=GET {
|
|
123
|
+
api_group = "account"
|
|
124
|
+
input = { user_id: 123 }
|
|
125
|
+
} as $user
|
|
126
|
+
|
|
116
127
|
expect.to_equal ($a) {
|
|
117
128
|
value = 42
|
|
118
129
|
}
|
|
@@ -12,6 +12,7 @@ export function workspaceTriggerDeclaration($) {
|
|
|
12
12
|
let hasActions = false;
|
|
13
13
|
let hasActive = false;
|
|
14
14
|
let hasDescription = false;
|
|
15
|
+
let hasGuid = false;
|
|
15
16
|
let hasHistory = false;
|
|
16
17
|
let hasInput = false;
|
|
17
18
|
let hasStack = false;
|
|
@@ -67,6 +68,13 @@ export function workspaceTriggerDeclaration($) {
|
|
|
67
68
|
$.SUBRULE($.descriptionFieldAttribute);
|
|
68
69
|
},
|
|
69
70
|
},
|
|
71
|
+
{
|
|
72
|
+
GATE: () => !hasGuid,
|
|
73
|
+
ALT: () => {
|
|
74
|
+
hasGuid = true;
|
|
75
|
+
$.SUBRULE($.guidFieldAttribute);
|
|
76
|
+
},
|
|
77
|
+
},
|
|
70
78
|
{
|
|
71
79
|
GATE: () => !hasHistory,
|
|
72
80
|
ALT: () => {
|