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,148 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* In-memory token store - useful for development and single-process deployments
|
|
3
|
+
*/
|
|
4
|
+
export class InMemoryTokenStore {
|
|
5
|
+
tokens = new Map();
|
|
6
|
+
async get(connectionId) {
|
|
7
|
+
const stored = this.tokens.get(connectionId);
|
|
8
|
+
if (!stored)
|
|
9
|
+
return null;
|
|
10
|
+
return {
|
|
11
|
+
accessToken: stored.accessToken,
|
|
12
|
+
refreshToken: stored.refreshToken,
|
|
13
|
+
expiresAt: stored.expiresAt,
|
|
14
|
+
refreshExpiresAt: stored.refreshExpiresAt,
|
|
15
|
+
tokenType: stored.tokenType,
|
|
16
|
+
scope: stored.scope,
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
async set(connectionId, tokens) {
|
|
20
|
+
const existing = this.tokens.get(connectionId);
|
|
21
|
+
this.tokens.set(connectionId, {
|
|
22
|
+
...tokens,
|
|
23
|
+
lastUsedAt: existing?.lastUsedAt ?? new Date(),
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
async delete(connectionId) {
|
|
27
|
+
this.tokens.delete(connectionId);
|
|
28
|
+
}
|
|
29
|
+
async touch(connectionId) {
|
|
30
|
+
const stored = this.tokens.get(connectionId);
|
|
31
|
+
if (stored) {
|
|
32
|
+
stored.lastUsedAt = new Date();
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
async list() {
|
|
36
|
+
return Array.from(this.tokens.keys());
|
|
37
|
+
}
|
|
38
|
+
/** Get tokens that need proactive refresh (approaching expiry or non-use expiry) */
|
|
39
|
+
async getTokensNeedingRefresh(bufferSeconds = 300) {
|
|
40
|
+
const now = new Date();
|
|
41
|
+
const buffer = bufferSeconds * 1000;
|
|
42
|
+
const needsRefresh = [];
|
|
43
|
+
for (const [connectionId, stored] of this.tokens) {
|
|
44
|
+
// Check access token expiry
|
|
45
|
+
if (stored.expiresAt) {
|
|
46
|
+
const expiresIn = stored.expiresAt.getTime() - now.getTime();
|
|
47
|
+
if (expiresIn < buffer) {
|
|
48
|
+
needsRefresh.push(connectionId);
|
|
49
|
+
continue;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
// Check refresh token expiry from non-use
|
|
53
|
+
if (stored.refreshExpiresAt && stored.lastUsedAt) {
|
|
54
|
+
const refreshExpiresIn = stored.refreshExpiresAt.getTime() - now.getTime();
|
|
55
|
+
if (refreshExpiresIn < buffer) {
|
|
56
|
+
needsRefresh.push(connectionId);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
return needsRefresh;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* File-based token store - persists tokens to a JSON file
|
|
65
|
+
* Suitable for CLI tools and single-user scenarios
|
|
66
|
+
*/
|
|
67
|
+
export class FileTokenStore {
|
|
68
|
+
filePath;
|
|
69
|
+
cache = null;
|
|
70
|
+
constructor(filePath) {
|
|
71
|
+
this.filePath = filePath;
|
|
72
|
+
}
|
|
73
|
+
async load() {
|
|
74
|
+
if (this.cache)
|
|
75
|
+
return this.cache;
|
|
76
|
+
try {
|
|
77
|
+
const fs = await import('node:fs/promises');
|
|
78
|
+
const content = await fs.readFile(this.filePath, 'utf-8');
|
|
79
|
+
const data = JSON.parse(content);
|
|
80
|
+
// Revive dates
|
|
81
|
+
const map = new Map();
|
|
82
|
+
for (const [key, value] of Object.entries(data)) {
|
|
83
|
+
map.set(key, {
|
|
84
|
+
...value,
|
|
85
|
+
expiresAt: value.expiresAt ? new Date(value.expiresAt) : undefined,
|
|
86
|
+
refreshExpiresAt: value.refreshExpiresAt ? new Date(value.refreshExpiresAt) : undefined,
|
|
87
|
+
lastUsedAt: value.lastUsedAt ? new Date(value.lastUsedAt) : undefined,
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
this.cache = map;
|
|
91
|
+
return map;
|
|
92
|
+
}
|
|
93
|
+
catch {
|
|
94
|
+
this.cache = new Map();
|
|
95
|
+
return this.cache;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
async save() {
|
|
99
|
+
if (!this.cache)
|
|
100
|
+
return;
|
|
101
|
+
const fs = await import('node:fs/promises');
|
|
102
|
+
const data = {};
|
|
103
|
+
for (const [key, value] of this.cache) {
|
|
104
|
+
data[key] = value;
|
|
105
|
+
}
|
|
106
|
+
await fs.writeFile(this.filePath, JSON.stringify(data, null, 2), 'utf-8');
|
|
107
|
+
}
|
|
108
|
+
async get(connectionId) {
|
|
109
|
+
const map = await this.load();
|
|
110
|
+
const stored = map.get(connectionId);
|
|
111
|
+
if (!stored)
|
|
112
|
+
return null;
|
|
113
|
+
return {
|
|
114
|
+
accessToken: stored.accessToken,
|
|
115
|
+
refreshToken: stored.refreshToken,
|
|
116
|
+
expiresAt: stored.expiresAt,
|
|
117
|
+
refreshExpiresAt: stored.refreshExpiresAt,
|
|
118
|
+
tokenType: stored.tokenType,
|
|
119
|
+
scope: stored.scope,
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
async set(connectionId, tokens) {
|
|
123
|
+
const map = await this.load();
|
|
124
|
+
const existing = map.get(connectionId);
|
|
125
|
+
map.set(connectionId, {
|
|
126
|
+
...tokens,
|
|
127
|
+
lastUsedAt: existing?.lastUsedAt ?? new Date(),
|
|
128
|
+
});
|
|
129
|
+
await this.save();
|
|
130
|
+
}
|
|
131
|
+
async delete(connectionId) {
|
|
132
|
+
const map = await this.load();
|
|
133
|
+
map.delete(connectionId);
|
|
134
|
+
await this.save();
|
|
135
|
+
}
|
|
136
|
+
async touch(connectionId) {
|
|
137
|
+
const map = await this.load();
|
|
138
|
+
const stored = map.get(connectionId);
|
|
139
|
+
if (stored) {
|
|
140
|
+
stored.lastUsedAt = new Date();
|
|
141
|
+
await this.save();
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
async list() {
|
|
145
|
+
const map = await this.load();
|
|
146
|
+
return Array.from(map.keys());
|
|
147
|
+
}
|
|
148
|
+
}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auth provider interface - abstracts token retrieval and refresh
|
|
3
|
+
*/
|
|
4
|
+
export interface AuthProvider {
|
|
5
|
+
/** Get a valid access token (refreshing if needed) */
|
|
6
|
+
getToken(): Promise<string>;
|
|
7
|
+
/** Force a token refresh */
|
|
8
|
+
refreshToken?(): Promise<string>;
|
|
9
|
+
/** Get token metadata (for monitoring) */
|
|
10
|
+
getTokenInfo?(): TokenInfo;
|
|
11
|
+
}
|
|
12
|
+
export interface TokenInfo {
|
|
13
|
+
/** When the access token expires */
|
|
14
|
+
expiresAt?: Date;
|
|
15
|
+
/** When the refresh token expires (for non-use expiry tracking) */
|
|
16
|
+
refreshExpiresAt?: Date;
|
|
17
|
+
/** Last time the token was successfully used */
|
|
18
|
+
lastUsedAt?: Date;
|
|
19
|
+
/** Connection/tenant identifier */
|
|
20
|
+
connectionId?: string;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* OAuth2 token set - what gets stored
|
|
24
|
+
*/
|
|
25
|
+
export interface OAuth2Tokens {
|
|
26
|
+
accessToken: string;
|
|
27
|
+
refreshToken?: string;
|
|
28
|
+
expiresAt?: Date;
|
|
29
|
+
refreshExpiresAt?: Date;
|
|
30
|
+
tokenType?: string;
|
|
31
|
+
scope?: string;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Token store interface - pluggable storage backend
|
|
35
|
+
*/
|
|
36
|
+
export interface TokenStore {
|
|
37
|
+
/** Get tokens for a connection */
|
|
38
|
+
get(connectionId: string): Promise<OAuth2Tokens | null>;
|
|
39
|
+
/** Store tokens for a connection */
|
|
40
|
+
set(connectionId: string, tokens: OAuth2Tokens): Promise<void>;
|
|
41
|
+
/** Delete tokens for a connection */
|
|
42
|
+
delete(connectionId: string): Promise<void>;
|
|
43
|
+
/** Update last used timestamp */
|
|
44
|
+
touch(connectionId: string): Promise<void>;
|
|
45
|
+
/** List all connections (for proactive refresh) */
|
|
46
|
+
list(): Promise<string[]>;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* OAuth2 configuration for token refresh
|
|
50
|
+
*/
|
|
51
|
+
export interface OAuth2Config {
|
|
52
|
+
tokenEndpoint: string;
|
|
53
|
+
clientId: string;
|
|
54
|
+
clientSecret?: string;
|
|
55
|
+
/** Seconds before expiry to trigger refresh (default: 300 = 5 min) */
|
|
56
|
+
refreshBuffer?: number;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Rate limit information extracted from response headers
|
|
60
|
+
*/
|
|
61
|
+
export interface RateLimitInfo {
|
|
62
|
+
/** Requests remaining in current window */
|
|
63
|
+
remaining?: number;
|
|
64
|
+
/** Total requests allowed in window */
|
|
65
|
+
limit?: number;
|
|
66
|
+
/** When the rate limit resets (Unix timestamp or Date) */
|
|
67
|
+
resetAt?: Date;
|
|
68
|
+
/** Retry after N seconds (from 429 response) */
|
|
69
|
+
retryAfter?: number;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Rate limit strategy
|
|
73
|
+
*/
|
|
74
|
+
export type RateLimitStrategy = 'pause' | 'throttle' | 'fail';
|
|
75
|
+
/**
|
|
76
|
+
* Rate limit configuration
|
|
77
|
+
*/
|
|
78
|
+
export interface RateLimitConfig {
|
|
79
|
+
/** Strategy when rate limited (default: 'pause') */
|
|
80
|
+
strategy?: RateLimitStrategy;
|
|
81
|
+
/** Max seconds to wait before failing (default: 300) */
|
|
82
|
+
maxWait?: number;
|
|
83
|
+
/** Log warning after waiting this many seconds (default: 10) */
|
|
84
|
+
notifyAt?: number;
|
|
85
|
+
/** Fallback rate limit if no headers (requests per minute) */
|
|
86
|
+
fallbackRpm?: number;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Rate limit event - emitted when rate limiting occurs
|
|
90
|
+
*/
|
|
91
|
+
export interface RateLimitEvent {
|
|
92
|
+
source: string;
|
|
93
|
+
endpoint?: string;
|
|
94
|
+
waitSeconds: number;
|
|
95
|
+
remaining?: number;
|
|
96
|
+
resetAt?: Date;
|
|
97
|
+
strategy: RateLimitStrategy;
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Rate limit event handlers
|
|
101
|
+
*/
|
|
102
|
+
export interface RateLimitCallbacks {
|
|
103
|
+
/** Called when rate limited and waiting */
|
|
104
|
+
onRateLimited?: (event: RateLimitEvent) => void;
|
|
105
|
+
/** Called when rate limit wait is complete */
|
|
106
|
+
onResumed?: (event: {
|
|
107
|
+
source: string;
|
|
108
|
+
endpoint?: string;
|
|
109
|
+
waitedSeconds: number;
|
|
110
|
+
}) => void;
|
|
111
|
+
/** Called periodically during long waits */
|
|
112
|
+
onWaiting?: (event: RateLimitEvent & {
|
|
113
|
+
elapsedSeconds: number;
|
|
114
|
+
}) => void;
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Rate limiter interface - tracks and enforces rate limits
|
|
118
|
+
*/
|
|
119
|
+
export interface RateLimiter {
|
|
120
|
+
/** Check if we should proceed with a request */
|
|
121
|
+
canProceed(source: string, endpoint?: string): Promise<boolean>;
|
|
122
|
+
/** Wait until we can proceed (blocks if rate limited) */
|
|
123
|
+
waitForCapacity(source: string, endpoint?: string): Promise<void>;
|
|
124
|
+
/** Record rate limit info from a response */
|
|
125
|
+
recordResponse(source: string, info: RateLimitInfo, endpoint?: string): void;
|
|
126
|
+
/** Get current rate limit status */
|
|
127
|
+
getStatus(source: string, endpoint?: string): RateLimitStatus;
|
|
128
|
+
/** Configure rate limiting for a source */
|
|
129
|
+
configure(source: string, config: RateLimitConfig): void;
|
|
130
|
+
/** Set event callbacks */
|
|
131
|
+
setCallbacks(callbacks: RateLimitCallbacks): void;
|
|
132
|
+
/** Get delay for throttle mode (returns 0 if no throttling needed) */
|
|
133
|
+
getThrottleDelay(source: string, endpoint?: string): number;
|
|
134
|
+
}
|
|
135
|
+
export interface RateLimitStatus {
|
|
136
|
+
remaining?: number;
|
|
137
|
+
limit?: number;
|
|
138
|
+
resetAt?: Date;
|
|
139
|
+
isLimited: boolean;
|
|
140
|
+
/** Seconds until reset (if limited) */
|
|
141
|
+
resetInSeconds?: number;
|
|
142
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/cli.d.ts
ADDED
package/dist/cli.js
ADDED
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { readFile, writeFile, mkdir } from 'node:fs/promises';
|
|
3
|
+
import { resolve, dirname } from 'node:path';
|
|
4
|
+
import { fromPath, Scheduler, loadMission } from './index.js';
|
|
5
|
+
import { ReqonError } from './errors/index.js';
|
|
6
|
+
import { loadEnv, loadCredentials } from './auth/credentials.js';
|
|
7
|
+
import { WebhookServer } from './webhook/index.js';
|
|
8
|
+
async function main() {
|
|
9
|
+
const args = process.argv.slice(2);
|
|
10
|
+
if (args.length === 0 || args.includes('--help') || args.includes('-h')) {
|
|
11
|
+
console.log(`
|
|
12
|
+
Reqon - A DSL for fetch, map, validate pipelines
|
|
13
|
+
|
|
14
|
+
Usage:
|
|
15
|
+
reqon <file.reqon|folder> [options]
|
|
16
|
+
|
|
17
|
+
Options:
|
|
18
|
+
--dry-run Run without making actual HTTP requests
|
|
19
|
+
--verbose Enable verbose logging
|
|
20
|
+
--auth <file> JSON file with auth credentials (supports env var interpolation)
|
|
21
|
+
--env <file> Path to .env file (default: .env in current directory)
|
|
22
|
+
--output <path> Export stores to JSON (file or directory)
|
|
23
|
+
--daemon Run as daemon, executing scheduled missions
|
|
24
|
+
--once Run scheduled missions once immediately, then exit
|
|
25
|
+
--webhook Enable webhook server for 'wait' steps
|
|
26
|
+
--webhook-port <n> Port for webhook server (default: 3000)
|
|
27
|
+
--webhook-url <url> Base URL for webhook endpoints (default: http://localhost:3000)
|
|
28
|
+
--help, -h Show this help message
|
|
29
|
+
|
|
30
|
+
Environment Variables:
|
|
31
|
+
Credentials in --auth files support env var interpolation:
|
|
32
|
+
$VAR_NAME, \${VAR_NAME}, \${VAR_NAME:-default}
|
|
33
|
+
|
|
34
|
+
Auto-discovery from env vars (no --auth file needed):
|
|
35
|
+
REQON_{SOURCE}_TOKEN Bearer token for source
|
|
36
|
+
REQON_{SOURCE}_TYPE Auth type (bearer, oauth2, api_key, basic)
|
|
37
|
+
REQON_{SOURCE}_API_KEY API key for source
|
|
38
|
+
|
|
39
|
+
Examples:
|
|
40
|
+
reqon sync-invoices.reqon --verbose
|
|
41
|
+
reqon ./sync-invoices/ --verbose # folder with mission.reqon + action files
|
|
42
|
+
reqon sync-invoices.reqon --auth ./credentials.json
|
|
43
|
+
reqon sync-invoices.reqon --env .env.production --auth ./credentials.json
|
|
44
|
+
reqon sync-invoices.reqon --output ./output.json
|
|
45
|
+
reqon sync-invoices.reqon --daemon --verbose
|
|
46
|
+
reqon sync-invoices.reqon --webhook --webhook-port 8080 --verbose
|
|
47
|
+
`);
|
|
48
|
+
process.exit(0);
|
|
49
|
+
}
|
|
50
|
+
const filePath = args[0];
|
|
51
|
+
const dryRun = args.includes('--dry-run');
|
|
52
|
+
const verbose = args.includes('--verbose');
|
|
53
|
+
const daemon = args.includes('--daemon');
|
|
54
|
+
const once = args.includes('--once');
|
|
55
|
+
const webhookEnabled = args.includes('--webhook');
|
|
56
|
+
// Parse webhook options
|
|
57
|
+
let webhookPort = 3000;
|
|
58
|
+
const webhookPortIndex = args.indexOf('--webhook-port');
|
|
59
|
+
if (webhookPortIndex !== -1 && args[webhookPortIndex + 1]) {
|
|
60
|
+
webhookPort = parseInt(args[webhookPortIndex + 1], 10);
|
|
61
|
+
}
|
|
62
|
+
let webhookUrl;
|
|
63
|
+
const webhookUrlIndex = args.indexOf('--webhook-url');
|
|
64
|
+
if (webhookUrlIndex !== -1 && args[webhookUrlIndex + 1]) {
|
|
65
|
+
webhookUrl = args[webhookUrlIndex + 1];
|
|
66
|
+
}
|
|
67
|
+
// Load .env file(s)
|
|
68
|
+
let envFile;
|
|
69
|
+
const envIndex = args.indexOf('--env');
|
|
70
|
+
if (envIndex !== -1 && args[envIndex + 1]) {
|
|
71
|
+
envFile = args[envIndex + 1];
|
|
72
|
+
}
|
|
73
|
+
const envResult = loadEnv({ envFile });
|
|
74
|
+
if (verbose && envResult.loaded) {
|
|
75
|
+
console.log(`Loaded ${envResult.count} env vars from: ${envResult.files.join(', ')}`);
|
|
76
|
+
}
|
|
77
|
+
// Load and resolve auth credentials
|
|
78
|
+
let auth;
|
|
79
|
+
const authIndex = args.indexOf('--auth');
|
|
80
|
+
if (authIndex !== -1 && args[authIndex + 1]) {
|
|
81
|
+
const authPath = resolve(args[authIndex + 1]);
|
|
82
|
+
const authContent = await readFile(authPath, 'utf-8');
|
|
83
|
+
const rawAuth = JSON.parse(authContent);
|
|
84
|
+
// Resolve env var references in the auth config
|
|
85
|
+
auth = loadCredentials(rawAuth);
|
|
86
|
+
}
|
|
87
|
+
let outputPath;
|
|
88
|
+
const outputIndex = args.indexOf('--output');
|
|
89
|
+
if (outputIndex !== -1 && args[outputIndex + 1]) {
|
|
90
|
+
outputPath = resolve(args[outputIndex + 1]);
|
|
91
|
+
}
|
|
92
|
+
// Daemon mode: run scheduled missions
|
|
93
|
+
if (daemon || once) {
|
|
94
|
+
await runDaemon(filePath, {
|
|
95
|
+
verbose,
|
|
96
|
+
dryRun,
|
|
97
|
+
auth: auth,
|
|
98
|
+
once,
|
|
99
|
+
});
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
// Single run mode (default)
|
|
103
|
+
console.log(`Running: ${filePath}`);
|
|
104
|
+
// Start webhook server if enabled
|
|
105
|
+
let webhookServer;
|
|
106
|
+
if (webhookEnabled) {
|
|
107
|
+
webhookServer = new WebhookServer({
|
|
108
|
+
port: webhookPort,
|
|
109
|
+
baseUrl: webhookUrl ?? `http://localhost:${webhookPort}`,
|
|
110
|
+
verbose,
|
|
111
|
+
});
|
|
112
|
+
await webhookServer.start();
|
|
113
|
+
console.log(`Webhook server started on port ${webhookPort}`);
|
|
114
|
+
}
|
|
115
|
+
try {
|
|
116
|
+
const result = await fromPath(filePath, {
|
|
117
|
+
dryRun,
|
|
118
|
+
verbose,
|
|
119
|
+
auth: auth,
|
|
120
|
+
webhookServer,
|
|
121
|
+
});
|
|
122
|
+
if (result.success) {
|
|
123
|
+
console.log(`\n✓ Mission completed successfully`);
|
|
124
|
+
console.log(` Duration: ${result.duration}ms`);
|
|
125
|
+
console.log(` Actions run: ${result.actionsRun.join(' → ')}`);
|
|
126
|
+
// Print store stats and optionally export
|
|
127
|
+
const storeData = {};
|
|
128
|
+
for (const [name, store] of result.stores) {
|
|
129
|
+
const items = await store.list();
|
|
130
|
+
console.log(` Store "${name}": ${items.length} items`);
|
|
131
|
+
storeData[name] = items;
|
|
132
|
+
}
|
|
133
|
+
// Export to JSON if requested
|
|
134
|
+
if (outputPath) {
|
|
135
|
+
await mkdir(dirname(outputPath), { recursive: true });
|
|
136
|
+
await writeFile(outputPath, JSON.stringify(storeData, null, 2));
|
|
137
|
+
console.log(` Output written to: ${outputPath}`);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
else {
|
|
141
|
+
console.log(`\n✗ Mission failed`);
|
|
142
|
+
for (const error of result.errors) {
|
|
143
|
+
console.error(` [${error.action}/${error.step}] ${error.message}`);
|
|
144
|
+
}
|
|
145
|
+
process.exit(1);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
catch (error) {
|
|
149
|
+
if (error instanceof ReqonError) {
|
|
150
|
+
console.error(error.format());
|
|
151
|
+
}
|
|
152
|
+
else {
|
|
153
|
+
console.error(`Error: ${error.message}`);
|
|
154
|
+
}
|
|
155
|
+
process.exit(1);
|
|
156
|
+
}
|
|
157
|
+
finally {
|
|
158
|
+
// Stop webhook server if it was started
|
|
159
|
+
if (webhookServer) {
|
|
160
|
+
await webhookServer.stop();
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
async function runDaemon(filePath, options) {
|
|
165
|
+
const absolutePath = resolve(filePath);
|
|
166
|
+
let program;
|
|
167
|
+
let baseDir;
|
|
168
|
+
try {
|
|
169
|
+
const result = await loadMission(absolutePath);
|
|
170
|
+
program = result.program;
|
|
171
|
+
baseDir = result.baseDir;
|
|
172
|
+
}
|
|
173
|
+
catch (error) {
|
|
174
|
+
if (error instanceof ReqonError) {
|
|
175
|
+
console.error(error.format());
|
|
176
|
+
}
|
|
177
|
+
else {
|
|
178
|
+
console.error(`Error loading mission: ${error.message}`);
|
|
179
|
+
}
|
|
180
|
+
process.exit(1);
|
|
181
|
+
}
|
|
182
|
+
// Find scheduled missions
|
|
183
|
+
const scheduledMissions = program.statements.filter((s) => s.type === 'MissionDefinition' && s.schedule);
|
|
184
|
+
if (scheduledMissions.length === 0) {
|
|
185
|
+
console.error('No scheduled missions found in the file');
|
|
186
|
+
console.error('Add a schedule to your mission, e.g.:');
|
|
187
|
+
console.error(' schedule: every 6 hours');
|
|
188
|
+
console.error(' schedule: cron "0 */6 * * *"');
|
|
189
|
+
process.exit(1);
|
|
190
|
+
}
|
|
191
|
+
console.log(`Found ${scheduledMissions.length} scheduled mission(s)`);
|
|
192
|
+
// Create scheduler with callbacks
|
|
193
|
+
const scheduler = new Scheduler({
|
|
194
|
+
verbose: options.verbose,
|
|
195
|
+
callbacks: {
|
|
196
|
+
onJobStarted: (event) => {
|
|
197
|
+
console.log(`[${formatTime(event.timestamp)}] Starting: ${event.missionName}`);
|
|
198
|
+
},
|
|
199
|
+
onJobCompleted: (event) => {
|
|
200
|
+
console.log(`[${formatTime(event.timestamp)}] Completed: ${event.missionName} (${event.duration}ms)`);
|
|
201
|
+
},
|
|
202
|
+
onJobFailed: (event) => {
|
|
203
|
+
console.error(`[${formatTime(event.timestamp)}] Failed: ${event.missionName} - ${event.error}`);
|
|
204
|
+
},
|
|
205
|
+
onJobSkipped: (event) => {
|
|
206
|
+
console.log(`[${formatTime(event.timestamp)}] Skipped: ${event.missionName} - ${event.reason}`);
|
|
207
|
+
},
|
|
208
|
+
},
|
|
209
|
+
}, {
|
|
210
|
+
dryRun: options.dryRun,
|
|
211
|
+
verbose: options.verbose,
|
|
212
|
+
auth: options.auth,
|
|
213
|
+
});
|
|
214
|
+
// Register scheduled missions
|
|
215
|
+
scheduler.registerProgram(program, absolutePath);
|
|
216
|
+
// Print job info
|
|
217
|
+
const jobs = scheduler.getJobs();
|
|
218
|
+
console.log('\nScheduled jobs:');
|
|
219
|
+
for (const job of jobs) {
|
|
220
|
+
const schedule = job.schedule;
|
|
221
|
+
let scheduleStr;
|
|
222
|
+
if (schedule.scheduleType === 'interval') {
|
|
223
|
+
scheduleStr = `every ${schedule.interval.value} ${schedule.interval.unit}`;
|
|
224
|
+
}
|
|
225
|
+
else if (schedule.scheduleType === 'cron') {
|
|
226
|
+
scheduleStr = `cron "${schedule.cronExpression}"`;
|
|
227
|
+
}
|
|
228
|
+
else {
|
|
229
|
+
scheduleStr = `at "${schedule.runAt}"`;
|
|
230
|
+
}
|
|
231
|
+
console.log(` - ${job.missionName}: ${scheduleStr}`);
|
|
232
|
+
if (job.nextRun) {
|
|
233
|
+
console.log(` Next run: ${job.nextRun.toISOString()}`);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
console.log('');
|
|
237
|
+
// Handle --once mode
|
|
238
|
+
if (options.once) {
|
|
239
|
+
console.log('Running all scheduled missions once...\n');
|
|
240
|
+
for (const job of jobs) {
|
|
241
|
+
await scheduler.trigger(job.missionName);
|
|
242
|
+
}
|
|
243
|
+
console.log('\nAll missions completed.');
|
|
244
|
+
return;
|
|
245
|
+
}
|
|
246
|
+
// Start daemon mode
|
|
247
|
+
console.log('Starting scheduler daemon (Ctrl+C to stop)...\n');
|
|
248
|
+
// Handle graceful shutdown
|
|
249
|
+
let shuttingDown = false;
|
|
250
|
+
const shutdown = async () => {
|
|
251
|
+
if (shuttingDown)
|
|
252
|
+
return;
|
|
253
|
+
shuttingDown = true;
|
|
254
|
+
console.log('\nShutting down scheduler...');
|
|
255
|
+
await scheduler.stop();
|
|
256
|
+
process.exit(0);
|
|
257
|
+
};
|
|
258
|
+
process.on('SIGINT', shutdown);
|
|
259
|
+
process.on('SIGTERM', shutdown);
|
|
260
|
+
// Start the scheduler
|
|
261
|
+
await scheduler.start();
|
|
262
|
+
// Keep the process running
|
|
263
|
+
await new Promise(() => {
|
|
264
|
+
// This promise never resolves - we wait for SIGINT/SIGTERM
|
|
265
|
+
});
|
|
266
|
+
}
|
|
267
|
+
function formatTime(date) {
|
|
268
|
+
return date.toISOString().replace('T', ' ').substring(0, 19);
|
|
269
|
+
}
|
|
270
|
+
main();
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|