reqon-dsl 0.2.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/.claude/settings.local.json +31 -0
- package/.claude/skills/api-integration.md +125 -0
- package/.claude/skills/database-schema.md +51 -0
- package/.claude/skills/dsl-design.md +80 -0
- package/.claude/skills/property-testing.md +143 -0
- package/.claude/skills/reqon/SKILL.md +44 -0
- package/.claude/skills/reqon/references/examples.md +206 -0
- package/.claude/skills/reqon/references/syntax.md +263 -0
- package/.claude/skills/vscode-extension.md +113 -0
- package/.github/dependabot.yml +32 -0
- package/.github/pull_request_template.md +21 -0
- package/.github/workflows/ci.yml +174 -0
- package/.github/workflows/release.yml +73 -0
- package/CLAUDE.md +72 -0
- package/CONTRIBUTING.md +161 -0
- package/README.md +235 -0
- package/TODO.md +51 -0
- package/dist/ast/index.d.ts +1 -0
- package/dist/ast/index.js +1 -0
- package/dist/ast/nodes.d.ts +237 -0
- package/dist/ast/nodes.js +12 -0
- package/dist/auth/auth.test.d.ts +1 -0
- package/dist/auth/auth.test.js +255 -0
- package/dist/auth/circuit-breaker.d.ts +115 -0
- package/dist/auth/circuit-breaker.js +267 -0
- package/dist/auth/credentials.d.ts +91 -0
- package/dist/auth/credentials.js +169 -0
- package/dist/auth/index.d.ts +5 -0
- package/dist/auth/index.js +8 -0
- package/dist/auth/oauth2-provider.d.ts +41 -0
- package/dist/auth/oauth2-provider.js +131 -0
- package/dist/auth/rate-limiter.d.ts +61 -0
- package/dist/auth/rate-limiter.js +380 -0
- package/dist/auth/token-store.d.ts +30 -0
- package/dist/auth/token-store.js +148 -0
- package/dist/auth/types.d.ts +142 -0
- package/dist/auth/types.js +1 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +270 -0
- package/dist/errors/errors.test.d.ts +1 -0
- package/dist/errors/errors.test.js +165 -0
- package/dist/errors/index.d.ts +83 -0
- package/dist/errors/index.js +159 -0
- package/dist/execution/execution.test.d.ts +1 -0
- package/dist/execution/execution.test.js +246 -0
- package/dist/execution/index.d.ts +4 -0
- package/dist/execution/index.js +2 -0
- package/dist/execution/state.d.ts +136 -0
- package/dist/execution/state.js +82 -0
- package/dist/execution/store.d.ts +52 -0
- package/dist/execution/store.js +120 -0
- package/dist/index.d.ts +27 -0
- package/dist/index.js +57 -0
- package/dist/integration.test.d.ts +1 -0
- package/dist/integration.test.js +168 -0
- package/dist/interpreter/context.d.ts +15 -0
- package/dist/interpreter/context.js +29 -0
- package/dist/interpreter/evaluator.d.ts +5 -0
- package/dist/interpreter/evaluator.js +223 -0
- package/dist/interpreter/evaluator.test.d.ts +1 -0
- package/dist/interpreter/evaluator.test.js +512 -0
- package/dist/interpreter/executor.d.ts +131 -0
- package/dist/interpreter/executor.js +663 -0
- package/dist/interpreter/fetch-handler.d.ts +43 -0
- package/dist/interpreter/fetch-handler.js +203 -0
- package/dist/interpreter/http.d.ts +57 -0
- package/dist/interpreter/http.js +210 -0
- package/dist/interpreter/http.test.d.ts +1 -0
- package/dist/interpreter/http.test.js +299 -0
- package/dist/interpreter/index.d.ts +7 -0
- package/dist/interpreter/index.js +7 -0
- package/dist/interpreter/pagination.d.ts +63 -0
- package/dist/interpreter/pagination.js +155 -0
- package/dist/interpreter/progress.test.d.ts +1 -0
- package/dist/interpreter/progress.test.js +216 -0
- package/dist/interpreter/schema-matcher.d.ts +16 -0
- package/dist/interpreter/schema-matcher.js +136 -0
- package/dist/interpreter/schema-matcher.test.d.ts +1 -0
- package/dist/interpreter/schema-matcher.test.js +122 -0
- package/dist/interpreter/signals.d.ts +57 -0
- package/dist/interpreter/signals.js +73 -0
- package/dist/interpreter/step-handlers/for-handler.d.ts +17 -0
- package/dist/interpreter/step-handlers/for-handler.js +51 -0
- package/dist/interpreter/step-handlers/index.d.ts +8 -0
- package/dist/interpreter/step-handlers/index.js +8 -0
- package/dist/interpreter/step-handlers/map-handler.d.ts +10 -0
- package/dist/interpreter/step-handlers/map-handler.js +20 -0
- package/dist/interpreter/step-handlers/match-handler.d.ts +27 -0
- package/dist/interpreter/step-handlers/match-handler.js +61 -0
- package/dist/interpreter/step-handlers/store-handler.d.ts +13 -0
- package/dist/interpreter/step-handlers/store-handler.js +66 -0
- package/dist/interpreter/step-handlers/types.d.ts +15 -0
- package/dist/interpreter/step-handlers/types.js +1 -0
- package/dist/interpreter/step-handlers/validate-handler.d.ts +10 -0
- package/dist/interpreter/step-handlers/validate-handler.js +26 -0
- package/dist/interpreter/step-handlers/webhook-handler.d.ts +36 -0
- package/dist/interpreter/step-handlers/webhook-handler.js +104 -0
- package/dist/lexer/index.d.ts +10 -0
- package/dist/lexer/index.js +12 -0
- package/dist/lexer/lexer.d.ts +24 -0
- package/dist/lexer/lexer.js +264 -0
- package/dist/lexer/lexer.test.d.ts +1 -0
- package/dist/lexer/lexer.test.js +259 -0
- package/dist/lexer/tokens.d.ts +69 -0
- package/dist/lexer/tokens.js +146 -0
- package/dist/loader/index.d.ts +36 -0
- package/dist/loader/index.js +220 -0
- package/dist/loader/loader.test.d.ts +1 -0
- package/dist/loader/loader.test.js +287 -0
- package/dist/oas/index.d.ts +4 -0
- package/dist/oas/index.js +2 -0
- package/dist/oas/loader.d.ts +21 -0
- package/dist/oas/loader.js +82 -0
- package/dist/oas/oas.test.d.ts +1 -0
- package/dist/oas/oas.test.js +218 -0
- package/dist/oas/validator.d.ts +12 -0
- package/dist/oas/validator.js +227 -0
- package/dist/parser/base.d.ts +33 -0
- package/dist/parser/base.js +97 -0
- package/dist/parser/expressions.d.ts +27 -0
- package/dist/parser/expressions.js +248 -0
- package/dist/parser/expressions.test.d.ts +1 -0
- package/dist/parser/expressions.test.js +378 -0
- package/dist/parser/index.d.ts +3 -0
- package/dist/parser/index.js +3 -0
- package/dist/parser/match.test.d.ts +1 -0
- package/dist/parser/match.test.js +254 -0
- package/dist/parser/parser.d.ts +68 -0
- package/dist/parser/parser.js +1229 -0
- package/dist/parser/parser.test.d.ts +1 -0
- package/dist/parser/parser.test.js +333 -0
- package/dist/parser/schedule.test.d.ts +1 -0
- package/dist/parser/schedule.test.js +241 -0
- package/dist/plugin.d.ts +35 -0
- package/dist/plugin.js +68 -0
- package/dist/scheduler/cron-parser.d.ts +32 -0
- package/dist/scheduler/cron-parser.js +198 -0
- package/dist/scheduler/cron-parser.test.d.ts +1 -0
- package/dist/scheduler/cron-parser.test.js +188 -0
- package/dist/scheduler/index.d.ts +3 -0
- package/dist/scheduler/index.js +2 -0
- package/dist/scheduler/scheduler.d.ts +81 -0
- package/dist/scheduler/scheduler.js +376 -0
- package/dist/scheduler/types.d.ts +65 -0
- package/dist/scheduler/types.js +1 -0
- package/dist/stores/factory.d.ts +36 -0
- package/dist/stores/factory.js +73 -0
- package/dist/stores/file.d.ts +60 -0
- package/dist/stores/file.js +173 -0
- package/dist/stores/file.test.d.ts +1 -0
- package/dist/stores/file.test.js +165 -0
- package/dist/stores/index.d.ts +6 -0
- package/dist/stores/index.js +5 -0
- package/dist/stores/memory.d.ts +19 -0
- package/dist/stores/memory.js +51 -0
- package/dist/stores/memory.test.d.ts +1 -0
- package/dist/stores/memory.test.js +157 -0
- package/dist/stores/postgrest.d.ts +55 -0
- package/dist/stores/postgrest.js +217 -0
- package/dist/stores/stores.test.d.ts +1 -0
- package/dist/stores/stores.test.js +158 -0
- package/dist/stores/types.d.ts +31 -0
- package/dist/stores/types.js +26 -0
- package/dist/sync/index.d.ts +4 -0
- package/dist/sync/index.js +2 -0
- package/dist/sync/state.d.ts +69 -0
- package/dist/sync/state.js +66 -0
- package/dist/sync/store.d.ts +49 -0
- package/dist/sync/store.js +93 -0
- package/dist/sync/sync.test.d.ts +1 -0
- package/dist/sync/sync.test.js +221 -0
- package/dist/utils/async.d.ts +7 -0
- package/dist/utils/async.js +9 -0
- package/dist/utils/file.d.ts +38 -0
- package/dist/utils/file.js +92 -0
- package/dist/utils/index.d.ts +4 -0
- package/dist/utils/index.js +4 -0
- package/dist/utils/logger.d.ts +34 -0
- package/dist/utils/logger.js +39 -0
- package/dist/utils/path.d.ts +12 -0
- package/dist/utils/path.js +41 -0
- package/dist/webhook/index.d.ts +8 -0
- package/dist/webhook/index.js +7 -0
- package/dist/webhook/server.d.ts +84 -0
- package/dist/webhook/server.js +319 -0
- package/dist/webhook/store.d.ts +67 -0
- package/dist/webhook/store.js +193 -0
- package/dist/webhook/types.d.ts +88 -0
- package/dist/webhook/types.js +6 -0
- package/docusaurus/README.md +41 -0
- package/docusaurus/docs/advanced/execution-state.md +283 -0
- package/docusaurus/docs/advanced/extending-reqon.md +388 -0
- package/docusaurus/docs/advanced/multi-file-missions.md +250 -0
- package/docusaurus/docs/advanced/parallel-execution.md +353 -0
- package/docusaurus/docs/api-reference.md +443 -0
- package/docusaurus/docs/authentication/api-key.md +339 -0
- package/docusaurus/docs/authentication/basic.md +276 -0
- package/docusaurus/docs/authentication/bearer.md +282 -0
- package/docusaurus/docs/authentication/oauth2.md +317 -0
- package/docusaurus/docs/authentication/overview.md +251 -0
- package/docusaurus/docs/cli.md +229 -0
- package/docusaurus/docs/core-concepts/actions.md +286 -0
- package/docusaurus/docs/core-concepts/missions.md +264 -0
- package/docusaurus/docs/core-concepts/schemas.md +353 -0
- package/docusaurus/docs/core-concepts/sources.md +339 -0
- package/docusaurus/docs/core-concepts/stores.md +332 -0
- package/docusaurus/docs/dsl-syntax/expressions.md +361 -0
- package/docusaurus/docs/dsl-syntax/fetch.md +293 -0
- package/docusaurus/docs/dsl-syntax/for-loops.md +324 -0
- package/docusaurus/docs/dsl-syntax/map.md +345 -0
- package/docusaurus/docs/dsl-syntax/match.md +387 -0
- package/docusaurus/docs/dsl-syntax/pipelines.md +397 -0
- package/docusaurus/docs/dsl-syntax/validate.md +401 -0
- package/docusaurus/docs/error-handling/dead-letter-queues.md +399 -0
- package/docusaurus/docs/error-handling/flow-control.md +337 -0
- package/docusaurus/docs/error-handling/retry-strategies.md +368 -0
- package/docusaurus/docs/examples.md +488 -0
- package/docusaurus/docs/getting-started.md +256 -0
- package/docusaurus/docs/http/circuit-breaker.md +401 -0
- package/docusaurus/docs/http/incremental-sync.md +394 -0
- package/docusaurus/docs/http/pagination.md +361 -0
- package/docusaurus/docs/http/rate-limiting.md +383 -0
- package/docusaurus/docs/http/requests.md +328 -0
- package/docusaurus/docs/http/retry.md +402 -0
- package/docusaurus/docs/intro.md +90 -0
- package/docusaurus/docs/openapi/loading-specs.md +305 -0
- package/docusaurus/docs/openapi/operation-calls.md +314 -0
- package/docusaurus/docs/openapi/overview.md +212 -0
- package/docusaurus/docs/openapi/response-validation.md +344 -0
- package/docusaurus/docs/scheduling/cron.md +305 -0
- package/docusaurus/docs/scheduling/daemon-mode.md +317 -0
- package/docusaurus/docs/scheduling/intervals.md +289 -0
- package/docusaurus/docs/scheduling/overview.md +231 -0
- package/docusaurus/docs/stores/custom-adapters.md +376 -0
- package/docusaurus/docs/stores/file.md +236 -0
- package/docusaurus/docs/stores/memory.md +193 -0
- package/docusaurus/docs/stores/overview.md +274 -0
- package/docusaurus/docs/stores/postgrest.md +316 -0
- package/docusaurus/docusaurus.config.ts +148 -0
- package/docusaurus/package-lock.json +18029 -0
- package/docusaurus/package.json +47 -0
- package/docusaurus/sidebars.ts +155 -0
- package/docusaurus/src/components/HomepageFeatures/index.tsx +105 -0
- package/docusaurus/src/components/HomepageFeatures/styles.module.css +12 -0
- package/docusaurus/src/css/custom.css +169 -0
- package/docusaurus/src/pages/index.module.css +48 -0
- package/docusaurus/src/pages/index.tsx +110 -0
- package/docusaurus/src/pages/markdown-page.md +7 -0
- package/docusaurus/static/.nojekyll +0 -0
- package/docusaurus/static/img/docusaurus-social-card.jpg +0 -0
- package/docusaurus/static/img/docusaurus.png +0 -0
- package/docusaurus/static/img/favicon.ico +0 -0
- package/docusaurus/static/img/logo.svg +10 -0
- package/docusaurus/static/img/undraw_docusaurus_mountain.svg +171 -0
- package/docusaurus/static/img/undraw_docusaurus_react.svg +170 -0
- package/docusaurus/static/img/undraw_docusaurus_tree.svg +40 -0
- package/docusaurus/tsconfig.json +8 -0
- package/examples/README.md +112 -0
- package/examples/error-handling/README.md +150 -0
- package/examples/error-handling/payment-processor.vague +287 -0
- package/examples/github-sync/README.md +74 -0
- package/examples/github-sync/fetch-issues.vague +47 -0
- package/examples/github-sync/fetch-prs.vague +40 -0
- package/examples/github-sync/mission.vague +101 -0
- package/examples/github-sync/normalize.vague +70 -0
- package/examples/jsonplaceholder/README.md +28 -0
- package/examples/jsonplaceholder/posts.vague +48 -0
- package/examples/petstore/README.md +35 -0
- package/examples/petstore/openapi.yaml +97 -0
- package/examples/petstore/sync.vague +52 -0
- package/examples/temporal-comparison/README.md +297 -0
- package/examples/temporal-comparison/reconciliation.vague +355 -0
- package/examples/temporal-comparison/temporal/activities/index.ts +8 -0
- package/examples/temporal-comparison/temporal/activities/shipstation.ts +225 -0
- package/examples/temporal-comparison/temporal/activities/shopify.ts +257 -0
- package/examples/temporal-comparison/temporal/activities/storage.ts +198 -0
- package/examples/temporal-comparison/temporal/activities/stripe.ts +169 -0
- package/examples/temporal-comparison/temporal/activities/validation.ts +205 -0
- package/examples/temporal-comparison/temporal/client/schedule.ts +218 -0
- package/examples/temporal-comparison/temporal/config/retry.ts +63 -0
- package/examples/temporal-comparison/temporal/types/index.ts +129 -0
- package/examples/temporal-comparison/temporal/workers/main.ts +130 -0
- package/examples/temporal-comparison/temporal/workflows/orderReconciliation.ts +262 -0
- package/examples/xero/README.md +88 -0
- package/examples/xero/invoices.vague +189 -0
- package/package.json +40 -0
- package/src/api-integration.test.ts +954 -0
- package/src/ast/index.ts +1 -0
- package/src/ast/nodes.ts +310 -0
- package/src/auth/auth.test.ts +326 -0
- package/src/auth/circuit-breaker.test.ts +390 -0
- package/src/auth/circuit-breaker.ts +379 -0
- package/src/auth/credentials.test.ts +273 -0
- package/src/auth/credentials.ts +246 -0
- package/src/auth/index.ts +40 -0
- package/src/auth/oauth2-provider.ts +177 -0
- package/src/auth/rate-limiter.ts +459 -0
- package/src/auth/token-store.ts +177 -0
- package/src/auth/types.ts +159 -0
- package/src/benchmark/e2e.bench.ts +288 -0
- package/src/benchmark/evaluator.bench.ts +331 -0
- package/src/benchmark/fixtures.ts +295 -0
- package/src/benchmark/index.ts +108 -0
- package/src/benchmark/lexer.bench.ts +69 -0
- package/src/benchmark/parser.bench.ts +103 -0
- package/src/benchmark/resilience.bench.ts +193 -0
- package/src/benchmark/store.bench.ts +147 -0
- package/src/benchmark/utils.ts +230 -0
- package/src/cli.ts +313 -0
- package/src/errors/errors.test.ts +234 -0
- package/src/errors/index.ts +223 -0
- package/src/execution/execution.test.ts +307 -0
- package/src/execution/index.ts +21 -0
- package/src/execution/state.ts +207 -0
- package/src/execution/store.ts +188 -0
- package/src/index.ts +169 -0
- package/src/integration.test.ts +192 -0
- package/src/interpreter/context.ts +57 -0
- package/src/interpreter/evaluator.test.ts +796 -0
- package/src/interpreter/evaluator.ts +245 -0
- package/src/interpreter/executor.ts +946 -0
- package/src/interpreter/fetch-handler.ts +302 -0
- package/src/interpreter/http.test.ts +423 -0
- package/src/interpreter/http.ts +308 -0
- package/src/interpreter/index.ts +32 -0
- package/src/interpreter/pagination.ts +207 -0
- package/src/interpreter/progress.test.ts +276 -0
- package/src/interpreter/schema-matcher.test.ts +160 -0
- package/src/interpreter/schema-matcher.ts +168 -0
- package/src/interpreter/signals.ts +73 -0
- package/src/interpreter/step-handlers/for-handler.ts +65 -0
- package/src/interpreter/step-handlers/index.ts +17 -0
- package/src/interpreter/step-handlers/map-handler.ts +24 -0
- package/src/interpreter/step-handlers/match-handler.ts +101 -0
- package/src/interpreter/step-handlers/store-handler.ts +78 -0
- package/src/interpreter/step-handlers/types.ts +17 -0
- package/src/interpreter/step-handlers/validate-handler.ts +30 -0
- package/src/interpreter/step-handlers/webhook-handler.ts +142 -0
- package/src/lexer/index.ts +18 -0
- package/src/lexer/lexer.test.ts +316 -0
- package/src/lexer/tokens.ts +179 -0
- package/src/loader/index.ts +288 -0
- package/src/loader/loader.test.ts +360 -0
- package/src/oas/index.ts +4 -0
- package/src/oas/loader.ts +126 -0
- package/src/oas/oas.test.ts +254 -0
- package/src/oas/validator.ts +299 -0
- package/src/parser/base.ts +124 -0
- package/src/parser/expressions.test.ts +525 -0
- package/src/parser/expressions.ts +314 -0
- package/src/parser/index.ts +3 -0
- package/src/parser/match.test.ts +296 -0
- package/src/parser/parser.test.ts +739 -0
- package/src/parser/parser.ts +1469 -0
- package/src/parser/schedule.test.ts +287 -0
- package/src/parser/webhook.test.ts +248 -0
- package/src/plugin.ts +83 -0
- package/src/scheduler/cron-parser.test.ts +236 -0
- package/src/scheduler/cron-parser.ts +236 -0
- package/src/scheduler/index.ts +10 -0
- package/src/scheduler/scheduler.ts +443 -0
- package/src/scheduler/types.ts +71 -0
- package/src/stores/factory.ts +104 -0
- package/src/stores/file.test.ts +276 -0
- package/src/stores/file.ts +211 -0
- package/src/stores/index.ts +6 -0
- package/src/stores/memory.test.ts +238 -0
- package/src/stores/memory.ts +63 -0
- package/src/stores/postgrest.test.ts +488 -0
- package/src/stores/postgrest.ts +263 -0
- package/src/stores/stores.test.ts +197 -0
- package/src/stores/types.ts +58 -0
- package/src/sync/index.ts +16 -0
- package/src/sync/state.ts +126 -0
- package/src/sync/store.ts +139 -0
- package/src/sync/sync.test.ts +271 -0
- package/src/utils/async.ts +10 -0
- package/src/utils/file.ts +106 -0
- package/src/utils/index.ts +14 -0
- package/src/utils/logger.ts +53 -0
- package/src/utils/path.ts +47 -0
- package/src/webhook/index.ts +15 -0
- package/src/webhook/server.test.ts +253 -0
- package/src/webhook/server.ts +389 -0
- package/src/webhook/store.ts +239 -0
- package/src/webhook/types.ts +93 -0
- package/tsconfig.json +17 -0
- package/vitest.config.ts +39 -0
|
@@ -0,0 +1,314 @@
|
|
|
1
|
+
import { TokenType, type Expression, type QualifiedName, type MatchArm, type Token } from 'vague-lang';
|
|
2
|
+
import { ReqonTokenType } from '../lexer/tokens.js';
|
|
3
|
+
import { ReqonParserBase } from './base.js';
|
|
4
|
+
|
|
5
|
+
// Extended expression type for Reqon's 'is' type checking
|
|
6
|
+
export interface IsExpression {
|
|
7
|
+
type: 'IsExpression';
|
|
8
|
+
operand: Expression;
|
|
9
|
+
typeCheck: string; // 'array', 'object', 'string', 'number', 'boolean', 'null', 'undefined'
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export class ReqonExpressionParser extends ReqonParserBase {
|
|
13
|
+
parseExpression(): Expression {
|
|
14
|
+
return this.parseTernary();
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
parseLogicalExpression(): Expression {
|
|
18
|
+
return this.parseOr();
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
private parseTernary(): Expression {
|
|
22
|
+
const condition = this.parseOr();
|
|
23
|
+
|
|
24
|
+
if (this.match(TokenType.QUESTION)) {
|
|
25
|
+
const consequent = this.parseTernary();
|
|
26
|
+
this.consume(TokenType.COLON, "Expected ':' in ternary expression");
|
|
27
|
+
const alternate = this.parseTernary();
|
|
28
|
+
return {
|
|
29
|
+
type: 'TernaryExpression',
|
|
30
|
+
condition,
|
|
31
|
+
consequent,
|
|
32
|
+
alternate,
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return condition;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
private parseOr(): Expression {
|
|
40
|
+
let left = this.parseAnd();
|
|
41
|
+
|
|
42
|
+
while (this.match(TokenType.OR)) {
|
|
43
|
+
const right = this.parseAnd();
|
|
44
|
+
left = { type: 'LogicalExpression', operator: 'or', left, right };
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return left;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
private parseAnd(): Expression {
|
|
51
|
+
let left = this.parseNot();
|
|
52
|
+
|
|
53
|
+
while (this.match(TokenType.AND)) {
|
|
54
|
+
const right = this.parseNot();
|
|
55
|
+
left = { type: 'LogicalExpression', operator: 'and', left, right };
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return left;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
private parseNot(): Expression {
|
|
62
|
+
if (this.match(TokenType.NOT)) {
|
|
63
|
+
const operand = this.parseNot();
|
|
64
|
+
return { type: 'NotExpression', operand };
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return this.parseSuperposition();
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
private parseSuperposition(): Expression {
|
|
71
|
+
const first = this.parseSuperpositionOption();
|
|
72
|
+
|
|
73
|
+
if (this.check(TokenType.PIPE)) {
|
|
74
|
+
const options = [first];
|
|
75
|
+
|
|
76
|
+
while (this.match(TokenType.PIPE)) {
|
|
77
|
+
options.push(this.parseSuperpositionOption());
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
return { type: 'SuperpositionExpression', options };
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return first.value;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
private parseSuperpositionOption(): { weight?: number; value: Expression } {
|
|
87
|
+
const expr = this.parseComparison();
|
|
88
|
+
|
|
89
|
+
if (expr.type === 'Literal' && expr.dataType === 'number' && this.check(TokenType.COLON)) {
|
|
90
|
+
this.advance();
|
|
91
|
+
const value = this.parseComparison();
|
|
92
|
+
return { weight: expr.value as number, value };
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
return { value: expr };
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
parseComparison(): Expression {
|
|
99
|
+
let left = this.parseRange();
|
|
100
|
+
|
|
101
|
+
while (this.checkAny(TokenType.LT, TokenType.GT, TokenType.LTE, TokenType.GTE, TokenType.DOUBLE_EQUALS, ReqonTokenType.NOT_EQUALS)) {
|
|
102
|
+
const token = this.advance();
|
|
103
|
+
// Normalize the operator value for NOT_EQUALS
|
|
104
|
+
const operator = token.type === ReqonTokenType.NOT_EQUALS ? '!=' : token.value;
|
|
105
|
+
const right = this.parseRange();
|
|
106
|
+
left = { type: 'BinaryExpression', operator, left, right };
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Check for 'is' type checking: expr is array, expr is string, etc.
|
|
110
|
+
if (this.check(ReqonTokenType.IS)) {
|
|
111
|
+
this.advance(); // consume 'is'
|
|
112
|
+
const typeCheck = this.consume(TokenType.IDENTIFIER, "Expected type name after 'is'").value;
|
|
113
|
+
return { type: 'IsExpression', operand: left, typeCheck } as unknown as Expression;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
return left;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
parseRange(): Expression {
|
|
120
|
+
const left = this.parseAdditive();
|
|
121
|
+
|
|
122
|
+
if (this.match(TokenType.DOTDOT)) {
|
|
123
|
+
const right = this.check(TokenType.NUMBER) ? this.parseAdditive() : undefined;
|
|
124
|
+
return { type: 'RangeExpression', min: left, max: right };
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
return left;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
parseAdditive(): Expression {
|
|
131
|
+
let left = this.parseMultiplicative();
|
|
132
|
+
|
|
133
|
+
while (this.checkAny(TokenType.PLUS, TokenType.MINUS)) {
|
|
134
|
+
const operator = this.advance().value;
|
|
135
|
+
const right = this.parseMultiplicative();
|
|
136
|
+
left = { type: 'BinaryExpression', operator, left, right };
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
return left;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
private parseMultiplicative(): Expression {
|
|
143
|
+
let left = this.parseUnary();
|
|
144
|
+
|
|
145
|
+
while (this.checkAny(TokenType.STAR, TokenType.SLASH)) {
|
|
146
|
+
const operator = this.advance().value;
|
|
147
|
+
const right = this.parseUnary();
|
|
148
|
+
left = { type: 'BinaryExpression', operator, left, right };
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
return left;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
private parseUnary(): Expression {
|
|
155
|
+
if (this.match(TokenType.CARET)) {
|
|
156
|
+
const path = this.parseQualifiedName();
|
|
157
|
+
return { type: 'ParentReference', path };
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
if (this.checkAny(TokenType.MINUS, TokenType.PLUS)) {
|
|
161
|
+
const operator = this.advance().value;
|
|
162
|
+
const operand = this.parseUnary();
|
|
163
|
+
return { type: 'UnaryExpression', operator, operand };
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
return this.parseCall();
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
private parseCall(): Expression {
|
|
170
|
+
let expr = this.parsePrimary();
|
|
171
|
+
|
|
172
|
+
while (true) {
|
|
173
|
+
if (this.match(TokenType.LPAREN)) {
|
|
174
|
+
const args: Expression[] = [];
|
|
175
|
+
if (!this.check(TokenType.RPAREN)) {
|
|
176
|
+
do {
|
|
177
|
+
args.push(this.parseExpression());
|
|
178
|
+
} while (this.match(TokenType.COMMA));
|
|
179
|
+
}
|
|
180
|
+
this.consume(TokenType.RPAREN, "Expected ')'");
|
|
181
|
+
|
|
182
|
+
if (expr.type === 'Identifier') {
|
|
183
|
+
expr = { type: 'CallExpression', callee: expr.name, arguments: args };
|
|
184
|
+
} else if (expr.type === 'QualifiedName') {
|
|
185
|
+
expr = { type: 'CallExpression', callee: expr.parts.join('.'), arguments: args };
|
|
186
|
+
}
|
|
187
|
+
} else if (this.match(TokenType.DOT)) {
|
|
188
|
+
const name = this.consume(TokenType.IDENTIFIER, 'Expected property name').value;
|
|
189
|
+
if (expr.type === 'Identifier') {
|
|
190
|
+
expr = { type: 'QualifiedName', parts: [expr.name, name] };
|
|
191
|
+
} else if (expr.type === 'QualifiedName') {
|
|
192
|
+
expr.parts.push(name);
|
|
193
|
+
}
|
|
194
|
+
} else {
|
|
195
|
+
break;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
return expr;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
parsePrimary(): Expression {
|
|
203
|
+
// Match expression
|
|
204
|
+
if (this.match(TokenType.MATCH)) {
|
|
205
|
+
return this.parseMatchExpression();
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// Ordered sequence
|
|
209
|
+
if (this.match(TokenType.LBRACKET)) {
|
|
210
|
+
return this.parseOrderedSequence();
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// Any of expression
|
|
214
|
+
if (this.match(TokenType.ANY)) {
|
|
215
|
+
this.consume(TokenType.OF, "Expected 'of'");
|
|
216
|
+
const collection = this.parseExpression();
|
|
217
|
+
let condition: Expression | undefined;
|
|
218
|
+
if (this.match(TokenType.WHERE)) {
|
|
219
|
+
condition = this.parseExpression();
|
|
220
|
+
}
|
|
221
|
+
return { type: 'AnyOfExpression', collection, condition };
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
// Parenthesized expression
|
|
225
|
+
if (this.match(TokenType.LPAREN)) {
|
|
226
|
+
const expr = this.parseExpression();
|
|
227
|
+
this.consume(TokenType.RPAREN, "Expected ')'");
|
|
228
|
+
return expr;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// Number literal
|
|
232
|
+
if (this.check(TokenType.NUMBER)) {
|
|
233
|
+
const value = parseFloat(this.advance().value);
|
|
234
|
+
return { type: 'Literal', value, dataType: 'number' };
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
// String literal
|
|
238
|
+
if (this.check(TokenType.STRING)) {
|
|
239
|
+
const value = this.advance().value;
|
|
240
|
+
return { type: 'Literal', value, dataType: 'string' };
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
// Null literal
|
|
244
|
+
if (this.match(TokenType.NULL)) {
|
|
245
|
+
return { type: 'Literal', value: null, dataType: 'null' };
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
// Boolean literals
|
|
249
|
+
if (this.match(TokenType.TRUE)) {
|
|
250
|
+
return { type: 'Literal', value: true, dataType: 'boolean' };
|
|
251
|
+
}
|
|
252
|
+
if (this.match(TokenType.FALSE)) {
|
|
253
|
+
return { type: 'Literal', value: false, dataType: 'boolean' };
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
// Identifier (including HTTP method tokens that can be used as identifiers)
|
|
257
|
+
if (this.checkIdentifier()) {
|
|
258
|
+
const name = this.advance().value;
|
|
259
|
+
return { type: 'Identifier', name };
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// .field shorthand
|
|
263
|
+
if (this.match(TokenType.DOT)) {
|
|
264
|
+
const name = this.consumeIdentifier("Expected field name after '.'").value;
|
|
265
|
+
return { type: 'Identifier', name };
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
throw this.error(`Unexpected token: ${this.peek().value}`);
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
private parseMatchExpression(): Expression {
|
|
272
|
+
const value = this.parsePrimary();
|
|
273
|
+
this.consume(TokenType.LBRACE, "Expected '{'");
|
|
274
|
+
|
|
275
|
+
const arms: MatchArm[] = [];
|
|
276
|
+
while (!this.check(TokenType.RBRACE) && !this.isAtEnd()) {
|
|
277
|
+
const pattern = this.parseExpression();
|
|
278
|
+
this.consume(TokenType.ARROW, "Expected '=>'");
|
|
279
|
+
const result = this.parseExpression();
|
|
280
|
+
arms.push({ pattern, result });
|
|
281
|
+
this.match(TokenType.COMMA);
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
this.consume(TokenType.RBRACE, "Expected '}'");
|
|
285
|
+
return { type: 'MatchExpression', value, arms };
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
private parseOrderedSequence(): Expression {
|
|
289
|
+
const elements: Expression[] = [];
|
|
290
|
+
|
|
291
|
+
if (this.check(TokenType.RBRACKET)) {
|
|
292
|
+
throw this.error('Ordered sequence cannot be empty');
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
do {
|
|
296
|
+
elements.push(this.parseExpression());
|
|
297
|
+
} while (this.match(TokenType.COMMA));
|
|
298
|
+
|
|
299
|
+
this.consume(TokenType.RBRACKET, "Expected ']'");
|
|
300
|
+
|
|
301
|
+
return { type: 'OrderedSequenceType', elements };
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
parseQualifiedName(): QualifiedName {
|
|
305
|
+
const parts: string[] = [];
|
|
306
|
+
parts.push(this.consume(TokenType.IDENTIFIER, 'Expected identifier').value);
|
|
307
|
+
|
|
308
|
+
while (this.match(TokenType.DOT)) {
|
|
309
|
+
parts.push(this.consume(TokenType.IDENTIFIER, 'Expected identifier').value);
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
return { type: 'QualifiedName', parts };
|
|
313
|
+
}
|
|
314
|
+
}
|
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { ReqonLexer } from '../lexer/index.js';
|
|
3
|
+
import { ReqonParser } from './parser.js';
|
|
4
|
+
import type { ActionDefinition, MatchStep } from '../ast/nodes.js';
|
|
5
|
+
|
|
6
|
+
describe('Match Step Parsing', () => {
|
|
7
|
+
function parseAction(source: string): ActionDefinition {
|
|
8
|
+
const lexer = new ReqonLexer(source);
|
|
9
|
+
const tokens = lexer.tokenize();
|
|
10
|
+
const parser = new ReqonParser(tokens, source);
|
|
11
|
+
const program = parser.parse();
|
|
12
|
+
|
|
13
|
+
const action = program.statements.find(
|
|
14
|
+
(s) => s.type === 'ActionDefinition'
|
|
15
|
+
) as ActionDefinition;
|
|
16
|
+
|
|
17
|
+
if (!action) {
|
|
18
|
+
throw new Error('No action found in program');
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
return action;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
describe('basic match step', () => {
|
|
25
|
+
it('parses match with single schema and flow directive', () => {
|
|
26
|
+
const action = parseAction(`
|
|
27
|
+
action HandleResponse {
|
|
28
|
+
match response {
|
|
29
|
+
SuccessResponse -> continue
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
`);
|
|
33
|
+
|
|
34
|
+
expect(action.steps).toHaveLength(1);
|
|
35
|
+
const matchStep = action.steps[0] as MatchStep;
|
|
36
|
+
expect(matchStep.type).toBe('MatchStep');
|
|
37
|
+
expect(matchStep.arms).toHaveLength(1);
|
|
38
|
+
expect(matchStep.arms[0].schema).toBe('SuccessResponse');
|
|
39
|
+
expect(matchStep.arms[0].flow).toEqual({ type: 'continue' });
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it('parses match with multiple schemas', () => {
|
|
43
|
+
const action = parseAction(`
|
|
44
|
+
action HandleResponse {
|
|
45
|
+
match response {
|
|
46
|
+
SuccessResponse -> continue,
|
|
47
|
+
NotFoundError -> skip,
|
|
48
|
+
ServerError -> abort "Something went wrong"
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
`);
|
|
52
|
+
|
|
53
|
+
expect(action.steps).toHaveLength(1);
|
|
54
|
+
const matchStep = action.steps[0] as MatchStep;
|
|
55
|
+
expect(matchStep.arms).toHaveLength(3);
|
|
56
|
+
|
|
57
|
+
expect(matchStep.arms[0].schema).toBe('SuccessResponse');
|
|
58
|
+
expect(matchStep.arms[0].flow).toEqual({ type: 'continue' });
|
|
59
|
+
|
|
60
|
+
expect(matchStep.arms[1].schema).toBe('NotFoundError');
|
|
61
|
+
expect(matchStep.arms[1].flow).toEqual({ type: 'skip' });
|
|
62
|
+
|
|
63
|
+
expect(matchStep.arms[2].schema).toBe('ServerError');
|
|
64
|
+
expect(matchStep.arms[2].flow).toEqual({
|
|
65
|
+
type: 'abort',
|
|
66
|
+
message: 'Something went wrong',
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
describe('flow directives', () => {
|
|
72
|
+
it('parses continue directive', () => {
|
|
73
|
+
const action = parseAction(`
|
|
74
|
+
action Test {
|
|
75
|
+
match response {
|
|
76
|
+
Schema -> continue
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
`);
|
|
80
|
+
|
|
81
|
+
const matchStep = action.steps[0] as MatchStep;
|
|
82
|
+
expect(matchStep.arms[0].flow).toEqual({ type: 'continue' });
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
it('parses skip directive', () => {
|
|
86
|
+
const action = parseAction(`
|
|
87
|
+
action Test {
|
|
88
|
+
match response {
|
|
89
|
+
Schema -> skip
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
`);
|
|
93
|
+
|
|
94
|
+
const matchStep = action.steps[0] as MatchStep;
|
|
95
|
+
expect(matchStep.arms[0].flow).toEqual({ type: 'skip' });
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
it('parses abort directive without message', () => {
|
|
99
|
+
const action = parseAction(`
|
|
100
|
+
action Test {
|
|
101
|
+
match response {
|
|
102
|
+
Schema -> abort
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
`);
|
|
106
|
+
|
|
107
|
+
const matchStep = action.steps[0] as MatchStep;
|
|
108
|
+
expect(matchStep.arms[0].flow).toEqual({ type: 'abort' });
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
it('parses abort directive with message', () => {
|
|
112
|
+
const action = parseAction(`
|
|
113
|
+
action Test {
|
|
114
|
+
match response {
|
|
115
|
+
Schema -> abort "Error occurred"
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
`);
|
|
119
|
+
|
|
120
|
+
const matchStep = action.steps[0] as MatchStep;
|
|
121
|
+
expect(matchStep.arms[0].flow).toEqual({
|
|
122
|
+
type: 'abort',
|
|
123
|
+
message: 'Error occurred',
|
|
124
|
+
});
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
it('parses queue directive without target', () => {
|
|
128
|
+
const action = parseAction(`
|
|
129
|
+
action Test {
|
|
130
|
+
match response {
|
|
131
|
+
Schema -> queue
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
`);
|
|
135
|
+
|
|
136
|
+
const matchStep = action.steps[0] as MatchStep;
|
|
137
|
+
expect(matchStep.arms[0].flow).toEqual({ type: 'queue' });
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
it('parses queue directive with target', () => {
|
|
141
|
+
const action = parseAction(`
|
|
142
|
+
action Test {
|
|
143
|
+
match response {
|
|
144
|
+
Schema -> queue deadLetterQueue
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
`);
|
|
148
|
+
|
|
149
|
+
const matchStep = action.steps[0] as MatchStep;
|
|
150
|
+
expect(matchStep.arms[0].flow).toEqual({
|
|
151
|
+
type: 'queue',
|
|
152
|
+
target: 'deadLetterQueue',
|
|
153
|
+
});
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
it('parses jump directive without then', () => {
|
|
157
|
+
const action = parseAction(`
|
|
158
|
+
action Test {
|
|
159
|
+
match response {
|
|
160
|
+
Schema -> jump RefreshToken
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
`);
|
|
164
|
+
|
|
165
|
+
const matchStep = action.steps[0] as MatchStep;
|
|
166
|
+
expect(matchStep.arms[0].flow).toEqual({
|
|
167
|
+
type: 'jump',
|
|
168
|
+
action: 'RefreshToken',
|
|
169
|
+
});
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
it('parses jump directive with then retry', () => {
|
|
173
|
+
const action = parseAction(`
|
|
174
|
+
action Test {
|
|
175
|
+
match response {
|
|
176
|
+
Schema -> jump RefreshToken then retry
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
`);
|
|
180
|
+
|
|
181
|
+
const matchStep = action.steps[0] as MatchStep;
|
|
182
|
+
expect(matchStep.arms[0].flow).toEqual({
|
|
183
|
+
type: 'jump',
|
|
184
|
+
action: 'RefreshToken',
|
|
185
|
+
then: 'retry',
|
|
186
|
+
});
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
it('parses jump directive with then continue', () => {
|
|
190
|
+
const action = parseAction(`
|
|
191
|
+
action Test {
|
|
192
|
+
match response {
|
|
193
|
+
Schema -> jump RefreshToken then continue
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
`);
|
|
197
|
+
|
|
198
|
+
const matchStep = action.steps[0] as MatchStep;
|
|
199
|
+
expect(matchStep.arms[0].flow).toEqual({
|
|
200
|
+
type: 'jump',
|
|
201
|
+
action: 'RefreshToken',
|
|
202
|
+
then: 'continue',
|
|
203
|
+
});
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
it('parses retry directive without config', () => {
|
|
207
|
+
const action = parseAction(`
|
|
208
|
+
action Test {
|
|
209
|
+
match response {
|
|
210
|
+
Schema -> retry
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
`);
|
|
214
|
+
|
|
215
|
+
const matchStep = action.steps[0] as MatchStep;
|
|
216
|
+
expect(matchStep.arms[0].flow).toEqual({ type: 'retry' });
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
it('parses retry directive with config', () => {
|
|
220
|
+
const action = parseAction(`
|
|
221
|
+
action Test {
|
|
222
|
+
match response {
|
|
223
|
+
Schema -> retry { maxAttempts: 5, backoff: exponential, initialDelay: 1000 }
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
`);
|
|
227
|
+
|
|
228
|
+
const matchStep = action.steps[0] as MatchStep;
|
|
229
|
+
expect(matchStep.arms[0].flow?.type).toBe('retry');
|
|
230
|
+
if (matchStep.arms[0].flow?.type === 'retry') {
|
|
231
|
+
expect(matchStep.arms[0].flow.backoff).toEqual({
|
|
232
|
+
maxAttempts: 5,
|
|
233
|
+
backoff: 'exponential',
|
|
234
|
+
initialDelay: 1000,
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
});
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
describe('match with steps', () => {
|
|
241
|
+
it('parses match with single inline step', () => {
|
|
242
|
+
const action = parseAction(`
|
|
243
|
+
action Test {
|
|
244
|
+
match response {
|
|
245
|
+
SuccessResponse -> store response -> cache
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
`);
|
|
249
|
+
|
|
250
|
+
const matchStep = action.steps[0] as MatchStep;
|
|
251
|
+
expect(matchStep.arms[0].schema).toBe('SuccessResponse');
|
|
252
|
+
expect(matchStep.arms[0].steps).toHaveLength(1);
|
|
253
|
+
expect(matchStep.arms[0].steps![0].type).toBe('StoreStep');
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
it('parses match with step block', () => {
|
|
257
|
+
const action = parseAction(`
|
|
258
|
+
action Test {
|
|
259
|
+
match response {
|
|
260
|
+
SuccessResponse -> {
|
|
261
|
+
store response -> cache,
|
|
262
|
+
store response -> backup
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
`);
|
|
267
|
+
|
|
268
|
+
const matchStep = action.steps[0] as MatchStep;
|
|
269
|
+
expect(matchStep.arms[0].schema).toBe('SuccessResponse');
|
|
270
|
+
expect(matchStep.arms[0].steps).toHaveLength(2);
|
|
271
|
+
expect(matchStep.arms[0].steps![0].type).toBe('StoreStep');
|
|
272
|
+
expect(matchStep.arms[0].steps![1].type).toBe('StoreStep');
|
|
273
|
+
});
|
|
274
|
+
});
|
|
275
|
+
|
|
276
|
+
describe('wildcard pattern', () => {
|
|
277
|
+
it('parses wildcard pattern at end', () => {
|
|
278
|
+
const action = parseAction(`
|
|
279
|
+
action Test {
|
|
280
|
+
match response {
|
|
281
|
+
SuccessResponse -> continue,
|
|
282
|
+
_ -> abort "Unexpected response"
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
`);
|
|
286
|
+
|
|
287
|
+
const matchStep = action.steps[0] as MatchStep;
|
|
288
|
+
expect(matchStep.arms).toHaveLength(2);
|
|
289
|
+
expect(matchStep.arms[1].schema).toBe('_');
|
|
290
|
+
expect(matchStep.arms[1].flow).toEqual({
|
|
291
|
+
type: 'abort',
|
|
292
|
+
message: 'Unexpected response',
|
|
293
|
+
});
|
|
294
|
+
});
|
|
295
|
+
});
|
|
296
|
+
});
|