@xano/xanoscript-language-server 12.7.0 → 12.9.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/README.md +6 -2
- package/lexer/channel.js +72 -0
- package/lexer/channel_trigger.js +23 -0
- package/lexer/message.js +31 -0
- package/lexer/realtime.js +26 -0
- package/lexer/realtime_server_trigger.js +38 -0
- package/lexer/tokens.js +39 -0
- package/package.json +4 -1
- package/parser/channel_parser.js +189 -0
- package/parser/channel_trigger_parser.js +145 -0
- package/parser/clauses/indexClause.js +1 -0
- package/parser/clauses/nakedStackFn.js +1 -0
- package/parser/clauses/realtimeServerClause.js +23 -0
- package/parser/clauses/register.js +5 -0
- package/parser/functions/realtime/realtimeGetSessionFn.js +20 -0
- package/parser/functions/realtime/register.js +12 -0
- package/parser/functions/realtimeFn.js +18 -0
- package/parser/functions/register.js +4 -0
- package/parser/message_parser.js +161 -0
- package/parser/parser.js +12 -0
- package/parser/realtime_server_trigger_parser.js +145 -0
- package/parser/register.js +14 -0
- package/utils.js +4 -0
package/README.md
CHANGED
|
@@ -57,8 +57,12 @@ The parser supports the following XanoScript constructs:
|
|
|
57
57
|
| `mcp_server` | MCP server definitions |
|
|
58
58
|
| `mcp_server_trigger` | MCP server trigger handlers |
|
|
59
59
|
| `middleware` | Middleware definitions |
|
|
60
|
-
| `realtime_channel` | Realtime channel definitions |
|
|
61
|
-
| `realtime_trigger` | Realtime trigger handlers |
|
|
60
|
+
| `realtime_channel` | Realtime v1 channel definitions |
|
|
61
|
+
| `realtime_trigger` | Realtime v1 trigger handlers |
|
|
62
|
+
| `channel` | Realtime v2 channel definitions (path templates) |
|
|
63
|
+
| `message` | Realtime v2 channel message handlers |
|
|
64
|
+
| `channel_trigger` | Realtime v2 channel lifecycle triggers (join/leave) |
|
|
65
|
+
| `realtime_server_trigger` | Realtime v2 connection lifecycle triggers (connect/disconnect) |
|
|
62
66
|
| `tool` | Tool definitions |
|
|
63
67
|
| `workspace` | Workspace definitions |
|
|
64
68
|
| `workspace_trigger` | Workspace trigger handlers |
|
package/lexer/channel.js
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { ServerDetailsToken } from "./ai.js";
|
|
2
|
+
import { Identifier } from "./identifier.js";
|
|
3
|
+
import { createTokenByName } from "./utils.js";
|
|
4
|
+
|
|
5
|
+
// "server" (channel's realtime_server reference).
|
|
6
|
+
// longer_alt points at server_details so the longer keyword still wins.
|
|
7
|
+
export const ServerToken = createTokenByName("server", {
|
|
8
|
+
longer_alt: [ServerDetailsToken, Identifier],
|
|
9
|
+
categories: [Identifier], // could also be an identifier
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
// "access"
|
|
13
|
+
export const AccessToken = createTokenByName("access", {
|
|
14
|
+
longer_alt: Identifier,
|
|
15
|
+
categories: [Identifier], // could also be an identifier
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
// "publish"
|
|
19
|
+
export const PublishToken = createTokenByName("publish", {
|
|
20
|
+
longer_alt: Identifier,
|
|
21
|
+
categories: [Identifier], // could also be an identifier
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
// "conversation"
|
|
25
|
+
export const ConversationToken = createTokenByName("conversation", {
|
|
26
|
+
longer_alt: Identifier,
|
|
27
|
+
categories: [Identifier], // could also be an identifier
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
// "delivery"
|
|
31
|
+
export const DeliveryToken = createTokenByName("delivery", {
|
|
32
|
+
longer_alt: Identifier,
|
|
33
|
+
categories: [Identifier], // could also be an identifier
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
// "rate_limit"
|
|
37
|
+
export const RateLimitToken = createTokenByName("rate_limit", {
|
|
38
|
+
longer_alt: Identifier,
|
|
39
|
+
categories: [Identifier], // could also be an identifier
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
// ServerToken must be lexed AFTER `server_details` (a longer AI keyword) so it
|
|
43
|
+
// does not shadow it; the rest must be lexed BEFORE shorter-prefix keywords
|
|
44
|
+
// (e.g. `delivery` before `del`). They are therefore spread at two different
|
|
45
|
+
// points in the master token list — see lexer/tokens.js.
|
|
46
|
+
export const ChannelServerToken = ServerToken;
|
|
47
|
+
|
|
48
|
+
export const ChannelObjectTokens = [
|
|
49
|
+
AccessToken,
|
|
50
|
+
PublishToken,
|
|
51
|
+
ConversationToken,
|
|
52
|
+
DeliveryToken,
|
|
53
|
+
RateLimitToken,
|
|
54
|
+
];
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Maps a token name to a type
|
|
58
|
+
* @param {string} token the token name
|
|
59
|
+
*/
|
|
60
|
+
export function mapTokenToType(token) {
|
|
61
|
+
switch (token) {
|
|
62
|
+
case ServerToken.name:
|
|
63
|
+
case AccessToken.name:
|
|
64
|
+
case PublishToken.name:
|
|
65
|
+
case ConversationToken.name:
|
|
66
|
+
case DeliveryToken.name:
|
|
67
|
+
case RateLimitToken.name:
|
|
68
|
+
return "keyword";
|
|
69
|
+
default:
|
|
70
|
+
return null;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { Identifier } from "./identifier.js";
|
|
2
|
+
import { createTokenByName } from "./utils.js";
|
|
3
|
+
|
|
4
|
+
// "channel_trigger"
|
|
5
|
+
export const ChannelTriggerToken = createTokenByName("channel_trigger", {
|
|
6
|
+
longer_alt: Identifier,
|
|
7
|
+
categories: [Identifier], // could also be an identifier
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
export const ChannelTriggerTokens = [ChannelTriggerToken];
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Maps a token name to a type
|
|
14
|
+
* @param {string} token the token name
|
|
15
|
+
*/
|
|
16
|
+
export function mapTokenToType(token) {
|
|
17
|
+
switch (token) {
|
|
18
|
+
case ChannelTriggerToken.name:
|
|
19
|
+
return "keyword";
|
|
20
|
+
default:
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
}
|
package/lexer/message.js
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { Identifier } from "./identifier.js";
|
|
2
|
+
import { createTokenByName } from "./utils.js";
|
|
3
|
+
|
|
4
|
+
// "message"
|
|
5
|
+
export const MessageToken = createTokenByName("message", {
|
|
6
|
+
longer_alt: Identifier,
|
|
7
|
+
categories: [Identifier], // could also be an identifier
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
// "deliver_to"
|
|
11
|
+
export const DeliverToToken = createTokenByName("deliver_to", {
|
|
12
|
+
longer_alt: Identifier,
|
|
13
|
+
categories: [Identifier], // could also be an identifier
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
export const MessageTokens = [MessageToken, DeliverToToken];
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Maps a token name to a type
|
|
20
|
+
* @param {string} token the token name
|
|
21
|
+
*/
|
|
22
|
+
export function mapTokenToType(token) {
|
|
23
|
+
switch (token) {
|
|
24
|
+
case MessageToken.name:
|
|
25
|
+
return "keyword";
|
|
26
|
+
case DeliverToToken.name:
|
|
27
|
+
return "variable";
|
|
28
|
+
default:
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { Identifier } from "./identifier.js";
|
|
2
|
+
import { createTokenByName } from "./utils.js";
|
|
3
|
+
|
|
4
|
+
// realtime
|
|
5
|
+
export const RealtimeToken = createTokenByName("realtime", {
|
|
6
|
+
longer_alt: Identifier,
|
|
7
|
+
categories: [Identifier],
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
// get_session
|
|
11
|
+
export const GetSessionToken = createTokenByName("get_session", {
|
|
12
|
+
longer_alt: Identifier,
|
|
13
|
+
categories: [Identifier],
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
export const RealtimeStatementTokens = [RealtimeToken, GetSessionToken];
|
|
17
|
+
|
|
18
|
+
export function mapTokenToType(token) {
|
|
19
|
+
switch (token) {
|
|
20
|
+
case RealtimeToken.name:
|
|
21
|
+
case GetSessionToken.name:
|
|
22
|
+
return "function";
|
|
23
|
+
default:
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { Identifier } from "./identifier.js";
|
|
2
|
+
import { createTokenByName } from "./utils.js";
|
|
3
|
+
|
|
4
|
+
// "realtime_server_trigger"
|
|
5
|
+
export const RealtimeServerTriggerToken = createTokenByName(
|
|
6
|
+
"realtime_server_trigger",
|
|
7
|
+
{
|
|
8
|
+
longer_alt: Identifier,
|
|
9
|
+
categories: [Identifier], // could also be an identifier
|
|
10
|
+
}
|
|
11
|
+
);
|
|
12
|
+
|
|
13
|
+
// "realtime_server" (parent-reference keyword used by realtimeServerClause).
|
|
14
|
+
// longer_alt points at the trigger token so `realtime_server_trigger` wins
|
|
15
|
+
// over the shorter `realtime_server` prefix.
|
|
16
|
+
export const RealtimeServerToken = createTokenByName("realtime_server", {
|
|
17
|
+
longer_alt: RealtimeServerTriggerToken,
|
|
18
|
+
categories: [Identifier], // could also be an identifier
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
export const RealtimeServerTriggerTokens = [
|
|
22
|
+
RealtimeServerTriggerToken,
|
|
23
|
+
RealtimeServerToken,
|
|
24
|
+
];
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Maps a token name to a type
|
|
28
|
+
* @param {string} token the token name
|
|
29
|
+
*/
|
|
30
|
+
export function mapTokenToType(token) {
|
|
31
|
+
switch (token) {
|
|
32
|
+
case RealtimeServerTriggerToken.name:
|
|
33
|
+
case RealtimeServerToken.name:
|
|
34
|
+
return "keyword";
|
|
35
|
+
default:
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
}
|
package/lexer/tokens.js
CHANGED
|
@@ -39,6 +39,15 @@ import {
|
|
|
39
39
|
mapTokenToType as mapBranchTokenToType,
|
|
40
40
|
} from "./branch.js";
|
|
41
41
|
import { CastTokens, mapTokenToType as mapCastTokenToType } from "./cast.js";
|
|
42
|
+
import {
|
|
43
|
+
ChannelObjectTokens,
|
|
44
|
+
ChannelServerToken,
|
|
45
|
+
mapTokenToType as mapChannelObjectTokenToType,
|
|
46
|
+
} from "./channel.js";
|
|
47
|
+
import {
|
|
48
|
+
ChannelTriggerTokens,
|
|
49
|
+
mapTokenToType as mapChannelTriggerTokenToType,
|
|
50
|
+
} from "./channel_trigger.js";
|
|
42
51
|
import { CloudTokens, mapTokenToType as mapCloudTokenToType } from "./cloud.js";
|
|
43
52
|
import {
|
|
44
53
|
ColumnTokens,
|
|
@@ -83,6 +92,10 @@ import {
|
|
|
83
92
|
mapTokenToType as mapMcpServerTriggerTokenToType,
|
|
84
93
|
McpServerTriggerTokens,
|
|
85
94
|
} from "./mcp_server_trigger.js";
|
|
95
|
+
import {
|
|
96
|
+
mapTokenToType as mapMessageTokenToType,
|
|
97
|
+
MessageTokens,
|
|
98
|
+
} from "./message.js";
|
|
86
99
|
import {
|
|
87
100
|
mapTokenToType as mapMiddlewareTokenToType,
|
|
88
101
|
MiddlewareTokens,
|
|
@@ -96,6 +109,14 @@ import {
|
|
|
96
109
|
ObjectTokens,
|
|
97
110
|
} from "./object.js";
|
|
98
111
|
import { mapTokenToType as mapQueryTokenToType, QueryTokens } from "./query.js";
|
|
112
|
+
import {
|
|
113
|
+
mapTokenToType as mapRealtimeTokenToType,
|
|
114
|
+
RealtimeStatementTokens,
|
|
115
|
+
} from "./realtime.js";
|
|
116
|
+
import {
|
|
117
|
+
mapTokenToType as mapRealtimeServerTriggerTokenToType,
|
|
118
|
+
RealtimeServerTriggerTokens,
|
|
119
|
+
} from "./realtime_server_trigger.js";
|
|
99
120
|
import {
|
|
100
121
|
mapTokenToType as mapRealtimeTriggerTokenToType,
|
|
101
122
|
RealtimeTriggerTokens,
|
|
@@ -365,7 +386,13 @@ export const allTokens = uniq([
|
|
|
365
386
|
...AgentTriggerTokens,
|
|
366
387
|
...McpServerTriggerTokens,
|
|
367
388
|
...McpServerTokens,
|
|
389
|
+
...RealtimeServerTriggerTokens,
|
|
368
390
|
...RealtimeTriggerTokens,
|
|
391
|
+
...ChannelTriggerTokens,
|
|
392
|
+
...MessageTokens,
|
|
393
|
+
// channel-object keywords (access/publish/conversation/delivery/rate_limit)
|
|
394
|
+
// must precede shorter-prefix keywords like `del` (db/redis)
|
|
395
|
+
...ChannelObjectTokens,
|
|
369
396
|
...ArrayFnTokens,
|
|
370
397
|
...ColumnTokens,
|
|
371
398
|
...LiteralTokens,
|
|
@@ -412,6 +439,10 @@ export const allTokens = uniq([
|
|
|
412
439
|
...WorkflowTestTokens,
|
|
413
440
|
...ApiGroupTokens,
|
|
414
441
|
...ApiFnTokens,
|
|
442
|
+
// bare `realtime` must be lexed AFTER the longer `realtime_*` keywords
|
|
443
|
+
// (realtime_trigger/realtime_channel/realtime_server + api.realtime_event)
|
|
444
|
+
// so it does not shadow them (UNREACHABLE lexer error otherwise).
|
|
445
|
+
...RealtimeStatementTokens,
|
|
415
446
|
...VariableTokens,
|
|
416
447
|
...TableTokens,
|
|
417
448
|
...CloudTokens,
|
|
@@ -427,6 +458,9 @@ export const allTokens = uniq([
|
|
|
427
458
|
...VarTokens,
|
|
428
459
|
...MathTokens,
|
|
429
460
|
...AiTokens, // AI tokens must come before AgentTokens to avoid conflicts
|
|
461
|
+
// `server` must be lexed AFTER `server_details` (an AI keyword) so it does not
|
|
462
|
+
// shadow it; its longer_alt keeps `server_details` input matching correctly.
|
|
463
|
+
ChannelServerToken,
|
|
430
464
|
...AgentTokens, // Must come before ToolTokens because of ToolsToken
|
|
431
465
|
...ToolTokens,
|
|
432
466
|
...AddonTokens,
|
|
@@ -450,6 +484,8 @@ const tokenMappers = [
|
|
|
450
484
|
mapArrayTokenToType,
|
|
451
485
|
mapBranchTokenToType,
|
|
452
486
|
mapCastTokenToType,
|
|
487
|
+
mapChannelObjectTokenToType,
|
|
488
|
+
mapChannelTriggerTokenToType,
|
|
453
489
|
mapCloudTokenToType,
|
|
454
490
|
mapColumnTokenToType,
|
|
455
491
|
mapCommentTokenToType,
|
|
@@ -464,10 +500,13 @@ const tokenMappers = [
|
|
|
464
500
|
mapMathTokenToType,
|
|
465
501
|
mapMcpServerTokenToType,
|
|
466
502
|
mapMcpServerTriggerTokenToType,
|
|
503
|
+
mapMessageTokenToType,
|
|
467
504
|
mapMiddlewareTokenToType,
|
|
468
505
|
mapMultilineTokenToType,
|
|
469
506
|
mapObjectTokenToType,
|
|
470
507
|
mapQueryTokenToType,
|
|
508
|
+
mapRealtimeTokenToType,
|
|
509
|
+
mapRealtimeServerTriggerTokenToType,
|
|
471
510
|
mapRealtimeTriggerTokenToType,
|
|
472
511
|
mapRedisTokenToType,
|
|
473
512
|
mapRunTokenToType,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xano/xanoscript-language-server",
|
|
3
|
-
"version": "12.
|
|
3
|
+
"version": "12.9.0",
|
|
4
4
|
"description": "Language Server Protocol implementation for XanoScript",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "server.js",
|
|
@@ -41,6 +41,8 @@
|
|
|
41
41
|
"scripts": {
|
|
42
42
|
"test-one": "mocha",
|
|
43
43
|
"test": "mocha \"**/*.spec.js\"",
|
|
44
|
+
"coverage": "c8 mocha \"**/*.spec.js\"",
|
|
45
|
+
"coverage:report": "c8 --reporter=html --reporter=text mocha \"**/*.spec.js\"",
|
|
44
46
|
"test-alt": "NODE_OPTIONS=\"--trace-warnings\" mocha --loader node \"*/**/*.spec.js\"",
|
|
45
47
|
"lint": "eslint . --ext .js",
|
|
46
48
|
"lint:fix": "eslint . --ext .js --fix",
|
|
@@ -60,6 +62,7 @@
|
|
|
60
62
|
},
|
|
61
63
|
"devDependencies": {
|
|
62
64
|
"@eslint/js": "^9.22.0",
|
|
65
|
+
"c8": "^11.0.0",
|
|
63
66
|
"chai": "^5.2.0",
|
|
64
67
|
"eslint": "^9.22.0",
|
|
65
68
|
"eslint-plugin-simple-import-sort": "^12.1.1",
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
import {
|
|
2
|
+
AccessToken,
|
|
3
|
+
ConversationToken,
|
|
4
|
+
DeliveryToken,
|
|
5
|
+
PublishToken,
|
|
6
|
+
RateLimitToken,
|
|
7
|
+
ServerToken,
|
|
8
|
+
} from "../lexer/channel.js";
|
|
9
|
+
import { ChannelToken } from "../lexer/columns.js";
|
|
10
|
+
import { EqualToken, LCurly, RCurly } from "../lexer/control.js";
|
|
11
|
+
import { StringLiteral } from "../lexer/literal.js";
|
|
12
|
+
import { Identifier, NewlineToken } from "../lexer/tokens.js";
|
|
13
|
+
|
|
14
|
+
export function channelDeclaration($) {
|
|
15
|
+
return () => {
|
|
16
|
+
let hasServer = false;
|
|
17
|
+
let hasAccess = false;
|
|
18
|
+
let hasPublish = false;
|
|
19
|
+
let hasConversation = false;
|
|
20
|
+
let hasDelivery = false;
|
|
21
|
+
let hasRateLimit = false;
|
|
22
|
+
let hasHistory = false;
|
|
23
|
+
let hasInput = false;
|
|
24
|
+
let hasDescription = false;
|
|
25
|
+
let hasGuid = false;
|
|
26
|
+
let hasTags = false;
|
|
27
|
+
|
|
28
|
+
$.sectionStack.push("channelDeclaration");
|
|
29
|
+
// Allow leading comments and newlines before the channel declaration
|
|
30
|
+
$.SUBRULE($.optionalCommentBlockFn);
|
|
31
|
+
|
|
32
|
+
const parent = $.CONSUME(ChannelToken); // channel
|
|
33
|
+
const nameToken = $.OR([
|
|
34
|
+
{ ALT: () => $.CONSUME(StringLiteral) },
|
|
35
|
+
{ ALT: () => $.CONSUME(Identifier) },
|
|
36
|
+
]);
|
|
37
|
+
$.validateDeclarationName(nameToken, "channel");
|
|
38
|
+
$.CONSUME(LCurly); // "{"
|
|
39
|
+
$.MANY(() => {
|
|
40
|
+
$.AT_LEAST_ONE(() => $.CONSUME(NewlineToken)); // at least one new line
|
|
41
|
+
$.OR2([
|
|
42
|
+
{
|
|
43
|
+
ALT: () => $.SUBRULE($.commentBlockFn),
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
GATE: () => !hasServer,
|
|
47
|
+
ALT: () => {
|
|
48
|
+
hasServer = true;
|
|
49
|
+
const subParent = $.CONSUME(ServerToken); // "server"
|
|
50
|
+
$.CONSUME(EqualToken); // "="
|
|
51
|
+
const result = $.CONSUME2(StringLiteral); // "server_name"
|
|
52
|
+
if (result.image === `""`) {
|
|
53
|
+
$.addMissingError(subParent, "server cannot be empty");
|
|
54
|
+
}
|
|
55
|
+
},
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
GATE: () => !hasAccess,
|
|
59
|
+
ALT: () => {
|
|
60
|
+
hasAccess = true;
|
|
61
|
+
const subParent = $.CONSUME(AccessToken); // "access"
|
|
62
|
+
$.CONSUME2(EqualToken); // "="
|
|
63
|
+
$.SUBRULE($.schemaParseObjectFn, {
|
|
64
|
+
ARGS: [
|
|
65
|
+
subParent,
|
|
66
|
+
{
|
|
67
|
+
"anonymous?": "[boolean]",
|
|
68
|
+
"presence?": "[boolean]",
|
|
69
|
+
},
|
|
70
|
+
],
|
|
71
|
+
});
|
|
72
|
+
},
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
GATE: () => !hasPublish,
|
|
76
|
+
ALT: () => {
|
|
77
|
+
hasPublish = true;
|
|
78
|
+
const subParent = $.CONSUME(PublishToken); // "publish"
|
|
79
|
+
$.CONSUME3(EqualToken); // "="
|
|
80
|
+
$.SUBRULE1($.schemaParseObjectFn, {
|
|
81
|
+
ARGS: [
|
|
82
|
+
subParent,
|
|
83
|
+
{
|
|
84
|
+
"who?": ["nobody", "anyone", "authenticated"],
|
|
85
|
+
"direct?": "[boolean]",
|
|
86
|
+
},
|
|
87
|
+
],
|
|
88
|
+
});
|
|
89
|
+
},
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
GATE: () => !hasConversation,
|
|
93
|
+
ALT: () => {
|
|
94
|
+
hasConversation = true;
|
|
95
|
+
const subParent = $.CONSUME(ConversationToken); // "conversation"
|
|
96
|
+
$.CONSUME4(EqualToken); // "="
|
|
97
|
+
$.SUBRULE2($.schemaParseObjectFn, {
|
|
98
|
+
ARGS: [
|
|
99
|
+
subParent,
|
|
100
|
+
{
|
|
101
|
+
"active?": "[boolean]",
|
|
102
|
+
"limit?": "[number]",
|
|
103
|
+
"ttl?": "[number]",
|
|
104
|
+
},
|
|
105
|
+
],
|
|
106
|
+
});
|
|
107
|
+
},
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
GATE: () => !hasDelivery,
|
|
111
|
+
ALT: () => {
|
|
112
|
+
hasDelivery = true;
|
|
113
|
+
const subParent = $.CONSUME(DeliveryToken); // "delivery"
|
|
114
|
+
$.CONSUME5(EqualToken); // "="
|
|
115
|
+
$.SUBRULE3($.schemaParseObjectFn, {
|
|
116
|
+
ARGS: [
|
|
117
|
+
subParent,
|
|
118
|
+
{
|
|
119
|
+
"guarantee?": ["at_most_once", "at_least_once"],
|
|
120
|
+
"per_recipient?": "[boolean]",
|
|
121
|
+
},
|
|
122
|
+
],
|
|
123
|
+
});
|
|
124
|
+
},
|
|
125
|
+
},
|
|
126
|
+
{
|
|
127
|
+
GATE: () => !hasRateLimit,
|
|
128
|
+
ALT: () => {
|
|
129
|
+
hasRateLimit = true;
|
|
130
|
+
const subParent = $.CONSUME(RateLimitToken); // "rate_limit"
|
|
131
|
+
$.CONSUME6(EqualToken); // "="
|
|
132
|
+
$.SUBRULE4($.schemaParseObjectFn, {
|
|
133
|
+
ARGS: [
|
|
134
|
+
subParent,
|
|
135
|
+
{
|
|
136
|
+
"messages_per_minute?": "[number]",
|
|
137
|
+
},
|
|
138
|
+
],
|
|
139
|
+
});
|
|
140
|
+
},
|
|
141
|
+
},
|
|
142
|
+
{
|
|
143
|
+
GATE: () => !hasHistory,
|
|
144
|
+
ALT: () => {
|
|
145
|
+
hasHistory = true;
|
|
146
|
+
$.SUBRULE($.historyClause);
|
|
147
|
+
},
|
|
148
|
+
},
|
|
149
|
+
{
|
|
150
|
+
GATE: () => !hasInput,
|
|
151
|
+
ALT: () => {
|
|
152
|
+
hasInput = true;
|
|
153
|
+
$.SUBRULE($.inputClause);
|
|
154
|
+
},
|
|
155
|
+
},
|
|
156
|
+
{
|
|
157
|
+
GATE: () => !hasDescription,
|
|
158
|
+
ALT: () => {
|
|
159
|
+
hasDescription = true;
|
|
160
|
+
$.SUBRULE($.descriptionFieldAttribute);
|
|
161
|
+
},
|
|
162
|
+
},
|
|
163
|
+
{
|
|
164
|
+
GATE: () => !hasGuid,
|
|
165
|
+
ALT: () => {
|
|
166
|
+
hasGuid = true;
|
|
167
|
+
$.SUBRULE($.guidFieldAttribute);
|
|
168
|
+
},
|
|
169
|
+
},
|
|
170
|
+
{
|
|
171
|
+
GATE: () => !hasTags,
|
|
172
|
+
ALT: () => {
|
|
173
|
+
hasTags = true;
|
|
174
|
+
$.SUBRULE($.tagsAttribute);
|
|
175
|
+
},
|
|
176
|
+
},
|
|
177
|
+
]);
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
if (!hasServer) {
|
|
181
|
+
$.addMissingError(parent, "{} is missing a server clause");
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
$.MANY1(() => $.CONSUME1(NewlineToken)); // optional new line
|
|
185
|
+
$.CONSUME(RCurly); // "}"
|
|
186
|
+
$.MANY2(() => $.CONSUME2(NewlineToken)); // optional new line
|
|
187
|
+
$.sectionStack.pop();
|
|
188
|
+
};
|
|
189
|
+
}
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import { ChannelTriggerToken } from "../lexer/channel_trigger.js";
|
|
2
|
+
import { EqualToken, LCurly, RCurly } from "../lexer/control.js";
|
|
3
|
+
import { StringLiteral } from "../lexer/literal.js";
|
|
4
|
+
import { ActionsToken, ActiveToken } from "../lexer/mcp_server_trigger.js";
|
|
5
|
+
import { Identifier, NewlineToken } from "../lexer/tokens.js";
|
|
6
|
+
|
|
7
|
+
export function channelTriggerDeclaration($) {
|
|
8
|
+
return () => {
|
|
9
|
+
let hasActions = false;
|
|
10
|
+
let hasActive = false;
|
|
11
|
+
let hasChannel = false;
|
|
12
|
+
let hasDescription = false;
|
|
13
|
+
let hasGuid = false;
|
|
14
|
+
let hasHistory = false;
|
|
15
|
+
let hasInput = false;
|
|
16
|
+
let hasResponse = false;
|
|
17
|
+
let hasStack = false;
|
|
18
|
+
let hasTags = false;
|
|
19
|
+
|
|
20
|
+
$.sectionStack.push("channelTriggerDeclaration");
|
|
21
|
+
// Allow leading comments and newlines before the channel_trigger declaration
|
|
22
|
+
$.SUBRULE($.optionalCommentBlockFn);
|
|
23
|
+
|
|
24
|
+
const parent = $.CONSUME(ChannelTriggerToken); // channel_trigger
|
|
25
|
+
const nameToken = $.OR([
|
|
26
|
+
{ ALT: () => $.CONSUME(StringLiteral) },
|
|
27
|
+
{ ALT: () => $.CONSUME(Identifier) },
|
|
28
|
+
]);
|
|
29
|
+
$.validateDeclarationName(nameToken, "channel_trigger");
|
|
30
|
+
$.CONSUME(LCurly); // "{"
|
|
31
|
+
$.MANY(() => {
|
|
32
|
+
$.AT_LEAST_ONE(() => $.CONSUME(NewlineToken)); // at least one new line
|
|
33
|
+
$.OR2([
|
|
34
|
+
{
|
|
35
|
+
ALT: () => $.SUBRULE($.commentBlockFn),
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
GATE: () => !hasActions,
|
|
39
|
+
ALT: () => {
|
|
40
|
+
hasActions = true;
|
|
41
|
+
|
|
42
|
+
const subParent = $.CONSUME(ActionsToken); // "actions"
|
|
43
|
+
$.CONSUME(EqualToken); // "="
|
|
44
|
+
|
|
45
|
+
$.SUBRULE($.schemaParseObjectFn, {
|
|
46
|
+
ARGS: [
|
|
47
|
+
subParent,
|
|
48
|
+
{
|
|
49
|
+
"join?": "[boolean]",
|
|
50
|
+
"leave?": "[boolean]",
|
|
51
|
+
},
|
|
52
|
+
],
|
|
53
|
+
});
|
|
54
|
+
},
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
GATE: () => !hasActive,
|
|
58
|
+
ALT: () => {
|
|
59
|
+
hasActive = true;
|
|
60
|
+
$.CONSUME(ActiveToken);
|
|
61
|
+
$.CONSUME2(EqualToken);
|
|
62
|
+
$.SUBRULE($.booleanValue);
|
|
63
|
+
},
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
GATE: () => !hasChannel,
|
|
67
|
+
ALT: () => {
|
|
68
|
+
hasChannel = true;
|
|
69
|
+
$.SUBRULE($.channelClause);
|
|
70
|
+
},
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
GATE: () => !hasDescription,
|
|
74
|
+
ALT: () => {
|
|
75
|
+
hasDescription = true;
|
|
76
|
+
$.SUBRULE($.descriptionFieldAttribute);
|
|
77
|
+
},
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
GATE: () => !hasGuid,
|
|
81
|
+
ALT: () => {
|
|
82
|
+
hasGuid = true;
|
|
83
|
+
$.SUBRULE($.guidFieldAttribute);
|
|
84
|
+
},
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
GATE: () => !hasHistory,
|
|
88
|
+
ALT: () => {
|
|
89
|
+
hasHistory = true;
|
|
90
|
+
$.SUBRULE($.historyClause);
|
|
91
|
+
},
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
GATE: () => !hasInput,
|
|
95
|
+
ALT: () => {
|
|
96
|
+
hasInput = true;
|
|
97
|
+
$.SUBRULE($.inputClause);
|
|
98
|
+
},
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
GATE: () => !hasResponse,
|
|
102
|
+
ALT: () => {
|
|
103
|
+
hasResponse = true;
|
|
104
|
+
$.SUBRULE($.responseClause);
|
|
105
|
+
},
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
GATE: () => !hasStack,
|
|
109
|
+
ALT: () => {
|
|
110
|
+
hasStack = true;
|
|
111
|
+
$.SUBRULE($.stackClause);
|
|
112
|
+
},
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
GATE: () => !hasTags,
|
|
116
|
+
ALT: () => {
|
|
117
|
+
hasTags = true;
|
|
118
|
+
$.SUBRULE($.tagsAttribute);
|
|
119
|
+
},
|
|
120
|
+
},
|
|
121
|
+
]);
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
if (!hasChannel) {
|
|
125
|
+
$.addMissingError(parent, "{} is missing a channel clause");
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
if (!hasInput) {
|
|
129
|
+
$.addMissingError(parent, "{} is missing an input clause");
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
if (!hasStack) {
|
|
133
|
+
$.addMissingError(parent, "{} is missing a stack clause");
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
if (!hasResponse) {
|
|
137
|
+
$.addMissingError(parent, "{} is missing a response clause");
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
$.MANY1(() => $.CONSUME1(NewlineToken)); // optional new line
|
|
141
|
+
$.CONSUME(RCurly); // "}"
|
|
142
|
+
$.MANY2(() => $.CONSUME2(NewlineToken)); // optional new line
|
|
143
|
+
$.sectionStack.pop();
|
|
144
|
+
};
|
|
145
|
+
}
|
|
@@ -36,6 +36,7 @@ export function nakedStackFn($) {
|
|
|
36
36
|
{ ALT: () => $.SUBRULE($.mathFn) },
|
|
37
37
|
{ ALT: () => $.SUBRULE($.textFn) },
|
|
38
38
|
{ ALT: () => $.SUBRULE($.utilFn) },
|
|
39
|
+
{ ALT: () => $.SUBRULE($.realtimeFn) },
|
|
39
40
|
{ ALT: () => $.SUBRULE($.preconditionFn) },
|
|
40
41
|
{ ALT: () => $.SUBRULE($.zipFn) },
|
|
41
42
|
{ ALT: () => $.SUBRULE($.objectFn) },
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { EqualToken } from "../../lexer/control.js";
|
|
2
|
+
import { StringLiteral } from "../../lexer/literal.js";
|
|
3
|
+
import { RealtimeServerToken } from "../../lexer/realtime_server_trigger.js";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @param {import('../base_parser.js').XanoBaseParser} $
|
|
7
|
+
* @returns
|
|
8
|
+
*/
|
|
9
|
+
export const realtimeServerClause = ($) => () => {
|
|
10
|
+
$.sectionStack.push("realtimeServerClause");
|
|
11
|
+
|
|
12
|
+
const parent = $.CONSUME(RealtimeServerToken); // realtime_server
|
|
13
|
+
$.CONSUME(EqualToken); // =
|
|
14
|
+
|
|
15
|
+
const result = $.CONSUME(StringLiteral); // "realtime_server_name"
|
|
16
|
+
|
|
17
|
+
// Don't allow empty values inside quotes
|
|
18
|
+
if (result.image === `""`) {
|
|
19
|
+
$.addMissingError(parent, "realtime_server cannot be empty");
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
$.sectionStack.pop();
|
|
23
|
+
};
|
|
@@ -12,6 +12,7 @@ import { mcpServerClause } from "./mcpServerClause.js";
|
|
|
12
12
|
import { middlewareClause } from "./middlewareClause.js";
|
|
13
13
|
import { nakedStackFn } from "./nakedStackFn.js";
|
|
14
14
|
import { outputClause } from "./outputClause.js";
|
|
15
|
+
import { realtimeServerClause } from "./realtimeServerClause.js";
|
|
15
16
|
import { responseClause } from "./responseClause.js";
|
|
16
17
|
import { scheduleClause } from "./scheduleClause.js";
|
|
17
18
|
import { schemaClause } from "./schemaClause.js";
|
|
@@ -39,6 +40,10 @@ export const register = ($) => {
|
|
|
39
40
|
$.inputClause = $.RULE("inputClause", inputClause($));
|
|
40
41
|
$.outputClause = $.RULE("outputClause", outputClause($));
|
|
41
42
|
$.mcpServerClause = $.RULE("mcpServerClause", mcpServerClause($));
|
|
43
|
+
$.realtimeServerClause = $.RULE(
|
|
44
|
+
"realtimeServerClause",
|
|
45
|
+
realtimeServerClause($)
|
|
46
|
+
);
|
|
42
47
|
$.middlewareClause = $.RULE("middlewareClause", middlewareClause($));
|
|
43
48
|
$.nakedStackFn = $.RULE("nakedStackFn", nakedStackFn($));
|
|
44
49
|
$.responseClause = $.RULE("responseClause", responseClause($));
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { GetSessionToken } from "../../../lexer/realtime.js";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @param {import('../../base_parser.js').XanoBaseParser} $
|
|
5
|
+
*/
|
|
6
|
+
export function realtimeGetSessionFn($) {
|
|
7
|
+
// realtime.get_session as $session
|
|
8
|
+
return () => {
|
|
9
|
+
const requiredAttrs = [];
|
|
10
|
+
const optionalAttrs = ["description", "disabled"];
|
|
11
|
+
|
|
12
|
+
$.sectionStack.push("realtimeGetSessionFn");
|
|
13
|
+
const fnToken = $.CONSUME(GetSessionToken); // "get_session"
|
|
14
|
+
$.SUBRULE($.functionAttrReq, {
|
|
15
|
+
ARGS: [fnToken, requiredAttrs, optionalAttrs],
|
|
16
|
+
});
|
|
17
|
+
$.SUBRULE($.asVariable, { ARGS: [fnToken] });
|
|
18
|
+
$.sectionStack.pop();
|
|
19
|
+
};
|
|
20
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { realtimeGetSessionFn } from "./realtimeGetSessionFn.js";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Registers all the parsers in this folder
|
|
5
|
+
* @param {import('../base_parser.js').XanoBaseParser} $
|
|
6
|
+
*/
|
|
7
|
+
export const register = ($) => {
|
|
8
|
+
$.realtimeGetSessionFn = $.RULE(
|
|
9
|
+
"realtimeGetSessionFn",
|
|
10
|
+
realtimeGetSessionFn($),
|
|
11
|
+
);
|
|
12
|
+
};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { RealtimeToken } from "../../lexer/realtime.js";
|
|
2
|
+
import { DotToken } from "../../lexer/tokens.js";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* realtime.get_session
|
|
6
|
+
* @param {import('../base_parser.js').XanoBaseParser} $
|
|
7
|
+
*/
|
|
8
|
+
export function realtimeFn($) {
|
|
9
|
+
return () => {
|
|
10
|
+
$.sectionStack.push("realtime");
|
|
11
|
+
$.CONSUME(RealtimeToken); // "realtime"
|
|
12
|
+
$.CONSUME(DotToken); // "."
|
|
13
|
+
$.OR([
|
|
14
|
+
{ ALT: () => $.SUBRULE($.realtimeGetSessionFn) }, // "realtime.get_session"
|
|
15
|
+
]);
|
|
16
|
+
$.sectionStack.pop();
|
|
17
|
+
};
|
|
18
|
+
}
|
|
@@ -22,6 +22,8 @@ import { mathFn } from "./mathFn.js";
|
|
|
22
22
|
import { middlewareCallFn } from "./middlewareCallFn.js";
|
|
23
23
|
import { register as register_object } from "./object/register.js";
|
|
24
24
|
import { objectFn } from "./objectFn.js";
|
|
25
|
+
import { register as register_realtime } from "./realtime/register.js";
|
|
26
|
+
import { realtimeFn } from "./realtimeFn.js";
|
|
25
27
|
import { register as register_redis } from "./redis/register.js";
|
|
26
28
|
import { redisFn } from "./redisFn.js";
|
|
27
29
|
import { register as register_schema } from "./schema/register.js";
|
|
@@ -63,6 +65,7 @@ export const register = ($) => {
|
|
|
63
65
|
$.debugFn = $.RULE("debugFn", debugFn($));
|
|
64
66
|
$.mathFn = $.RULE("mathFn", mathFn($));
|
|
65
67
|
$.objectFn = $.RULE("objectFn", objectFn($));
|
|
68
|
+
$.realtimeFn = $.RULE("realtimeFn", realtimeFn($));
|
|
66
69
|
$.redisFn = $.RULE("redisFn", redisFn($));
|
|
67
70
|
$.securityFn = $.RULE("securityFn", securityFn($));
|
|
68
71
|
$.storageFn = $.RULE("storageFn", storageFn($));
|
|
@@ -86,6 +89,7 @@ export const register = ($) => {
|
|
|
86
89
|
register_debug($);
|
|
87
90
|
register_math($);
|
|
88
91
|
register_object($);
|
|
92
|
+
register_realtime($);
|
|
89
93
|
register_redis($);
|
|
90
94
|
register_security($);
|
|
91
95
|
register_storage($);
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
import { EqualToken, LCurly, RCurly } from "../lexer/control.js";
|
|
2
|
+
import { StringLiteral } from "../lexer/literal.js";
|
|
3
|
+
import { DeliverToToken, MessageToken } from "../lexer/message.js";
|
|
4
|
+
import { AuthToken, Identifier, NewlineToken } from "../lexer/tokens.js";
|
|
5
|
+
|
|
6
|
+
export function messageDeclaration($) {
|
|
7
|
+
return () => {
|
|
8
|
+
let hasAuth = false;
|
|
9
|
+
let hasChannel = false;
|
|
10
|
+
let hasDeliverTo = false;
|
|
11
|
+
let hasDescription = false;
|
|
12
|
+
let hasGuid = false;
|
|
13
|
+
let hasHistory = false;
|
|
14
|
+
let hasInput = false;
|
|
15
|
+
let hasMiddleware = false;
|
|
16
|
+
let hasResponse = false;
|
|
17
|
+
let hasStack = false;
|
|
18
|
+
let hasTags = false;
|
|
19
|
+
|
|
20
|
+
$.sectionStack.push("messageDeclaration");
|
|
21
|
+
// Allow leading comments and newlines before the message declaration
|
|
22
|
+
$.SUBRULE($.optionalCommentBlockFn);
|
|
23
|
+
|
|
24
|
+
const parent = $.CONSUME(MessageToken);
|
|
25
|
+
const nameToken = $.OR([
|
|
26
|
+
{ ALT: () => $.CONSUME(StringLiteral) },
|
|
27
|
+
{ ALT: () => $.CONSUME(Identifier) },
|
|
28
|
+
]);
|
|
29
|
+
$.validateDeclarationName(nameToken, "message");
|
|
30
|
+
$.CONSUME(LCurly); // "{"
|
|
31
|
+
$.MANY(() => {
|
|
32
|
+
$.AT_LEAST_ONE(() => $.CONSUME(NewlineToken)); // at least one new line
|
|
33
|
+
$.OR2([
|
|
34
|
+
{ ALT: () => $.SUBRULE($.commentBlockFn) },
|
|
35
|
+
{
|
|
36
|
+
GATE: () => !hasAuth,
|
|
37
|
+
ALT: () => {
|
|
38
|
+
hasAuth = true;
|
|
39
|
+
// message auth references an auth DBO by name, e.g. auth = "user"
|
|
40
|
+
$.CONSUME(AuthToken); // "auth"
|
|
41
|
+
$.CONSUME2(EqualToken); // "="
|
|
42
|
+
$.CONSUME2(StringLiteral); // "user"
|
|
43
|
+
},
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
GATE: () => !hasChannel,
|
|
47
|
+
ALT: () => {
|
|
48
|
+
hasChannel = true;
|
|
49
|
+
$.SUBRULE($.channelClause);
|
|
50
|
+
},
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
GATE: () => !hasDeliverTo,
|
|
54
|
+
ALT: () => {
|
|
55
|
+
hasDeliverTo = true;
|
|
56
|
+
|
|
57
|
+
const subParent = $.CONSUME(DeliverToToken); // "deliver_to"
|
|
58
|
+
$.CONSUME(EqualToken); // "="
|
|
59
|
+
$.SUBRULE($.schemaParseEnumFn, {
|
|
60
|
+
ARGS: [
|
|
61
|
+
subParent,
|
|
62
|
+
["channel", "sender", "others", "explicit"],
|
|
63
|
+
],
|
|
64
|
+
});
|
|
65
|
+
},
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
GATE: () => !hasDescription,
|
|
69
|
+
ALT: () => {
|
|
70
|
+
hasDescription = true;
|
|
71
|
+
$.SUBRULE($.descriptionFieldAttribute);
|
|
72
|
+
},
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
GATE: () => !hasGuid,
|
|
76
|
+
ALT: () => {
|
|
77
|
+
hasGuid = true;
|
|
78
|
+
$.SUBRULE($.guidFieldAttribute);
|
|
79
|
+
},
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
GATE: () => !hasHistory,
|
|
83
|
+
ALT: () => {
|
|
84
|
+
hasHistory = true;
|
|
85
|
+
$.SUBRULE($.historyClause);
|
|
86
|
+
},
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
GATE: () => !hasInput,
|
|
90
|
+
ALT: () => {
|
|
91
|
+
hasInput = true;
|
|
92
|
+
$.SUBRULE($.inputClause);
|
|
93
|
+
},
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
GATE: () => !hasMiddleware,
|
|
97
|
+
ALT: () => {
|
|
98
|
+
hasMiddleware = true;
|
|
99
|
+
$.SUBRULE($.middlewareClause);
|
|
100
|
+
},
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
GATE: () => !hasResponse,
|
|
104
|
+
ALT: () => {
|
|
105
|
+
hasResponse = true;
|
|
106
|
+
$.SUBRULE($.responseClause);
|
|
107
|
+
},
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
GATE: () => !hasStack,
|
|
111
|
+
ALT: () => {
|
|
112
|
+
hasStack = true;
|
|
113
|
+
$.SUBRULE($.stackClause, {
|
|
114
|
+
ARGS: [{ allowCallStatements: true }],
|
|
115
|
+
});
|
|
116
|
+
},
|
|
117
|
+
},
|
|
118
|
+
{
|
|
119
|
+
GATE: () => !hasTags,
|
|
120
|
+
ALT: () => {
|
|
121
|
+
hasTags = true;
|
|
122
|
+
$.SUBRULE($.tagsAttribute);
|
|
123
|
+
},
|
|
124
|
+
},
|
|
125
|
+
]);
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
if (!hasChannel) {
|
|
129
|
+
$.addMissingError(
|
|
130
|
+
parent,
|
|
131
|
+
`{} ${nameToken.image} is missing a channel clause`,
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
if (!hasInput) {
|
|
136
|
+
$.addMissingError(
|
|
137
|
+
parent,
|
|
138
|
+
`{} ${nameToken.image} is missing an input clause`,
|
|
139
|
+
);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
if (!hasStack) {
|
|
143
|
+
$.addMissingError(
|
|
144
|
+
parent,
|
|
145
|
+
`{} ${nameToken.image} is missing a stack clause`,
|
|
146
|
+
);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
if (!hasResponse) {
|
|
150
|
+
$.addMissingError(
|
|
151
|
+
parent,
|
|
152
|
+
`{} ${nameToken.image} is missing a response clause`,
|
|
153
|
+
);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
$.MANY1(() => $.CONSUME1(NewlineToken)); // optional new line
|
|
157
|
+
$.CONSUME(RCurly); // "}"
|
|
158
|
+
$.MANY2(() => $.CONSUME2(NewlineToken)); // optional new line
|
|
159
|
+
$.sectionStack.pop();
|
|
160
|
+
};
|
|
161
|
+
}
|
package/parser/parser.js
CHANGED
|
@@ -36,6 +36,12 @@ export function xanoscriptParser(text, scheme, preTokenized = null) {
|
|
|
36
36
|
case "branch":
|
|
37
37
|
parser.branchDeclaration();
|
|
38
38
|
return parser;
|
|
39
|
+
case "channel":
|
|
40
|
+
parser.channelDeclaration();
|
|
41
|
+
return parser;
|
|
42
|
+
case "channel_trigger":
|
|
43
|
+
parser.channelTriggerDeclaration();
|
|
44
|
+
return parser;
|
|
39
45
|
case "error_trigger":
|
|
40
46
|
parser.errorTriggerDeclaration();
|
|
41
47
|
return parser;
|
|
@@ -48,9 +54,15 @@ export function xanoscriptParser(text, scheme, preTokenized = null) {
|
|
|
48
54
|
case "mcp_server_trigger":
|
|
49
55
|
parser.mcpServerTriggerDeclaration();
|
|
50
56
|
return parser;
|
|
57
|
+
case "message":
|
|
58
|
+
parser.messageDeclaration();
|
|
59
|
+
return parser;
|
|
51
60
|
case "middleware":
|
|
52
61
|
parser.middlewareDeclaration();
|
|
53
62
|
return parser;
|
|
63
|
+
case "realtime_server_trigger":
|
|
64
|
+
parser.realtimeServerTriggerDeclaration();
|
|
65
|
+
return parser;
|
|
54
66
|
case "realtime_trigger":
|
|
55
67
|
parser.realtimeTriggerDeclaration();
|
|
56
68
|
return parser;
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import { EqualToken, LCurly, RCurly } from "../lexer/control.js";
|
|
2
|
+
import { StringLiteral } from "../lexer/literal.js";
|
|
3
|
+
import { ActionsToken, ActiveToken } from "../lexer/mcp_server_trigger.js";
|
|
4
|
+
import { RealtimeServerTriggerToken } from "../lexer/realtime_server_trigger.js";
|
|
5
|
+
import { Identifier, NewlineToken } from "../lexer/tokens.js";
|
|
6
|
+
|
|
7
|
+
export function realtimeServerTriggerDeclaration($) {
|
|
8
|
+
return () => {
|
|
9
|
+
let hasActions = false;
|
|
10
|
+
let hasActive = false;
|
|
11
|
+
let hasDescription = false;
|
|
12
|
+
let hasGuid = false;
|
|
13
|
+
let hasHistory = false;
|
|
14
|
+
let hasInput = false;
|
|
15
|
+
let hasRealtimeServer = false;
|
|
16
|
+
let hasResponse = false;
|
|
17
|
+
let hasStack = false;
|
|
18
|
+
let hasTags = false;
|
|
19
|
+
|
|
20
|
+
$.sectionStack.push("realtimeServerTriggerDeclaration");
|
|
21
|
+
// Allow leading comments and newlines before the declaration
|
|
22
|
+
$.SUBRULE($.optionalCommentBlockFn);
|
|
23
|
+
|
|
24
|
+
const parent = $.CONSUME(RealtimeServerTriggerToken); // realtime_server_trigger
|
|
25
|
+
const nameToken = $.OR([
|
|
26
|
+
{ ALT: () => $.CONSUME(StringLiteral) },
|
|
27
|
+
{ ALT: () => $.CONSUME(Identifier) },
|
|
28
|
+
]);
|
|
29
|
+
$.validateDeclarationName(nameToken, "realtime_server_trigger");
|
|
30
|
+
$.CONSUME(LCurly); // "{"
|
|
31
|
+
$.MANY(() => {
|
|
32
|
+
$.AT_LEAST_ONE(() => $.CONSUME(NewlineToken)); // at least one new line
|
|
33
|
+
$.OR2([
|
|
34
|
+
{
|
|
35
|
+
ALT: () => $.SUBRULE($.commentBlockFn),
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
GATE: () => !hasActions,
|
|
39
|
+
ALT: () => {
|
|
40
|
+
hasActions = true;
|
|
41
|
+
|
|
42
|
+
const subParent = $.CONSUME(ActionsToken); // "actions"
|
|
43
|
+
$.CONSUME(EqualToken); // "="
|
|
44
|
+
|
|
45
|
+
$.SUBRULE($.schemaParseObjectFn, {
|
|
46
|
+
ARGS: [
|
|
47
|
+
subParent,
|
|
48
|
+
{
|
|
49
|
+
"connect?": "[boolean]",
|
|
50
|
+
"disconnect?": "[boolean]",
|
|
51
|
+
},
|
|
52
|
+
],
|
|
53
|
+
});
|
|
54
|
+
},
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
GATE: () => !hasActive,
|
|
58
|
+
ALT: () => {
|
|
59
|
+
hasActive = true;
|
|
60
|
+
$.CONSUME(ActiveToken);
|
|
61
|
+
$.CONSUME2(EqualToken);
|
|
62
|
+
$.SUBRULE($.booleanValue);
|
|
63
|
+
},
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
GATE: () => !hasDescription,
|
|
67
|
+
ALT: () => {
|
|
68
|
+
hasDescription = true;
|
|
69
|
+
$.SUBRULE($.descriptionFieldAttribute);
|
|
70
|
+
},
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
GATE: () => !hasGuid,
|
|
74
|
+
ALT: () => {
|
|
75
|
+
hasGuid = true;
|
|
76
|
+
$.SUBRULE($.guidFieldAttribute);
|
|
77
|
+
},
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
GATE: () => !hasHistory,
|
|
81
|
+
ALT: () => {
|
|
82
|
+
hasHistory = true;
|
|
83
|
+
$.SUBRULE($.historyClause);
|
|
84
|
+
},
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
GATE: () => !hasInput,
|
|
88
|
+
ALT: () => {
|
|
89
|
+
hasInput = true;
|
|
90
|
+
$.SUBRULE($.inputClause);
|
|
91
|
+
},
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
GATE: () => !hasRealtimeServer,
|
|
95
|
+
ALT: () => {
|
|
96
|
+
hasRealtimeServer = true;
|
|
97
|
+
$.SUBRULE($.realtimeServerClause);
|
|
98
|
+
},
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
GATE: () => !hasResponse,
|
|
102
|
+
ALT: () => {
|
|
103
|
+
hasResponse = true;
|
|
104
|
+
$.SUBRULE($.responseClause);
|
|
105
|
+
},
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
GATE: () => !hasStack,
|
|
109
|
+
ALT: () => {
|
|
110
|
+
hasStack = true;
|
|
111
|
+
$.SUBRULE($.stackClause);
|
|
112
|
+
},
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
GATE: () => !hasTags,
|
|
116
|
+
ALT: () => {
|
|
117
|
+
hasTags = true;
|
|
118
|
+
$.SUBRULE($.tagsAttribute);
|
|
119
|
+
},
|
|
120
|
+
},
|
|
121
|
+
]);
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
if (!hasRealtimeServer) {
|
|
125
|
+
$.addMissingError(parent, "{} is missing a realtime_server clause");
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
if (!hasInput) {
|
|
129
|
+
$.addMissingError(parent, "{} is missing an input clause");
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
if (!hasStack) {
|
|
133
|
+
$.addMissingError(parent, "{} is missing a stack clause");
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
if (!hasResponse) {
|
|
137
|
+
$.addMissingError(parent, "{} is missing a response clause");
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
$.MANY1(() => $.CONSUME1(NewlineToken)); // optional new line
|
|
141
|
+
$.CONSUME(RCurly); // "}"
|
|
142
|
+
$.MANY2(() => $.CONSUME2(NewlineToken)); // optional new line
|
|
143
|
+
$.sectionStack.pop();
|
|
144
|
+
};
|
|
145
|
+
}
|
package/parser/register.js
CHANGED
|
@@ -4,6 +4,8 @@ import { agentTriggerDeclaration } from "./agent_trigger_parser.js";
|
|
|
4
4
|
import { apiGroupDeclaration } from "./api_group_parser.js";
|
|
5
5
|
import { register as register_attributes } from "./attributes/register.js";
|
|
6
6
|
import { branchDeclaration } from "./branch_parser.js";
|
|
7
|
+
import { channelDeclaration } from "./channel_parser.js";
|
|
8
|
+
import { channelTriggerDeclaration } from "./channel_trigger_parser.js";
|
|
7
9
|
import { register as register_clauses } from "./clauses/register.js";
|
|
8
10
|
import { register as register_definitions } from "./definitions/register.js";
|
|
9
11
|
import { errorTriggerDeclaration } from "./error_trigger_parser.js";
|
|
@@ -12,10 +14,12 @@ import { register as register_functions } from "./functions/register.js";
|
|
|
12
14
|
import { register as register_generic } from "./generic/register.js";
|
|
13
15
|
import { mcpServerDeclaration } from "./mcp_server_parser.js";
|
|
14
16
|
import { mcpServerTriggerDeclaration } from "./mcp_server_trigger_parser.js";
|
|
17
|
+
import { messageDeclaration } from "./message_parser.js";
|
|
15
18
|
import { register as register_metadata } from "./metadata/register.js";
|
|
16
19
|
import { middlewareDeclaration } from "./middleware_parser.js";
|
|
17
20
|
import { queryDeclaration } from "./query_parser.js";
|
|
18
21
|
import { realtimeChannelDeclaration } from "./realtime_channel_parser.js";
|
|
22
|
+
import { realtimeServerTriggerDeclaration } from "./realtime_server_trigger_parser.js";
|
|
19
23
|
import { realtimeTriggerDeclaration } from "./realtime_trigger_parser.js";
|
|
20
24
|
import {
|
|
21
25
|
runDeclaration,
|
|
@@ -51,6 +55,11 @@ export const register = ($) => {
|
|
|
51
55
|
$.queryDeclaration = $.RULE("queryDeclaration", queryDeclaration($));
|
|
52
56
|
$.apiGroupDeclaration = $.RULE("apiGroupDeclaration", apiGroupDeclaration($));
|
|
53
57
|
$.branchDeclaration = $.RULE("branchDeclaration", branchDeclaration($));
|
|
58
|
+
$.channelDeclaration = $.RULE("channelDeclaration", channelDeclaration($));
|
|
59
|
+
$.channelTriggerDeclaration = $.RULE(
|
|
60
|
+
"channelTriggerDeclaration",
|
|
61
|
+
channelTriggerDeclaration($)
|
|
62
|
+
);
|
|
54
63
|
$.errorTriggerDeclaration = $.RULE(
|
|
55
64
|
"errorTriggerDeclaration",
|
|
56
65
|
errorTriggerDeclaration($)
|
|
@@ -64,10 +73,15 @@ export const register = ($) => {
|
|
|
64
73
|
"mcpServerTriggerDeclaration",
|
|
65
74
|
mcpServerTriggerDeclaration($)
|
|
66
75
|
);
|
|
76
|
+
$.messageDeclaration = $.RULE("messageDeclaration", messageDeclaration($));
|
|
67
77
|
$.middlewareDeclaration = $.RULE(
|
|
68
78
|
"middlewareDeclaration",
|
|
69
79
|
middlewareDeclaration($)
|
|
70
80
|
);
|
|
81
|
+
$.realtimeServerTriggerDeclaration = $.RULE(
|
|
82
|
+
"realtimeServerTriggerDeclaration",
|
|
83
|
+
realtimeServerTriggerDeclaration($)
|
|
84
|
+
);
|
|
71
85
|
$.realtimeTriggerDeclaration = $.RULE(
|
|
72
86
|
"realtimeTriggerDeclaration",
|
|
73
87
|
realtimeTriggerDeclaration($)
|
package/utils.js
CHANGED
|
@@ -23,12 +23,16 @@ const schemeByFirstWord = {
|
|
|
23
23
|
agent_trigger: "agent_trigger",
|
|
24
24
|
api_group: "api_group",
|
|
25
25
|
branch: "branch",
|
|
26
|
+
channel: "channel",
|
|
27
|
+
channel_trigger: "channel_trigger",
|
|
26
28
|
error_trigger: "error_trigger",
|
|
27
29
|
function: "cfn",
|
|
28
30
|
mcp_server: "mcp_server",
|
|
29
31
|
mcp_server_trigger: "mcp_server_trigger",
|
|
32
|
+
message: "message",
|
|
30
33
|
middleware: "middleware",
|
|
31
34
|
query: "api",
|
|
35
|
+
realtime_server_trigger: "realtime_server_trigger",
|
|
32
36
|
realtime_trigger: "realtime_trigger",
|
|
33
37
|
realtime_channel: "realtime_channel",
|
|
34
38
|
run: "run",
|