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,43 @@
|
|
|
1
|
+
import type { FetchStep } from '../ast/nodes.js';
|
|
2
|
+
import type { ExecutionContext } from './context.js';
|
|
3
|
+
import type { OASSource } from '../oas/index.js';
|
|
4
|
+
import { type SyncStore } from '../sync/index.js';
|
|
5
|
+
export interface FetchHandlerDeps {
|
|
6
|
+
ctx: ExecutionContext;
|
|
7
|
+
oasSources: Map<string, OASSource>;
|
|
8
|
+
sourceConfigs: Map<string, {
|
|
9
|
+
config: {
|
|
10
|
+
validateResponses?: boolean;
|
|
11
|
+
};
|
|
12
|
+
}>;
|
|
13
|
+
syncStore?: SyncStore;
|
|
14
|
+
missionName?: string;
|
|
15
|
+
executionId?: string;
|
|
16
|
+
dryRun?: boolean;
|
|
17
|
+
log: (message: string) => void;
|
|
18
|
+
}
|
|
19
|
+
export interface FetchResult {
|
|
20
|
+
data: unknown;
|
|
21
|
+
checkpointKey?: string;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Handles HTTP fetch operations including pagination and incremental sync.
|
|
25
|
+
* Extracted from MissionExecutor for better separation of concerns.
|
|
26
|
+
*/
|
|
27
|
+
export declare class FetchHandler {
|
|
28
|
+
private deps;
|
|
29
|
+
constructor(deps: FetchHandlerDeps);
|
|
30
|
+
/**
|
|
31
|
+
* Execute a fetch step, handling OAS resolution, pagination, and sync checkpoints.
|
|
32
|
+
*/
|
|
33
|
+
execute(step: FetchStep): Promise<FetchResult>;
|
|
34
|
+
/**
|
|
35
|
+
* Record a sync checkpoint after successful fetch.
|
|
36
|
+
*/
|
|
37
|
+
recordCheckpoint(key: string, step: FetchStep, data: unknown): Promise<void>;
|
|
38
|
+
private resolveFetchTarget;
|
|
39
|
+
private resolveSinceParams;
|
|
40
|
+
private executePaginated;
|
|
41
|
+
private countRecords;
|
|
42
|
+
private validateOASResponse;
|
|
43
|
+
}
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
import { evaluate, interpolatePath } from './evaluator.js';
|
|
2
|
+
import { resolveOperation, getResponseSchema, validateResponse } from '../oas/index.js';
|
|
3
|
+
import { generateCheckpointKey, formatSinceDate } from '../sync/index.js';
|
|
4
|
+
import { extractNestedValue } from '../utils/path.js';
|
|
5
|
+
import { createPaginationStrategy } from './pagination.js';
|
|
6
|
+
/** Maximum pages to fetch to prevent infinite loops */
|
|
7
|
+
const MAX_PAGINATION_PAGES = 100;
|
|
8
|
+
/**
|
|
9
|
+
* Handles HTTP fetch operations including pagination and incremental sync.
|
|
10
|
+
* Extracted from MissionExecutor for better separation of concerns.
|
|
11
|
+
*/
|
|
12
|
+
export class FetchHandler {
|
|
13
|
+
deps;
|
|
14
|
+
constructor(deps) {
|
|
15
|
+
this.deps = deps;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Execute a fetch step, handling OAS resolution, pagination, and sync checkpoints.
|
|
19
|
+
*/
|
|
20
|
+
async execute(step) {
|
|
21
|
+
const resolved = this.resolveFetchTarget(step);
|
|
22
|
+
const client = this.deps.ctx.sources.get(resolved.sourceName);
|
|
23
|
+
if (!client) {
|
|
24
|
+
throw new Error(`Source not found: ${resolved.sourceName}`);
|
|
25
|
+
}
|
|
26
|
+
// Resolve "since" query parameter for incremental sync
|
|
27
|
+
const { query: sinceQuery, checkpointKey } = await this.resolveSinceParams(step, resolved.sourceName, resolved.operationId, resolved.path);
|
|
28
|
+
if (this.deps.dryRun) {
|
|
29
|
+
this.deps.log('(dry run - skipping actual request)');
|
|
30
|
+
return { data: { dryRun: true }, checkpointKey };
|
|
31
|
+
}
|
|
32
|
+
// Execute with or without pagination
|
|
33
|
+
let data;
|
|
34
|
+
if (step.paginate) {
|
|
35
|
+
data = await this.executePaginated(step, client, resolved.path, resolved.method, resolved.sourceName, resolved.operationId, sinceQuery);
|
|
36
|
+
}
|
|
37
|
+
else {
|
|
38
|
+
const response = await client.request({
|
|
39
|
+
method: resolved.method,
|
|
40
|
+
path: resolved.path,
|
|
41
|
+
query: Object.keys(sinceQuery).length > 0 ? sinceQuery : undefined,
|
|
42
|
+
body: step.body ? evaluate(step.body, this.deps.ctx) : undefined,
|
|
43
|
+
}, step.retry);
|
|
44
|
+
await this.validateOASResponse(resolved.sourceName, resolved.operationId, response.data);
|
|
45
|
+
data = response.data;
|
|
46
|
+
}
|
|
47
|
+
return { data, checkpointKey };
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Record a sync checkpoint after successful fetch.
|
|
51
|
+
*/
|
|
52
|
+
async recordCheckpoint(key, step, data) {
|
|
53
|
+
if (!this.deps.syncStore)
|
|
54
|
+
return;
|
|
55
|
+
let syncedAt = new Date();
|
|
56
|
+
// If updateFrom is specified, extract the timestamp from response
|
|
57
|
+
if (step.since?.updateFrom && data && typeof data === 'object') {
|
|
58
|
+
const extracted = extractNestedValue(data, step.since.updateFrom);
|
|
59
|
+
if (extracted instanceof Date) {
|
|
60
|
+
syncedAt = extracted;
|
|
61
|
+
}
|
|
62
|
+
else if (typeof extracted === 'string' || typeof extracted === 'number') {
|
|
63
|
+
const parsed = new Date(extracted);
|
|
64
|
+
if (!isNaN(parsed.getTime())) {
|
|
65
|
+
syncedAt = parsed;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
// Count records if response is an array
|
|
70
|
+
const recordCount = this.countRecords(data);
|
|
71
|
+
await this.deps.syncStore.recordSync({
|
|
72
|
+
key,
|
|
73
|
+
syncedAt,
|
|
74
|
+
recordCount,
|
|
75
|
+
mission: this.deps.missionName,
|
|
76
|
+
executionId: this.deps.executionId,
|
|
77
|
+
});
|
|
78
|
+
this.deps.log(`Recorded sync checkpoint: ${key} at ${syncedAt.toISOString()}`);
|
|
79
|
+
}
|
|
80
|
+
resolveFetchTarget(step) {
|
|
81
|
+
if (step.operationRef) {
|
|
82
|
+
// OAS-style: resolve from operationId
|
|
83
|
+
const sourceName = step.operationRef.source;
|
|
84
|
+
const operationId = step.operationRef.operationId;
|
|
85
|
+
const oasSource = this.deps.oasSources.get(sourceName);
|
|
86
|
+
if (!oasSource) {
|
|
87
|
+
throw new Error(`Source '${sourceName}' does not have an OAS spec. Use 'source ${sourceName} from "spec.yaml"'`);
|
|
88
|
+
}
|
|
89
|
+
const operation = resolveOperation(oasSource, operationId);
|
|
90
|
+
const path = interpolatePath(operation.path, this.deps.ctx);
|
|
91
|
+
this.deps.log(`Fetching: ${sourceName}.${operationId} -> ${operation.method} ${path}`);
|
|
92
|
+
return { sourceName, method: operation.method, path, operationId };
|
|
93
|
+
}
|
|
94
|
+
// Traditional: explicit method + path
|
|
95
|
+
const sourceName = step.source ?? this.deps.ctx.sources.keys().next().value;
|
|
96
|
+
const method = step.method;
|
|
97
|
+
let path;
|
|
98
|
+
if (step.path.type === 'Literal' && step.path.dataType === 'string') {
|
|
99
|
+
path = interpolatePath(step.path.value, this.deps.ctx);
|
|
100
|
+
}
|
|
101
|
+
else {
|
|
102
|
+
path = String(evaluate(step.path, this.deps.ctx));
|
|
103
|
+
}
|
|
104
|
+
this.deps.log(`Fetching: ${method} ${path}`);
|
|
105
|
+
return { sourceName, method, path };
|
|
106
|
+
}
|
|
107
|
+
async resolveSinceParams(step, sourceName, operationId, path) {
|
|
108
|
+
const query = {};
|
|
109
|
+
if (!step.since || !this.deps.syncStore) {
|
|
110
|
+
return { query };
|
|
111
|
+
}
|
|
112
|
+
const checkpointKey = step.since.key ?? generateCheckpointKey(sourceName, operationId, path);
|
|
113
|
+
if (step.since.type === 'lastSync') {
|
|
114
|
+
const lastSync = await this.deps.syncStore.getLastSync(checkpointKey);
|
|
115
|
+
const paramName = step.since.param ?? 'since';
|
|
116
|
+
const format = step.since.format ?? 'iso';
|
|
117
|
+
query[paramName] = formatSinceDate(lastSync, format);
|
|
118
|
+
this.deps.log(`Incremental sync: ${paramName}=${query[paramName]} (key: ${checkpointKey})`);
|
|
119
|
+
}
|
|
120
|
+
else if (step.since.type === 'expression' && step.since.expression) {
|
|
121
|
+
const value = evaluate(step.since.expression, this.deps.ctx);
|
|
122
|
+
const paramName = step.since.param ?? 'since';
|
|
123
|
+
query[paramName] = String(value);
|
|
124
|
+
}
|
|
125
|
+
return { query, checkpointKey };
|
|
126
|
+
}
|
|
127
|
+
async executePaginated(step, client, basePath, method, sourceName, operationId, sinceQuery = {}) {
|
|
128
|
+
const allResults = [];
|
|
129
|
+
const paginate = step.paginate;
|
|
130
|
+
const strategy = createPaginationStrategy(paginate);
|
|
131
|
+
const ctx = {
|
|
132
|
+
page: 0,
|
|
133
|
+
pageSize: paginate.pageSize,
|
|
134
|
+
};
|
|
135
|
+
let hasMore = true;
|
|
136
|
+
while (hasMore) {
|
|
137
|
+
// Build query with pagination params
|
|
138
|
+
const paginationQuery = strategy.buildQuery(ctx);
|
|
139
|
+
const query = { ...sinceQuery, ...paginationQuery };
|
|
140
|
+
this.deps.log(`Fetching page ${ctx.page + 1}...`);
|
|
141
|
+
const response = await client.request({ method, path: basePath, query }, step.retry);
|
|
142
|
+
await this.validateOASResponse(sourceName, operationId, response.data);
|
|
143
|
+
// Temporarily set response for until condition evaluation
|
|
144
|
+
this.deps.ctx.response = response.data;
|
|
145
|
+
// Check until condition
|
|
146
|
+
if (step.until) {
|
|
147
|
+
const shouldStop = evaluate(step.until, this.deps.ctx);
|
|
148
|
+
if (shouldStop) {
|
|
149
|
+
break;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
// Extract results using strategy
|
|
153
|
+
const pageResult = strategy.extractResults(response.data, ctx);
|
|
154
|
+
allResults.push(...pageResult.items);
|
|
155
|
+
hasMore = pageResult.hasMore;
|
|
156
|
+
// Update cursor for next iteration
|
|
157
|
+
if (pageResult.nextCursor) {
|
|
158
|
+
ctx.cursor = pageResult.nextCursor;
|
|
159
|
+
}
|
|
160
|
+
ctx.page++;
|
|
161
|
+
// Safety limit
|
|
162
|
+
if (ctx.page >= MAX_PAGINATION_PAGES) {
|
|
163
|
+
this.deps.log(`Warning: pagination limit (${MAX_PAGINATION_PAGES}) reached`);
|
|
164
|
+
hasMore = false;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
this.deps.log(`Fetched ${allResults.length} total items`);
|
|
168
|
+
return allResults;
|
|
169
|
+
}
|
|
170
|
+
countRecords(data) {
|
|
171
|
+
if (Array.isArray(data)) {
|
|
172
|
+
return data.length;
|
|
173
|
+
}
|
|
174
|
+
if (data && typeof data === 'object') {
|
|
175
|
+
for (const val of Object.values(data)) {
|
|
176
|
+
if (Array.isArray(val)) {
|
|
177
|
+
return val.length;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
return undefined;
|
|
182
|
+
}
|
|
183
|
+
async validateOASResponse(sourceName, operationId, data) {
|
|
184
|
+
if (!operationId)
|
|
185
|
+
return;
|
|
186
|
+
const sourceConfig = this.deps.sourceConfigs.get(sourceName);
|
|
187
|
+
if (!sourceConfig?.config.validateResponses)
|
|
188
|
+
return;
|
|
189
|
+
const oasSource = this.deps.oasSources.get(sourceName);
|
|
190
|
+
if (!oasSource)
|
|
191
|
+
return;
|
|
192
|
+
const schema = getResponseSchema(oasSource, operationId);
|
|
193
|
+
if (!schema) {
|
|
194
|
+
this.deps.log(`No response schema found for ${operationId}`);
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
const result = validateResponse(data, schema);
|
|
198
|
+
if (!result.valid) {
|
|
199
|
+
const errorMessages = result.errors.map((e) => ` ${e.path}: ${e.message}`).join('\n');
|
|
200
|
+
this.deps.log(`Response validation warnings for ${operationId}:\n${errorMessages}`);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import type { RetryConfig } from '../ast/nodes.js';
|
|
2
|
+
import type { RateLimiter } from '../auth/types.js';
|
|
3
|
+
import { CircuitBreaker } from '../auth/circuit-breaker.js';
|
|
4
|
+
export interface HttpClientConfig {
|
|
5
|
+
baseUrl: string;
|
|
6
|
+
headers?: Record<string, string>;
|
|
7
|
+
auth?: AuthProvider;
|
|
8
|
+
rateLimiter?: RateLimiter;
|
|
9
|
+
circuitBreaker?: CircuitBreaker;
|
|
10
|
+
/** Source name for rate limit and circuit breaker tracking */
|
|
11
|
+
sourceName?: string;
|
|
12
|
+
}
|
|
13
|
+
export interface AuthProvider {
|
|
14
|
+
getToken(): Promise<string>;
|
|
15
|
+
refreshToken?(): Promise<string>;
|
|
16
|
+
}
|
|
17
|
+
export interface HttpRequest {
|
|
18
|
+
method: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
|
|
19
|
+
path: string;
|
|
20
|
+
body?: unknown;
|
|
21
|
+
headers?: Record<string, string>;
|
|
22
|
+
query?: Record<string, string>;
|
|
23
|
+
}
|
|
24
|
+
export interface HttpResponse<T = unknown> {
|
|
25
|
+
status: number;
|
|
26
|
+
data: T;
|
|
27
|
+
headers: Record<string, string>;
|
|
28
|
+
}
|
|
29
|
+
export declare class HttpClient {
|
|
30
|
+
private config;
|
|
31
|
+
constructor(config: HttpClientConfig);
|
|
32
|
+
request<T = unknown>(req: HttpRequest, retry?: RetryConfig): Promise<HttpResponse<T>>;
|
|
33
|
+
private buildUrl;
|
|
34
|
+
private buildHeaders;
|
|
35
|
+
private calculateDelay;
|
|
36
|
+
}
|
|
37
|
+
export declare class BearerAuthProvider implements AuthProvider {
|
|
38
|
+
private token;
|
|
39
|
+
constructor(token: string);
|
|
40
|
+
getToken(): Promise<string>;
|
|
41
|
+
}
|
|
42
|
+
export declare class OAuth2AuthProvider implements AuthProvider {
|
|
43
|
+
private accessToken;
|
|
44
|
+
private refreshTokenValue?;
|
|
45
|
+
private tokenEndpoint?;
|
|
46
|
+
private clientId?;
|
|
47
|
+
private clientSecret?;
|
|
48
|
+
constructor(config: {
|
|
49
|
+
accessToken: string;
|
|
50
|
+
refreshToken?: string;
|
|
51
|
+
tokenEndpoint?: string;
|
|
52
|
+
clientId?: string;
|
|
53
|
+
clientSecret?: string;
|
|
54
|
+
});
|
|
55
|
+
getToken(): Promise<string>;
|
|
56
|
+
refreshToken(): Promise<string>;
|
|
57
|
+
}
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
import { parseRateLimitHeaders } from '../auth/rate-limiter.js';
|
|
2
|
+
import { CircuitBreakerError } from '../auth/circuit-breaker.js';
|
|
3
|
+
import { sleep } from '../utils/async.js';
|
|
4
|
+
/** Default retry configuration values */
|
|
5
|
+
const DEFAULT_RETRY = {
|
|
6
|
+
MAX_ATTEMPTS: 3,
|
|
7
|
+
INITIAL_DELAY_MS: 1000,
|
|
8
|
+
MAX_DELAY_MS: 30000,
|
|
9
|
+
BACKOFF: 'exponential',
|
|
10
|
+
};
|
|
11
|
+
export class HttpClient {
|
|
12
|
+
config;
|
|
13
|
+
constructor(config) {
|
|
14
|
+
this.config = config;
|
|
15
|
+
}
|
|
16
|
+
async request(req, retry) {
|
|
17
|
+
const url = this.buildUrl(req.path, req.query);
|
|
18
|
+
const headers = await this.buildHeaders(req.headers);
|
|
19
|
+
const fetchOptions = {
|
|
20
|
+
method: req.method,
|
|
21
|
+
headers,
|
|
22
|
+
body: req.body ? JSON.stringify(req.body) : undefined,
|
|
23
|
+
};
|
|
24
|
+
const maxAttempts = retry?.maxAttempts ?? DEFAULT_RETRY.MAX_ATTEMPTS;
|
|
25
|
+
const backoff = retry?.backoff ?? DEFAULT_RETRY.BACKOFF;
|
|
26
|
+
const initialDelay = retry?.initialDelay ?? DEFAULT_RETRY.INITIAL_DELAY_MS;
|
|
27
|
+
const maxDelay = retry?.maxDelay ?? DEFAULT_RETRY.MAX_DELAY_MS;
|
|
28
|
+
let lastError = null;
|
|
29
|
+
// Check circuit breaker before attempting requests
|
|
30
|
+
if (this.config.circuitBreaker && this.config.sourceName) {
|
|
31
|
+
// This will throw CircuitBreakerError if circuit is open
|
|
32
|
+
this.config.circuitBreaker.ensureCanProceed(this.config.sourceName, req.path);
|
|
33
|
+
}
|
|
34
|
+
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
35
|
+
try {
|
|
36
|
+
// Re-check circuit breaker on retries (state may have changed)
|
|
37
|
+
if (attempt > 1 && this.config.circuitBreaker && this.config.sourceName) {
|
|
38
|
+
if (!this.config.circuitBreaker.canProceed(this.config.sourceName, req.path)) {
|
|
39
|
+
// Circuit opened during retries, fail fast
|
|
40
|
+
throw new CircuitBreakerError(this.config.sourceName, req.path, this.config.circuitBreaker.getStatus(this.config.sourceName, req.path).nextAttemptTime?.getTime() ?? 0 - Date.now());
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
// Wait for rate limit capacity if we have a rate limiter
|
|
44
|
+
if (this.config.rateLimiter && this.config.sourceName) {
|
|
45
|
+
await this.config.rateLimiter.waitForCapacity(this.config.sourceName, req.path);
|
|
46
|
+
}
|
|
47
|
+
const response = await fetch(url, fetchOptions);
|
|
48
|
+
// Extract and record rate limit info from response headers
|
|
49
|
+
const responseHeaders = {};
|
|
50
|
+
response.headers.forEach((value, key) => {
|
|
51
|
+
responseHeaders[key] = value;
|
|
52
|
+
});
|
|
53
|
+
if (this.config.rateLimiter && this.config.sourceName) {
|
|
54
|
+
const rateLimitInfo = parseRateLimitHeaders(responseHeaders);
|
|
55
|
+
// Add retry-after from 429 responses
|
|
56
|
+
if (response.status === 429) {
|
|
57
|
+
const retryAfter = response.headers.get('Retry-After');
|
|
58
|
+
if (retryAfter) {
|
|
59
|
+
rateLimitInfo.retryAfter = parseInt(retryAfter, 10);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
this.config.rateLimiter.recordResponse(this.config.sourceName, rateLimitInfo, req.path);
|
|
63
|
+
}
|
|
64
|
+
// Handle rate limiting
|
|
65
|
+
if (response.status === 429) {
|
|
66
|
+
const retryAfter = response.headers.get('Retry-After');
|
|
67
|
+
const delay = retryAfter ? parseInt(retryAfter, 10) * 1000 : this.calculateDelay(attempt, backoff, initialDelay, maxDelay);
|
|
68
|
+
await sleep(delay);
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
// Handle server errors with retry
|
|
72
|
+
if (response.status >= 500) {
|
|
73
|
+
// Record failure in circuit breaker
|
|
74
|
+
if (this.config.circuitBreaker && this.config.sourceName) {
|
|
75
|
+
this.config.circuitBreaker.recordFailure(this.config.sourceName, req.path, response.status);
|
|
76
|
+
}
|
|
77
|
+
if (attempt < maxAttempts) {
|
|
78
|
+
const delay = this.calculateDelay(attempt, backoff, initialDelay, maxDelay);
|
|
79
|
+
await sleep(delay);
|
|
80
|
+
continue;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
// Handle 401 - try token refresh
|
|
84
|
+
if (response.status === 401 && this.config.auth?.refreshToken && attempt < maxAttempts) {
|
|
85
|
+
await this.config.auth.refreshToken();
|
|
86
|
+
// Rebuild headers with new token
|
|
87
|
+
const newHeaders = await this.buildHeaders(req.headers);
|
|
88
|
+
fetchOptions.headers = newHeaders;
|
|
89
|
+
continue;
|
|
90
|
+
}
|
|
91
|
+
const data = await response.json();
|
|
92
|
+
// Record success in circuit breaker
|
|
93
|
+
if (this.config.circuitBreaker && this.config.sourceName && response.status < 500) {
|
|
94
|
+
this.config.circuitBreaker.recordSuccess(this.config.sourceName, req.path);
|
|
95
|
+
}
|
|
96
|
+
return {
|
|
97
|
+
status: response.status,
|
|
98
|
+
data,
|
|
99
|
+
headers: responseHeaders,
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
catch (error) {
|
|
103
|
+
lastError = error;
|
|
104
|
+
// Re-throw circuit breaker errors immediately
|
|
105
|
+
if (error instanceof CircuitBreakerError) {
|
|
106
|
+
throw error;
|
|
107
|
+
}
|
|
108
|
+
// Record network errors in circuit breaker
|
|
109
|
+
if (this.config.circuitBreaker && this.config.sourceName) {
|
|
110
|
+
this.config.circuitBreaker.recordFailure(this.config.sourceName, req.path, undefined, true);
|
|
111
|
+
}
|
|
112
|
+
if (attempt < maxAttempts) {
|
|
113
|
+
const delay = this.calculateDelay(attempt, backoff, initialDelay, maxDelay);
|
|
114
|
+
await sleep(delay);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
throw lastError ?? new Error('Request failed after all retries');
|
|
119
|
+
}
|
|
120
|
+
buildUrl(path, query) {
|
|
121
|
+
const base = this.config.baseUrl.replace(/\/$/, '');
|
|
122
|
+
const cleanPath = path.startsWith('/') ? path : `/${path}`;
|
|
123
|
+
let url = `${base}${cleanPath}`;
|
|
124
|
+
if (query && Object.keys(query).length > 0) {
|
|
125
|
+
const params = new URLSearchParams(query);
|
|
126
|
+
url += `?${params.toString()}`;
|
|
127
|
+
}
|
|
128
|
+
return url;
|
|
129
|
+
}
|
|
130
|
+
async buildHeaders(requestHeaders) {
|
|
131
|
+
const headers = {
|
|
132
|
+
'Content-Type': 'application/json',
|
|
133
|
+
Accept: 'application/json',
|
|
134
|
+
...this.config.headers,
|
|
135
|
+
...requestHeaders,
|
|
136
|
+
};
|
|
137
|
+
if (this.config.auth) {
|
|
138
|
+
const token = await this.config.auth.getToken();
|
|
139
|
+
headers['Authorization'] = `Bearer ${token}`;
|
|
140
|
+
}
|
|
141
|
+
return headers;
|
|
142
|
+
}
|
|
143
|
+
calculateDelay(attempt, backoff, initialDelay, maxDelay) {
|
|
144
|
+
let delay;
|
|
145
|
+
switch (backoff) {
|
|
146
|
+
case 'exponential':
|
|
147
|
+
delay = initialDelay * Math.pow(2, attempt - 1);
|
|
148
|
+
break;
|
|
149
|
+
case 'linear':
|
|
150
|
+
delay = initialDelay * attempt;
|
|
151
|
+
break;
|
|
152
|
+
case 'constant':
|
|
153
|
+
default:
|
|
154
|
+
delay = initialDelay;
|
|
155
|
+
}
|
|
156
|
+
// Add jitter (±10%)
|
|
157
|
+
const jitter = delay * 0.1 * (Math.random() * 2 - 1);
|
|
158
|
+
delay = Math.min(delay + jitter, maxDelay);
|
|
159
|
+
return Math.round(delay);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
// Simple token-based auth provider
|
|
163
|
+
export class BearerAuthProvider {
|
|
164
|
+
token;
|
|
165
|
+
constructor(token) {
|
|
166
|
+
this.token = token;
|
|
167
|
+
}
|
|
168
|
+
async getToken() {
|
|
169
|
+
return this.token;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
// OAuth2 auth provider (simplified)
|
|
173
|
+
export class OAuth2AuthProvider {
|
|
174
|
+
accessToken;
|
|
175
|
+
refreshTokenValue;
|
|
176
|
+
tokenEndpoint;
|
|
177
|
+
clientId;
|
|
178
|
+
clientSecret;
|
|
179
|
+
constructor(config) {
|
|
180
|
+
this.accessToken = config.accessToken;
|
|
181
|
+
this.refreshTokenValue = config.refreshToken;
|
|
182
|
+
this.tokenEndpoint = config.tokenEndpoint;
|
|
183
|
+
this.clientId = config.clientId;
|
|
184
|
+
this.clientSecret = config.clientSecret;
|
|
185
|
+
}
|
|
186
|
+
async getToken() {
|
|
187
|
+
return this.accessToken;
|
|
188
|
+
}
|
|
189
|
+
async refreshToken() {
|
|
190
|
+
if (!this.refreshTokenValue || !this.tokenEndpoint) {
|
|
191
|
+
throw new Error('Cannot refresh token: missing refresh token or endpoint');
|
|
192
|
+
}
|
|
193
|
+
const response = await fetch(this.tokenEndpoint, {
|
|
194
|
+
method: 'POST',
|
|
195
|
+
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
|
196
|
+
body: new URLSearchParams({
|
|
197
|
+
grant_type: 'refresh_token',
|
|
198
|
+
refresh_token: this.refreshTokenValue,
|
|
199
|
+
client_id: this.clientId ?? '',
|
|
200
|
+
client_secret: this.clientSecret ?? '',
|
|
201
|
+
}),
|
|
202
|
+
});
|
|
203
|
+
const data = await response.json();
|
|
204
|
+
this.accessToken = data.access_token;
|
|
205
|
+
if (data.refresh_token) {
|
|
206
|
+
this.refreshTokenValue = data.refresh_token;
|
|
207
|
+
}
|
|
208
|
+
return this.accessToken;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|