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,136 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Execution State - Durable state for resumable missions
|
|
3
|
+
*
|
|
4
|
+
* Tracks progress through pipeline stages, enabling:
|
|
5
|
+
* - Resume from last successful step after failures
|
|
6
|
+
* - Idempotent re-execution
|
|
7
|
+
* - Progress visibility
|
|
8
|
+
*/
|
|
9
|
+
export type ExecutionStatus = 'pending' | 'running' | 'completed' | 'failed' | 'paused';
|
|
10
|
+
export type StageStatus = 'pending' | 'running' | 'completed' | 'failed' | 'skipped';
|
|
11
|
+
/**
|
|
12
|
+
* State for a single pipeline stage (action execution)
|
|
13
|
+
*/
|
|
14
|
+
export interface StageState {
|
|
15
|
+
/** Action name */
|
|
16
|
+
action: string;
|
|
17
|
+
/** Current status */
|
|
18
|
+
status: StageStatus;
|
|
19
|
+
/** When this stage started */
|
|
20
|
+
startedAt?: Date;
|
|
21
|
+
/** When this stage completed/failed */
|
|
22
|
+
completedAt?: Date;
|
|
23
|
+
/** Error message if failed */
|
|
24
|
+
error?: string;
|
|
25
|
+
/** Number of items processed (for actions with loops) */
|
|
26
|
+
itemsProcessed?: number;
|
|
27
|
+
/** Total items to process */
|
|
28
|
+
itemsTotal?: number;
|
|
29
|
+
/** Retry attempt number (0 = first attempt) */
|
|
30
|
+
attempt: number;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Checkpoint within an action (for resuming mid-action)
|
|
34
|
+
*/
|
|
35
|
+
export interface Checkpoint {
|
|
36
|
+
/** Stage index in pipeline */
|
|
37
|
+
stageIndex: number;
|
|
38
|
+
/** Step index within action */
|
|
39
|
+
stepIndex: number;
|
|
40
|
+
/** For loops: current item index */
|
|
41
|
+
itemIndex?: number;
|
|
42
|
+
/** Saved context variables */
|
|
43
|
+
variables?: Record<string, unknown>;
|
|
44
|
+
/** Timestamp */
|
|
45
|
+
createdAt: Date;
|
|
46
|
+
/** Webhook wait state (for resuming webhook waits) */
|
|
47
|
+
webhookWait?: WebhookWaitState;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* State for waiting on webhook callbacks
|
|
51
|
+
*/
|
|
52
|
+
export interface WebhookWaitState {
|
|
53
|
+
/** Webhook registration ID */
|
|
54
|
+
registrationId: string;
|
|
55
|
+
/** Path for the webhook endpoint */
|
|
56
|
+
path: string;
|
|
57
|
+
/** Full webhook URL */
|
|
58
|
+
webhookUrl: string;
|
|
59
|
+
/** Number of expected events */
|
|
60
|
+
expectedEvents: number;
|
|
61
|
+
/** Number of events received so far */
|
|
62
|
+
receivedEvents: number;
|
|
63
|
+
/** When the wait started */
|
|
64
|
+
waitStartedAt: Date;
|
|
65
|
+
/** When the wait expires */
|
|
66
|
+
expiresAt: Date;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Complete execution state for a mission run
|
|
70
|
+
*/
|
|
71
|
+
export interface ExecutionState {
|
|
72
|
+
/** Unique execution ID */
|
|
73
|
+
id: string;
|
|
74
|
+
/** Mission name */
|
|
75
|
+
mission: string;
|
|
76
|
+
/** Overall status */
|
|
77
|
+
status: ExecutionStatus;
|
|
78
|
+
/** When execution started */
|
|
79
|
+
startedAt: Date;
|
|
80
|
+
/** When execution completed/failed */
|
|
81
|
+
completedAt?: Date;
|
|
82
|
+
/** Total duration in ms */
|
|
83
|
+
duration?: number;
|
|
84
|
+
/** State of each pipeline stage */
|
|
85
|
+
stages: StageState[];
|
|
86
|
+
/** Latest checkpoint for resume */
|
|
87
|
+
checkpoint?: Checkpoint;
|
|
88
|
+
/** Execution errors */
|
|
89
|
+
errors: ExecutionStateError[];
|
|
90
|
+
/** Metadata (user-provided context) */
|
|
91
|
+
metadata?: Record<string, unknown>;
|
|
92
|
+
}
|
|
93
|
+
export interface ExecutionStateError {
|
|
94
|
+
stageIndex: number;
|
|
95
|
+
action: string;
|
|
96
|
+
step: string;
|
|
97
|
+
message: string;
|
|
98
|
+
timestamp: Date;
|
|
99
|
+
attempt: number;
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Options for creating a new execution
|
|
103
|
+
*/
|
|
104
|
+
export interface CreateExecutionOptions {
|
|
105
|
+
/** Mission name */
|
|
106
|
+
mission: string;
|
|
107
|
+
/** Pipeline stage names (actions) */
|
|
108
|
+
stages: string[];
|
|
109
|
+
/** Optional metadata */
|
|
110
|
+
metadata?: Record<string, unknown>;
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Generate a unique execution ID
|
|
114
|
+
*/
|
|
115
|
+
export declare function generateExecutionId(): string;
|
|
116
|
+
/**
|
|
117
|
+
* Create initial execution state
|
|
118
|
+
*/
|
|
119
|
+
export declare function createExecutionState(options: CreateExecutionOptions): ExecutionState;
|
|
120
|
+
/**
|
|
121
|
+
* Find the stage to resume from
|
|
122
|
+
* Returns the index of the first non-completed stage, or -1 if all complete
|
|
123
|
+
*/
|
|
124
|
+
export declare function findResumePoint(state: ExecutionState): number;
|
|
125
|
+
/**
|
|
126
|
+
* Check if execution can be resumed
|
|
127
|
+
*/
|
|
128
|
+
export declare function canResume(state: ExecutionState): boolean;
|
|
129
|
+
/**
|
|
130
|
+
* Calculate execution progress as percentage
|
|
131
|
+
*/
|
|
132
|
+
export declare function getProgress(state: ExecutionState): number;
|
|
133
|
+
/**
|
|
134
|
+
* Get a summary of the execution state
|
|
135
|
+
*/
|
|
136
|
+
export declare function getExecutionSummary(state: ExecutionState): string;
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Execution State - Durable state for resumable missions
|
|
3
|
+
*
|
|
4
|
+
* Tracks progress through pipeline stages, enabling:
|
|
5
|
+
* - Resume from last successful step after failures
|
|
6
|
+
* - Idempotent re-execution
|
|
7
|
+
* - Progress visibility
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* Generate a unique execution ID
|
|
11
|
+
*/
|
|
12
|
+
export function generateExecutionId() {
|
|
13
|
+
const timestamp = Date.now().toString(36);
|
|
14
|
+
const random = Math.random().toString(36).substring(2, 8);
|
|
15
|
+
return `exec_${timestamp}_${random}`;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Create initial execution state
|
|
19
|
+
*/
|
|
20
|
+
export function createExecutionState(options) {
|
|
21
|
+
return {
|
|
22
|
+
id: generateExecutionId(),
|
|
23
|
+
mission: options.mission,
|
|
24
|
+
status: 'pending',
|
|
25
|
+
startedAt: new Date(),
|
|
26
|
+
stages: options.stages.map((action) => ({
|
|
27
|
+
action,
|
|
28
|
+
status: 'pending',
|
|
29
|
+
attempt: 0,
|
|
30
|
+
})),
|
|
31
|
+
errors: [],
|
|
32
|
+
metadata: options.metadata,
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Find the stage to resume from
|
|
37
|
+
* Returns the index of the first non-completed stage, or -1 if all complete
|
|
38
|
+
*/
|
|
39
|
+
export function findResumePoint(state) {
|
|
40
|
+
// If there's a checkpoint, use it
|
|
41
|
+
if (state.checkpoint) {
|
|
42
|
+
return state.checkpoint.stageIndex;
|
|
43
|
+
}
|
|
44
|
+
// Otherwise, find first non-completed stage
|
|
45
|
+
for (let i = 0; i < state.stages.length; i++) {
|
|
46
|
+
const stage = state.stages[i];
|
|
47
|
+
if (stage.status !== 'completed' && stage.status !== 'skipped') {
|
|
48
|
+
return i;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return -1; // All stages complete
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Check if execution can be resumed
|
|
55
|
+
*/
|
|
56
|
+
export function canResume(state) {
|
|
57
|
+
return state.status === 'failed' || state.status === 'paused';
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Calculate execution progress as percentage
|
|
61
|
+
*/
|
|
62
|
+
export function getProgress(state) {
|
|
63
|
+
if (state.stages.length === 0)
|
|
64
|
+
return 100;
|
|
65
|
+
const completed = state.stages.filter((s) => s.status === 'completed' || s.status === 'skipped').length;
|
|
66
|
+
return Math.round((completed / state.stages.length) * 100);
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Get a summary of the execution state
|
|
70
|
+
*/
|
|
71
|
+
export function getExecutionSummary(state) {
|
|
72
|
+
const progress = getProgress(state);
|
|
73
|
+
const completed = state.stages.filter((s) => s.status === 'completed').length;
|
|
74
|
+
const failed = state.stages.filter((s) => s.status === 'failed').length;
|
|
75
|
+
const pending = state.stages.filter((s) => s.status === 'pending').length;
|
|
76
|
+
let summary = `${state.mission} [${state.id}]: ${state.status} (${progress}%)`;
|
|
77
|
+
summary += ` - ${completed} completed, ${failed} failed, ${pending} pending`;
|
|
78
|
+
if (state.duration) {
|
|
79
|
+
summary += ` - ${Math.round(state.duration / 1000)}s`;
|
|
80
|
+
}
|
|
81
|
+
return summary;
|
|
82
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import type { ExecutionState } from './state.js';
|
|
2
|
+
/**
|
|
3
|
+
* Execution state store interface
|
|
4
|
+
*/
|
|
5
|
+
export interface ExecutionStore {
|
|
6
|
+
/** Save execution state */
|
|
7
|
+
save(state: ExecutionState): Promise<void>;
|
|
8
|
+
/** Load execution state by ID */
|
|
9
|
+
load(id: string): Promise<ExecutionState | null>;
|
|
10
|
+
/** List all executions for a mission */
|
|
11
|
+
listByMission(mission: string): Promise<ExecutionState[]>;
|
|
12
|
+
/** List recent executions */
|
|
13
|
+
listRecent(limit?: number): Promise<ExecutionState[]>;
|
|
14
|
+
/** Delete execution state */
|
|
15
|
+
delete(id: string): Promise<void>;
|
|
16
|
+
/** Find the latest execution for a mission */
|
|
17
|
+
findLatest(mission: string): Promise<ExecutionState | null>;
|
|
18
|
+
/** Find resumable executions (failed/paused) */
|
|
19
|
+
findResumable(mission: string): Promise<ExecutionState[]>;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* File-based execution state store
|
|
23
|
+
* Stores each execution as a JSON file in .reqon-data/executions/
|
|
24
|
+
*/
|
|
25
|
+
export declare class FileExecutionStore implements ExecutionStore {
|
|
26
|
+
private baseDir;
|
|
27
|
+
private initialized;
|
|
28
|
+
constructor(baseDir?: string);
|
|
29
|
+
private getFilePath;
|
|
30
|
+
private deserialize;
|
|
31
|
+
save(state: ExecutionState): Promise<void>;
|
|
32
|
+
load(id: string): Promise<ExecutionState | null>;
|
|
33
|
+
listByMission(mission: string): Promise<ExecutionState[]>;
|
|
34
|
+
listRecent(limit?: number): Promise<ExecutionState[]>;
|
|
35
|
+
delete(id: string): Promise<void>;
|
|
36
|
+
findLatest(mission: string): Promise<ExecutionState | null>;
|
|
37
|
+
findResumable(mission: string): Promise<ExecutionState[]>;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* In-memory execution store (for testing)
|
|
41
|
+
*/
|
|
42
|
+
export declare class MemoryExecutionStore implements ExecutionStore {
|
|
43
|
+
private states;
|
|
44
|
+
save(state: ExecutionState): Promise<void>;
|
|
45
|
+
load(id: string): Promise<ExecutionState | null>;
|
|
46
|
+
listByMission(mission: string): Promise<ExecutionState[]>;
|
|
47
|
+
listRecent(limit?: number): Promise<ExecutionState[]>;
|
|
48
|
+
delete(id: string): Promise<void>;
|
|
49
|
+
findLatest(mission: string): Promise<ExecutionState | null>;
|
|
50
|
+
findResumable(mission: string): Promise<ExecutionState[]>;
|
|
51
|
+
clear(): void;
|
|
52
|
+
}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { join } from 'node:path';
|
|
2
|
+
import { ensureDirectory, writeJsonFile, readJsonFile, listFiles, deleteFile, restoreDates, restoreDatesInArray, } from '../utils/file.js';
|
|
3
|
+
/**
|
|
4
|
+
* File-based execution state store
|
|
5
|
+
* Stores each execution as a JSON file in .reqon-data/executions/
|
|
6
|
+
*/
|
|
7
|
+
export class FileExecutionStore {
|
|
8
|
+
baseDir;
|
|
9
|
+
initialized;
|
|
10
|
+
constructor(baseDir = '.reqon-data/executions') {
|
|
11
|
+
this.baseDir = baseDir;
|
|
12
|
+
this.initialized = ensureDirectory(this.baseDir);
|
|
13
|
+
}
|
|
14
|
+
getFilePath(id) {
|
|
15
|
+
return join(this.baseDir, `${id}.json`);
|
|
16
|
+
}
|
|
17
|
+
deserialize(parsed) {
|
|
18
|
+
// Restore Date objects
|
|
19
|
+
restoreDates(parsed, ['startedAt', 'completedAt']);
|
|
20
|
+
if (parsed.checkpoint && typeof parsed.checkpoint === 'object') {
|
|
21
|
+
restoreDates(parsed.checkpoint, ['createdAt']);
|
|
22
|
+
}
|
|
23
|
+
restoreDatesInArray(parsed.stages, ['startedAt', 'completedAt']);
|
|
24
|
+
restoreDatesInArray(parsed.errors, ['timestamp']);
|
|
25
|
+
return parsed;
|
|
26
|
+
}
|
|
27
|
+
async save(state) {
|
|
28
|
+
await this.initialized;
|
|
29
|
+
const filePath = this.getFilePath(state.id);
|
|
30
|
+
await writeJsonFile(filePath, state);
|
|
31
|
+
}
|
|
32
|
+
async load(id) {
|
|
33
|
+
await this.initialized;
|
|
34
|
+
const filePath = this.getFilePath(id);
|
|
35
|
+
const parsed = await readJsonFile(filePath);
|
|
36
|
+
return parsed ? this.deserialize(parsed) : null;
|
|
37
|
+
}
|
|
38
|
+
async listByMission(mission) {
|
|
39
|
+
const all = await this.listRecent();
|
|
40
|
+
return all.filter((s) => s.mission === mission);
|
|
41
|
+
}
|
|
42
|
+
async listRecent(limit = 50) {
|
|
43
|
+
await this.initialized;
|
|
44
|
+
const files = await listFiles(this.baseDir, '.json');
|
|
45
|
+
const states = [];
|
|
46
|
+
for (const file of files) {
|
|
47
|
+
const parsed = await readJsonFile(file);
|
|
48
|
+
if (parsed) {
|
|
49
|
+
states.push(this.deserialize(parsed));
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
// Sort by start time, newest first
|
|
53
|
+
states.sort((a, b) => b.startedAt.getTime() - a.startedAt.getTime());
|
|
54
|
+
return states.slice(0, limit);
|
|
55
|
+
}
|
|
56
|
+
async delete(id) {
|
|
57
|
+
await this.initialized;
|
|
58
|
+
await deleteFile(this.getFilePath(id));
|
|
59
|
+
}
|
|
60
|
+
async findLatest(mission) {
|
|
61
|
+
const executions = await this.listByMission(mission);
|
|
62
|
+
return executions[0] ?? null;
|
|
63
|
+
}
|
|
64
|
+
async findResumable(mission) {
|
|
65
|
+
const executions = await this.listByMission(mission);
|
|
66
|
+
return executions.filter((e) => e.status === 'failed' || e.status === 'paused');
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* In-memory execution store (for testing)
|
|
71
|
+
*/
|
|
72
|
+
export class MemoryExecutionStore {
|
|
73
|
+
states = new Map();
|
|
74
|
+
async save(state) {
|
|
75
|
+
// Deep clone to avoid reference issues
|
|
76
|
+
this.states.set(state.id, JSON.parse(JSON.stringify(state)));
|
|
77
|
+
}
|
|
78
|
+
async load(id) {
|
|
79
|
+
const state = this.states.get(id);
|
|
80
|
+
if (!state)
|
|
81
|
+
return null;
|
|
82
|
+
// Restore Date objects
|
|
83
|
+
const restored = JSON.parse(JSON.stringify(state));
|
|
84
|
+
restored.startedAt = new Date(restored.startedAt);
|
|
85
|
+
if (restored.completedAt) {
|
|
86
|
+
restored.completedAt = new Date(restored.completedAt);
|
|
87
|
+
}
|
|
88
|
+
return restored;
|
|
89
|
+
}
|
|
90
|
+
async listByMission(mission) {
|
|
91
|
+
const all = await this.listRecent();
|
|
92
|
+
return all.filter((s) => s.mission === mission);
|
|
93
|
+
}
|
|
94
|
+
async listRecent(limit = 50) {
|
|
95
|
+
const states = Array.from(this.states.values()).map((s) => {
|
|
96
|
+
const restored = JSON.parse(JSON.stringify(s));
|
|
97
|
+
restored.startedAt = new Date(restored.startedAt);
|
|
98
|
+
if (restored.completedAt) {
|
|
99
|
+
restored.completedAt = new Date(restored.completedAt);
|
|
100
|
+
}
|
|
101
|
+
return restored;
|
|
102
|
+
});
|
|
103
|
+
states.sort((a, b) => b.startedAt.getTime() - a.startedAt.getTime());
|
|
104
|
+
return states.slice(0, limit);
|
|
105
|
+
}
|
|
106
|
+
async delete(id) {
|
|
107
|
+
this.states.delete(id);
|
|
108
|
+
}
|
|
109
|
+
async findLatest(mission) {
|
|
110
|
+
const executions = await this.listByMission(mission);
|
|
111
|
+
return executions[0] ?? null;
|
|
112
|
+
}
|
|
113
|
+
async findResumable(mission) {
|
|
114
|
+
const executions = await this.listByMission(mission);
|
|
115
|
+
return executions.filter((e) => e.status === 'failed' || e.status === 'paused');
|
|
116
|
+
}
|
|
117
|
+
clear() {
|
|
118
|
+
this.states.clear();
|
|
119
|
+
}
|
|
120
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export { ReqonLexer, ReqonTokenType, type ReqonToken } from './lexer/index.js';
|
|
2
|
+
export { reqonPlugin, registerReqonPlugin } from './plugin.js';
|
|
3
|
+
export { ReqonParser } from './parser/index.js';
|
|
4
|
+
export * from './ast/index.js';
|
|
5
|
+
export { MissionExecutor, HttpClient, BearerAuthProvider, OAuth2AuthProvider, createContext, evaluate, type ExecutionResult, type ExecutionError, type ExecutorConfig, type ExecutionContext, type ProgressCallbacks, type ExecutionStartEvent, type ExecutionCompleteEvent, type StageStartEvent, type StageCompleteEvent, } from './interpreter/index.js';
|
|
6
|
+
export { MemoryStore, FileStore, createStore, type StoreAdapter, type StoreFilter, type StoreConfig, } from './stores/index.js';
|
|
7
|
+
export { createExecutionState, findResumePoint, canResume, getProgress, getExecutionSummary, FileExecutionStore, MemoryExecutionStore, type ExecutionState, type ExecutionStore, type StageState, } from './execution/index.js';
|
|
8
|
+
export { Scheduler, parseCronExpression, getNextRunTime, intervalToMs, shouldRunNow, type ScheduledJob, type SchedulerState, type ScheduleEvent, type SchedulerCallbacks, type SchedulerConfig, type ScheduledMission, } from './scheduler/index.js';
|
|
9
|
+
export { generateCheckpointKey, formatSinceDate, parseSinceDate, EPOCH, FileSyncStore, MemorySyncStore, type SyncCheckpoint, type SyncStore, } from './sync/index.js';
|
|
10
|
+
export { ReqonError, ParseError, LexerError, RuntimeError, ValidationError, formatErrors, getSourceLine, getSourceContext, type SourceLocation, type ErrorContext, } from './errors/index.js';
|
|
11
|
+
export { loadMission, isMissionFolder, getMissionName, type LoadResult, type LoadOptions, } from './loader/index.js';
|
|
12
|
+
export { loadEnv, loadCredentials, resolveCredentials, resolveEnvString, hasEnvReference, credentialsFromEnv, type CredentialsConfig, type LoadEnvResult, type AuthCredentials, type SourceCredentials, } from './auth/credentials.js';
|
|
13
|
+
export { WebhookServer, MemoryWebhookStore, FileWebhookStore, type WebhookStore, type WebhookServerConfig, type WebhookServerCallbacks, type WebhookRegistration, type WebhookEvent, type WaitResult, } from './webhook/index.js';
|
|
14
|
+
import { type ExecutorConfig } from './interpreter/index.js';
|
|
15
|
+
import type { ReqonProgram } from './ast/index.js';
|
|
16
|
+
export declare function parse(source: string, filePath?: string): ReqonProgram;
|
|
17
|
+
export declare function execute(source: string, config?: ExecutorConfig): Promise<import('./interpreter/index.js').ExecutionResult>;
|
|
18
|
+
export declare function fromFile(filePath: string, config?: ExecutorConfig): Promise<import('./interpreter/index.js').ExecutionResult>;
|
|
19
|
+
/**
|
|
20
|
+
* Load and execute a mission from a file or folder.
|
|
21
|
+
*
|
|
22
|
+
* Supports both:
|
|
23
|
+
* - Single file: ./sync-invoices.reqon
|
|
24
|
+
* - Folder: ./sync-invoices/ (with mission.reqon + action files)
|
|
25
|
+
*/
|
|
26
|
+
export declare function fromPath(path: string, config?: ExecutorConfig): Promise<import('./interpreter/index.js').ExecutionResult>;
|
|
27
|
+
export declare function reqon(strings: TemplateStringsArray, ...values: unknown[]): ReqonProgram;
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
export { ReqonLexer, ReqonTokenType } from './lexer/index.js';
|
|
2
|
+
export { reqonPlugin, registerReqonPlugin } from './plugin.js';
|
|
3
|
+
export { ReqonParser } from './parser/index.js';
|
|
4
|
+
export * from './ast/index.js';
|
|
5
|
+
export { MissionExecutor, HttpClient, BearerAuthProvider, OAuth2AuthProvider, createContext, evaluate, } from './interpreter/index.js';
|
|
6
|
+
export { MemoryStore, FileStore, createStore, } from './stores/index.js';
|
|
7
|
+
export { createExecutionState, findResumePoint, canResume, getProgress, getExecutionSummary, FileExecutionStore, MemoryExecutionStore, } from './execution/index.js';
|
|
8
|
+
export { Scheduler, parseCronExpression, getNextRunTime, intervalToMs, shouldRunNow, } from './scheduler/index.js';
|
|
9
|
+
export { generateCheckpointKey, formatSinceDate, parseSinceDate, EPOCH, FileSyncStore, MemorySyncStore, } from './sync/index.js';
|
|
10
|
+
export { ReqonError, ParseError, LexerError, RuntimeError, ValidationError, formatErrors, getSourceLine, getSourceContext, } from './errors/index.js';
|
|
11
|
+
export { loadMission, isMissionFolder, getMissionName, } from './loader/index.js';
|
|
12
|
+
export { loadEnv, loadCredentials, resolveCredentials, resolveEnvString, hasEnvReference, credentialsFromEnv, } from './auth/credentials.js';
|
|
13
|
+
export { WebhookServer, MemoryWebhookStore, FileWebhookStore, } from './webhook/index.js';
|
|
14
|
+
import { readFile } from 'node:fs/promises';
|
|
15
|
+
import { resolve } from 'node:path';
|
|
16
|
+
import { ReqonLexer } from './lexer/index.js';
|
|
17
|
+
import { ReqonParser } from './parser/index.js';
|
|
18
|
+
import { MissionExecutor } from './interpreter/index.js';
|
|
19
|
+
import { loadMission } from './loader/index.js';
|
|
20
|
+
export function parse(source, filePath) {
|
|
21
|
+
const lexer = new ReqonLexer(source);
|
|
22
|
+
const tokens = lexer.tokenize();
|
|
23
|
+
const parser = new ReqonParser(tokens, source, filePath);
|
|
24
|
+
return parser.parse();
|
|
25
|
+
}
|
|
26
|
+
export async function execute(source, config = {}) {
|
|
27
|
+
const program = parse(source);
|
|
28
|
+
const executor = new MissionExecutor(config);
|
|
29
|
+
return executor.execute(program);
|
|
30
|
+
}
|
|
31
|
+
export async function fromFile(filePath, config = {}) {
|
|
32
|
+
const absolutePath = resolve(filePath);
|
|
33
|
+
const source = await readFile(absolutePath, 'utf-8');
|
|
34
|
+
const program = parse(source, absolutePath);
|
|
35
|
+
const executor = new MissionExecutor(config);
|
|
36
|
+
return executor.execute(program);
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Load and execute a mission from a file or folder.
|
|
40
|
+
*
|
|
41
|
+
* Supports both:
|
|
42
|
+
* - Single file: ./sync-invoices.reqon
|
|
43
|
+
* - Folder: ./sync-invoices/ (with mission.reqon + action files)
|
|
44
|
+
*/
|
|
45
|
+
export async function fromPath(path, config = {}) {
|
|
46
|
+
const { program } = await loadMission(path);
|
|
47
|
+
const executor = new MissionExecutor(config);
|
|
48
|
+
return executor.execute(program);
|
|
49
|
+
}
|
|
50
|
+
// Tagged template literal for inline missions
|
|
51
|
+
export function reqon(strings, ...values) {
|
|
52
|
+
let source = strings[0];
|
|
53
|
+
for (let i = 0; i < values.length; i++) {
|
|
54
|
+
source += String(values[i]) + strings[i + 1];
|
|
55
|
+
}
|
|
56
|
+
return parse(source);
|
|
57
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { parse, execute } from './index.js';
|
|
3
|
+
import { MemoryStore } from './stores/index.js';
|
|
4
|
+
describe('Reqon Integration', () => {
|
|
5
|
+
it('parses and executes a simple mission with dry run', async () => {
|
|
6
|
+
const source = `
|
|
7
|
+
mission TestMission {
|
|
8
|
+
source API {
|
|
9
|
+
auth: bearer,
|
|
10
|
+
base: "https://api.example.com"
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
store items: memory("items")
|
|
14
|
+
|
|
15
|
+
action FetchItems {
|
|
16
|
+
fetch GET "/items"
|
|
17
|
+
|
|
18
|
+
store response -> items {
|
|
19
|
+
key: .id
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
run FetchItems
|
|
24
|
+
}
|
|
25
|
+
`;
|
|
26
|
+
const program = parse(source);
|
|
27
|
+
expect(program.type).toBe('ReqonProgram');
|
|
28
|
+
const result = await execute(source, { dryRun: true, verbose: false });
|
|
29
|
+
expect(result.success).toBe(true);
|
|
30
|
+
expect(result.actionsRun).toContain('FetchItems');
|
|
31
|
+
});
|
|
32
|
+
it('executes a mission with mock data flow', async () => {
|
|
33
|
+
const source = `
|
|
34
|
+
mission MockDataFlow {
|
|
35
|
+
source API {
|
|
36
|
+
auth: bearer,
|
|
37
|
+
base: "https://api.example.com"
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
store raw: memory("raw")
|
|
41
|
+
store processed: memory("processed")
|
|
42
|
+
|
|
43
|
+
action Process {
|
|
44
|
+
for item in raw {
|
|
45
|
+
map item -> Processed {
|
|
46
|
+
id: .id,
|
|
47
|
+
name: .title,
|
|
48
|
+
value: .amount
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
store response -> processed {
|
|
52
|
+
key: .id
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
run Process
|
|
58
|
+
}
|
|
59
|
+
`;
|
|
60
|
+
// Pre-populate the raw store
|
|
61
|
+
const rawStore = new MemoryStore('raw');
|
|
62
|
+
await rawStore.set('1', { id: '1', title: 'Item 1', amount: 100 });
|
|
63
|
+
await rawStore.set('2', { id: '2', title: 'Item 2', amount: 200 });
|
|
64
|
+
const result = await execute(source, {
|
|
65
|
+
dryRun: false,
|
|
66
|
+
verbose: false,
|
|
67
|
+
stores: { raw: rawStore },
|
|
68
|
+
});
|
|
69
|
+
expect(result.success).toBe(true);
|
|
70
|
+
expect(result.actionsRun).toContain('Process');
|
|
71
|
+
// Check processed store
|
|
72
|
+
const processedStore = result.stores.get('processed');
|
|
73
|
+
expect(processedStore).toBeDefined();
|
|
74
|
+
const items = await processedStore.list();
|
|
75
|
+
expect(items).toHaveLength(2);
|
|
76
|
+
expect(items[0]).toMatchObject({ id: '1', name: 'Item 1', value: 100 });
|
|
77
|
+
});
|
|
78
|
+
it('parses the Xero example file', async () => {
|
|
79
|
+
const fs = await import('node:fs/promises');
|
|
80
|
+
const source = await fs.readFile('./examples/xero/invoices.reqon', 'utf-8');
|
|
81
|
+
const program = parse(source);
|
|
82
|
+
expect(program.type).toBe('ReqonProgram');
|
|
83
|
+
expect(program.statements).toHaveLength(1);
|
|
84
|
+
const mission = program.statements[0];
|
|
85
|
+
if (mission.type === 'MissionDefinition') {
|
|
86
|
+
expect(mission.name).toBe('SyncXeroInvoices');
|
|
87
|
+
expect(mission.sources).toHaveLength(1);
|
|
88
|
+
expect(mission.stores).toHaveLength(2);
|
|
89
|
+
expect(mission.actions).toHaveLength(3);
|
|
90
|
+
expect(mission.pipeline.stages).toHaveLength(3);
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
it('handles validation failures gracefully', async () => {
|
|
94
|
+
const source = `
|
|
95
|
+
mission ValidationTest {
|
|
96
|
+
source API {
|
|
97
|
+
auth: bearer,
|
|
98
|
+
base: "https://api.example.com"
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
store items: memory("items")
|
|
102
|
+
|
|
103
|
+
action Validate {
|
|
104
|
+
for item in items {
|
|
105
|
+
validate item {
|
|
106
|
+
assume .value > 0
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
run Validate
|
|
112
|
+
}
|
|
113
|
+
`;
|
|
114
|
+
const itemsStore = new MemoryStore('items');
|
|
115
|
+
await itemsStore.set('1', { id: '1', value: -5 }); // Invalid: negative value
|
|
116
|
+
const result = await execute(source, {
|
|
117
|
+
stores: { items: itemsStore },
|
|
118
|
+
});
|
|
119
|
+
// Validation should fail
|
|
120
|
+
expect(result.success).toBe(false);
|
|
121
|
+
expect(result.errors.length).toBeGreaterThan(0);
|
|
122
|
+
});
|
|
123
|
+
it('processes match expressions in map steps', async () => {
|
|
124
|
+
const source = `
|
|
125
|
+
mission MatchTest {
|
|
126
|
+
source API {
|
|
127
|
+
auth: bearer,
|
|
128
|
+
base: "https://api.example.com"
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
store input: memory("input")
|
|
132
|
+
store output: memory("output")
|
|
133
|
+
|
|
134
|
+
action Transform {
|
|
135
|
+
for item in input {
|
|
136
|
+
map item -> Output {
|
|
137
|
+
id: .id,
|
|
138
|
+
status: match .state {
|
|
139
|
+
"A" => "active",
|
|
140
|
+
"I" => "inactive",
|
|
141
|
+
_ => "unknown"
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
store response -> output {
|
|
146
|
+
key: .id
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
run Transform
|
|
152
|
+
}
|
|
153
|
+
`;
|
|
154
|
+
const inputStore = new MemoryStore('input');
|
|
155
|
+
await inputStore.set('1', { id: '1', state: 'A' });
|
|
156
|
+
await inputStore.set('2', { id: '2', state: 'I' });
|
|
157
|
+
await inputStore.set('3', { id: '3', state: 'X' });
|
|
158
|
+
const result = await execute(source, {
|
|
159
|
+
stores: { input: inputStore },
|
|
160
|
+
});
|
|
161
|
+
expect(result.success).toBe(true);
|
|
162
|
+
const outputStore = result.stores.get('output');
|
|
163
|
+
const items = await outputStore.list();
|
|
164
|
+
expect(items).toContainEqual(expect.objectContaining({ id: '1', status: 'active' }));
|
|
165
|
+
expect(items).toContainEqual(expect.objectContaining({ id: '2', status: 'inactive' }));
|
|
166
|
+
expect(items).toContainEqual(expect.objectContaining({ id: '3', status: 'unknown' }));
|
|
167
|
+
});
|
|
168
|
+
});
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { SchemaDefinition } from 'vague-lang';
|
|
2
|
+
import type { StoreAdapter } from '../stores/types.js';
|
|
3
|
+
import type { HttpClient } from './http.js';
|
|
4
|
+
export interface ExecutionContext {
|
|
5
|
+
stores: Map<string, StoreAdapter>;
|
|
6
|
+
sources: Map<string, HttpClient>;
|
|
7
|
+
schemas: Map<string, SchemaDefinition>;
|
|
8
|
+
variables: Map<string, unknown>;
|
|
9
|
+
response?: unknown;
|
|
10
|
+
parent?: ExecutionContext;
|
|
11
|
+
}
|
|
12
|
+
export declare function createContext(): ExecutionContext;
|
|
13
|
+
export declare function childContext(parent: ExecutionContext): ExecutionContext;
|
|
14
|
+
export declare function getVariable(ctx: ExecutionContext, name: string): unknown;
|
|
15
|
+
export declare function setVariable(ctx: ExecutionContext, name: string, value: unknown): void;
|