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,230 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Benchmarking utilities for Reqon
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export interface BenchmarkResult {
|
|
6
|
+
name: string;
|
|
7
|
+
iterations: number;
|
|
8
|
+
totalMs: number;
|
|
9
|
+
avgMs: number;
|
|
10
|
+
minMs: number;
|
|
11
|
+
maxMs: number;
|
|
12
|
+
opsPerSec: number;
|
|
13
|
+
samples: number[];
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface BenchmarkOptions {
|
|
17
|
+
iterations?: number;
|
|
18
|
+
warmupIterations?: number;
|
|
19
|
+
name?: string;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const DEFAULT_OPTIONS: Required<BenchmarkOptions> = {
|
|
23
|
+
iterations: 1000,
|
|
24
|
+
warmupIterations: 100,
|
|
25
|
+
name: 'benchmark',
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* High-resolution timer using process.hrtime.bigint()
|
|
30
|
+
*/
|
|
31
|
+
export function hrTimeMs(): number {
|
|
32
|
+
return Number(process.hrtime.bigint()) / 1_000_000;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Run a synchronous benchmark
|
|
37
|
+
*/
|
|
38
|
+
export function benchmarkSync<T>(
|
|
39
|
+
fn: () => T,
|
|
40
|
+
options: BenchmarkOptions = {}
|
|
41
|
+
): BenchmarkResult {
|
|
42
|
+
const opts = { ...DEFAULT_OPTIONS, ...options };
|
|
43
|
+
const samples: number[] = [];
|
|
44
|
+
|
|
45
|
+
// Warmup phase
|
|
46
|
+
for (let i = 0; i < opts.warmupIterations; i++) {
|
|
47
|
+
fn();
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Measurement phase
|
|
51
|
+
for (let i = 0; i < opts.iterations; i++) {
|
|
52
|
+
const start = hrTimeMs();
|
|
53
|
+
fn();
|
|
54
|
+
const end = hrTimeMs();
|
|
55
|
+
samples.push(end - start);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return calculateStats(opts.name, samples);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Run an asynchronous benchmark
|
|
63
|
+
*/
|
|
64
|
+
export async function benchmarkAsync<T>(
|
|
65
|
+
fn: () => Promise<T>,
|
|
66
|
+
options: BenchmarkOptions = {}
|
|
67
|
+
): Promise<BenchmarkResult> {
|
|
68
|
+
const opts = { ...DEFAULT_OPTIONS, ...options };
|
|
69
|
+
const samples: number[] = [];
|
|
70
|
+
|
|
71
|
+
// Warmup phase
|
|
72
|
+
for (let i = 0; i < opts.warmupIterations; i++) {
|
|
73
|
+
await fn();
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Measurement phase
|
|
77
|
+
for (let i = 0; i < opts.iterations; i++) {
|
|
78
|
+
const start = hrTimeMs();
|
|
79
|
+
await fn();
|
|
80
|
+
const end = hrTimeMs();
|
|
81
|
+
samples.push(end - start);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return calculateStats(opts.name, samples);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Calculate statistics from samples
|
|
89
|
+
*/
|
|
90
|
+
function calculateStats(name: string, samples: number[]): BenchmarkResult {
|
|
91
|
+
const sorted = [...samples].sort((a, b) => a - b);
|
|
92
|
+
const totalMs = samples.reduce((sum, s) => sum + s, 0);
|
|
93
|
+
const avgMs = totalMs / samples.length;
|
|
94
|
+
const minMs = sorted[0];
|
|
95
|
+
const maxMs = sorted[sorted.length - 1];
|
|
96
|
+
const opsPerSec = 1000 / avgMs;
|
|
97
|
+
|
|
98
|
+
return {
|
|
99
|
+
name,
|
|
100
|
+
iterations: samples.length,
|
|
101
|
+
totalMs,
|
|
102
|
+
avgMs,
|
|
103
|
+
minMs,
|
|
104
|
+
maxMs,
|
|
105
|
+
opsPerSec,
|
|
106
|
+
samples,
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Format a benchmark result for display
|
|
112
|
+
*/
|
|
113
|
+
export function formatResult(result: BenchmarkResult): string {
|
|
114
|
+
const lines = [
|
|
115
|
+
`┌─ ${result.name}`,
|
|
116
|
+
`│ Iterations: ${result.iterations.toLocaleString()}`,
|
|
117
|
+
`│ Total time: ${result.totalMs.toFixed(2)}ms`,
|
|
118
|
+
`│ Avg time: ${result.avgMs.toFixed(4)}ms`,
|
|
119
|
+
`│ Min time: ${result.minMs.toFixed(4)}ms`,
|
|
120
|
+
`│ Max time: ${result.maxMs.toFixed(4)}ms`,
|
|
121
|
+
`│ Ops/sec: ${result.opsPerSec.toFixed(2)}`,
|
|
122
|
+
`└──────────────────────────────────`,
|
|
123
|
+
];
|
|
124
|
+
return lines.join('\n');
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Format multiple benchmark results as a comparison table
|
|
129
|
+
*/
|
|
130
|
+
export function formatResultsTable(results: BenchmarkResult[]): string {
|
|
131
|
+
const nameWidth = Math.max(...results.map((r) => r.name.length), 10);
|
|
132
|
+
const header = `| ${'Name'.padEnd(nameWidth)} | Ops/sec | Avg (ms) | Min (ms) | Max (ms) |`;
|
|
133
|
+
const separator = `|${'-'.repeat(nameWidth + 2)}|--------------|------------|------------|------------|`;
|
|
134
|
+
|
|
135
|
+
const rows = results.map((r) => {
|
|
136
|
+
return `| ${r.name.padEnd(nameWidth)} | ${r.opsPerSec.toFixed(2).padStart(12)} | ${r.avgMs.toFixed(4).padStart(10)} | ${r.minMs.toFixed(4).padStart(10)} | ${r.maxMs.toFixed(4).padStart(10)} |`;
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
return [separator, header, separator, ...rows, separator].join('\n');
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Suite for grouping related benchmarks
|
|
144
|
+
*/
|
|
145
|
+
export class BenchmarkSuite {
|
|
146
|
+
private results: BenchmarkResult[] = [];
|
|
147
|
+
private name: string;
|
|
148
|
+
|
|
149
|
+
constructor(name: string) {
|
|
150
|
+
this.name = name;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
addSync<T>(name: string, fn: () => T, options?: BenchmarkOptions): this {
|
|
154
|
+
const result = benchmarkSync(fn, { ...options, name });
|
|
155
|
+
this.results.push(result);
|
|
156
|
+
return this;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
async addAsync<T>(
|
|
160
|
+
name: string,
|
|
161
|
+
fn: () => Promise<T>,
|
|
162
|
+
options?: BenchmarkOptions
|
|
163
|
+
): Promise<this> {
|
|
164
|
+
const result = await benchmarkAsync(fn, { ...options, name });
|
|
165
|
+
this.results.push(result);
|
|
166
|
+
return this;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
getResults(): BenchmarkResult[] {
|
|
170
|
+
return [...this.results];
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
print(): void {
|
|
174
|
+
console.log(`\n${'='.repeat(60)}`);
|
|
175
|
+
console.log(`Benchmark Suite: ${this.name}`);
|
|
176
|
+
console.log(`${'='.repeat(60)}\n`);
|
|
177
|
+
console.log(formatResultsTable(this.results));
|
|
178
|
+
console.log('');
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
toJSON(): object {
|
|
182
|
+
return {
|
|
183
|
+
name: this.name,
|
|
184
|
+
timestamp: new Date().toISOString(),
|
|
185
|
+
results: this.results.map((r) => ({
|
|
186
|
+
name: r.name,
|
|
187
|
+
iterations: r.iterations,
|
|
188
|
+
totalMs: r.totalMs,
|
|
189
|
+
avgMs: r.avgMs,
|
|
190
|
+
minMs: r.minMs,
|
|
191
|
+
maxMs: r.maxMs,
|
|
192
|
+
opsPerSec: r.opsPerSec,
|
|
193
|
+
})),
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Generate a string of specified size for testing
|
|
200
|
+
*/
|
|
201
|
+
export function generateString(sizeKb: number): string {
|
|
202
|
+
const chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789 \n';
|
|
203
|
+
const targetLength = sizeKb * 1024;
|
|
204
|
+
let result = '';
|
|
205
|
+
while (result.length < targetLength) {
|
|
206
|
+
result += chars[Math.floor(Math.random() * chars.length)];
|
|
207
|
+
}
|
|
208
|
+
return result;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* Generate random data for store benchmarks
|
|
213
|
+
*/
|
|
214
|
+
export function generateRecords(count: number): Record<string, unknown>[] {
|
|
215
|
+
const records: Record<string, unknown>[] = [];
|
|
216
|
+
for (let i = 0; i < count; i++) {
|
|
217
|
+
records.push({
|
|
218
|
+
id: `record-${i}`,
|
|
219
|
+
name: `Test Record ${i}`,
|
|
220
|
+
value: Math.random() * 1000,
|
|
221
|
+
active: Math.random() > 0.5,
|
|
222
|
+
tags: ['tag1', 'tag2', 'tag3'].slice(0, Math.floor(Math.random() * 3) + 1),
|
|
223
|
+
nested: {
|
|
224
|
+
field1: `nested-${i}`,
|
|
225
|
+
field2: i * 2,
|
|
226
|
+
},
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
return records;
|
|
230
|
+
}
|
package/src/cli.ts
ADDED
|
@@ -0,0 +1,313 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { readFile, writeFile, mkdir } from 'node:fs/promises';
|
|
4
|
+
import { resolve, dirname } from 'node:path';
|
|
5
|
+
import { fromPath, parse, Scheduler, loadMission } from './index.js';
|
|
6
|
+
import type { ScheduleEvent } from './scheduler/index.js';
|
|
7
|
+
import { ReqonError } from './errors/index.js';
|
|
8
|
+
import { loadEnv, loadCredentials } from './auth/credentials.js';
|
|
9
|
+
import { WebhookServer } from './webhook/index.js';
|
|
10
|
+
|
|
11
|
+
async function main() {
|
|
12
|
+
const args = process.argv.slice(2);
|
|
13
|
+
|
|
14
|
+
if (args.length === 0 || args.includes('--help') || args.includes('-h')) {
|
|
15
|
+
console.log(`
|
|
16
|
+
Reqon - A DSL for fetch, map, validate pipelines
|
|
17
|
+
|
|
18
|
+
Usage:
|
|
19
|
+
reqon <file.reqon|folder> [options]
|
|
20
|
+
|
|
21
|
+
Options:
|
|
22
|
+
--dry-run Run without making actual HTTP requests
|
|
23
|
+
--verbose Enable verbose logging
|
|
24
|
+
--auth <file> JSON file with auth credentials (supports env var interpolation)
|
|
25
|
+
--env <file> Path to .env file (default: .env in current directory)
|
|
26
|
+
--output <path> Export stores to JSON (file or directory)
|
|
27
|
+
--daemon Run as daemon, executing scheduled missions
|
|
28
|
+
--once Run scheduled missions once immediately, then exit
|
|
29
|
+
--webhook Enable webhook server for 'wait' steps
|
|
30
|
+
--webhook-port <n> Port for webhook server (default: 3000)
|
|
31
|
+
--webhook-url <url> Base URL for webhook endpoints (default: http://localhost:3000)
|
|
32
|
+
--help, -h Show this help message
|
|
33
|
+
|
|
34
|
+
Environment Variables:
|
|
35
|
+
Credentials in --auth files support env var interpolation:
|
|
36
|
+
$VAR_NAME, \${VAR_NAME}, \${VAR_NAME:-default}
|
|
37
|
+
|
|
38
|
+
Auto-discovery from env vars (no --auth file needed):
|
|
39
|
+
REQON_{SOURCE}_TOKEN Bearer token for source
|
|
40
|
+
REQON_{SOURCE}_TYPE Auth type (bearer, oauth2, api_key, basic)
|
|
41
|
+
REQON_{SOURCE}_API_KEY API key for source
|
|
42
|
+
|
|
43
|
+
Examples:
|
|
44
|
+
reqon sync-invoices.reqon --verbose
|
|
45
|
+
reqon ./sync-invoices/ --verbose # folder with mission.reqon + action files
|
|
46
|
+
reqon sync-invoices.reqon --auth ./credentials.json
|
|
47
|
+
reqon sync-invoices.reqon --env .env.production --auth ./credentials.json
|
|
48
|
+
reqon sync-invoices.reqon --output ./output.json
|
|
49
|
+
reqon sync-invoices.reqon --daemon --verbose
|
|
50
|
+
reqon sync-invoices.reqon --webhook --webhook-port 8080 --verbose
|
|
51
|
+
`);
|
|
52
|
+
process.exit(0);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const filePath = args[0];
|
|
56
|
+
const dryRun = args.includes('--dry-run');
|
|
57
|
+
const verbose = args.includes('--verbose');
|
|
58
|
+
const daemon = args.includes('--daemon');
|
|
59
|
+
const once = args.includes('--once');
|
|
60
|
+
const webhookEnabled = args.includes('--webhook');
|
|
61
|
+
|
|
62
|
+
// Parse webhook options
|
|
63
|
+
let webhookPort = 3000;
|
|
64
|
+
const webhookPortIndex = args.indexOf('--webhook-port');
|
|
65
|
+
if (webhookPortIndex !== -1 && args[webhookPortIndex + 1]) {
|
|
66
|
+
webhookPort = parseInt(args[webhookPortIndex + 1], 10);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
let webhookUrl: string | undefined;
|
|
70
|
+
const webhookUrlIndex = args.indexOf('--webhook-url');
|
|
71
|
+
if (webhookUrlIndex !== -1 && args[webhookUrlIndex + 1]) {
|
|
72
|
+
webhookUrl = args[webhookUrlIndex + 1];
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Load .env file(s)
|
|
76
|
+
let envFile: string | undefined;
|
|
77
|
+
const envIndex = args.indexOf('--env');
|
|
78
|
+
if (envIndex !== -1 && args[envIndex + 1]) {
|
|
79
|
+
envFile = args[envIndex + 1];
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const envResult = loadEnv({ envFile });
|
|
83
|
+
if (verbose && envResult.loaded) {
|
|
84
|
+
console.log(`Loaded ${envResult.count} env vars from: ${envResult.files.join(', ')}`);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Load and resolve auth credentials
|
|
88
|
+
let auth: Record<string, unknown> | undefined;
|
|
89
|
+
const authIndex = args.indexOf('--auth');
|
|
90
|
+
if (authIndex !== -1 && args[authIndex + 1]) {
|
|
91
|
+
const authPath = resolve(args[authIndex + 1]);
|
|
92
|
+
const authContent = await readFile(authPath, 'utf-8');
|
|
93
|
+
const rawAuth = JSON.parse(authContent);
|
|
94
|
+
// Resolve env var references in the auth config
|
|
95
|
+
auth = loadCredentials(rawAuth);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
let outputPath: string | undefined;
|
|
99
|
+
const outputIndex = args.indexOf('--output');
|
|
100
|
+
if (outputIndex !== -1 && args[outputIndex + 1]) {
|
|
101
|
+
outputPath = resolve(args[outputIndex + 1]);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Daemon mode: run scheduled missions
|
|
105
|
+
if (daemon || once) {
|
|
106
|
+
await runDaemon(filePath, {
|
|
107
|
+
verbose,
|
|
108
|
+
dryRun,
|
|
109
|
+
auth: auth as Record<string, { type: 'bearer' | 'oauth2'; token?: string; accessToken?: string }>,
|
|
110
|
+
once,
|
|
111
|
+
});
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// Single run mode (default)
|
|
116
|
+
console.log(`Running: ${filePath}`);
|
|
117
|
+
|
|
118
|
+
// Start webhook server if enabled
|
|
119
|
+
let webhookServer: WebhookServer | undefined;
|
|
120
|
+
if (webhookEnabled) {
|
|
121
|
+
webhookServer = new WebhookServer({
|
|
122
|
+
port: webhookPort,
|
|
123
|
+
baseUrl: webhookUrl ?? `http://localhost:${webhookPort}`,
|
|
124
|
+
verbose,
|
|
125
|
+
});
|
|
126
|
+
await webhookServer.start();
|
|
127
|
+
console.log(`Webhook server started on port ${webhookPort}`);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
try {
|
|
131
|
+
const result = await fromPath(filePath, {
|
|
132
|
+
dryRun,
|
|
133
|
+
verbose,
|
|
134
|
+
auth: auth as Record<string, { type: 'bearer' | 'oauth2'; token?: string; accessToken?: string }>,
|
|
135
|
+
webhookServer,
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
if (result.success) {
|
|
139
|
+
console.log(`\n✓ Mission completed successfully`);
|
|
140
|
+
console.log(` Duration: ${result.duration}ms`);
|
|
141
|
+
console.log(` Actions run: ${result.actionsRun.join(' → ')}`);
|
|
142
|
+
|
|
143
|
+
// Print store stats and optionally export
|
|
144
|
+
const storeData: Record<string, unknown[]> = {};
|
|
145
|
+
for (const [name, store] of result.stores) {
|
|
146
|
+
const items = await store.list();
|
|
147
|
+
console.log(` Store "${name}": ${items.length} items`);
|
|
148
|
+
storeData[name] = items;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// Export to JSON if requested
|
|
152
|
+
if (outputPath) {
|
|
153
|
+
await mkdir(dirname(outputPath), { recursive: true });
|
|
154
|
+
await writeFile(outputPath, JSON.stringify(storeData, null, 2));
|
|
155
|
+
console.log(` Output written to: ${outputPath}`);
|
|
156
|
+
}
|
|
157
|
+
} else {
|
|
158
|
+
console.log(`\n✗ Mission failed`);
|
|
159
|
+
for (const error of result.errors) {
|
|
160
|
+
console.error(` [${error.action}/${error.step}] ${error.message}`);
|
|
161
|
+
}
|
|
162
|
+
process.exit(1);
|
|
163
|
+
}
|
|
164
|
+
} catch (error) {
|
|
165
|
+
if (error instanceof ReqonError) {
|
|
166
|
+
console.error(error.format());
|
|
167
|
+
} else {
|
|
168
|
+
console.error(`Error: ${(error as Error).message}`);
|
|
169
|
+
}
|
|
170
|
+
process.exit(1);
|
|
171
|
+
} finally {
|
|
172
|
+
// Stop webhook server if it was started
|
|
173
|
+
if (webhookServer) {
|
|
174
|
+
await webhookServer.stop();
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
interface DaemonOptions {
|
|
180
|
+
verbose: boolean;
|
|
181
|
+
dryRun: boolean;
|
|
182
|
+
auth?: Record<string, { type: 'bearer' | 'oauth2'; token?: string; accessToken?: string }>;
|
|
183
|
+
once: boolean;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
async function runDaemon(filePath: string, options: DaemonOptions): Promise<void> {
|
|
187
|
+
const absolutePath = resolve(filePath);
|
|
188
|
+
|
|
189
|
+
let program;
|
|
190
|
+
let baseDir: string;
|
|
191
|
+
try {
|
|
192
|
+
const result = await loadMission(absolutePath);
|
|
193
|
+
program = result.program;
|
|
194
|
+
baseDir = result.baseDir;
|
|
195
|
+
} catch (error) {
|
|
196
|
+
if (error instanceof ReqonError) {
|
|
197
|
+
console.error(error.format());
|
|
198
|
+
} else {
|
|
199
|
+
console.error(`Error loading mission: ${(error as Error).message}`);
|
|
200
|
+
}
|
|
201
|
+
process.exit(1);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// Find scheduled missions
|
|
205
|
+
const scheduledMissions = program.statements.filter(
|
|
206
|
+
(s) => s.type === 'MissionDefinition' && s.schedule
|
|
207
|
+
);
|
|
208
|
+
|
|
209
|
+
if (scheduledMissions.length === 0) {
|
|
210
|
+
console.error('No scheduled missions found in the file');
|
|
211
|
+
console.error('Add a schedule to your mission, e.g.:');
|
|
212
|
+
console.error(' schedule: every 6 hours');
|
|
213
|
+
console.error(' schedule: cron "0 */6 * * *"');
|
|
214
|
+
process.exit(1);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
console.log(`Found ${scheduledMissions.length} scheduled mission(s)`);
|
|
218
|
+
|
|
219
|
+
// Create scheduler with callbacks
|
|
220
|
+
const scheduler = new Scheduler(
|
|
221
|
+
{
|
|
222
|
+
verbose: options.verbose,
|
|
223
|
+
callbacks: {
|
|
224
|
+
onJobStarted: (event: ScheduleEvent) => {
|
|
225
|
+
console.log(`[${formatTime(event.timestamp)}] Starting: ${event.missionName}`);
|
|
226
|
+
},
|
|
227
|
+
onJobCompleted: (event: ScheduleEvent) => {
|
|
228
|
+
console.log(
|
|
229
|
+
`[${formatTime(event.timestamp)}] Completed: ${event.missionName} (${event.duration}ms)`
|
|
230
|
+
);
|
|
231
|
+
},
|
|
232
|
+
onJobFailed: (event: ScheduleEvent) => {
|
|
233
|
+
console.error(
|
|
234
|
+
`[${formatTime(event.timestamp)}] Failed: ${event.missionName} - ${event.error}`
|
|
235
|
+
);
|
|
236
|
+
},
|
|
237
|
+
onJobSkipped: (event: ScheduleEvent) => {
|
|
238
|
+
console.log(
|
|
239
|
+
`[${formatTime(event.timestamp)}] Skipped: ${event.missionName} - ${event.reason}`
|
|
240
|
+
);
|
|
241
|
+
},
|
|
242
|
+
},
|
|
243
|
+
},
|
|
244
|
+
{
|
|
245
|
+
dryRun: options.dryRun,
|
|
246
|
+
verbose: options.verbose,
|
|
247
|
+
auth: options.auth,
|
|
248
|
+
}
|
|
249
|
+
);
|
|
250
|
+
|
|
251
|
+
// Register scheduled missions
|
|
252
|
+
scheduler.registerProgram(program, absolutePath);
|
|
253
|
+
|
|
254
|
+
// Print job info
|
|
255
|
+
const jobs = scheduler.getJobs();
|
|
256
|
+
console.log('\nScheduled jobs:');
|
|
257
|
+
for (const job of jobs) {
|
|
258
|
+
const schedule = job.schedule;
|
|
259
|
+
let scheduleStr: string;
|
|
260
|
+
if (schedule.scheduleType === 'interval') {
|
|
261
|
+
scheduleStr = `every ${schedule.interval!.value} ${schedule.interval!.unit}`;
|
|
262
|
+
} else if (schedule.scheduleType === 'cron') {
|
|
263
|
+
scheduleStr = `cron "${schedule.cronExpression}"`;
|
|
264
|
+
} else {
|
|
265
|
+
scheduleStr = `at "${schedule.runAt}"`;
|
|
266
|
+
}
|
|
267
|
+
console.log(` - ${job.missionName}: ${scheduleStr}`);
|
|
268
|
+
if (job.nextRun) {
|
|
269
|
+
console.log(` Next run: ${job.nextRun.toISOString()}`);
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
console.log('');
|
|
273
|
+
|
|
274
|
+
// Handle --once mode
|
|
275
|
+
if (options.once) {
|
|
276
|
+
console.log('Running all scheduled missions once...\n');
|
|
277
|
+
for (const job of jobs) {
|
|
278
|
+
await scheduler.trigger(job.missionName);
|
|
279
|
+
}
|
|
280
|
+
console.log('\nAll missions completed.');
|
|
281
|
+
return;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
// Start daemon mode
|
|
285
|
+
console.log('Starting scheduler daemon (Ctrl+C to stop)...\n');
|
|
286
|
+
|
|
287
|
+
// Handle graceful shutdown
|
|
288
|
+
let shuttingDown = false;
|
|
289
|
+
const shutdown = async () => {
|
|
290
|
+
if (shuttingDown) return;
|
|
291
|
+
shuttingDown = true;
|
|
292
|
+
console.log('\nShutting down scheduler...');
|
|
293
|
+
await scheduler.stop();
|
|
294
|
+
process.exit(0);
|
|
295
|
+
};
|
|
296
|
+
|
|
297
|
+
process.on('SIGINT', shutdown);
|
|
298
|
+
process.on('SIGTERM', shutdown);
|
|
299
|
+
|
|
300
|
+
// Start the scheduler
|
|
301
|
+
await scheduler.start();
|
|
302
|
+
|
|
303
|
+
// Keep the process running
|
|
304
|
+
await new Promise(() => {
|
|
305
|
+
// This promise never resolves - we wait for SIGINT/SIGTERM
|
|
306
|
+
});
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
function formatTime(date: Date): string {
|
|
310
|
+
return date.toISOString().replace('T', ' ').substring(0, 19);
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
main();
|