@xano/xanoscript-language-server 12.21.0 → 12.22.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.
- package/lexer/db.js +8 -0
- package/lexer/realtime.js +8 -0
- package/onHover/functions.md +25 -0
- package/package.json +1 -1
- package/parser/agent_parser.js +3 -1
- package/parser/attributes/descriptionFieldAttribute.js +8 -1
- package/parser/attributes/docsFieldAttribute.js +8 -1
- package/parser/branch_parser.js +25 -9
- package/parser/channel_parser.js +9 -0
- package/parser/clauses/flexibleStringClause.js +8 -1
- package/parser/clauses/mcpMetadataClauses.js +8 -2
- package/parser/clauses/nakedStackFn.js +4 -0
- package/parser/clauses/viewClause.js +8 -4
- package/parser/definitions/columnDefinition.js +7 -2
- package/parser/definitions/enumColumnDefinition.js +7 -2
- package/parser/definitions/objectColumnDefinition.js +7 -2
- package/parser/error_trigger_parser.js +13 -1
- package/parser/functions/controls/switchCaseFn.js +3 -0
- package/parser/functions/db/dbAddFn.js +2 -1
- package/parser/functions/db/dbAddOrEditFn.js +2 -1
- package/parser/functions/db/dbEditFn.js +2 -1
- package/parser/functions/db/dbExternalFn.js +2 -0
- package/parser/functions/realtime/realtimeKickFn.js +35 -0
- package/parser/functions/realtime/register.js +2 -0
- package/parser/functions/realtimeFn.js +1 -0
- package/parser/functions/register.js +2 -0
- package/parser/functions/storage/storageCreateImageFn.js +3 -1
- package/parser/functions/workflowTestCallFn.js +47 -0
- package/parser/generic/stringArray.js +9 -2
- package/parser/mcp_server_trigger_parser.js +3 -1
package/lexer/db.js
CHANGED
|
@@ -145,6 +145,12 @@ export const OracleToken = createTokenByName("oracle", {
|
|
|
145
145
|
categories: [Identifier],
|
|
146
146
|
});
|
|
147
147
|
|
|
148
|
+
// db.external.snowflake
|
|
149
|
+
export const SnowflakeToken = createTokenByName("snowflake", {
|
|
150
|
+
longer_alt: Identifier,
|
|
151
|
+
categories: [Identifier],
|
|
152
|
+
});
|
|
153
|
+
|
|
148
154
|
export const DbTokens = [
|
|
149
155
|
DbToken,
|
|
150
156
|
QueryToken,
|
|
@@ -169,6 +175,7 @@ export const DbTokens = [
|
|
|
169
175
|
MysqlToken,
|
|
170
176
|
PostgresToken,
|
|
171
177
|
OracleToken,
|
|
178
|
+
SnowflakeToken,
|
|
172
179
|
WhereToken,
|
|
173
180
|
];
|
|
174
181
|
|
|
@@ -183,6 +190,7 @@ export function mapTokenToType(token) {
|
|
|
183
190
|
case MysqlToken.name:
|
|
184
191
|
case PostgresToken.name:
|
|
185
192
|
case OracleToken.name:
|
|
193
|
+
case SnowflakeToken.name:
|
|
186
194
|
case QueryToken.name:
|
|
187
195
|
case GetToken.name:
|
|
188
196
|
case AddToken.name:
|
package/lexer/realtime.js
CHANGED
|
@@ -19,10 +19,17 @@ export const PublishToken = createTokenByName("publish", {
|
|
|
19
19
|
categories: [Identifier],
|
|
20
20
|
});
|
|
21
21
|
|
|
22
|
+
// kick
|
|
23
|
+
export const KickToken = createTokenByName("kick", {
|
|
24
|
+
longer_alt: Identifier,
|
|
25
|
+
categories: [Identifier],
|
|
26
|
+
});
|
|
27
|
+
|
|
22
28
|
export const RealtimeStatementTokens = [
|
|
23
29
|
RealtimeToken,
|
|
24
30
|
GetSessionToken,
|
|
25
31
|
PublishToken,
|
|
32
|
+
KickToken,
|
|
26
33
|
];
|
|
27
34
|
|
|
28
35
|
export function mapTokenToType(token) {
|
|
@@ -30,6 +37,7 @@ export function mapTokenToType(token) {
|
|
|
30
37
|
case RealtimeToken.name:
|
|
31
38
|
case GetSessionToken.name:
|
|
32
39
|
case PublishToken.name:
|
|
40
|
+
case KickToken.name:
|
|
33
41
|
return "function";
|
|
34
42
|
default:
|
|
35
43
|
return null;
|
package/onHover/functions.md
CHANGED
|
@@ -460,6 +460,18 @@ trigger.call "onNewOrder" {
|
|
|
460
460
|
|
|
461
461
|
- `trigger.call` can call any trigger defined within your workspace, table, realtime, agentic or API triggers.
|
|
462
462
|
|
|
463
|
+
# workflow_test.call
|
|
464
|
+
|
|
465
|
+
```xs
|
|
466
|
+
workflow_test.call "sign up" {
|
|
467
|
+
datasource = "test"
|
|
468
|
+
} as $signup
|
|
469
|
+
```
|
|
470
|
+
|
|
471
|
+
**Note:** `workflow_test.call` is only available within `workflow_test`.
|
|
472
|
+
|
|
473
|
+
Runs another workflow test as a step of this one, so a chain of tests can build on each other. `datasource` optionally picks the datasource the called test runs against.
|
|
474
|
+
|
|
463
475
|
# tool.call
|
|
464
476
|
|
|
465
477
|
```xs
|
|
@@ -1167,6 +1179,19 @@ db.external.postgres.direct_query {
|
|
|
1167
1179
|
|
|
1168
1180
|
Performs a SQL query directly on an external PostgreSQL database. The `response_type` indicates if the result is a `list` or a `single` record. The `connection_string` establishes the database connection, and `arg` provides values for placeholders. The results are stored in the `as` variable, here `$recent_customers`.
|
|
1169
1181
|
|
|
1182
|
+
# db.external.snowflake.direct_query
|
|
1183
|
+
|
|
1184
|
+
```xs
|
|
1185
|
+
db.external.snowflake.direct_query {
|
|
1186
|
+
sql = "SELECT * FROM customers WHERE customers.last_purchase > ?"
|
|
1187
|
+
response_type = "list"
|
|
1188
|
+
connection_string = $env.SNOWFLAKE_CONNECTION_STRING
|
|
1189
|
+
arg = $input.date_threshold
|
|
1190
|
+
} as $recent_customers
|
|
1191
|
+
```
|
|
1192
|
+
|
|
1193
|
+
Performs a SQL query directly on an external Snowflake database. The `response_type` indicates if the result is a `list` or a `single` record. The `connection_string` establishes the database connection, and `arg` provides values for placeholders. The results are stored in the `as` variable, here `$recent_customers`.
|
|
1194
|
+
|
|
1170
1195
|
# debug.stop
|
|
1171
1196
|
|
|
1172
1197
|
```xs
|
package/package.json
CHANGED
package/parser/agent_parser.js
CHANGED
|
@@ -206,7 +206,9 @@ export function agentDeclaration($) {
|
|
|
206
206
|
"api_version?": "[string]",
|
|
207
207
|
"baseURL?": "[string]",
|
|
208
208
|
"compatibility?": ["strict", "compatible", ""],
|
|
209
|
-
|
|
209
|
+
// Free-form JSON text in the Studio form, written back
|
|
210
|
+
// verbatim, so any string is valid.
|
|
211
|
+
"dynamic_retrival?": "[string]",
|
|
210
212
|
"headers?": "[string]",
|
|
211
213
|
"include_thoughts?": "[boolean]",
|
|
212
214
|
"model?": "[string]",
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { EqualToken } from "../../lexer/control.js";
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
SingleQuotedStringLiteral,
|
|
4
|
+
StringLiteral,
|
|
5
|
+
} from "../../lexer/literal.js";
|
|
3
6
|
import { MultiLineStringToken } from "../../lexer/multiline.js";
|
|
4
7
|
import { DescriptionToken } from "../../lexer/tokens.js";
|
|
5
8
|
|
|
@@ -15,6 +18,10 @@ export function descriptionFieldAttribute($) {
|
|
|
15
18
|
{
|
|
16
19
|
ALT: () => $.CONSUME(StringLiteral), // e.g., "A description of the field"
|
|
17
20
|
},
|
|
21
|
+
{
|
|
22
|
+
// the platform writes a value holding a double quote as '...'
|
|
23
|
+
ALT: () => $.CONSUME(SingleQuotedStringLiteral),
|
|
24
|
+
},
|
|
18
25
|
{
|
|
19
26
|
ALT: () => $.CONSUME(MultiLineStringToken), // e.g., "fieldDescription"
|
|
20
27
|
},
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { EqualToken } from "../../lexer/control.js";
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
SingleQuotedStringLiteral,
|
|
4
|
+
StringLiteral,
|
|
5
|
+
} from "../../lexer/literal.js";
|
|
3
6
|
import { MultiLineStringToken } from "../../lexer/multiline.js";
|
|
4
7
|
import { DocsToken } from "../../lexer/tokens.js";
|
|
5
8
|
|
|
@@ -15,6 +18,10 @@ export function docsFieldAttribute($) {
|
|
|
15
18
|
{
|
|
16
19
|
ALT: () => $.CONSUME(StringLiteral), // e.g., "A docs of the field"
|
|
17
20
|
},
|
|
21
|
+
{
|
|
22
|
+
// the platform writes a value holding a double quote as '...'
|
|
23
|
+
ALT: () => $.CONSUME(SingleQuotedStringLiteral),
|
|
24
|
+
},
|
|
18
25
|
{
|
|
19
26
|
ALT: () => $.CONSUME(MultiLineStringToken), // e.g., "fieldDocs"
|
|
20
27
|
},
|
package/parser/branch_parser.js
CHANGED
|
@@ -15,30 +15,46 @@ export function branchDeclaration($) {
|
|
|
15
15
|
]);
|
|
16
16
|
$.validateDeclarationName(nameToken, "branch");
|
|
17
17
|
|
|
18
|
+
// Mirrors the branch document the platform writes on pull. A middleware
|
|
19
|
+
// entry carries `active: false` when the middleware is disabled.
|
|
20
|
+
const middlewareEntry = { name: "[string]", "active?": "[boolean]" };
|
|
18
21
|
const middlewareSchema = {
|
|
19
|
-
pre: [
|
|
20
|
-
post: [
|
|
22
|
+
pre: [middlewareEntry],
|
|
23
|
+
post: [middlewareEntry],
|
|
21
24
|
};
|
|
22
25
|
|
|
26
|
+
// `0` is a valid limit, and the backend writes `task: 0` (not `false`)
|
|
27
|
+
// for a disabled task history.
|
|
28
|
+
const historyValues = [false, 0, 10, 100, 1000, 10000, "all"];
|
|
29
|
+
|
|
23
30
|
$.SUBRULE($.schemaParseAttributeFn, {
|
|
24
31
|
ARGS: [
|
|
25
32
|
parent,
|
|
26
33
|
{
|
|
27
|
-
color: "[string]",
|
|
28
34
|
"description?": "[string]",
|
|
35
|
+
// Omitted by the backend when the branch has no color.
|
|
36
|
+
"color?": "[string]",
|
|
29
37
|
middleware: {
|
|
30
38
|
function: middlewareSchema,
|
|
31
39
|
query: middlewareSchema,
|
|
32
40
|
task: middlewareSchema,
|
|
33
41
|
tool: middlewareSchema,
|
|
42
|
+
// Always written by the platform.
|
|
43
|
+
"message?": middlewareSchema,
|
|
44
|
+
// Written only when the branch has some.
|
|
45
|
+
"prompt?": middlewareSchema,
|
|
46
|
+
"resource?": middlewareSchema,
|
|
34
47
|
},
|
|
35
48
|
history: {
|
|
36
|
-
function:
|
|
37
|
-
query:
|
|
38
|
-
task:
|
|
39
|
-
tool:
|
|
40
|
-
trigger:
|
|
41
|
-
middleware:
|
|
49
|
+
function: historyValues,
|
|
50
|
+
query: historyValues,
|
|
51
|
+
task: historyValues,
|
|
52
|
+
tool: historyValues,
|
|
53
|
+
trigger: historyValues,
|
|
54
|
+
middleware: historyValues,
|
|
55
|
+
// Written only when they differ from the default.
|
|
56
|
+
"prompt?": historyValues,
|
|
57
|
+
"resource?": historyValues,
|
|
42
58
|
},
|
|
43
59
|
},
|
|
44
60
|
],
|
package/parser/channel_parser.js
CHANGED
|
@@ -22,6 +22,7 @@ export function channelDeclaration($) {
|
|
|
22
22
|
let hasMiddleware = false;
|
|
23
23
|
let hasInput = false;
|
|
24
24
|
let hasDescription = false;
|
|
25
|
+
let hasDocs = false;
|
|
25
26
|
let hasGuid = false;
|
|
26
27
|
let hasTags = false;
|
|
27
28
|
let hasActive = false;
|
|
@@ -156,6 +157,14 @@ export function channelDeclaration($) {
|
|
|
156
157
|
$.SUBRULE($.inputClause);
|
|
157
158
|
},
|
|
158
159
|
},
|
|
160
|
+
{
|
|
161
|
+
// written by the platform when the channel has docs
|
|
162
|
+
GATE: () => !hasDocs,
|
|
163
|
+
ALT: () => {
|
|
164
|
+
hasDocs = true;
|
|
165
|
+
$.SUBRULE($.docsFieldAttribute);
|
|
166
|
+
},
|
|
167
|
+
},
|
|
159
168
|
{
|
|
160
169
|
GATE: () => !hasDescription,
|
|
161
170
|
ALT: () => {
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { EqualToken } from "../../lexer/control.js";
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
SingleQuotedStringLiteral,
|
|
4
|
+
StringLiteral,
|
|
5
|
+
} from "../../lexer/literal.js";
|
|
3
6
|
import { MultiLineStringToken } from "../../lexer/multiline.js";
|
|
4
7
|
import { InstructionsToken } from "../../lexer/tool.js";
|
|
5
8
|
|
|
@@ -26,6 +29,10 @@ export function flexibleStringClause($) {
|
|
|
26
29
|
{
|
|
27
30
|
ALT: () => $.CONSUME(StringLiteral), // e.g., "A instructions of the field"
|
|
28
31
|
},
|
|
32
|
+
{
|
|
33
|
+
// the platform writes a value holding a double quote as '...'
|
|
34
|
+
ALT: () => $.CONSUME(SingleQuotedStringLiteral),
|
|
35
|
+
},
|
|
29
36
|
{
|
|
30
37
|
ALT: () => $.CONSUME(MultiLineStringToken), // e.g., "fieldinstructions"
|
|
31
38
|
},
|
|
@@ -103,8 +103,14 @@ export function mcpTitleClause($) {
|
|
|
103
103
|
$.sectionStack.push("mcpTitleClause");
|
|
104
104
|
$.CONSUME(TitleToken); // "title"
|
|
105
105
|
$.CONSUME(EqualToken); // "="
|
|
106
|
-
|
|
107
|
-
|
|
106
|
+
// The platform writes a title holding a double quote as '...' and one
|
|
107
|
+
// holding a newline as a """ block.
|
|
108
|
+
const value = $.OR([
|
|
109
|
+
{ ALT: () => $.CONSUME(StringLiteral) }, // "..."
|
|
110
|
+
{ ALT: () => $.CONSUME(SingleQuotedStringLiteral) }, // '...'
|
|
111
|
+
{ ALT: () => $.CONSUME(MultiLineStringToken) }, // """..."""
|
|
112
|
+
]);
|
|
113
|
+
if (stringValue(value) === "") {
|
|
108
114
|
$.addMissingError(value, "title cannot be empty");
|
|
109
115
|
}
|
|
110
116
|
$.sectionStack.pop();
|
|
@@ -71,6 +71,10 @@ export function nakedStackFn($) {
|
|
|
71
71
|
GATE: () => options.allowCallStatements,
|
|
72
72
|
ALT: () => $.SUBRULE($.triggerCallFn),
|
|
73
73
|
},
|
|
74
|
+
{
|
|
75
|
+
GATE: () => options.allowCallStatements,
|
|
76
|
+
ALT: () => $.SUBRULE($.workflowTestCallFn),
|
|
77
|
+
},
|
|
74
78
|
{ ALT: () => $.SUBRULE($.workflowExpectFn) },
|
|
75
79
|
]);
|
|
76
80
|
// write-before-elicit lint (see functions/mcp/elicitLint.js)
|
|
@@ -22,10 +22,13 @@ export function viewClause($) {
|
|
|
22
22
|
ARGS: [
|
|
23
23
|
parent,
|
|
24
24
|
{
|
|
25
|
+
// The platform writes each of alias/term/search/sort/hide only
|
|
26
|
+
// when the view has it.
|
|
25
27
|
"[string]": {
|
|
28
|
+
"term?": "[string]",
|
|
26
29
|
"search?": "[query]",
|
|
27
30
|
"hide?": ["[string]"],
|
|
28
|
-
sort: { "[string]": ["asc", "desc", "rand"] },
|
|
31
|
+
"sort?": { "[string]": ["asc", "desc", "rand"] },
|
|
29
32
|
id: "[string]",
|
|
30
33
|
"alias?": "[string]",
|
|
31
34
|
},
|
|
@@ -34,14 +37,15 @@ export function viewClause($) {
|
|
|
34
37
|
],
|
|
35
38
|
});
|
|
36
39
|
|
|
37
|
-
// each key is the view name, ensure
|
|
40
|
+
// each key is the view name, ensure it has some criteria
|
|
38
41
|
for (const viewName in captured) {
|
|
42
|
+
const hasTerm = !!captured[viewName].term;
|
|
39
43
|
const hasSearch = !!captured[viewName].search;
|
|
40
44
|
const hasSort = !!captured[viewName].sort;
|
|
41
|
-
if (!hasSearch && !hasSort && !captured[viewName].hide) {
|
|
45
|
+
if (!hasTerm && !hasSearch && !hasSort && !captured[viewName].hide) {
|
|
42
46
|
$.addMissingError(
|
|
43
47
|
parent,
|
|
44
|
-
`view ${viewName} must have either a search, sort or hide criteria`,
|
|
48
|
+
`view ${viewName} must have either a term, search, sort or hide criteria`,
|
|
45
49
|
);
|
|
46
50
|
}
|
|
47
51
|
}
|
|
@@ -24,6 +24,7 @@ import {
|
|
|
24
24
|
} from "../../lexer/columns.js";
|
|
25
25
|
import { Question } from "../../lexer/control.js";
|
|
26
26
|
import { Identifier } from "../../lexer/identifier.js";
|
|
27
|
+
import { StringLiteral } from "../../lexer/literal.js";
|
|
27
28
|
import { getVarName } from "../generic/utils.js";
|
|
28
29
|
|
|
29
30
|
const DEFAULT_CONFIG = {
|
|
@@ -87,7 +88,11 @@ export function columnDefinition($) {
|
|
|
87
88
|
});
|
|
88
89
|
|
|
89
90
|
// Column name (e.g., "user_id", "optional_column", "required_column")
|
|
90
|
-
|
|
91
|
+
// A name that is not an identifier (e.g. `created-at`) is written quoted.
|
|
92
|
+
const nameToken = $.OR9([
|
|
93
|
+
{ ALT: () => $.CONSUME(Identifier) },
|
|
94
|
+
{ ALT: () => $.CONSUME(StringLiteral) },
|
|
95
|
+
]);
|
|
91
96
|
|
|
92
97
|
// ? (is it an optional field)
|
|
93
98
|
$.OPTION6(() => {
|
|
@@ -147,7 +152,7 @@ export function columnDefinition($) {
|
|
|
147
152
|
|
|
148
153
|
// add the variable to the registry
|
|
149
154
|
$.addInput(
|
|
150
|
-
nameToken
|
|
155
|
+
getVarName(nameToken),
|
|
151
156
|
inputTypeToken.image,
|
|
152
157
|
iterable,
|
|
153
158
|
nullable,
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { EnumToken } from "../../lexer/columns.js";
|
|
2
2
|
import { Question } from "../../lexer/control.js";
|
|
3
3
|
import { Identifier } from "../../lexer/identifier.js";
|
|
4
|
+
import { StringLiteral } from "../../lexer/literal.js";
|
|
4
5
|
import { getVarName } from "../generic/utils.js";
|
|
5
6
|
|
|
6
7
|
/**
|
|
@@ -30,7 +31,11 @@ export function enumColumnDefinition($) {
|
|
|
30
31
|
}); // ? (optional field)
|
|
31
32
|
|
|
32
33
|
// Column name (e.g., "user_id", "optional_column", "required_column")
|
|
33
|
-
|
|
34
|
+
// A name that is not an identifier (e.g. `created-at`) is written quoted.
|
|
35
|
+
const name = $.OR9([
|
|
36
|
+
{ ALT: () => $.CONSUME(Identifier) },
|
|
37
|
+
{ ALT: () => $.CONSUME(StringLiteral) },
|
|
38
|
+
]);
|
|
34
39
|
|
|
35
40
|
// add variable to schema
|
|
36
41
|
schema[getVarName(name)] = {
|
|
@@ -49,7 +54,7 @@ export function enumColumnDefinition($) {
|
|
|
49
54
|
$.SUBRULE($.enumColumnMetadataDefinition, { ARGS: [parent] }); // Enum metadata
|
|
50
55
|
|
|
51
56
|
// add the variable to the registry
|
|
52
|
-
$.addInput(name
|
|
57
|
+
$.addInput(getVarName(name), "enum", iterable, nullable, optional);
|
|
53
58
|
|
|
54
59
|
$.sectionStack.pop();
|
|
55
60
|
};
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { ObjectToken } from "../../lexer/columns.js";
|
|
2
2
|
import { Question } from "../../lexer/control.js";
|
|
3
|
+
import { StringLiteral } from "../../lexer/literal.js";
|
|
3
4
|
import { Identifier } from "../../lexer/tokens.js";
|
|
4
5
|
import { getVarName } from "../generic/utils.js";
|
|
5
6
|
|
|
@@ -35,7 +36,11 @@ export function objectColumnDefinition($) {
|
|
|
35
36
|
});
|
|
36
37
|
|
|
37
38
|
// Column name (e.g., "user_id", "optional_column", "required_column")
|
|
38
|
-
|
|
39
|
+
// A name that is not an identifier (e.g. `created-at`) is written quoted.
|
|
40
|
+
const name = $.OR9([
|
|
41
|
+
{ ALT: () => $.CONSUME(Identifier) },
|
|
42
|
+
{ ALT: () => $.CONSUME(StringLiteral) },
|
|
43
|
+
]);
|
|
39
44
|
|
|
40
45
|
// add variable to schema
|
|
41
46
|
schema[getVarName(name)] = {
|
|
@@ -52,7 +57,7 @@ export function objectColumnDefinition($) {
|
|
|
52
57
|
|
|
53
58
|
$.SUBRULE($.objectColumnMetadataDefinition); // Object metadata
|
|
54
59
|
|
|
55
|
-
$.addInput(name
|
|
60
|
+
$.addInput(getVarName(name), "enum", iterable, nullable, optional);
|
|
56
61
|
|
|
57
62
|
$.sectionStack.pop();
|
|
58
63
|
};
|
|
@@ -1,10 +1,12 @@
|
|
|
1
|
-
import { LCurly, RCurly } from "../lexer/control.js";
|
|
1
|
+
import { EqualToken, LCurly, RCurly } from "../lexer/control.js";
|
|
2
2
|
import { ErrorTriggerToken } from "../lexer/error_trigger.js";
|
|
3
3
|
import { StringLiteral } from "../lexer/literal.js";
|
|
4
|
+
import { ActiveToken } from "../lexer/mcp_server_trigger.js";
|
|
4
5
|
import { Identifier, NewlineToken } from "../lexer/tokens.js";
|
|
5
6
|
|
|
6
7
|
export function errorTriggerDeclaration($) {
|
|
7
8
|
return () => {
|
|
9
|
+
let hasActive = false;
|
|
8
10
|
let hasDescription = false;
|
|
9
11
|
let hasGuid = false;
|
|
10
12
|
let hasHistory = false;
|
|
@@ -27,6 +29,16 @@ export function errorTriggerDeclaration($) {
|
|
|
27
29
|
$.AT_LEAST_ONE(() => $.CONSUME(NewlineToken)); // at least one new line
|
|
28
30
|
$.OR2([
|
|
29
31
|
{ ALT: () => $.SUBRULE($.commentBlockFn) },
|
|
32
|
+
{
|
|
33
|
+
// the platform writes `active = false` for a paused trigger
|
|
34
|
+
GATE: () => !hasActive,
|
|
35
|
+
ALT: () => {
|
|
36
|
+
hasActive = true;
|
|
37
|
+
$.CONSUME(ActiveToken);
|
|
38
|
+
$.CONSUME(EqualToken);
|
|
39
|
+
$.SUBRULE($.booleanValue);
|
|
40
|
+
},
|
|
41
|
+
},
|
|
30
42
|
{
|
|
31
43
|
GATE: () => !hasDescription,
|
|
32
44
|
ALT: () => {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { ExclamationToken } from "../../../lexer/cast.js";
|
|
1
2
|
import {
|
|
2
3
|
BreakToken,
|
|
3
4
|
CaseToken,
|
|
@@ -12,6 +13,8 @@ export function switchCaseFn($) {
|
|
|
12
13
|
return () => {
|
|
13
14
|
$.sectionStack.push("switchCaseFn");
|
|
14
15
|
$.SUBRULE($.optionalCommentBlockFn);
|
|
16
|
+
// a disabled case is written `!case`, like a disabled statement
|
|
17
|
+
$.OPTION2(() => $.CONSUME(ExclamationToken));
|
|
15
18
|
$.CONSUME(CaseToken); // "case"
|
|
16
19
|
$.CONSUME(LParent); // "("
|
|
17
20
|
$.SUBRULE($.expressionFn);
|
|
@@ -24,7 +24,8 @@ export function dbAddFn($) {
|
|
|
24
24
|
ARGS: [
|
|
25
25
|
fnToken,
|
|
26
26
|
{
|
|
27
|
-
data
|
|
27
|
+
// The platform writes `data = {}` when no field is set.
|
|
28
|
+
data: { "[string]?": "[expression]" },
|
|
28
29
|
"description?": "[string]",
|
|
29
30
|
"disabled?": "[boolean]",
|
|
30
31
|
"enforce_hidden_fields?": "[boolean]",
|
|
@@ -28,7 +28,8 @@ export function dbAddOrEditFn($) {
|
|
|
28
28
|
{
|
|
29
29
|
field_name: "[expression]",
|
|
30
30
|
field_value: "[expression]",
|
|
31
|
-
data
|
|
31
|
+
// The platform writes `data = {}` when no field is set.
|
|
32
|
+
data: { "[string]?": "[expression]" },
|
|
32
33
|
"description?": "[string]",
|
|
33
34
|
"disabled?": "[boolean]",
|
|
34
35
|
"enforce_hidden_fields?": "[boolean]",
|
|
@@ -28,7 +28,8 @@ export function dbEditFn($) {
|
|
|
28
28
|
{
|
|
29
29
|
field_name: "[expression]",
|
|
30
30
|
field_value: "[expression]",
|
|
31
|
-
data
|
|
31
|
+
// The platform writes `data = {}` when no field is set.
|
|
32
|
+
data: { "[string]?": "[expression]" },
|
|
32
33
|
"description?": "[string]",
|
|
33
34
|
"disabled?": "[boolean]",
|
|
34
35
|
"enforce_hidden_fields?": "[boolean]",
|
|
@@ -4,6 +4,7 @@ import {
|
|
|
4
4
|
MysqlToken,
|
|
5
5
|
OracleToken,
|
|
6
6
|
PostgresToken,
|
|
7
|
+
SnowflakeToken,
|
|
7
8
|
} from "../../../lexer/db.js";
|
|
8
9
|
import { DotToken } from "../../../lexer/tokens.js";
|
|
9
10
|
|
|
@@ -20,6 +21,7 @@ export function dbExternalFn($) {
|
|
|
20
21
|
{ ALT: () => $.CONSUME(MysqlToken) }, // "external.mysql"
|
|
21
22
|
{ ALT: () => $.CONSUME(OracleToken) }, // "external.oracle"
|
|
22
23
|
{ ALT: () => $.CONSUME(PostgresToken) }, // "external.postgres"
|
|
24
|
+
{ ALT: () => $.CONSUME(SnowflakeToken) }, // "external.snowflake"
|
|
23
25
|
]);
|
|
24
26
|
$.CONSUME1(DotToken); // "."
|
|
25
27
|
$.SUBRULE($.dbExternalDirectQueryFn); // "external.direct_query"
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { KickToken } from "../../../lexer/realtime.js";
|
|
2
|
+
|
|
3
|
+
// realtime.kick {
|
|
4
|
+
// realtime_server = "main"
|
|
5
|
+
// channel = "rooms/42"
|
|
6
|
+
// auth_table = "user" // the identity to remove
|
|
7
|
+
// auth_id = $input.user_id
|
|
8
|
+
// reason = "removed" // optional — sent to the removed connections
|
|
9
|
+
// }
|
|
10
|
+
//
|
|
11
|
+
// Takes one identity's sockets out of one realtime v2 channel
|
|
12
|
+
// (mvp:realtime_kick). A channel's join
|
|
13
|
+
// gate runs once, so this is the step that removes a member whose access was
|
|
14
|
+
// revoked. Each of their sockets leaves the channel and is told it was kicked;
|
|
15
|
+
// it stays connected to its other channels.
|
|
16
|
+
// The identity is required (a kick names who to remove), so auth_table and
|
|
17
|
+
// auth_id are required here, though the kind schema lists them optional the way
|
|
18
|
+
// realtime.publish does. Like realtime.publish it has NO `as $var` binding.
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* @param {import('../../base_parser.js').XanoBaseParser} $
|
|
22
|
+
*/
|
|
23
|
+
export function realtimeKickFn($) {
|
|
24
|
+
const requiredAttrs = ["realtime_server", "channel", "auth_table", "auth_id"];
|
|
25
|
+
const optionalAttrs = ["reason", "description", "disabled"];
|
|
26
|
+
|
|
27
|
+
return () => {
|
|
28
|
+
$.sectionStack.push("realtimeKickFn");
|
|
29
|
+
const fnToken = $.CONSUME(KickToken); // "kick"
|
|
30
|
+
$.SUBRULE($.functionAttrReq, {
|
|
31
|
+
ARGS: [fnToken, requiredAttrs, optionalAttrs],
|
|
32
|
+
});
|
|
33
|
+
$.sectionStack.pop();
|
|
34
|
+
};
|
|
35
|
+
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { realtimeGetSessionFn } from "./realtimeGetSessionFn.js";
|
|
2
|
+
import { realtimeKickFn } from "./realtimeKickFn.js";
|
|
2
3
|
import { realtimePublishFn } from "./realtimePublishFn.js";
|
|
3
4
|
|
|
4
5
|
/**
|
|
@@ -11,4 +12,5 @@ export const register = ($) => {
|
|
|
11
12
|
realtimeGetSessionFn($),
|
|
12
13
|
);
|
|
13
14
|
$.realtimePublishFn = $.RULE("realtimePublishFn", realtimePublishFn($));
|
|
15
|
+
$.realtimeKickFn = $.RULE("realtimeKickFn", realtimeKickFn($));
|
|
14
16
|
};
|
|
@@ -13,6 +13,7 @@ export function realtimeFn($) {
|
|
|
13
13
|
$.OR([
|
|
14
14
|
{ ALT: () => $.SUBRULE($.realtimeGetSessionFn) }, // "realtime.get_session"
|
|
15
15
|
{ ALT: () => $.SUBRULE($.realtimePublishFn) }, // "realtime.publish"
|
|
16
|
+
{ ALT: () => $.SUBRULE($.realtimeKickFn) }, // "realtime.kick"
|
|
16
17
|
]);
|
|
17
18
|
$.sectionStack.pop();
|
|
18
19
|
};
|
|
@@ -45,6 +45,7 @@ import { utilFn } from "./utilFn.js";
|
|
|
45
45
|
import { varFn } from "./varFn.js";
|
|
46
46
|
import { webflowFn } from "./webflowFn.js";
|
|
47
47
|
import { workflowExpectFn } from "./workflowExpectFn.js";
|
|
48
|
+
import { workflowTestCallFn } from "./workflowTestCallFn.js";
|
|
48
49
|
import { register as register_zip } from "./zip/register.js";
|
|
49
50
|
import { zipFn } from "./zipFn.js";
|
|
50
51
|
|
|
@@ -81,6 +82,7 @@ export const register = ($) => {
|
|
|
81
82
|
$.workflowExpectFn = $.RULE("workflowExpectFn", workflowExpectFn($));
|
|
82
83
|
$.toolCallFn = $.RULE("toolCallFn", toolCallFn($));
|
|
83
84
|
$.triggerCallFn = $.RULE("triggerCallFn", triggerCallFn($));
|
|
85
|
+
$.workflowTestCallFn = $.RULE("workflowTestCallFn", workflowTestCallFn($));
|
|
84
86
|
register_ai($);
|
|
85
87
|
register_schema($);
|
|
86
88
|
register_api($);
|
|
@@ -13,7 +13,9 @@ export function storageCreateImageFn($) {
|
|
|
13
13
|
{
|
|
14
14
|
value: "[expression]",
|
|
15
15
|
access: ["public", "private"],
|
|
16
|
-
|
|
16
|
+
// Optional, as on create_audio/create_video: the platform omits it
|
|
17
|
+
// when unset.
|
|
18
|
+
"filename?": "[expression]",
|
|
17
19
|
"description?": "[string]",
|
|
18
20
|
"disabled?": "[boolean]",
|
|
19
21
|
},
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { CallToken } from "../../lexer/function.js";
|
|
2
|
+
import { StringLiteral } from "../../lexer/literal.js";
|
|
3
|
+
import { DotToken, Identifier } from "../../lexer/tokens.js";
|
|
4
|
+
import { WorkflowTestToken } from "../../lexer/workflow_test.js";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* workflow_test.call "other test" { datasource = "test" } as $result
|
|
8
|
+
*
|
|
9
|
+
* Runs another workflow test as a step. The platform resolves the target
|
|
10
|
+
* only inside a workflow test.
|
|
11
|
+
* @param {import('../base_parser.js').XanoBaseParser} $
|
|
12
|
+
*/
|
|
13
|
+
export function workflowTestCallFn($) {
|
|
14
|
+
return () => {
|
|
15
|
+
$.sectionStack.push("workflowTestCallFn");
|
|
16
|
+
|
|
17
|
+
$.CONSUME(WorkflowTestToken); // "workflow_test"
|
|
18
|
+
$.CONSUME(DotToken); // "."
|
|
19
|
+
const fnToken = $.CONSUME(CallToken); // "call"
|
|
20
|
+
|
|
21
|
+
if (!$.sectionStack.includes("workflowTestDeclaration")) {
|
|
22
|
+
$.addInvalidValueError(
|
|
23
|
+
fnToken,
|
|
24
|
+
"workflow_test.call can only be used within workflow_test contexts",
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
$.OR([
|
|
29
|
+
{ ALT: () => $.CONSUME(StringLiteral) }, // "other test"
|
|
30
|
+
{ ALT: () => $.CONSUME(Identifier) }, // other_test
|
|
31
|
+
]);
|
|
32
|
+
|
|
33
|
+
$.SUBRULE($.schemaParseAttributeFn, {
|
|
34
|
+
ARGS: [
|
|
35
|
+
fnToken,
|
|
36
|
+
{
|
|
37
|
+
"datasource?": "[string]",
|
|
38
|
+
"description?": "[string]",
|
|
39
|
+
"disabled?": "[boolean]",
|
|
40
|
+
"mock?": { "![string]": "[expression]" },
|
|
41
|
+
},
|
|
42
|
+
],
|
|
43
|
+
});
|
|
44
|
+
$.SUBRULE($.asVariable, { ARGS: [fnToken] });
|
|
45
|
+
$.sectionStack.pop();
|
|
46
|
+
};
|
|
47
|
+
}
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { CommaToken, LSquare, RSquare } from "../../lexer/control.js";
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
SingleQuotedStringLiteral,
|
|
4
|
+
StringLiteral,
|
|
5
|
+
} from "../../lexer/literal.js";
|
|
3
6
|
import { NewlineToken } from "../../lexer/tokens.js";
|
|
4
7
|
|
|
5
8
|
/**
|
|
@@ -18,7 +21,11 @@ export const stringArray = ($) => () => {
|
|
|
18
21
|
"Missing comma or newline between string values in the array"
|
|
19
22
|
);
|
|
20
23
|
}
|
|
21
|
-
|
|
24
|
+
// '...' is how the platform writes a value holding a double quote
|
|
25
|
+
token = $.OR([
|
|
26
|
+
{ ALT: () => $.CONSUME(StringLiteral) },
|
|
27
|
+
{ ALT: () => $.CONSUME(SingleQuotedStringLiteral) },
|
|
28
|
+
]);
|
|
22
29
|
hasSeparator = false;
|
|
23
30
|
$.OPTION(() => {
|
|
24
31
|
$.CONSUME(CommaToken);
|
|
@@ -49,7 +49,9 @@ export function mcpServerTriggerDeclaration($) {
|
|
|
49
49
|
ARGS: [
|
|
50
50
|
subParent,
|
|
51
51
|
{
|
|
52
|
-
|
|
52
|
+
// Only the actions that are on are written, so a trigger
|
|
53
|
+
// with connection off is written as `actions = {}`.
|
|
54
|
+
"connection?": "[boolean]",
|
|
53
55
|
},
|
|
54
56
|
],
|
|
55
57
|
});
|