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,297 +0,0 @@
|
|
|
1
|
-
# Reqon vs Temporal: E-Commerce Order Reconciliation
|
|
2
|
-
|
|
3
|
-
This example demonstrates a complex multi-vendor order reconciliation pipeline that syncs data from Shopify, Stripe, and ShipStation, validates for discrepancies, and stores results in a database.
|
|
4
|
-
|
|
5
|
-
## The Scenario
|
|
6
|
-
|
|
7
|
-
An e-commerce company needs to:
|
|
8
|
-
1. Sync orders from Shopify (source of truth for orders)
|
|
9
|
-
2. Sync payments from Stripe (source of truth for payments)
|
|
10
|
-
3. Sync shipments from ShipStation (source of truth for fulfillment)
|
|
11
|
-
4. Cross-reference and validate all three data sources
|
|
12
|
-
5. Flag discrepancies (unpaid orders, missing shipments, refund mismatches)
|
|
13
|
-
6. Store reconciled data with audit trail
|
|
14
|
-
7. Handle rate limits, retries, and pagination across all APIs
|
|
15
|
-
8. Run daily with incremental sync
|
|
16
|
-
|
|
17
|
-
## The Numbers
|
|
18
|
-
|
|
19
|
-
| Metric | Reqon | Temporal |
|
|
20
|
-
|--------|-------|----------|
|
|
21
|
-
| **Total Lines of Code** | ~280 | ~1,500+ |
|
|
22
|
-
| **Files** | 1 | 12 |
|
|
23
|
-
| **Dependencies** | 1 (reqon) | 8+ (@temporalio/*, axios, pg, stripe) |
|
|
24
|
-
| **Infrastructure** | None | Temporal Server + PostgreSQL + Workers |
|
|
25
|
-
| **Setup Time** | 0 minutes | 30+ minutes |
|
|
26
|
-
| **Learning Curve** | Hours | Days/Weeks |
|
|
27
|
-
|
|
28
|
-
## Side-by-Side Comparison
|
|
29
|
-
|
|
30
|
-
### 1. API Pagination
|
|
31
|
-
|
|
32
|
-
**Reqon** (2 lines):
|
|
33
|
-
```vague
|
|
34
|
-
paginate: cursor(page_info, 250, "link.next"),
|
|
35
|
-
until: response.orders.length == 0,
|
|
36
|
-
```
|
|
37
|
-
|
|
38
|
-
**Temporal** (40+ lines):
|
|
39
|
-
```typescript
|
|
40
|
-
let cursor: string | null = null;
|
|
41
|
-
do {
|
|
42
|
-
Context.current().heartbeat({ page: pageCount, ordersFound: allOrders.length });
|
|
43
|
-
const { orders, nextCursor } = await client.fetchOrdersPage(cursor, since);
|
|
44
|
-
for (const order of orders) {
|
|
45
|
-
allOrders.push(transformOrder(order));
|
|
46
|
-
}
|
|
47
|
-
cursor = nextCursor;
|
|
48
|
-
pageCount++;
|
|
49
|
-
} while (cursor);
|
|
50
|
-
```
|
|
51
|
-
|
|
52
|
-
### 2. Rate Limiting
|
|
53
|
-
|
|
54
|
-
**Reqon** (4 lines):
|
|
55
|
-
```vague
|
|
56
|
-
rateLimit: {
|
|
57
|
-
strategy: "pause",
|
|
58
|
-
maxWait: 120,
|
|
59
|
-
fallbackRpm: 40
|
|
60
|
-
}
|
|
61
|
-
```
|
|
62
|
-
|
|
63
|
-
**Temporal** (50+ lines):
|
|
64
|
-
```typescript
|
|
65
|
-
class ShopifyClient {
|
|
66
|
-
private rateLimitState: RateLimitState | null = null;
|
|
67
|
-
|
|
68
|
-
private updateRateLimitState(headers: Record<string, string>): void {
|
|
69
|
-
// Parse headers, calculate remaining, track reset time...
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
private async waitForRateLimit(): Promise<void> {
|
|
73
|
-
if (!this.rateLimitState) return;
|
|
74
|
-
if (this.rateLimitState.remaining < 5) {
|
|
75
|
-
const waitTime = Math.max(0, this.rateLimitState.resetAt.getTime() - Date.now());
|
|
76
|
-
if (waitTime > 0) {
|
|
77
|
-
await new Promise((resolve) => setTimeout(resolve, waitTime));
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
```
|
|
83
|
-
|
|
84
|
-
### 3. Schema Transformation
|
|
85
|
-
|
|
86
|
-
**Reqon** (15 lines):
|
|
87
|
-
```vague
|
|
88
|
-
map order -> UnifiedOrder {
|
|
89
|
-
order_id: "shopify_" + .id,
|
|
90
|
-
customer_email: .customer.email,
|
|
91
|
-
total_amount: .total_price,
|
|
92
|
-
payment_status: match .financial_status {
|
|
93
|
-
"paid" => "captured",
|
|
94
|
-
"pending" => "pending",
|
|
95
|
-
"refunded" => "refunded",
|
|
96
|
-
_ => "unknown"
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
```
|
|
100
|
-
|
|
101
|
-
**Temporal** (30+ lines):
|
|
102
|
-
```typescript
|
|
103
|
-
function transformOrder(order: ShopifyOrder): UnifiedOrder {
|
|
104
|
-
const paymentStatusMap: Record<string, string> = {
|
|
105
|
-
paid: 'captured',
|
|
106
|
-
partially_paid: 'partial',
|
|
107
|
-
pending: 'pending',
|
|
108
|
-
refunded: 'refunded',
|
|
109
|
-
partially_refunded: 'partial_refund',
|
|
110
|
-
};
|
|
111
|
-
|
|
112
|
-
return {
|
|
113
|
-
order_id: `shopify_${order.id}`,
|
|
114
|
-
customer_email: order.customer?.email || '',
|
|
115
|
-
total_amount: parseFloat(order.total_price),
|
|
116
|
-
payment_status: paymentStatusMap[order.financial_status] || 'unknown',
|
|
117
|
-
// ... 10 more fields
|
|
118
|
-
};
|
|
119
|
-
}
|
|
120
|
-
```
|
|
121
|
-
|
|
122
|
-
### 4. Validation Rules
|
|
123
|
-
|
|
124
|
-
**Reqon** (5 lines):
|
|
125
|
-
```vague
|
|
126
|
-
validate order {
|
|
127
|
-
assume payment_exists == true
|
|
128
|
-
} or {
|
|
129
|
-
store { type: "missing_payment", ... } -> discrepancies { ... }
|
|
130
|
-
}
|
|
131
|
-
```
|
|
132
|
-
|
|
133
|
-
**Temporal** (50+ lines):
|
|
134
|
-
```typescript
|
|
135
|
-
export async function validateOrdersHavePayments(
|
|
136
|
-
orders: UnifiedOrder[],
|
|
137
|
-
payments: UnifiedPayment[]
|
|
138
|
-
): Promise<Discrepancy[]> {
|
|
139
|
-
const discrepancies: Discrepancy[] = [];
|
|
140
|
-
const paymentsByOrderId = new Map<string, UnifiedPayment[]>();
|
|
141
|
-
|
|
142
|
-
for (const payment of payments) {
|
|
143
|
-
const existing = paymentsByOrderId.get(payment.order_id) || [];
|
|
144
|
-
existing.push(payment);
|
|
145
|
-
paymentsByOrderId.set(payment.order_id, existing);
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
const threeDaysAgo = new Date(Date.now() - 3 * 24 * 60 * 60 * 1000);
|
|
149
|
-
|
|
150
|
-
for (const order of orders) {
|
|
151
|
-
if (order.payment_status === 'pending' && order.created_at < threeDaysAgo) {
|
|
152
|
-
const orderPayments = paymentsByOrderId.get(order.order_id);
|
|
153
|
-
if (!orderPayments || orderPayments.length === 0) {
|
|
154
|
-
discrepancies.push({
|
|
155
|
-
id: `disc_${order.order_id}_no_payment`,
|
|
156
|
-
// ... more fields
|
|
157
|
-
});
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
return discrepancies;
|
|
162
|
-
}
|
|
163
|
-
```
|
|
164
|
-
|
|
165
|
-
### 5. Pipeline Orchestration with Parallel Execution
|
|
166
|
-
|
|
167
|
-
**Reqon** (3 lines):
|
|
168
|
-
```vague
|
|
169
|
-
// Fetch from all three APIs in parallel, then validate
|
|
170
|
-
run [SyncShopifyOrders, SyncStripePayments, SyncShipStationShipments]
|
|
171
|
-
then ValidateReconciliation
|
|
172
|
-
```
|
|
173
|
-
Brackets indicate parallel execution - all three sync actions run concurrently for maximum performance.
|
|
174
|
-
|
|
175
|
-
**Temporal** (100+ lines):
|
|
176
|
-
```typescript
|
|
177
|
-
export async function orderReconciliationWorkflow(): Promise<ReconciliationResult> {
|
|
178
|
-
// Initialize state, queries, signals...
|
|
179
|
-
setHandler(getStatusQuery, () => state);
|
|
180
|
-
setHandler(cancelSignal, () => { cancelled = true; });
|
|
181
|
-
setHandler(pauseSignal, () => { paused = true; });
|
|
182
|
-
setHandler(resumeSignal, () => { paused = false; });
|
|
183
|
-
|
|
184
|
-
try {
|
|
185
|
-
state.status = 'syncing_shopify';
|
|
186
|
-
await checkPauseAndCancel();
|
|
187
|
-
const shopifyLastSync = await storage.getCheckpoint('shopify_orders_last_sync');
|
|
188
|
-
const orders = await shopify.fetchAllShopifyOrders(shopifyLastSync);
|
|
189
|
-
state.ordersProcessed = await storage.upsertOrders(orders);
|
|
190
|
-
await storage.setCheckpoint('shopify_orders_last_sync', new Date().toISOString());
|
|
191
|
-
|
|
192
|
-
// Repeat for Stripe...
|
|
193
|
-
// Repeat for ShipStation...
|
|
194
|
-
// Validation step...
|
|
195
|
-
} catch (error) {
|
|
196
|
-
state.status = 'failed';
|
|
197
|
-
throw error;
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
```
|
|
201
|
-
|
|
202
|
-
### 6. Running the Pipeline
|
|
203
|
-
|
|
204
|
-
**Reqon**:
|
|
205
|
-
```bash
|
|
206
|
-
# Run once
|
|
207
|
-
reqon reconciliation.vague --auth ./credentials.json
|
|
208
|
-
|
|
209
|
-
# Dry run
|
|
210
|
-
reqon reconciliation.vague --dry-run
|
|
211
|
-
|
|
212
|
-
# Resume from checkpoint
|
|
213
|
-
reqon reconciliation.vague --resume exec-abc123
|
|
214
|
-
|
|
215
|
-
# Schedule with cron
|
|
216
|
-
0 2 * * * reqon reconciliation.vague
|
|
217
|
-
```
|
|
218
|
-
|
|
219
|
-
**Temporal**:
|
|
220
|
-
```bash
|
|
221
|
-
# 1. Start PostgreSQL
|
|
222
|
-
docker-compose up -d postgresql
|
|
223
|
-
|
|
224
|
-
# 2. Start Temporal Server
|
|
225
|
-
docker-compose up -d temporal temporal-ui
|
|
226
|
-
|
|
227
|
-
# 3. Wait for server to be ready
|
|
228
|
-
sleep 30
|
|
229
|
-
|
|
230
|
-
# 4. Start worker (in separate terminal)
|
|
231
|
-
npx ts-node src/workers/main.ts
|
|
232
|
-
|
|
233
|
-
# 5. Trigger workflow
|
|
234
|
-
npx ts-node src/client/schedule.ts run
|
|
235
|
-
|
|
236
|
-
# 6. (Optional) Create schedule
|
|
237
|
-
npx ts-node src/client/schedule.ts schedule
|
|
238
|
-
```
|
|
239
|
-
|
|
240
|
-
## When to Use Which
|
|
241
|
-
|
|
242
|
-
### Use Reqon When:
|
|
243
|
-
- ✅ Your workflow is data synchronization / ETL
|
|
244
|
-
- ✅ You need to fetch, transform, validate, and store data
|
|
245
|
-
- ✅ You want minimal infrastructure
|
|
246
|
-
- ✅ Business users need to understand the pipeline
|
|
247
|
-
- ✅ You need quick iteration and prototyping
|
|
248
|
-
- ✅ The "happy path" is the main path
|
|
249
|
-
- ✅ Retry/backoff/rate-limiting are your main concerns
|
|
250
|
-
|
|
251
|
-
### Use Temporal When:
|
|
252
|
-
- ⚡ You need human-in-the-loop workflows
|
|
253
|
-
- ⚡ You have complex branching/conditional logic
|
|
254
|
-
- ⚡ You need workflow versioning/migration
|
|
255
|
-
- ⚡ You need sub-second latency on signals
|
|
256
|
-
- ⚡ You're building microservice orchestration
|
|
257
|
-
- ⚡ You need the full power of a programming language
|
|
258
|
-
- ⚡ You already have Temporal infrastructure
|
|
259
|
-
|
|
260
|
-
## Files
|
|
261
|
-
|
|
262
|
-
### Reqon Solution
|
|
263
|
-
- `reconciliation.vague` - **The complete solution in ~280 lines**
|
|
264
|
-
|
|
265
|
-
### Temporal Solution (11 files, ~1,500 lines)
|
|
266
|
-
```
|
|
267
|
-
temporal/
|
|
268
|
-
├── types/
|
|
269
|
-
│ └── index.ts # Type definitions (130 lines)
|
|
270
|
-
├── config/
|
|
271
|
-
│ └── retry.ts # Retry configuration (50 lines)
|
|
272
|
-
├── activities/
|
|
273
|
-
│ ├── index.ts # Activity exports
|
|
274
|
-
│ ├── shopify.ts # Shopify API (220 lines)
|
|
275
|
-
│ ├── stripe.ts # Stripe API (150 lines)
|
|
276
|
-
│ ├── shipstation.ts # ShipStation API (180 lines)
|
|
277
|
-
│ ├── validation.ts # Validation logic (180 lines)
|
|
278
|
-
│ └── storage.ts # Database operations (160 lines)
|
|
279
|
-
├── workflows/
|
|
280
|
-
│ └── orderReconciliation.ts # Main workflow (200 lines)
|
|
281
|
-
├── workers/
|
|
282
|
-
│ └── main.ts # Worker setup (100 lines)
|
|
283
|
-
└── client/
|
|
284
|
-
└── schedule.ts # Scheduler client (180 lines)
|
|
285
|
-
```
|
|
286
|
-
|
|
287
|
-
## The Bottom Line
|
|
288
|
-
|
|
289
|
-
For data synchronization pipelines, Reqon offers:
|
|
290
|
-
|
|
291
|
-
1. **10x less code** - Focus on *what* you want, not *how* to do it
|
|
292
|
-
2. **Zero infrastructure** - Run anywhere Node.js runs
|
|
293
|
-
3. **Domain-specific** - Built specifically for fetch/transform/validate/store
|
|
294
|
-
4. **Readable** - Business logic is visible, not buried in code
|
|
295
|
-
5. **Batteries included** - Pagination, rate limiting, retries, checkpointing built-in
|
|
296
|
-
|
|
297
|
-
Temporal is a powerful general-purpose workflow engine, but for the specific domain of data pipelines, it's like using a chainsaw to cut butter. Reqon is purpose-built for this domain and shows in every aspect of the developer experience.
|
|
@@ -1,355 +0,0 @@
|
|
|
1
|
-
// E-Commerce Order Reconciliation Pipeline
|
|
2
|
-
// Syncs and validates orders across Shopify, Stripe, and ShipStation
|
|
3
|
-
// ~150 lines of declarative code vs ~1,500+ lines in Temporal
|
|
4
|
-
|
|
5
|
-
mission OrderReconciliation {
|
|
6
|
-
|
|
7
|
-
// ============================================================
|
|
8
|
-
// SOURCES - API definitions with auth and rate limiting
|
|
9
|
-
// ============================================================
|
|
10
|
-
|
|
11
|
-
source Shopify {
|
|
12
|
-
auth: oauth2,
|
|
13
|
-
base: "https://mystore.myshopify.com/admin/api/2024-01",
|
|
14
|
-
headers: { "X-Shopify-Access-Token": "${SHOPIFY_TOKEN}" },
|
|
15
|
-
rateLimit: {
|
|
16
|
-
strategy: "pause",
|
|
17
|
-
maxWait: 120,
|
|
18
|
-
fallbackRpm: 40
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
source Stripe {
|
|
23
|
-
auth: bearer,
|
|
24
|
-
base: "https://api.stripe.com/v1",
|
|
25
|
-
rateLimit: {
|
|
26
|
-
strategy: "throttle",
|
|
27
|
-
maxWait: 60,
|
|
28
|
-
fallbackRpm: 100
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
source ShipStation {
|
|
33
|
-
auth: basic,
|
|
34
|
-
base: "https://ssapi.shipstation.com",
|
|
35
|
-
rateLimit: {
|
|
36
|
-
strategy: "pause",
|
|
37
|
-
maxWait: 30,
|
|
38
|
-
fallbackRpm: 40
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
// ============================================================
|
|
43
|
-
// STORES - Where we persist reconciled data
|
|
44
|
-
// ============================================================
|
|
45
|
-
|
|
46
|
-
store orders: sql("reconciled_orders")
|
|
47
|
-
store payments: sql("reconciled_payments")
|
|
48
|
-
store shipments: sql("reconciled_shipments")
|
|
49
|
-
store discrepancies: sql("audit_discrepancies")
|
|
50
|
-
store sync_state: sql("sync_checkpoints")
|
|
51
|
-
|
|
52
|
-
// ============================================================
|
|
53
|
-
// SCHEMAS - Unified data models
|
|
54
|
-
// ============================================================
|
|
55
|
-
|
|
56
|
-
schema UnifiedOrder {
|
|
57
|
-
order_id: string,
|
|
58
|
-
external_id: string,
|
|
59
|
-
source: string,
|
|
60
|
-
customer_email: string,
|
|
61
|
-
total_amount: decimal,
|
|
62
|
-
currency: string,
|
|
63
|
-
status: string,
|
|
64
|
-
created_at: date,
|
|
65
|
-
line_items_count: int,
|
|
66
|
-
payment_status: string,
|
|
67
|
-
fulfillment_status: string,
|
|
68
|
-
synced_at: date
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
schema UnifiedPayment {
|
|
72
|
-
payment_id: string,
|
|
73
|
-
order_id: string,
|
|
74
|
-
amount: decimal,
|
|
75
|
-
currency: string,
|
|
76
|
-
status: string,
|
|
77
|
-
method: string,
|
|
78
|
-
captured_at: date,
|
|
79
|
-
refunded_amount: decimal
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
schema UnifiedShipment {
|
|
83
|
-
shipment_id: string,
|
|
84
|
-
order_id: string,
|
|
85
|
-
carrier: string,
|
|
86
|
-
tracking_number: string,
|
|
87
|
-
status: string,
|
|
88
|
-
shipped_at: date,
|
|
89
|
-
delivered_at: date
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
schema Discrepancy {
|
|
93
|
-
id: string,
|
|
94
|
-
order_id: string,
|
|
95
|
-
type: string,
|
|
96
|
-
severity: string,
|
|
97
|
-
description: string,
|
|
98
|
-
shopify_value: string,
|
|
99
|
-
stripe_value: string,
|
|
100
|
-
shipstation_value: string,
|
|
101
|
-
detected_at: date
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
// ============================================================
|
|
105
|
-
// ACTION: Sync Shopify Orders (with incremental sync)
|
|
106
|
-
// ============================================================
|
|
107
|
-
|
|
108
|
-
action SyncShopifyOrders {
|
|
109
|
-
// Get last sync timestamp for incremental sync
|
|
110
|
-
get "/orders.json" {
|
|
111
|
-
source: "Shopify",
|
|
112
|
-
body: {
|
|
113
|
-
"status": "any",
|
|
114
|
-
"updated_at_min": sync_state.get("shopify_orders_last_sync"),
|
|
115
|
-
"limit": 250
|
|
116
|
-
},
|
|
117
|
-
paginate: cursor(page_info, 250, "link.next"),
|
|
118
|
-
until: response.orders.length == 0,
|
|
119
|
-
retry: {
|
|
120
|
-
maxAttempts: 5,
|
|
121
|
-
backoff: "exponential",
|
|
122
|
-
initialDelay: 2000,
|
|
123
|
-
maxDelay: 60000
|
|
124
|
-
}
|
|
125
|
-
},
|
|
126
|
-
|
|
127
|
-
// Transform each order to unified schema
|
|
128
|
-
for order in response.orders {
|
|
129
|
-
map order -> UnifiedOrder {
|
|
130
|
-
order_id: "shopify_" + .id,
|
|
131
|
-
external_id: .id,
|
|
132
|
-
source: "shopify",
|
|
133
|
-
customer_email: .customer.email,
|
|
134
|
-
total_amount: .total_price,
|
|
135
|
-
currency: .currency,
|
|
136
|
-
status: .status,
|
|
137
|
-
created_at: .created_at,
|
|
138
|
-
line_items_count: length(.line_items),
|
|
139
|
-
payment_status: match .financial_status {
|
|
140
|
-
"paid" => "captured",
|
|
141
|
-
"partially_paid" => "partial",
|
|
142
|
-
"pending" => "pending",
|
|
143
|
-
"refunded" => "refunded",
|
|
144
|
-
"partially_refunded" => "partial_refund",
|
|
145
|
-
_ => "unknown"
|
|
146
|
-
},
|
|
147
|
-
fulfillment_status: match .fulfillment_status {
|
|
148
|
-
"fulfilled" => "shipped",
|
|
149
|
-
"partial" => "partial",
|
|
150
|
-
null => "unfulfilled",
|
|
151
|
-
_ => .fulfillment_status
|
|
152
|
-
},
|
|
153
|
-
synced_at: now()
|
|
154
|
-
},
|
|
155
|
-
|
|
156
|
-
store response -> orders { key: .order_id, upsert: true }
|
|
157
|
-
},
|
|
158
|
-
|
|
159
|
-
// Update checkpoint
|
|
160
|
-
store { "shopify_orders_last_sync": now() } -> sync_state { key: "shopify_orders_last_sync" }
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
// ============================================================
|
|
164
|
-
// ACTION: Sync Stripe Payments
|
|
165
|
-
// ============================================================
|
|
166
|
-
|
|
167
|
-
action SyncStripePayments {
|
|
168
|
-
get "/payment_intents" {
|
|
169
|
-
source: "Stripe",
|
|
170
|
-
body: {
|
|
171
|
-
"limit": 100,
|
|
172
|
-
"created[gte]": sync_state.get("stripe_payments_last_sync"),
|
|
173
|
-
"expand[]": "data.charges"
|
|
174
|
-
},
|
|
175
|
-
paginate: cursor(starting_after, 100, "data[-1].id"),
|
|
176
|
-
until: response.has_more == false,
|
|
177
|
-
retry: {
|
|
178
|
-
maxAttempts: 3,
|
|
179
|
-
backoff: "exponential",
|
|
180
|
-
initialDelay: 1000
|
|
181
|
-
}
|
|
182
|
-
},
|
|
183
|
-
|
|
184
|
-
for payment in response.data where .metadata.shopify_order_id != null {
|
|
185
|
-
map payment -> UnifiedPayment {
|
|
186
|
-
payment_id: "stripe_" + .id,
|
|
187
|
-
order_id: "shopify_" + .metadata.shopify_order_id,
|
|
188
|
-
amount: .amount / 100, // Stripe uses cents
|
|
189
|
-
currency: .currency,
|
|
190
|
-
status: match .status {
|
|
191
|
-
"succeeded" => "captured",
|
|
192
|
-
"requires_capture" => "authorized",
|
|
193
|
-
"canceled" => "voided",
|
|
194
|
-
_ => .status
|
|
195
|
-
},
|
|
196
|
-
method: .payment_method_types[0],
|
|
197
|
-
captured_at: .charges.data[0].created,
|
|
198
|
-
refunded_amount: .charges.data[0].amount_refunded / 100
|
|
199
|
-
},
|
|
200
|
-
|
|
201
|
-
store response -> payments { key: .payment_id, upsert: true }
|
|
202
|
-
},
|
|
203
|
-
|
|
204
|
-
store { "stripe_payments_last_sync": now() } -> sync_state { key: "stripe_payments_last_sync" }
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
// ============================================================
|
|
208
|
-
// ACTION: Sync ShipStation Shipments
|
|
209
|
-
// ============================================================
|
|
210
|
-
|
|
211
|
-
action SyncShipStationShipments {
|
|
212
|
-
get "/shipments" {
|
|
213
|
-
source: "ShipStation",
|
|
214
|
-
body: {
|
|
215
|
-
"pageSize": 500,
|
|
216
|
-
"createDateStart": sync_state.get("shipstation_last_sync"),
|
|
217
|
-
"sortBy": "CreateDate"
|
|
218
|
-
},
|
|
219
|
-
paginate: page(page, 500),
|
|
220
|
-
until: response.shipments.length == 0,
|
|
221
|
-
retry: {
|
|
222
|
-
maxAttempts: 4,
|
|
223
|
-
backoff: "exponential",
|
|
224
|
-
initialDelay: 3000
|
|
225
|
-
}
|
|
226
|
-
},
|
|
227
|
-
|
|
228
|
-
for shipment in response.shipments {
|
|
229
|
-
map shipment -> UnifiedShipment {
|
|
230
|
-
shipment_id: "ss_" + .shipmentId,
|
|
231
|
-
order_id: "shopify_" + .orderNumber,
|
|
232
|
-
carrier: .carrierCode,
|
|
233
|
-
tracking_number: .trackingNumber,
|
|
234
|
-
status: match .voided {
|
|
235
|
-
true => "voided",
|
|
236
|
-
_ => match .trackingNumber {
|
|
237
|
-
null => "label_created",
|
|
238
|
-
_ => "shipped"
|
|
239
|
-
}
|
|
240
|
-
},
|
|
241
|
-
shipped_at: .shipDate,
|
|
242
|
-
delivered_at: .deliveryDate
|
|
243
|
-
},
|
|
244
|
-
|
|
245
|
-
store response -> shipments { key: .shipment_id, upsert: true }
|
|
246
|
-
},
|
|
247
|
-
|
|
248
|
-
store { "shipstation_last_sync": now() } -> sync_state { key: "shipstation_last_sync" }
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
// ============================================================
|
|
252
|
-
// ACTION: Cross-Reference and Validate
|
|
253
|
-
// ============================================================
|
|
254
|
-
|
|
255
|
-
action ValidateReconciliation {
|
|
256
|
-
// Check for orders without payments
|
|
257
|
-
for order in orders where .payment_status == "pending" and .created_at < now() - days(3) {
|
|
258
|
-
let payment_exists = any of payments where .order_id == order.order_id,
|
|
259
|
-
|
|
260
|
-
validate order {
|
|
261
|
-
assume payment_exists == true
|
|
262
|
-
} or {
|
|
263
|
-
store {
|
|
264
|
-
id: "disc_" + order.order_id + "_no_payment",
|
|
265
|
-
order_id: order.order_id,
|
|
266
|
-
type: "missing_payment",
|
|
267
|
-
severity: "high",
|
|
268
|
-
description: "Order older than 3 days has no matching Stripe payment",
|
|
269
|
-
shopify_value: order.total_amount,
|
|
270
|
-
stripe_value: null,
|
|
271
|
-
shipstation_value: null,
|
|
272
|
-
detected_at: now()
|
|
273
|
-
} -> discrepancies { key: .id, upsert: true }
|
|
274
|
-
}
|
|
275
|
-
},
|
|
276
|
-
|
|
277
|
-
// Check for payment/order amount mismatches
|
|
278
|
-
for order in orders {
|
|
279
|
-
let matching_payments = payments where .order_id == order.order_id,
|
|
280
|
-
let total_paid = sum(matching_payments.amount),
|
|
281
|
-
|
|
282
|
-
validate order {
|
|
283
|
-
assume total_paid >= order.total_amount - 0.01,
|
|
284
|
-
assume total_paid <= order.total_amount + 0.01
|
|
285
|
-
} or {
|
|
286
|
-
store {
|
|
287
|
-
id: "disc_" + order.order_id + "_amount_mismatch",
|
|
288
|
-
order_id: order.order_id,
|
|
289
|
-
type: "amount_mismatch",
|
|
290
|
-
severity: match abs(order.total_amount - total_paid) {
|
|
291
|
-
x where x > 100 => "critical",
|
|
292
|
-
x where x > 10 => "high",
|
|
293
|
-
_ => "medium"
|
|
294
|
-
},
|
|
295
|
-
description: "Order total does not match payment total",
|
|
296
|
-
shopify_value: order.total_amount,
|
|
297
|
-
stripe_value: total_paid,
|
|
298
|
-
shipstation_value: null,
|
|
299
|
-
detected_at: now()
|
|
300
|
-
} -> discrepancies { key: .id, upsert: true }
|
|
301
|
-
}
|
|
302
|
-
},
|
|
303
|
-
|
|
304
|
-
// Check for shipped orders without shipment records
|
|
305
|
-
for order in orders where .fulfillment_status == "shipped" {
|
|
306
|
-
let shipment_exists = any of shipments where .order_id == order.order_id,
|
|
307
|
-
|
|
308
|
-
validate order {
|
|
309
|
-
assume shipment_exists == true
|
|
310
|
-
} or {
|
|
311
|
-
store {
|
|
312
|
-
id: "disc_" + order.order_id + "_no_shipment",
|
|
313
|
-
order_id: order.order_id,
|
|
314
|
-
type: "missing_shipment_record",
|
|
315
|
-
severity: "medium",
|
|
316
|
-
description: "Shopify shows fulfilled but no ShipStation shipment found",
|
|
317
|
-
shopify_value: order.fulfillment_status,
|
|
318
|
-
stripe_value: null,
|
|
319
|
-
shipstation_value: null,
|
|
320
|
-
detected_at: now()
|
|
321
|
-
} -> discrepancies { key: .id, upsert: true }
|
|
322
|
-
}
|
|
323
|
-
},
|
|
324
|
-
|
|
325
|
-
// Check for refund discrepancies
|
|
326
|
-
for payment in payments where .refunded_amount > 0 {
|
|
327
|
-
let order = first(orders where .order_id == payment.order_id),
|
|
328
|
-
|
|
329
|
-
validate payment {
|
|
330
|
-
assume order.payment_status == "refunded" or order.payment_status == "partial_refund"
|
|
331
|
-
} or {
|
|
332
|
-
store {
|
|
333
|
-
id: "disc_" + payment.order_id + "_refund_mismatch",
|
|
334
|
-
order_id: payment.order_id,
|
|
335
|
-
type: "refund_status_mismatch",
|
|
336
|
-
severity: "high",
|
|
337
|
-
description: "Stripe shows refund but Shopify payment status not updated",
|
|
338
|
-
shopify_value: order.payment_status,
|
|
339
|
-
stripe_value: payment.refunded_amount,
|
|
340
|
-
shipstation_value: null,
|
|
341
|
-
detected_at: now()
|
|
342
|
-
} -> discrepancies { key: .id, upsert: true }
|
|
343
|
-
}
|
|
344
|
-
}
|
|
345
|
-
}
|
|
346
|
-
|
|
347
|
-
// ============================================================
|
|
348
|
-
// PIPELINE: Parallel fetch, then sequential validation
|
|
349
|
-
// ============================================================
|
|
350
|
-
|
|
351
|
-
// Fetch from all three APIs in parallel for maximum performance
|
|
352
|
-
// ValidateReconciliation runs after all three complete
|
|
353
|
-
run [SyncShopifyOrders, SyncStripePayments, SyncShipStationShipments]
|
|
354
|
-
then ValidateReconciliation
|
|
355
|
-
}
|