reqon-dsl 0.2.0 → 0.3.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/CHANGELOG.md +7 -0
- package/README.md +22 -0
- package/dist/ast/nodes.d.ts +83 -4
- package/dist/ast/nodes.js +14 -0
- package/dist/auth/circuit-breaker.js +7 -6
- package/dist/auth/rate-limiter.d.ts +4 -0
- package/dist/auth/rate-limiter.js +9 -16
- package/dist/cli.d.ts +13 -0
- package/dist/cli.js +84 -4
- package/dist/config/constants.d.ts +141 -0
- package/dist/config/constants.js +128 -0
- package/dist/config/index.d.ts +4 -0
- package/dist/config/index.js +4 -0
- package/dist/control/index.d.ts +2 -0
- package/dist/control/index.js +1 -0
- package/dist/control/server.d.ts +88 -0
- package/dist/control/server.js +238 -0
- package/dist/control/types.d.ts +55 -0
- package/dist/control/types.js +7 -0
- package/dist/debug/cli-debugger.d.ts +17 -0
- package/dist/debug/cli-debugger.js +180 -0
- package/dist/debug/controller.d.ts +94 -0
- package/dist/debug/controller.js +45 -0
- package/dist/debug/index.d.ts +6 -0
- package/dist/debug/index.js +5 -0
- package/dist/errors/index.d.ts +67 -0
- package/dist/errors/index.js +89 -1
- package/dist/execution/index.d.ts +1 -1
- package/dist/execution/state.d.ts +24 -0
- package/dist/index.d.ts +21 -1
- package/dist/index.js +33 -2
- package/dist/interpreter/context.d.ts +14 -0
- package/dist/interpreter/context.js +15 -0
- package/dist/interpreter/evaluator.d.ts +63 -1
- package/dist/interpreter/evaluator.js +186 -39
- package/dist/interpreter/executor.d.ts +70 -14
- package/dist/interpreter/executor.js +503 -174
- package/dist/interpreter/fetch-handler.d.ts +9 -0
- package/dist/interpreter/fetch-handler.js +133 -24
- package/dist/interpreter/http.d.ts +5 -0
- package/dist/interpreter/http.js +26 -12
- package/dist/interpreter/index.d.ts +3 -1
- package/dist/interpreter/index.js +2 -0
- package/dist/interpreter/pagination.d.ts +11 -2
- package/dist/interpreter/pagination.js +95 -31
- package/dist/interpreter/signals.d.ts +8 -0
- package/dist/interpreter/signals.js +12 -0
- package/dist/interpreter/source-manager.d.ts +75 -0
- package/dist/interpreter/source-manager.js +157 -0
- package/dist/interpreter/step-handlers/apply-handler.d.ts +29 -0
- package/dist/interpreter/step-handlers/apply-handler.js +79 -0
- package/dist/interpreter/step-handlers/for-handler.d.ts +13 -0
- package/dist/interpreter/step-handlers/for-handler.js +71 -4
- package/dist/interpreter/step-handlers/index.d.ts +4 -2
- package/dist/interpreter/step-handlers/index.js +4 -2
- package/dist/interpreter/step-handlers/match-handler.d.ts +9 -0
- package/dist/interpreter/step-handlers/match-handler.js +43 -16
- package/dist/interpreter/step-handlers/pause-handler.d.ts +52 -0
- package/dist/interpreter/step-handlers/pause-handler.js +87 -0
- package/dist/interpreter/step-handlers/store-handler.d.ts +11 -1
- package/dist/interpreter/step-handlers/store-handler.js +45 -13
- package/dist/interpreter/step-handlers/types.d.ts +3 -0
- package/dist/interpreter/step-handlers/validate-handler.d.ts +2 -1
- package/dist/interpreter/step-handlers/validate-handler.js +4 -2
- package/dist/interpreter/step-handlers/webhook-handler.d.ts +3 -0
- package/dist/interpreter/step-handlers/webhook-handler.js +18 -2
- package/dist/interpreter/store-manager.d.ts +46 -0
- package/dist/interpreter/store-manager.js +66 -0
- package/dist/lexer/index.d.ts +11 -4
- package/dist/lexer/index.js +11 -4
- package/dist/lexer/tokens.d.ts +17 -1
- package/dist/lexer/tokens.js +36 -0
- package/dist/mcp/index.d.ts +11 -0
- package/dist/mcp/index.js +11 -0
- package/dist/mcp/server.d.ts +17 -0
- package/dist/mcp/server.js +451 -0
- package/dist/oas/index.d.ts +2 -0
- package/dist/oas/index.js +1 -0
- package/dist/oas/mock-generator.d.ts +12 -0
- package/dist/oas/mock-generator.js +187 -0
- package/dist/observability/events.d.ts +244 -0
- package/dist/observability/events.js +90 -0
- package/dist/observability/index.d.ts +15 -0
- package/dist/observability/index.js +12 -0
- package/dist/observability/logger.d.ts +106 -0
- package/dist/observability/logger.js +259 -0
- package/dist/observability/otel.d.ts +135 -0
- package/dist/observability/otel.js +386 -0
- package/dist/parser/action-parser.d.ts +105 -0
- package/dist/parser/action-parser.js +645 -0
- package/dist/parser/expressions.d.ts +13 -0
- package/dist/parser/expressions.js +72 -2
- package/dist/parser/fetch-parser.d.ts +27 -0
- package/dist/parser/fetch-parser.js +269 -0
- package/dist/parser/index.d.ts +17 -0
- package/dist/parser/index.js +17 -0
- package/dist/parser/parser.d.ts +44 -46
- package/dist/parser/parser.js +122 -1070
- package/dist/parser/pipeline-parser.d.ts +12 -0
- package/dist/parser/pipeline-parser.js +52 -0
- package/dist/parser/schedule-parser.d.ts +7 -0
- package/dist/parser/schedule-parser.js +137 -0
- package/dist/parser/source-parser.d.ts +9 -0
- package/dist/parser/source-parser.js +151 -0
- package/dist/pause/index.d.ts +14 -0
- package/dist/pause/index.js +11 -0
- package/dist/pause/manager.d.ts +118 -0
- package/dist/pause/manager.js +245 -0
- package/dist/pause/state.d.ts +93 -0
- package/dist/pause/state.js +103 -0
- package/dist/pause/store.d.ts +61 -0
- package/dist/pause/store.js +156 -0
- package/dist/plugin.d.ts +9 -12
- package/dist/plugin.js +10 -13
- package/dist/stores/factory.d.ts +1 -1
- package/dist/stores/factory.js +3 -2
- package/dist/stores/file.d.ts +26 -0
- package/dist/stores/file.js +64 -10
- package/dist/stores/index.d.ts +16 -1
- package/dist/stores/index.js +16 -1
- package/dist/stores/memory.d.ts +4 -0
- package/dist/stores/memory.js +11 -0
- package/dist/stores/types.d.ts +17 -0
- package/dist/stores/types.js +12 -0
- package/dist/trace/index.d.ts +16 -0
- package/dist/trace/index.js +12 -0
- package/dist/trace/recorder.d.ts +71 -0
- package/dist/trace/recorder.js +144 -0
- package/dist/trace/replay.d.ts +132 -0
- package/dist/trace/replay.js +264 -0
- package/dist/trace/state.d.ts +102 -0
- package/dist/trace/state.js +86 -0
- package/dist/trace/store.d.ts +69 -0
- package/dist/trace/store.js +225 -0
- package/dist/utils/index.d.ts +1 -0
- package/dist/utils/index.js +1 -0
- package/dist/utils/type-guards.d.ts +58 -0
- package/dist/utils/type-guards.js +92 -0
- package/dist/webhook/server.js +7 -6
- package/package.json +55 -6
- package/.claude/settings.local.json +0 -31
- package/.claude/skills/api-integration.md +0 -125
- package/.claude/skills/database-schema.md +0 -51
- package/.claude/skills/dsl-design.md +0 -80
- package/.claude/skills/property-testing.md +0 -143
- package/.claude/skills/reqon/SKILL.md +0 -44
- package/.claude/skills/reqon/references/examples.md +0 -206
- package/.claude/skills/reqon/references/syntax.md +0 -263
- package/.claude/skills/vscode-extension.md +0 -113
- package/.github/dependabot.yml +0 -32
- package/.github/pull_request_template.md +0 -21
- package/.github/workflows/ci.yml +0 -174
- package/.github/workflows/release.yml +0 -73
- package/CLAUDE.md +0 -72
- package/CONTRIBUTING.md +0 -161
- package/TODO.md +0 -51
- package/dist/auth/auth.test.d.ts +0 -1
- package/dist/auth/auth.test.js +0 -255
- package/dist/errors/errors.test.d.ts +0 -1
- package/dist/errors/errors.test.js +0 -165
- package/dist/execution/execution.test.d.ts +0 -1
- package/dist/execution/execution.test.js +0 -246
- package/dist/integration.test.d.ts +0 -1
- package/dist/integration.test.js +0 -168
- package/dist/interpreter/evaluator.test.d.ts +0 -1
- package/dist/interpreter/evaluator.test.js +0 -512
- package/dist/interpreter/http.test.d.ts +0 -1
- package/dist/interpreter/http.test.js +0 -299
- package/dist/interpreter/progress.test.d.ts +0 -1
- package/dist/interpreter/progress.test.js +0 -216
- package/dist/interpreter/schema-matcher.test.d.ts +0 -1
- package/dist/interpreter/schema-matcher.test.js +0 -122
- package/dist/lexer/lexer.d.ts +0 -24
- package/dist/lexer/lexer.js +0 -264
- package/dist/lexer/lexer.test.d.ts +0 -1
- package/dist/lexer/lexer.test.js +0 -259
- package/dist/loader/loader.test.d.ts +0 -1
- package/dist/loader/loader.test.js +0 -287
- package/dist/oas/oas.test.d.ts +0 -1
- package/dist/oas/oas.test.js +0 -218
- package/dist/parser/expressions.test.d.ts +0 -1
- package/dist/parser/expressions.test.js +0 -378
- package/dist/parser/match.test.d.ts +0 -1
- package/dist/parser/match.test.js +0 -254
- package/dist/parser/parser.test.d.ts +0 -1
- package/dist/parser/parser.test.js +0 -333
- package/dist/parser/schedule.test.d.ts +0 -1
- package/dist/parser/schedule.test.js +0 -241
- package/dist/scheduler/cron-parser.test.d.ts +0 -1
- package/dist/scheduler/cron-parser.test.js +0 -188
- package/dist/stores/file.test.d.ts +0 -1
- package/dist/stores/file.test.js +0 -165
- package/dist/stores/memory.test.d.ts +0 -1
- package/dist/stores/memory.test.js +0 -157
- package/dist/stores/stores.test.d.ts +0 -1
- package/dist/stores/stores.test.js +0 -158
- package/dist/sync/sync.test.d.ts +0 -1
- package/dist/sync/sync.test.js +0 -221
- package/docusaurus/README.md +0 -41
- package/docusaurus/docs/advanced/execution-state.md +0 -283
- package/docusaurus/docs/advanced/extending-reqon.md +0 -388
- package/docusaurus/docs/advanced/multi-file-missions.md +0 -250
- package/docusaurus/docs/advanced/parallel-execution.md +0 -353
- package/docusaurus/docs/api-reference.md +0 -443
- package/docusaurus/docs/authentication/api-key.md +0 -339
- package/docusaurus/docs/authentication/basic.md +0 -276
- package/docusaurus/docs/authentication/bearer.md +0 -282
- package/docusaurus/docs/authentication/oauth2.md +0 -317
- package/docusaurus/docs/authentication/overview.md +0 -251
- package/docusaurus/docs/cli.md +0 -229
- package/docusaurus/docs/core-concepts/actions.md +0 -286
- package/docusaurus/docs/core-concepts/missions.md +0 -264
- package/docusaurus/docs/core-concepts/schemas.md +0 -353
- package/docusaurus/docs/core-concepts/sources.md +0 -339
- package/docusaurus/docs/core-concepts/stores.md +0 -332
- package/docusaurus/docs/dsl-syntax/expressions.md +0 -361
- package/docusaurus/docs/dsl-syntax/fetch.md +0 -293
- package/docusaurus/docs/dsl-syntax/for-loops.md +0 -324
- package/docusaurus/docs/dsl-syntax/map.md +0 -345
- package/docusaurus/docs/dsl-syntax/match.md +0 -387
- package/docusaurus/docs/dsl-syntax/pipelines.md +0 -397
- package/docusaurus/docs/dsl-syntax/validate.md +0 -401
- package/docusaurus/docs/error-handling/dead-letter-queues.md +0 -399
- package/docusaurus/docs/error-handling/flow-control.md +0 -337
- package/docusaurus/docs/error-handling/retry-strategies.md +0 -368
- package/docusaurus/docs/examples.md +0 -488
- package/docusaurus/docs/getting-started.md +0 -256
- package/docusaurus/docs/http/circuit-breaker.md +0 -401
- package/docusaurus/docs/http/incremental-sync.md +0 -394
- package/docusaurus/docs/http/pagination.md +0 -361
- package/docusaurus/docs/http/rate-limiting.md +0 -383
- package/docusaurus/docs/http/requests.md +0 -328
- package/docusaurus/docs/http/retry.md +0 -402
- package/docusaurus/docs/intro.md +0 -90
- package/docusaurus/docs/openapi/loading-specs.md +0 -305
- package/docusaurus/docs/openapi/operation-calls.md +0 -314
- package/docusaurus/docs/openapi/overview.md +0 -212
- package/docusaurus/docs/openapi/response-validation.md +0 -344
- package/docusaurus/docs/scheduling/cron.md +0 -305
- package/docusaurus/docs/scheduling/daemon-mode.md +0 -317
- package/docusaurus/docs/scheduling/intervals.md +0 -289
- package/docusaurus/docs/scheduling/overview.md +0 -231
- package/docusaurus/docs/stores/custom-adapters.md +0 -376
- package/docusaurus/docs/stores/file.md +0 -236
- package/docusaurus/docs/stores/memory.md +0 -193
- package/docusaurus/docs/stores/overview.md +0 -274
- package/docusaurus/docs/stores/postgrest.md +0 -316
- package/docusaurus/docusaurus.config.ts +0 -148
- package/docusaurus/package-lock.json +0 -18029
- package/docusaurus/package.json +0 -47
- package/docusaurus/sidebars.ts +0 -155
- package/docusaurus/src/components/HomepageFeatures/index.tsx +0 -105
- package/docusaurus/src/components/HomepageFeatures/styles.module.css +0 -12
- package/docusaurus/src/css/custom.css +0 -169
- package/docusaurus/src/pages/index.module.css +0 -48
- package/docusaurus/src/pages/index.tsx +0 -110
- package/docusaurus/src/pages/markdown-page.md +0 -7
- 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 +0 -10
- package/docusaurus/static/img/undraw_docusaurus_mountain.svg +0 -171
- package/docusaurus/static/img/undraw_docusaurus_react.svg +0 -170
- package/docusaurus/static/img/undraw_docusaurus_tree.svg +0 -40
- package/docusaurus/tsconfig.json +0 -8
- package/examples/README.md +0 -112
- package/examples/error-handling/README.md +0 -150
- package/examples/error-handling/payment-processor.vague +0 -287
- package/examples/github-sync/README.md +0 -74
- package/examples/github-sync/fetch-issues.vague +0 -47
- package/examples/github-sync/fetch-prs.vague +0 -40
- package/examples/github-sync/mission.vague +0 -101
- package/examples/github-sync/normalize.vague +0 -70
- package/examples/jsonplaceholder/README.md +0 -28
- package/examples/jsonplaceholder/posts.vague +0 -48
- package/examples/petstore/README.md +0 -35
- package/examples/petstore/openapi.yaml +0 -97
- package/examples/petstore/sync.vague +0 -52
- package/examples/temporal-comparison/README.md +0 -297
- package/examples/temporal-comparison/reconciliation.vague +0 -355
- package/examples/temporal-comparison/temporal/activities/index.ts +0 -8
- package/examples/temporal-comparison/temporal/activities/shipstation.ts +0 -225
- package/examples/temporal-comparison/temporal/activities/shopify.ts +0 -257
- package/examples/temporal-comparison/temporal/activities/storage.ts +0 -198
- package/examples/temporal-comparison/temporal/activities/stripe.ts +0 -169
- package/examples/temporal-comparison/temporal/activities/validation.ts +0 -205
- package/examples/temporal-comparison/temporal/client/schedule.ts +0 -218
- package/examples/temporal-comparison/temporal/config/retry.ts +0 -63
- package/examples/temporal-comparison/temporal/types/index.ts +0 -129
- package/examples/temporal-comparison/temporal/workers/main.ts +0 -130
- package/examples/temporal-comparison/temporal/workflows/orderReconciliation.ts +0 -262
- package/examples/xero/README.md +0 -88
- package/examples/xero/invoices.vague +0 -189
- package/src/api-integration.test.ts +0 -954
- package/src/ast/index.ts +0 -1
- package/src/ast/nodes.ts +0 -310
- package/src/auth/auth.test.ts +0 -326
- package/src/auth/circuit-breaker.test.ts +0 -390
- package/src/auth/circuit-breaker.ts +0 -379
- package/src/auth/credentials.test.ts +0 -273
- package/src/auth/credentials.ts +0 -246
- package/src/auth/index.ts +0 -40
- package/src/auth/oauth2-provider.ts +0 -177
- package/src/auth/rate-limiter.ts +0 -459
- package/src/auth/token-store.ts +0 -177
- package/src/auth/types.ts +0 -159
- package/src/benchmark/e2e.bench.ts +0 -288
- package/src/benchmark/evaluator.bench.ts +0 -331
- package/src/benchmark/fixtures.ts +0 -295
- package/src/benchmark/index.ts +0 -108
- package/src/benchmark/lexer.bench.ts +0 -69
- package/src/benchmark/parser.bench.ts +0 -103
- package/src/benchmark/resilience.bench.ts +0 -193
- package/src/benchmark/store.bench.ts +0 -147
- package/src/benchmark/utils.ts +0 -230
- package/src/cli.ts +0 -313
- package/src/errors/errors.test.ts +0 -234
- package/src/errors/index.ts +0 -223
- package/src/execution/execution.test.ts +0 -307
- package/src/execution/index.ts +0 -21
- package/src/execution/state.ts +0 -207
- package/src/execution/store.ts +0 -188
- package/src/index.ts +0 -169
- package/src/integration.test.ts +0 -192
- package/src/interpreter/context.ts +0 -57
- package/src/interpreter/evaluator.test.ts +0 -796
- package/src/interpreter/evaluator.ts +0 -245
- package/src/interpreter/executor.ts +0 -946
- package/src/interpreter/fetch-handler.ts +0 -302
- package/src/interpreter/http.test.ts +0 -423
- package/src/interpreter/http.ts +0 -308
- package/src/interpreter/index.ts +0 -32
- package/src/interpreter/pagination.ts +0 -207
- package/src/interpreter/progress.test.ts +0 -276
- package/src/interpreter/schema-matcher.test.ts +0 -160
- package/src/interpreter/schema-matcher.ts +0 -168
- package/src/interpreter/signals.ts +0 -73
- package/src/interpreter/step-handlers/for-handler.ts +0 -65
- package/src/interpreter/step-handlers/index.ts +0 -17
- package/src/interpreter/step-handlers/map-handler.ts +0 -24
- package/src/interpreter/step-handlers/match-handler.ts +0 -101
- package/src/interpreter/step-handlers/store-handler.ts +0 -78
- package/src/interpreter/step-handlers/types.ts +0 -17
- package/src/interpreter/step-handlers/validate-handler.ts +0 -30
- package/src/interpreter/step-handlers/webhook-handler.ts +0 -142
- package/src/lexer/index.ts +0 -18
- package/src/lexer/lexer.test.ts +0 -316
- package/src/lexer/tokens.ts +0 -179
- package/src/loader/index.ts +0 -288
- package/src/loader/loader.test.ts +0 -360
- package/src/oas/index.ts +0 -4
- package/src/oas/loader.ts +0 -126
- package/src/oas/oas.test.ts +0 -254
- package/src/oas/validator.ts +0 -299
- package/src/parser/base.ts +0 -124
- package/src/parser/expressions.test.ts +0 -525
- package/src/parser/expressions.ts +0 -314
- package/src/parser/index.ts +0 -3
- package/src/parser/match.test.ts +0 -296
- package/src/parser/parser.test.ts +0 -739
- package/src/parser/parser.ts +0 -1469
- package/src/parser/schedule.test.ts +0 -287
- package/src/parser/webhook.test.ts +0 -248
- package/src/plugin.ts +0 -83
- package/src/scheduler/cron-parser.test.ts +0 -236
- package/src/scheduler/cron-parser.ts +0 -236
- package/src/scheduler/index.ts +0 -10
- package/src/scheduler/scheduler.ts +0 -443
- package/src/scheduler/types.ts +0 -71
- package/src/stores/factory.ts +0 -104
- package/src/stores/file.test.ts +0 -276
- package/src/stores/file.ts +0 -211
- package/src/stores/index.ts +0 -6
- package/src/stores/memory.test.ts +0 -238
- package/src/stores/memory.ts +0 -63
- package/src/stores/postgrest.test.ts +0 -488
- package/src/stores/postgrest.ts +0 -263
- package/src/stores/stores.test.ts +0 -197
- package/src/stores/types.ts +0 -58
- package/src/sync/index.ts +0 -16
- package/src/sync/state.ts +0 -126
- package/src/sync/store.ts +0 -139
- package/src/sync/sync.test.ts +0 -271
- package/src/utils/async.ts +0 -10
- package/src/utils/file.ts +0 -106
- package/src/utils/index.ts +0 -14
- package/src/utils/logger.ts +0 -53
- package/src/utils/path.ts +0 -47
- package/src/webhook/index.ts +0 -15
- package/src/webhook/server.test.ts +0 -253
- package/src/webhook/server.ts +0 -389
- package/src/webhook/store.ts +0 -239
- package/src/webhook/types.ts +0 -93
- package/tsconfig.json +0 -17
- package/vitest.config.ts +0 -39
|
@@ -1,236 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from 'vitest';
|
|
2
|
-
import {
|
|
3
|
-
parseCronExpression,
|
|
4
|
-
getNextCronRun,
|
|
5
|
-
intervalToMs,
|
|
6
|
-
getNextRunTime,
|
|
7
|
-
shouldRunNow,
|
|
8
|
-
} from './cron-parser.js';
|
|
9
|
-
import type { ScheduleDefinition, IntervalSchedule } from '../ast/nodes.js';
|
|
10
|
-
|
|
11
|
-
describe('parseCronExpression', () => {
|
|
12
|
-
it('should parse simple cron expressions', () => {
|
|
13
|
-
const schedule = parseCronExpression('0 * * * *');
|
|
14
|
-
expect(schedule.minute).toEqual([0]);
|
|
15
|
-
expect(schedule.hour).toHaveLength(24); // All hours
|
|
16
|
-
expect(schedule.dayOfMonth).toHaveLength(31);
|
|
17
|
-
expect(schedule.month).toHaveLength(12);
|
|
18
|
-
expect(schedule.dayOfWeek).toHaveLength(7);
|
|
19
|
-
});
|
|
20
|
-
|
|
21
|
-
it('should parse specific values', () => {
|
|
22
|
-
const schedule = parseCronExpression('30 9 15 6 1');
|
|
23
|
-
expect(schedule.minute).toEqual([30]);
|
|
24
|
-
expect(schedule.hour).toEqual([9]);
|
|
25
|
-
expect(schedule.dayOfMonth).toEqual([15]);
|
|
26
|
-
expect(schedule.month).toEqual([6]);
|
|
27
|
-
expect(schedule.dayOfWeek).toEqual([1]);
|
|
28
|
-
});
|
|
29
|
-
|
|
30
|
-
it('should parse ranges', () => {
|
|
31
|
-
const schedule = parseCronExpression('0-5 9-17 * * 1-5');
|
|
32
|
-
expect(schedule.minute).toEqual([0, 1, 2, 3, 4, 5]);
|
|
33
|
-
expect(schedule.hour).toEqual([9, 10, 11, 12, 13, 14, 15, 16, 17]);
|
|
34
|
-
expect(schedule.dayOfWeek).toEqual([1, 2, 3, 4, 5]);
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
it('should parse step values', () => {
|
|
38
|
-
const schedule = parseCronExpression('*/15 */6 * * *');
|
|
39
|
-
expect(schedule.minute).toEqual([0, 15, 30, 45]);
|
|
40
|
-
expect(schedule.hour).toEqual([0, 6, 12, 18]);
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
it('should parse lists', () => {
|
|
44
|
-
const schedule = parseCronExpression('0,30 9,12,18 * * *');
|
|
45
|
-
expect(schedule.minute).toEqual([0, 30]);
|
|
46
|
-
expect(schedule.hour).toEqual([9, 12, 18]);
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
it('should parse combined expressions', () => {
|
|
50
|
-
const schedule = parseCronExpression('0 9-17/2 * * 1-5');
|
|
51
|
-
expect(schedule.minute).toEqual([0]);
|
|
52
|
-
expect(schedule.hour).toEqual([9, 11, 13, 15, 17]);
|
|
53
|
-
expect(schedule.dayOfWeek).toEqual([1, 2, 3, 4, 5]);
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
it('should throw on invalid cron expression', () => {
|
|
57
|
-
expect(() => parseCronExpression('0 * *')).toThrow('expected 5 fields');
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
it('should throw on out of range values', () => {
|
|
61
|
-
expect(() => parseCronExpression('60 * * * *')).toThrow('out of range');
|
|
62
|
-
expect(() => parseCronExpression('* 25 * * *')).toThrow('out of range');
|
|
63
|
-
});
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
describe('getNextCronRun', () => {
|
|
67
|
-
it('should calculate next run for every minute', () => {
|
|
68
|
-
const schedule = parseCronExpression('* * * * *');
|
|
69
|
-
const now = new Date('2025-01-20T10:30:00Z');
|
|
70
|
-
const next = getNextCronRun(schedule, now);
|
|
71
|
-
|
|
72
|
-
expect(next.getMinutes()).toBe(31);
|
|
73
|
-
expect(next.getHours()).toBe(10);
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
it('should calculate next run for specific time', () => {
|
|
77
|
-
const schedule = parseCronExpression('0 9 * * *');
|
|
78
|
-
const now = new Date('2025-01-20T10:30:00Z');
|
|
79
|
-
const next = getNextCronRun(schedule, now);
|
|
80
|
-
|
|
81
|
-
// Should be 9:00 the next day
|
|
82
|
-
expect(next.getMinutes()).toBe(0);
|
|
83
|
-
expect(next.getHours()).toBe(9);
|
|
84
|
-
expect(next.getDate()).toBe(21);
|
|
85
|
-
});
|
|
86
|
-
|
|
87
|
-
it('should handle hourly schedules', () => {
|
|
88
|
-
const schedule = parseCronExpression('0 * * * *');
|
|
89
|
-
const now = new Date('2025-01-20T10:30:00Z');
|
|
90
|
-
const next = getNextCronRun(schedule, now);
|
|
91
|
-
|
|
92
|
-
expect(next.getMinutes()).toBe(0);
|
|
93
|
-
expect(next.getHours()).toBe(11);
|
|
94
|
-
});
|
|
95
|
-
|
|
96
|
-
it('should handle day of week constraints', () => {
|
|
97
|
-
const schedule = parseCronExpression('0 9 * * 1'); // Monday only
|
|
98
|
-
const now = new Date('2025-01-20T10:30:00Z'); // This is a Monday
|
|
99
|
-
const next = getNextCronRun(schedule, now);
|
|
100
|
-
|
|
101
|
-
// Should be next Monday at 9:00
|
|
102
|
-
expect(next.getDay()).toBe(1); // Monday
|
|
103
|
-
expect(next.getHours()).toBe(9);
|
|
104
|
-
expect(next.getMinutes()).toBe(0);
|
|
105
|
-
});
|
|
106
|
-
});
|
|
107
|
-
|
|
108
|
-
describe('intervalToMs', () => {
|
|
109
|
-
it('should convert seconds to ms', () => {
|
|
110
|
-
const interval: IntervalSchedule = { value: 30, unit: 'seconds' };
|
|
111
|
-
expect(intervalToMs(interval)).toBe(30_000);
|
|
112
|
-
});
|
|
113
|
-
|
|
114
|
-
it('should convert minutes to ms', () => {
|
|
115
|
-
const interval: IntervalSchedule = { value: 5, unit: 'minutes' };
|
|
116
|
-
expect(intervalToMs(interval)).toBe(5 * 60 * 1000);
|
|
117
|
-
});
|
|
118
|
-
|
|
119
|
-
it('should convert hours to ms', () => {
|
|
120
|
-
const interval: IntervalSchedule = { value: 6, unit: 'hours' };
|
|
121
|
-
expect(intervalToMs(interval)).toBe(6 * 60 * 60 * 1000);
|
|
122
|
-
});
|
|
123
|
-
|
|
124
|
-
it('should convert days to ms', () => {
|
|
125
|
-
const interval: IntervalSchedule = { value: 1, unit: 'days' };
|
|
126
|
-
expect(intervalToMs(interval)).toBe(24 * 60 * 60 * 1000);
|
|
127
|
-
});
|
|
128
|
-
|
|
129
|
-
it('should convert weeks to ms', () => {
|
|
130
|
-
const interval: IntervalSchedule = { value: 1, unit: 'weeks' };
|
|
131
|
-
expect(intervalToMs(interval)).toBe(7 * 24 * 60 * 60 * 1000);
|
|
132
|
-
});
|
|
133
|
-
});
|
|
134
|
-
|
|
135
|
-
describe('getNextRunTime', () => {
|
|
136
|
-
it('should calculate next run for interval schedule', () => {
|
|
137
|
-
const schedule: ScheduleDefinition = {
|
|
138
|
-
type: 'ScheduleDefinition',
|
|
139
|
-
scheduleType: 'interval',
|
|
140
|
-
interval: { value: 6, unit: 'hours' },
|
|
141
|
-
};
|
|
142
|
-
|
|
143
|
-
const now = new Date('2025-01-20T10:00:00Z');
|
|
144
|
-
const next = getNextRunTime(schedule, now);
|
|
145
|
-
|
|
146
|
-
expect(next).not.toBeNull();
|
|
147
|
-
expect(next!.getTime()).toBe(now.getTime() + 6 * 60 * 60 * 1000);
|
|
148
|
-
});
|
|
149
|
-
|
|
150
|
-
it('should calculate next run for cron schedule', () => {
|
|
151
|
-
const schedule: ScheduleDefinition = {
|
|
152
|
-
type: 'ScheduleDefinition',
|
|
153
|
-
scheduleType: 'cron',
|
|
154
|
-
cronExpression: '0 */6 * * *',
|
|
155
|
-
};
|
|
156
|
-
|
|
157
|
-
const now = new Date('2025-01-20T10:30:00Z');
|
|
158
|
-
const next = getNextRunTime(schedule, now);
|
|
159
|
-
|
|
160
|
-
expect(next).not.toBeNull();
|
|
161
|
-
expect(next!.getMinutes()).toBe(0);
|
|
162
|
-
expect(next!.getHours()).toBe(12); // Next 6-hour mark
|
|
163
|
-
});
|
|
164
|
-
|
|
165
|
-
it('should calculate next run for one-time schedule', () => {
|
|
166
|
-
const schedule: ScheduleDefinition = {
|
|
167
|
-
type: 'ScheduleDefinition',
|
|
168
|
-
scheduleType: 'once',
|
|
169
|
-
runAt: '2025-01-25T15:00:00Z',
|
|
170
|
-
};
|
|
171
|
-
|
|
172
|
-
const now = new Date('2025-01-20T10:00:00Z');
|
|
173
|
-
const next = getNextRunTime(schedule, now);
|
|
174
|
-
|
|
175
|
-
expect(next).not.toBeNull();
|
|
176
|
-
expect(next!.toISOString()).toBe('2025-01-25T15:00:00.000Z');
|
|
177
|
-
});
|
|
178
|
-
|
|
179
|
-
it('should return null for past one-time schedule', () => {
|
|
180
|
-
const schedule: ScheduleDefinition = {
|
|
181
|
-
type: 'ScheduleDefinition',
|
|
182
|
-
scheduleType: 'once',
|
|
183
|
-
runAt: '2025-01-15T15:00:00Z',
|
|
184
|
-
};
|
|
185
|
-
|
|
186
|
-
const now = new Date('2025-01-20T10:00:00Z');
|
|
187
|
-
const next = getNextRunTime(schedule, now);
|
|
188
|
-
|
|
189
|
-
expect(next).toBeNull();
|
|
190
|
-
});
|
|
191
|
-
});
|
|
192
|
-
|
|
193
|
-
describe('shouldRunNow', () => {
|
|
194
|
-
it('should return true for interval schedule that is due', () => {
|
|
195
|
-
const schedule: ScheduleDefinition = {
|
|
196
|
-
type: 'ScheduleDefinition',
|
|
197
|
-
scheduleType: 'interval',
|
|
198
|
-
interval: { value: 1, unit: 'hours' },
|
|
199
|
-
};
|
|
200
|
-
|
|
201
|
-
const lastRun = new Date(Date.now() - 61 * 60 * 1000); // 61 minutes ago
|
|
202
|
-
expect(shouldRunNow(schedule, lastRun)).toBe(true);
|
|
203
|
-
});
|
|
204
|
-
|
|
205
|
-
it('should return false for interval schedule not yet due', () => {
|
|
206
|
-
const schedule: ScheduleDefinition = {
|
|
207
|
-
type: 'ScheduleDefinition',
|
|
208
|
-
scheduleType: 'interval',
|
|
209
|
-
interval: { value: 1, unit: 'hours' },
|
|
210
|
-
};
|
|
211
|
-
|
|
212
|
-
const lastRun = new Date(Date.now() - 30 * 60 * 1000); // 30 minutes ago
|
|
213
|
-
expect(shouldRunNow(schedule, lastRun)).toBe(false);
|
|
214
|
-
});
|
|
215
|
-
|
|
216
|
-
it('should return true for first run of interval schedule', () => {
|
|
217
|
-
const schedule: ScheduleDefinition = {
|
|
218
|
-
type: 'ScheduleDefinition',
|
|
219
|
-
scheduleType: 'interval',
|
|
220
|
-
interval: { value: 1, unit: 'hours' },
|
|
221
|
-
};
|
|
222
|
-
|
|
223
|
-
expect(shouldRunNow(schedule, undefined)).toBe(true);
|
|
224
|
-
});
|
|
225
|
-
|
|
226
|
-
it('should return false for one-time schedule that already ran', () => {
|
|
227
|
-
const schedule: ScheduleDefinition = {
|
|
228
|
-
type: 'ScheduleDefinition',
|
|
229
|
-
scheduleType: 'once',
|
|
230
|
-
runAt: '2025-01-15T15:00:00Z',
|
|
231
|
-
};
|
|
232
|
-
|
|
233
|
-
const lastRun = new Date('2025-01-15T15:00:00Z');
|
|
234
|
-
expect(shouldRunNow(schedule, lastRun)).toBe(false);
|
|
235
|
-
});
|
|
236
|
-
});
|
|
@@ -1,236 +0,0 @@
|
|
|
1
|
-
import type { ScheduleDefinition, IntervalSchedule } from '../ast/nodes.js';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Parse a cron expression and calculate the next run time
|
|
5
|
-
*
|
|
6
|
-
* Cron format: "minute hour day-of-month month day-of-week"
|
|
7
|
-
* Supports: numbers, ranges (1-5), steps (*/5), lists (1,3,5), and wildcards (*)
|
|
8
|
-
*/
|
|
9
|
-
export function parseCronExpression(expression: string): CronSchedule {
|
|
10
|
-
const parts = expression.trim().split(/\s+/);
|
|
11
|
-
|
|
12
|
-
if (parts.length !== 5) {
|
|
13
|
-
throw new Error(`Invalid cron expression: expected 5 fields, got ${parts.length}`);
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
return {
|
|
17
|
-
minute: parseField(parts[0], 0, 59),
|
|
18
|
-
hour: parseField(parts[1], 0, 23),
|
|
19
|
-
dayOfMonth: parseField(parts[2], 1, 31),
|
|
20
|
-
month: parseField(parts[3], 1, 12),
|
|
21
|
-
dayOfWeek: parseField(parts[4], 0, 6), // 0 = Sunday
|
|
22
|
-
};
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
interface CronSchedule {
|
|
26
|
-
minute: number[];
|
|
27
|
-
hour: number[];
|
|
28
|
-
dayOfMonth: number[];
|
|
29
|
-
month: number[];
|
|
30
|
-
dayOfWeek: number[];
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
function parseField(field: string, min: number, max: number): number[] {
|
|
34
|
-
const values: Set<number> = new Set();
|
|
35
|
-
|
|
36
|
-
for (const part of field.split(',')) {
|
|
37
|
-
if (part === '*') {
|
|
38
|
-
// All values
|
|
39
|
-
for (let i = min; i <= max; i++) {
|
|
40
|
-
values.add(i);
|
|
41
|
-
}
|
|
42
|
-
} else if (part.includes('/')) {
|
|
43
|
-
// Step values (e.g., */5 or 1-10/2)
|
|
44
|
-
const [range, stepStr] = part.split('/');
|
|
45
|
-
const step = parseInt(stepStr, 10);
|
|
46
|
-
|
|
47
|
-
let start = min;
|
|
48
|
-
let end = max;
|
|
49
|
-
|
|
50
|
-
if (range !== '*') {
|
|
51
|
-
if (range.includes('-')) {
|
|
52
|
-
const [rangeStart, rangeEnd] = range.split('-').map((n) => parseInt(n, 10));
|
|
53
|
-
start = rangeStart;
|
|
54
|
-
end = rangeEnd;
|
|
55
|
-
} else {
|
|
56
|
-
start = parseInt(range, 10);
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
for (let i = start; i <= end; i += step) {
|
|
61
|
-
values.add(i);
|
|
62
|
-
}
|
|
63
|
-
} else if (part.includes('-')) {
|
|
64
|
-
// Range (e.g., 1-5)
|
|
65
|
-
const [start, end] = part.split('-').map((n) => parseInt(n, 10));
|
|
66
|
-
for (let i = start; i <= end; i++) {
|
|
67
|
-
values.add(i);
|
|
68
|
-
}
|
|
69
|
-
} else {
|
|
70
|
-
// Single value
|
|
71
|
-
values.add(parseInt(part, 10));
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
// Validate all values are in range
|
|
76
|
-
for (const value of values) {
|
|
77
|
-
if (value < min || value > max) {
|
|
78
|
-
throw new Error(`Cron field value ${value} out of range [${min}, ${max}]`);
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
return Array.from(values).sort((a, b) => a - b);
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
/**
|
|
86
|
-
* Calculate the next run time for a cron schedule
|
|
87
|
-
*/
|
|
88
|
-
export function getNextCronRun(schedule: CronSchedule, after: Date = new Date()): Date {
|
|
89
|
-
const next = new Date(after);
|
|
90
|
-
next.setSeconds(0, 0);
|
|
91
|
-
next.setMinutes(next.getMinutes() + 1); // Start from next minute
|
|
92
|
-
|
|
93
|
-
// Try up to 4 years to find a match
|
|
94
|
-
const maxIterations = 4 * 366 * 24 * 60;
|
|
95
|
-
|
|
96
|
-
for (let i = 0; i < maxIterations; i++) {
|
|
97
|
-
// Check month
|
|
98
|
-
if (!schedule.month.includes(next.getMonth() + 1)) {
|
|
99
|
-
// Move to first day of next matching month
|
|
100
|
-
next.setMonth(next.getMonth() + 1);
|
|
101
|
-
next.setDate(1);
|
|
102
|
-
next.setHours(0, 0, 0, 0);
|
|
103
|
-
continue;
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
// Check day of month
|
|
107
|
-
if (!schedule.dayOfMonth.includes(next.getDate())) {
|
|
108
|
-
next.setDate(next.getDate() + 1);
|
|
109
|
-
next.setHours(0, 0, 0, 0);
|
|
110
|
-
continue;
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
// Check day of week
|
|
114
|
-
if (!schedule.dayOfWeek.includes(next.getDay())) {
|
|
115
|
-
next.setDate(next.getDate() + 1);
|
|
116
|
-
next.setHours(0, 0, 0, 0);
|
|
117
|
-
continue;
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
// Check hour
|
|
121
|
-
if (!schedule.hour.includes(next.getHours())) {
|
|
122
|
-
next.setHours(next.getHours() + 1);
|
|
123
|
-
next.setMinutes(0, 0, 0);
|
|
124
|
-
continue;
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
// Check minute
|
|
128
|
-
if (!schedule.minute.includes(next.getMinutes())) {
|
|
129
|
-
next.setMinutes(next.getMinutes() + 1);
|
|
130
|
-
continue;
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
// Found a match!
|
|
134
|
-
return next;
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
throw new Error('Could not find next cron run time within 4 years');
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
/**
|
|
141
|
-
* Convert interval schedule to milliseconds
|
|
142
|
-
*/
|
|
143
|
-
export function intervalToMs(interval: IntervalSchedule): number {
|
|
144
|
-
const multipliers: Record<IntervalSchedule['unit'], number> = {
|
|
145
|
-
seconds: 1000,
|
|
146
|
-
minutes: 60 * 1000,
|
|
147
|
-
hours: 60 * 60 * 1000,
|
|
148
|
-
days: 24 * 60 * 60 * 1000,
|
|
149
|
-
weeks: 7 * 24 * 60 * 60 * 1000,
|
|
150
|
-
};
|
|
151
|
-
|
|
152
|
-
return interval.value * multipliers[interval.unit];
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
/**
|
|
156
|
-
* Calculate next run time based on schedule definition
|
|
157
|
-
*/
|
|
158
|
-
export function getNextRunTime(schedule: ScheduleDefinition, after: Date = new Date()): Date | null {
|
|
159
|
-
switch (schedule.scheduleType) {
|
|
160
|
-
case 'interval': {
|
|
161
|
-
if (!schedule.interval) {
|
|
162
|
-
throw new Error('Interval schedule missing interval configuration');
|
|
163
|
-
}
|
|
164
|
-
const intervalMs = intervalToMs(schedule.interval);
|
|
165
|
-
return new Date(after.getTime() + intervalMs);
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
case 'cron': {
|
|
169
|
-
if (!schedule.cronExpression) {
|
|
170
|
-
throw new Error('Cron schedule missing cron expression');
|
|
171
|
-
}
|
|
172
|
-
const cronSchedule = parseCronExpression(schedule.cronExpression);
|
|
173
|
-
return getNextCronRun(cronSchedule, after);
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
case 'once': {
|
|
177
|
-
if (!schedule.runAt) {
|
|
178
|
-
throw new Error('One-time schedule missing runAt datetime');
|
|
179
|
-
}
|
|
180
|
-
const runAt = new Date(schedule.runAt);
|
|
181
|
-
// If the scheduled time is in the past, return null (job should not run)
|
|
182
|
-
if (runAt <= after) {
|
|
183
|
-
return null;
|
|
184
|
-
}
|
|
185
|
-
return runAt;
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
default:
|
|
189
|
-
throw new Error(`Unknown schedule type: ${schedule.scheduleType}`);
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
/**
|
|
194
|
-
* Check if a schedule should run now (within the check interval)
|
|
195
|
-
*/
|
|
196
|
-
export function shouldRunNow(
|
|
197
|
-
schedule: ScheduleDefinition,
|
|
198
|
-
lastRun: Date | undefined,
|
|
199
|
-
checkIntervalMs: number = 1000
|
|
200
|
-
): boolean {
|
|
201
|
-
const now = new Date();
|
|
202
|
-
|
|
203
|
-
switch (schedule.scheduleType) {
|
|
204
|
-
case 'interval': {
|
|
205
|
-
if (!lastRun) return true; // Never run before, run now
|
|
206
|
-
if (!schedule.interval) return false;
|
|
207
|
-
|
|
208
|
-
const intervalMs = intervalToMs(schedule.interval);
|
|
209
|
-
const elapsed = now.getTime() - lastRun.getTime();
|
|
210
|
-
return elapsed >= intervalMs;
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
case 'cron': {
|
|
214
|
-
if (!schedule.cronExpression) return false;
|
|
215
|
-
|
|
216
|
-
const cronSchedule = parseCronExpression(schedule.cronExpression);
|
|
217
|
-
const nextRun = getNextCronRun(cronSchedule, lastRun ?? new Date(0));
|
|
218
|
-
|
|
219
|
-
// Check if we're within the check interval of the next run time
|
|
220
|
-
const diff = Math.abs(now.getTime() - nextRun.getTime());
|
|
221
|
-
return diff <= checkIntervalMs;
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
case 'once': {
|
|
225
|
-
if (!schedule.runAt) return false;
|
|
226
|
-
if (lastRun) return false; // Already ran
|
|
227
|
-
|
|
228
|
-
const runAt = new Date(schedule.runAt);
|
|
229
|
-
const diff = Math.abs(now.getTime() - runAt.getTime());
|
|
230
|
-
return diff <= checkIntervalMs;
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
default:
|
|
234
|
-
return false;
|
|
235
|
-
}
|
|
236
|
-
}
|
package/src/scheduler/index.ts
DELETED
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
export { Scheduler } from './scheduler.js';
|
|
2
|
-
export { parseCronExpression, getNextRunTime, intervalToMs, shouldRunNow } from './cron-parser.js';
|
|
3
|
-
export type {
|
|
4
|
-
ScheduledJob,
|
|
5
|
-
SchedulerState,
|
|
6
|
-
ScheduleEvent,
|
|
7
|
-
SchedulerCallbacks,
|
|
8
|
-
SchedulerConfig,
|
|
9
|
-
ScheduledMission,
|
|
10
|
-
} from './types.js';
|