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
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Trace Store - Persistence for execution traces
|
|
3
|
+
*
|
|
4
|
+
* Stores complete execution traces for time-travel debugging,
|
|
5
|
+
* enabling replay and step-by-step inspection of past runs.
|
|
6
|
+
*/
|
|
7
|
+
import { join } from 'node:path';
|
|
8
|
+
import { ensureDirectory, writeJsonFile, readJsonFile, listFiles, deleteFile, restoreDates, restoreDatesInArray, } from '../utils/file.js';
|
|
9
|
+
/**
|
|
10
|
+
* File-based trace store
|
|
11
|
+
* Stores traces in .reqon-data/traces/ with separate files for metadata and snapshots
|
|
12
|
+
*/
|
|
13
|
+
export class FileTraceStore {
|
|
14
|
+
baseDir;
|
|
15
|
+
initialized;
|
|
16
|
+
constructor(baseDir = '.reqon-data/traces') {
|
|
17
|
+
this.baseDir = baseDir;
|
|
18
|
+
this.initialized = ensureDirectory(this.baseDir);
|
|
19
|
+
}
|
|
20
|
+
getTraceDir(id) {
|
|
21
|
+
return join(this.baseDir, id);
|
|
22
|
+
}
|
|
23
|
+
getMetadataPath(id) {
|
|
24
|
+
return join(this.getTraceDir(id), 'metadata.json');
|
|
25
|
+
}
|
|
26
|
+
getSnapshotPath(id, index) {
|
|
27
|
+
return join(this.getTraceDir(id), `snapshot_${index.toString().padStart(6, '0')}.json`);
|
|
28
|
+
}
|
|
29
|
+
async deserializeTrace(parsed) {
|
|
30
|
+
restoreDates(parsed, ['startedAt', 'completedAt']);
|
|
31
|
+
if (parsed.snapshots && Array.isArray(parsed.snapshots)) {
|
|
32
|
+
restoreDatesInArray(parsed.snapshots, ['timestamp']);
|
|
33
|
+
}
|
|
34
|
+
return parsed;
|
|
35
|
+
}
|
|
36
|
+
deserializeSnapshot(parsed) {
|
|
37
|
+
restoreDates(parsed, ['timestamp']);
|
|
38
|
+
return parsed;
|
|
39
|
+
}
|
|
40
|
+
async save(trace) {
|
|
41
|
+
await this.initialized;
|
|
42
|
+
const traceDir = this.getTraceDir(trace.id);
|
|
43
|
+
await ensureDirectory(traceDir);
|
|
44
|
+
// Save metadata separately from snapshots for efficient listing
|
|
45
|
+
const metadata = {
|
|
46
|
+
id: trace.id,
|
|
47
|
+
mission: trace.mission,
|
|
48
|
+
mode: trace.mode,
|
|
49
|
+
startedAt: trace.startedAt,
|
|
50
|
+
completedAt: trace.completedAt,
|
|
51
|
+
duration: trace.duration,
|
|
52
|
+
success: trace.success,
|
|
53
|
+
metadata: trace.metadata,
|
|
54
|
+
snapshotCount: trace.snapshots.length,
|
|
55
|
+
};
|
|
56
|
+
await writeJsonFile(this.getMetadataPath(trace.id), metadata);
|
|
57
|
+
// Save each snapshot separately for efficient random access
|
|
58
|
+
for (let i = 0; i < trace.snapshots.length; i++) {
|
|
59
|
+
await writeJsonFile(this.getSnapshotPath(trace.id, i), trace.snapshots[i]);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
async appendSnapshot(traceId, snapshot) {
|
|
63
|
+
await this.initialized;
|
|
64
|
+
const traceDir = this.getTraceDir(traceId);
|
|
65
|
+
await ensureDirectory(traceDir);
|
|
66
|
+
// Load current metadata to get snapshot count
|
|
67
|
+
const metadataPath = this.getMetadataPath(traceId);
|
|
68
|
+
const metadata = await readJsonFile(metadataPath);
|
|
69
|
+
let snapshotCount = 0;
|
|
70
|
+
if (metadata && typeof metadata.snapshotCount === 'number') {
|
|
71
|
+
snapshotCount = metadata.snapshotCount;
|
|
72
|
+
}
|
|
73
|
+
// Save the new snapshot
|
|
74
|
+
await writeJsonFile(this.getSnapshotPath(traceId, snapshotCount), snapshot);
|
|
75
|
+
// Update metadata with new count
|
|
76
|
+
if (metadata) {
|
|
77
|
+
metadata.snapshotCount = snapshotCount + 1;
|
|
78
|
+
await writeJsonFile(metadataPath, metadata);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
async load(id) {
|
|
82
|
+
await this.initialized;
|
|
83
|
+
const metadata = await this.getMetadata(id);
|
|
84
|
+
if (!metadata)
|
|
85
|
+
return null;
|
|
86
|
+
// Load all snapshots
|
|
87
|
+
const snapshots = [];
|
|
88
|
+
const snapshotFiles = await listFiles(this.getTraceDir(id), '.json');
|
|
89
|
+
for (const file of snapshotFiles) {
|
|
90
|
+
if (file.includes('snapshot_')) {
|
|
91
|
+
const parsed = await readJsonFile(file);
|
|
92
|
+
if (parsed) {
|
|
93
|
+
snapshots.push(this.deserializeSnapshot(parsed));
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
// Sort by index
|
|
98
|
+
snapshots.sort((a, b) => a.index - b.index);
|
|
99
|
+
return {
|
|
100
|
+
...metadata,
|
|
101
|
+
snapshots,
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
async loadSnapshot(traceId, snapshotIndex) {
|
|
105
|
+
await this.initialized;
|
|
106
|
+
const snapshotPath = this.getSnapshotPath(traceId, snapshotIndex);
|
|
107
|
+
const parsed = await readJsonFile(snapshotPath);
|
|
108
|
+
return parsed ? this.deserializeSnapshot(parsed) : null;
|
|
109
|
+
}
|
|
110
|
+
async listByMission(mission, limit = 50) {
|
|
111
|
+
const all = await this.listRecent(limit * 2); // Load more to filter
|
|
112
|
+
return all.filter((t) => t.mission === mission).slice(0, limit);
|
|
113
|
+
}
|
|
114
|
+
async listRecent(limit = 50) {
|
|
115
|
+
await this.initialized;
|
|
116
|
+
const entries = await listFiles(this.baseDir, '');
|
|
117
|
+
const traces = [];
|
|
118
|
+
// Read metadata files from subdirectories
|
|
119
|
+
for (const entry of entries) {
|
|
120
|
+
if (entry.endsWith('metadata.json')) {
|
|
121
|
+
const parsed = await readJsonFile(entry);
|
|
122
|
+
if (parsed) {
|
|
123
|
+
const trace = await this.deserializeTrace(parsed);
|
|
124
|
+
// Return without snapshots for listing efficiency
|
|
125
|
+
traces.push({ ...trace, snapshots: [] });
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
// Sort by start time, newest first
|
|
130
|
+
traces.sort((a, b) => b.startedAt.getTime() - a.startedAt.getTime());
|
|
131
|
+
return traces.slice(0, limit);
|
|
132
|
+
}
|
|
133
|
+
async delete(id) {
|
|
134
|
+
await this.initialized;
|
|
135
|
+
const traceDir = this.getTraceDir(id);
|
|
136
|
+
const files = await listFiles(traceDir, '.json');
|
|
137
|
+
for (const file of files) {
|
|
138
|
+
await deleteFile(file);
|
|
139
|
+
}
|
|
140
|
+
// Note: Directory cleanup would require additional fs.rmdir
|
|
141
|
+
}
|
|
142
|
+
async findLatest(mission) {
|
|
143
|
+
const traces = await this.listByMission(mission, 1);
|
|
144
|
+
return traces[0] ?? null;
|
|
145
|
+
}
|
|
146
|
+
async getMetadata(id) {
|
|
147
|
+
await this.initialized;
|
|
148
|
+
const parsed = await readJsonFile(this.getMetadataPath(id));
|
|
149
|
+
if (!parsed)
|
|
150
|
+
return null;
|
|
151
|
+
restoreDates(parsed, ['startedAt', 'completedAt']);
|
|
152
|
+
const { snapshotCount: _snapshotCount, ...rest } = parsed;
|
|
153
|
+
return rest;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* In-memory trace store (for testing)
|
|
158
|
+
*/
|
|
159
|
+
export class MemoryTraceStore {
|
|
160
|
+
traces = new Map();
|
|
161
|
+
async save(trace) {
|
|
162
|
+
this.traces.set(trace.id, JSON.parse(JSON.stringify(trace)));
|
|
163
|
+
}
|
|
164
|
+
async appendSnapshot(traceId, snapshot) {
|
|
165
|
+
const trace = this.traces.get(traceId);
|
|
166
|
+
if (trace) {
|
|
167
|
+
trace.snapshots.push(JSON.parse(JSON.stringify(snapshot)));
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
async load(id) {
|
|
171
|
+
const trace = this.traces.get(id);
|
|
172
|
+
if (!trace)
|
|
173
|
+
return null;
|
|
174
|
+
const restored = JSON.parse(JSON.stringify(trace));
|
|
175
|
+
restored.startedAt = new Date(restored.startedAt);
|
|
176
|
+
if (restored.completedAt) {
|
|
177
|
+
restored.completedAt = new Date(restored.completedAt);
|
|
178
|
+
}
|
|
179
|
+
for (const snapshot of restored.snapshots) {
|
|
180
|
+
snapshot.timestamp = new Date(snapshot.timestamp);
|
|
181
|
+
}
|
|
182
|
+
return restored;
|
|
183
|
+
}
|
|
184
|
+
async loadSnapshot(traceId, snapshotIndex) {
|
|
185
|
+
const trace = this.traces.get(traceId);
|
|
186
|
+
if (!trace || snapshotIndex >= trace.snapshots.length)
|
|
187
|
+
return null;
|
|
188
|
+
const snapshot = JSON.parse(JSON.stringify(trace.snapshots[snapshotIndex]));
|
|
189
|
+
snapshot.timestamp = new Date(snapshot.timestamp);
|
|
190
|
+
return snapshot;
|
|
191
|
+
}
|
|
192
|
+
async listByMission(mission, limit = 50) {
|
|
193
|
+
const all = await this.listRecent();
|
|
194
|
+
return all.filter((t) => t.mission === mission).slice(0, limit);
|
|
195
|
+
}
|
|
196
|
+
async listRecent(limit = 50) {
|
|
197
|
+
const traces = Array.from(this.traces.values()).map((t) => {
|
|
198
|
+
const restored = JSON.parse(JSON.stringify(t));
|
|
199
|
+
restored.startedAt = new Date(restored.startedAt);
|
|
200
|
+
if (restored.completedAt) {
|
|
201
|
+
restored.completedAt = new Date(restored.completedAt);
|
|
202
|
+
}
|
|
203
|
+
return { ...restored, snapshots: [] };
|
|
204
|
+
});
|
|
205
|
+
traces.sort((a, b) => b.startedAt.getTime() - a.startedAt.getTime());
|
|
206
|
+
return traces.slice(0, limit);
|
|
207
|
+
}
|
|
208
|
+
async delete(id) {
|
|
209
|
+
this.traces.delete(id);
|
|
210
|
+
}
|
|
211
|
+
async findLatest(mission) {
|
|
212
|
+
const traces = await this.listByMission(mission, 1);
|
|
213
|
+
return traces[0] ?? null;
|
|
214
|
+
}
|
|
215
|
+
async getMetadata(id) {
|
|
216
|
+
const trace = await this.load(id);
|
|
217
|
+
if (!trace)
|
|
218
|
+
return null;
|
|
219
|
+
const { snapshots: _snapshots, ...metadata } = trace;
|
|
220
|
+
return metadata;
|
|
221
|
+
}
|
|
222
|
+
clear() {
|
|
223
|
+
this.traces.clear();
|
|
224
|
+
}
|
|
225
|
+
}
|
package/dist/utils/index.d.ts
CHANGED
|
@@ -2,3 +2,4 @@ export { sleep } from './async.js';
|
|
|
2
2
|
export { extractNestedValue, traversePath } from './path.js';
|
|
3
3
|
export { type Logger, ConsoleLogger, SilentLogger, createLogger } from './logger.js';
|
|
4
4
|
export { ensureDirectory, ensureParentDirectory, serialize, writeJsonFile, readJsonFile, listFiles, deleteFile, restoreDates, restoreDatesInArray, } from './file.js';
|
|
5
|
+
export { isRecord, isObject, isArrayOf, isString, isNumber, isBoolean, isDefined, isPresent, getProperty, getNestedProperty, hasProperty, hasTypedProperty, } from './type-guards.js';
|
package/dist/utils/index.js
CHANGED
|
@@ -2,3 +2,4 @@ export { sleep } from './async.js';
|
|
|
2
2
|
export { extractNestedValue, traversePath } from './path.js';
|
|
3
3
|
export { ConsoleLogger, SilentLogger, createLogger } from './logger.js';
|
|
4
4
|
export { ensureDirectory, ensureParentDirectory, serialize, writeJsonFile, readJsonFile, listFiles, deleteFile, restoreDates, restoreDatesInArray, } from './file.js';
|
|
5
|
+
export { isRecord, isObject, isArrayOf, isString, isNumber, isBoolean, isDefined, isPresent, getProperty, getNestedProperty, hasProperty, hasTypedProperty, } from './type-guards.js';
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Type guard utilities to reduce type assertions and improve type safety.
|
|
3
|
+
* These guards provide runtime type checking with TypeScript type narrowing.
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Type guard to check if a value is a non-null object (excluding arrays).
|
|
7
|
+
* Narrows the type to Record<string, unknown> for safe property access.
|
|
8
|
+
*/
|
|
9
|
+
export declare function isRecord(value: unknown): value is Record<string, unknown>;
|
|
10
|
+
/**
|
|
11
|
+
* Type guard to check if a value is a non-null object (including arrays).
|
|
12
|
+
* Useful when you need to access properties on any object-like value.
|
|
13
|
+
*/
|
|
14
|
+
export declare function isObject(value: unknown): value is object;
|
|
15
|
+
/**
|
|
16
|
+
* Type guard to check if a value is an array of a specific type.
|
|
17
|
+
* Performs runtime check on each element using the provided guard.
|
|
18
|
+
*/
|
|
19
|
+
export declare function isArrayOf<T>(value: unknown, guard: (item: unknown) => item is T): value is T[];
|
|
20
|
+
/**
|
|
21
|
+
* Type guard to check if a value is a string.
|
|
22
|
+
*/
|
|
23
|
+
export declare function isString(value: unknown): value is string;
|
|
24
|
+
/**
|
|
25
|
+
* Type guard to check if a value is a number.
|
|
26
|
+
*/
|
|
27
|
+
export declare function isNumber(value: unknown): value is number;
|
|
28
|
+
/**
|
|
29
|
+
* Type guard to check if a value is a boolean.
|
|
30
|
+
*/
|
|
31
|
+
export declare function isBoolean(value: unknown): value is boolean;
|
|
32
|
+
/**
|
|
33
|
+
* Type guard to check if a value is defined (not undefined).
|
|
34
|
+
*/
|
|
35
|
+
export declare function isDefined<T>(value: T | undefined): value is T;
|
|
36
|
+
/**
|
|
37
|
+
* Type guard to check if a value is not null or undefined.
|
|
38
|
+
*/
|
|
39
|
+
export declare function isPresent<T>(value: T | null | undefined): value is T;
|
|
40
|
+
/**
|
|
41
|
+
* Safely access a property on an unknown value.
|
|
42
|
+
* Returns undefined if the value is not an object or the property doesn't exist.
|
|
43
|
+
*/
|
|
44
|
+
export declare function getProperty(value: unknown, key: string): unknown;
|
|
45
|
+
/**
|
|
46
|
+
* Safely access a nested property path on an unknown value.
|
|
47
|
+
* Returns undefined if any part of the path is not an object.
|
|
48
|
+
*/
|
|
49
|
+
export declare function getNestedProperty(value: unknown, path: string[]): unknown;
|
|
50
|
+
/**
|
|
51
|
+
* Check if an object has a specific property.
|
|
52
|
+
* Narrows the type to include that property.
|
|
53
|
+
*/
|
|
54
|
+
export declare function hasProperty<K extends string>(value: unknown, key: K): value is Record<K, unknown>;
|
|
55
|
+
/**
|
|
56
|
+
* Check if an object has a specific property with a specific type.
|
|
57
|
+
*/
|
|
58
|
+
export declare function hasTypedProperty<K extends string, T>(value: unknown, key: K, guard: (v: unknown) => v is T): value is Record<K, T>;
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Type guard utilities to reduce type assertions and improve type safety.
|
|
3
|
+
* These guards provide runtime type checking with TypeScript type narrowing.
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Type guard to check if a value is a non-null object (excluding arrays).
|
|
7
|
+
* Narrows the type to Record<string, unknown> for safe property access.
|
|
8
|
+
*/
|
|
9
|
+
export function isRecord(value) {
|
|
10
|
+
return value !== null && typeof value === 'object' && !Array.isArray(value);
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Type guard to check if a value is a non-null object (including arrays).
|
|
14
|
+
* Useful when you need to access properties on any object-like value.
|
|
15
|
+
*/
|
|
16
|
+
export function isObject(value) {
|
|
17
|
+
return value !== null && typeof value === 'object';
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Type guard to check if a value is an array of a specific type.
|
|
21
|
+
* Performs runtime check on each element using the provided guard.
|
|
22
|
+
*/
|
|
23
|
+
export function isArrayOf(value, guard) {
|
|
24
|
+
return Array.isArray(value) && value.every(guard);
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Type guard to check if a value is a string.
|
|
28
|
+
*/
|
|
29
|
+
export function isString(value) {
|
|
30
|
+
return typeof value === 'string';
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Type guard to check if a value is a number.
|
|
34
|
+
*/
|
|
35
|
+
export function isNumber(value) {
|
|
36
|
+
return typeof value === 'number';
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Type guard to check if a value is a boolean.
|
|
40
|
+
*/
|
|
41
|
+
export function isBoolean(value) {
|
|
42
|
+
return typeof value === 'boolean';
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Type guard to check if a value is defined (not undefined).
|
|
46
|
+
*/
|
|
47
|
+
export function isDefined(value) {
|
|
48
|
+
return value !== undefined;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Type guard to check if a value is not null or undefined.
|
|
52
|
+
*/
|
|
53
|
+
export function isPresent(value) {
|
|
54
|
+
return value !== null && value !== undefined;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Safely access a property on an unknown value.
|
|
58
|
+
* Returns undefined if the value is not an object or the property doesn't exist.
|
|
59
|
+
*/
|
|
60
|
+
export function getProperty(value, key) {
|
|
61
|
+
if (isRecord(value)) {
|
|
62
|
+
return value[key];
|
|
63
|
+
}
|
|
64
|
+
return undefined;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Safely access a nested property path on an unknown value.
|
|
68
|
+
* Returns undefined if any part of the path is not an object.
|
|
69
|
+
*/
|
|
70
|
+
export function getNestedProperty(value, path) {
|
|
71
|
+
let current = value;
|
|
72
|
+
for (const key of path) {
|
|
73
|
+
if (!isRecord(current)) {
|
|
74
|
+
return undefined;
|
|
75
|
+
}
|
|
76
|
+
current = current[key];
|
|
77
|
+
}
|
|
78
|
+
return current;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Check if an object has a specific property.
|
|
82
|
+
* Narrows the type to include that property.
|
|
83
|
+
*/
|
|
84
|
+
export function hasProperty(value, key) {
|
|
85
|
+
return isRecord(value) && key in value;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Check if an object has a specific property with a specific type.
|
|
89
|
+
*/
|
|
90
|
+
export function hasTypedProperty(value, key, guard) {
|
|
91
|
+
return hasProperty(value, key) && guard(value[key]);
|
|
92
|
+
}
|
package/dist/webhook/server.js
CHANGED
|
@@ -8,6 +8,7 @@ import { createServer } from 'node:http';
|
|
|
8
8
|
import { parse as parseUrl } from 'node:url';
|
|
9
9
|
import { randomUUID } from 'node:crypto';
|
|
10
10
|
import { MemoryWebhookStore } from './store.js';
|
|
11
|
+
import { WEBHOOK_DEFAULTS } from '../config/index.js';
|
|
11
12
|
/**
|
|
12
13
|
* Webhook Server
|
|
13
14
|
*
|
|
@@ -23,10 +24,10 @@ export class WebhookServer {
|
|
|
23
24
|
running = false;
|
|
24
25
|
constructor(config = {}, store, callbacks = {}) {
|
|
25
26
|
this.config = {
|
|
26
|
-
port: config.port ??
|
|
27
|
-
host: config.host ??
|
|
28
|
-
baseUrl: config.baseUrl ?? `http://localhost:${config.port ??
|
|
29
|
-
defaultTimeout: config.defaultTimeout ??
|
|
27
|
+
port: config.port ?? WEBHOOK_DEFAULTS.PORT,
|
|
28
|
+
host: config.host ?? WEBHOOK_DEFAULTS.HOST,
|
|
29
|
+
baseUrl: config.baseUrl ?? `http://localhost:${config.port ?? WEBHOOK_DEFAULTS.PORT}`,
|
|
30
|
+
defaultTimeout: config.defaultTimeout ?? WEBHOOK_DEFAULTS.DEFAULT_TIMEOUT_MS,
|
|
30
31
|
verbose: config.verbose ?? false,
|
|
31
32
|
};
|
|
32
33
|
this.store = store ?? new MemoryWebhookStore();
|
|
@@ -46,8 +47,8 @@ export class WebhookServer {
|
|
|
46
47
|
this.server.listen(this.config.port, this.config.host, () => {
|
|
47
48
|
this.running = true;
|
|
48
49
|
this.log(`Webhook server listening on ${this.config.host}:${this.config.port}`);
|
|
49
|
-
// Start cleanup interval
|
|
50
|
-
this.cleanupInterval = setInterval(() => this.cleanup(),
|
|
50
|
+
// Start cleanup interval
|
|
51
|
+
this.cleanupInterval = setInterval(() => this.cleanup(), WEBHOOK_DEFAULTS.CLEANUP_INTERVAL_MS);
|
|
51
52
|
resolve();
|
|
52
53
|
});
|
|
53
54
|
});
|
package/package.json
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "reqon-dsl",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "A DSL framework for fetch, map, validate pipelines - built on Vague",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
7
7
|
"types": "dist/index.d.ts",
|
|
8
8
|
"bin": {
|
|
9
|
-
"reqon": "dist/cli.js"
|
|
9
|
+
"reqon": "dist/cli.js",
|
|
10
|
+
"reqon-mcp": "dist/mcp/server.js"
|
|
10
11
|
},
|
|
11
12
|
"scripts": {
|
|
12
13
|
"build": "tsc",
|
|
@@ -14,27 +15,75 @@
|
|
|
14
15
|
"test": "vitest",
|
|
15
16
|
"test:run": "vitest run",
|
|
16
17
|
"test:coverage": "vitest run --coverage",
|
|
18
|
+
"lint": "eslint src/",
|
|
19
|
+
"lint:fix": "eslint src/ --fix",
|
|
20
|
+
"format": "prettier --write src/",
|
|
21
|
+
"format:check": "prettier --check src/",
|
|
17
22
|
"bench": "npx tsx src/benchmark/index.ts",
|
|
18
23
|
"bench:lexer": "npx tsx src/benchmark/index.ts --suite lexer",
|
|
19
24
|
"bench:parser": "npx tsx src/benchmark/index.ts --suite parser",
|
|
20
25
|
"bench:evaluator": "npx tsx src/benchmark/index.ts --suite evaluator",
|
|
21
26
|
"bench:store": "npx tsx src/benchmark/index.ts --suite store",
|
|
22
27
|
"bench:resilience": "npx tsx src/benchmark/index.ts --suite resilience",
|
|
23
|
-
"bench:e2e": "npx tsx src/benchmark/index.ts --suite e2e"
|
|
28
|
+
"bench:e2e": "npx tsx src/benchmark/index.ts --suite e2e",
|
|
29
|
+
"prerelease": "npm run test:run && npm run build",
|
|
30
|
+
"release": "node scripts/release.js",
|
|
31
|
+
"release:dry-run": "npm publish --dry-run",
|
|
32
|
+
"version:patch": "npm version patch --no-git-tag-version",
|
|
33
|
+
"version:minor": "npm version minor --no-git-tag-version",
|
|
34
|
+
"version:major": "npm version major --no-git-tag-version",
|
|
35
|
+
"prepare": "husky"
|
|
24
36
|
},
|
|
37
|
+
"files": [
|
|
38
|
+
"dist",
|
|
39
|
+
"CHANGELOG.md",
|
|
40
|
+
"README.md"
|
|
41
|
+
],
|
|
25
42
|
"engines": {
|
|
26
43
|
"node": ">=18"
|
|
27
44
|
},
|
|
45
|
+
"repository": {
|
|
46
|
+
"type": "git",
|
|
47
|
+
"url": "git+https://github.com/mcclowes/reqon.git"
|
|
48
|
+
},
|
|
49
|
+
"bugs": {
|
|
50
|
+
"url": "https://github.com/mcclowes/reqon/issues"
|
|
51
|
+
},
|
|
52
|
+
"homepage": "https://github.com/mcclowes/reqon#readme",
|
|
53
|
+
"keywords": [
|
|
54
|
+
"dsl",
|
|
55
|
+
"data-pipeline",
|
|
56
|
+
"fetch",
|
|
57
|
+
"api",
|
|
58
|
+
"declarative",
|
|
59
|
+
"etl",
|
|
60
|
+
"vague"
|
|
61
|
+
],
|
|
28
62
|
"author": "Max Clayton Clowes <max@mcclowes.com>",
|
|
29
63
|
"license": "ISC",
|
|
30
64
|
"dependencies": {
|
|
31
65
|
"@apidevtools/swagger-parser": "^12.1.0",
|
|
32
|
-
"
|
|
33
|
-
"
|
|
66
|
+
"@modelcontextprotocol/sdk": "^1.12.0",
|
|
67
|
+
"dotenv": "^17.2.3",
|
|
68
|
+
"vague-lang": "^3.3.0",
|
|
69
|
+
"zod": "^4.2.1"
|
|
70
|
+
},
|
|
71
|
+
"lint-staged": {
|
|
72
|
+
"src/**/*.{ts,tsx}": [
|
|
73
|
+
"eslint --fix",
|
|
74
|
+
"prettier --write"
|
|
75
|
+
]
|
|
34
76
|
},
|
|
35
77
|
"devDependencies": {
|
|
36
|
-
"@
|
|
78
|
+
"@eslint/js": "^9.17.0",
|
|
79
|
+
"@types/node": "^25.0.3",
|
|
80
|
+
"@vitest/coverage-v8": "^4.0.16",
|
|
81
|
+
"eslint": "^9.17.0",
|
|
82
|
+
"husky": "^9.1.7",
|
|
83
|
+
"lint-staged": "^16.2.7",
|
|
84
|
+
"prettier": "^3.4.2",
|
|
37
85
|
"typescript": "^5.9.3",
|
|
86
|
+
"typescript-eslint": "^8.18.2",
|
|
38
87
|
"vitest": "^4.0.15"
|
|
39
88
|
}
|
|
40
89
|
}
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"permissions": {
|
|
3
|
-
"allow": [
|
|
4
|
-
"Bash(npm init -y)",
|
|
5
|
-
"Bash(npm install)",
|
|
6
|
-
"Bash(npm run build:*)",
|
|
7
|
-
"Bash(npm run test:run:*)",
|
|
8
|
-
"Bash(npx vitest run src/debug.test.ts)",
|
|
9
|
-
"Bash(npx vitest:*)",
|
|
10
|
-
"Bash(git init:*)",
|
|
11
|
-
"Bash(git add:*)",
|
|
12
|
-
"Bash(git commit:*)",
|
|
13
|
-
"Bash(git push:*)",
|
|
14
|
-
"Bash(npm install:*)",
|
|
15
|
-
"Bash(node dist/cli.js:*)",
|
|
16
|
-
"Bash(ls:*)",
|
|
17
|
-
"Bash(node:*)",
|
|
18
|
-
"Bash(cat:*)",
|
|
19
|
-
"Bash(git checkout:*)",
|
|
20
|
-
"Bash(git rebase:*)",
|
|
21
|
-
"Bash(find:*)",
|
|
22
|
-
"Bash(git fetch:*)",
|
|
23
|
-
"Bash(xargs sed:*)",
|
|
24
|
-
"Bash(for f in examples/**/*.reqon)",
|
|
25
|
-
"Bash(do mv $f $f%.reqon.vague)",
|
|
26
|
-
"Bash(done)",
|
|
27
|
-
"Bash(npx:*)",
|
|
28
|
-
"Skill(reqon)"
|
|
29
|
-
]
|
|
30
|
-
}
|
|
31
|
-
}
|
|
@@ -1,125 +0,0 @@
|
|
|
1
|
-
# API Integration Patterns Skill
|
|
2
|
-
|
|
3
|
-
Use this skill when implementing API client functionality, authentication flows, rate limiting, or pagination in Reqon.
|
|
4
|
-
|
|
5
|
-
## Capabilities
|
|
6
|
-
|
|
7
|
-
### OAuth2 Token Refresh Flows
|
|
8
|
-
- Implement automatic token refresh on 401 responses
|
|
9
|
-
- Handle refresh token rotation
|
|
10
|
-
- Store and retrieve tokens securely
|
|
11
|
-
- Support authorization_code, client_credentials, and refresh_token grants
|
|
12
|
-
|
|
13
|
-
### Rate Limiting with Exponential Backoff
|
|
14
|
-
- Parse rate limit headers (X-RateLimit-*, Retry-After)
|
|
15
|
-
- Implement exponential backoff with jitter
|
|
16
|
-
- Queue requests when rate limited
|
|
17
|
-
- Respect per-endpoint rate limits
|
|
18
|
-
|
|
19
|
-
### Pagination Pattern Implementations
|
|
20
|
-
- **Cursor-based**: Use `next_cursor` or `after` parameters
|
|
21
|
-
- **Offset-based**: Use `offset` and `limit` parameters
|
|
22
|
-
- **Page-based**: Use `page` and `per_page` parameters
|
|
23
|
-
- **Link header**: Parse RFC 5988 Link headers
|
|
24
|
-
|
|
25
|
-
## Context Files
|
|
26
|
-
When using this skill, read:
|
|
27
|
-
- `src/interpreter/http.ts` - HTTP client implementation
|
|
28
|
-
- `src/interpreter/executor.ts` - Fetch execution logic
|
|
29
|
-
- `src/ast/nodes.ts` - FetchNode, PaginationConfig types
|
|
30
|
-
|
|
31
|
-
## Implementation Patterns
|
|
32
|
-
|
|
33
|
-
### OAuth2 Token Refresh
|
|
34
|
-
```typescript
|
|
35
|
-
async function fetchWithAuth(url: string, auth: OAuth2Auth): Promise<Response> {
|
|
36
|
-
let response = await fetch(url, {
|
|
37
|
-
headers: { Authorization: `Bearer ${auth.accessToken}` }
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
if (response.status === 401 && auth.refreshToken) {
|
|
41
|
-
const newTokens = await refreshAccessToken(auth);
|
|
42
|
-
auth.accessToken = newTokens.access_token;
|
|
43
|
-
response = await fetch(url, {
|
|
44
|
-
headers: { Authorization: `Bearer ${auth.accessToken}` }
|
|
45
|
-
});
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
return response;
|
|
49
|
-
}
|
|
50
|
-
```
|
|
51
|
-
|
|
52
|
-
### Exponential Backoff
|
|
53
|
-
```typescript
|
|
54
|
-
async function fetchWithBackoff(
|
|
55
|
-
url: string,
|
|
56
|
-
maxRetries = 5,
|
|
57
|
-
baseDelay = 1000
|
|
58
|
-
): Promise<Response> {
|
|
59
|
-
for (let attempt = 0; attempt < maxRetries; attempt++) {
|
|
60
|
-
const response = await fetch(url);
|
|
61
|
-
|
|
62
|
-
if (response.status === 429) {
|
|
63
|
-
const retryAfter = response.headers.get('Retry-After');
|
|
64
|
-
const delay = retryAfter
|
|
65
|
-
? parseInt(retryAfter) * 1000
|
|
66
|
-
: baseDelay * Math.pow(2, attempt) + Math.random() * 1000;
|
|
67
|
-
await sleep(delay);
|
|
68
|
-
continue;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
return response;
|
|
72
|
-
}
|
|
73
|
-
throw new Error('Max retries exceeded');
|
|
74
|
-
}
|
|
75
|
-
```
|
|
76
|
-
|
|
77
|
-
### Pagination Patterns
|
|
78
|
-
|
|
79
|
-
#### Cursor-based
|
|
80
|
-
```typescript
|
|
81
|
-
async function* paginateCursor(baseUrl: string) {
|
|
82
|
-
let cursor: string | undefined;
|
|
83
|
-
do {
|
|
84
|
-
const url = cursor ? `${baseUrl}?cursor=${cursor}` : baseUrl;
|
|
85
|
-
const response = await fetch(url);
|
|
86
|
-
const data = await response.json();
|
|
87
|
-
yield data.items;
|
|
88
|
-
cursor = data.next_cursor;
|
|
89
|
-
} while (cursor);
|
|
90
|
-
}
|
|
91
|
-
```
|
|
92
|
-
|
|
93
|
-
#### Offset-based
|
|
94
|
-
```typescript
|
|
95
|
-
async function* paginateOffset(baseUrl: string, limit = 100) {
|
|
96
|
-
let offset = 0;
|
|
97
|
-
let hasMore = true;
|
|
98
|
-
while (hasMore) {
|
|
99
|
-
const response = await fetch(`${baseUrl}?offset=${offset}&limit=${limit}`);
|
|
100
|
-
const data = await response.json();
|
|
101
|
-
yield data.items;
|
|
102
|
-
offset += limit;
|
|
103
|
-
hasMore = data.items.length === limit;
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
```
|
|
107
|
-
|
|
108
|
-
#### Link Header
|
|
109
|
-
```typescript
|
|
110
|
-
function parseLinks(header: string): Record<string, string> {
|
|
111
|
-
const links: Record<string, string> = {};
|
|
112
|
-
header.split(',').forEach(part => {
|
|
113
|
-
const match = part.match(/<([^>]+)>;\s*rel="([^"]+)"/);
|
|
114
|
-
if (match) links[match[2]] = match[1];
|
|
115
|
-
});
|
|
116
|
-
return links;
|
|
117
|
-
}
|
|
118
|
-
```
|
|
119
|
-
|
|
120
|
-
## Rate Limit Headers
|
|
121
|
-
Common headers to parse:
|
|
122
|
-
- `X-RateLimit-Limit` - Request limit per window
|
|
123
|
-
- `X-RateLimit-Remaining` - Requests remaining
|
|
124
|
-
- `X-RateLimit-Reset` - Unix timestamp when limit resets
|
|
125
|
-
- `Retry-After` - Seconds to wait (on 429/503)
|