updated skills
This commit is contained in:
@@ -56,10 +56,12 @@ browser. Zero browser/Node deps.
|
||||
- [builtin](./src/tags/builtin.md) — Provides `defaultTagHandlers()`, a `TagRegistry` populated with handlers for the most-used IANA-registered CBOR tags. C…
|
||||
- [registry](./src/tags/registry.md) — `TagRegistry` is the in-memory mutable container for CBOR tag handlers. The decoder consults a snapshot of the registry…
|
||||
- [tagged](./src/tags/tagged.md) — Provides the `Tagged<T>` value object used by `@suaveplan/cbor` to materialise RFC 8949 §3.4 tagged items. The decoder…
|
||||
|
||||
<!-- end:auto-docs --> - half-float — Decodes 2 big-endian bytes representin…
|
||||
- [decoder](./src/decoder/decoder.md) — <!-- begin:auto-docs --> _(no submodules)_- half-float — Decodes 2 big-endian bytes representin…
|
||||
- [half-float](./src/decoder/half-float.md) — Decodes 2 big-endian bytes representing IEEE 754 binary16 to a JavaScript `number`. The decoder is always available — n…
|
||||
- [well-formed](./src/decoder/well-formed.md) — `isWellFormed(bytes)` returns `true` iff `bytes` represent a single well-formed CBOR item per RFC 8949 §5.1. The functi…
|
||||
|
||||
- [decoder](./src/decoder/decoder.md) — <!-- begin:auto-docs --> _(no submodules)_- half-float — Decodes 2 big-endian bytes representin…
|
||||
- [half-float](./src/decoder/half-float.md) — Decodes 2 big-endian bytes representing IEEE 754 binary16 to a JavaScript `number`. The decoder is always available — n…
|
||||
- [well-formed](./src/decoder/well-formed.md) — `isWellFormed(bytes)` returns `true` iff `bytes` represent a single well-formed CBOR item per RFC 8949 §5.1. The functi…
|
||||
- [deterministic](./src/deterministic/deterministic.md) — <!-- begin:auto-docs --> _(no submodules)_- [deterministic](./src/deterministic/deterministic.md) — <!-- begin:auto-docs --> _(no submodules)_- [diagnostic](./src/diagnostic/diagnostic.md) — <!-- begin:auto-docs --> _(no submodules)_- [diagnostic](./src/diagnostic/diagnostic.md) — <!-- begin:auto-docs --> _(no submodules)_- [encoder](./src/encoder/encoder.md) — <!-- begin:auto-docs --> _(no submodules)_- half-float — Encodes a JavaScript `number` as IEEE……
|
||||
- [encoder](./src/encoder/encoder.md) — <!-- begin:auto-docs --> _(no submodules)_- half-float — Encodes a JavaScript `number` as IEEE……
|
||||
- [half-float](./src/encoder/half-float.md) — Encodes a JavaScript `number` as IEEE 754 binary16 (CBOR major 7 / value 25) when the round-trip is exact, and otherwis…
|
||||
@@ -180,4 +182,3 @@ const value = decode(bytes);
|
||||
- RFC 8949 §5.1 — strict-mode well-formedness; every malformed-input class
|
||||
raises `CborError(MALFORMED)`
|
||||
- RFC 8742 §2 + §3 — CBOR Sequences encode + decode + async-iterator stream
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs --> - half-float — Decodes 2 big-endian bytes representin…
|
||||
|
||||
- [half-float](./half-float.md) — Decodes 2 big-endian bytes representing IEEE 754 binary16 to a JavaScript `number`. The decoder is always available — n…
|
||||
- [well-formed](./well-formed.md) — `isWellFormed(bytes)` returns `true` iff `bytes` represent a single well-formed CBOR item per RFC 8949 §5.1. The functi…
|
||||
- half-float — Decodes 2 big-endian bytes representin…
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs --> - half-float — Encodes a JavaScript `number` as IEEE……
|
||||
|
||||
- [half-float](./half-float.md) — Encodes a JavaScript `number` as IEEE 754 binary16 (CBOR major 7 / value 25) when the round-trip is exact, and otherwis…
|
||||
- half-float — Encodes a JavaScript `number` as IEEE……
|
||||
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -13,7 +13,9 @@ Typed, generic finite state machine (FSM) for TypeScript. Ships a synchronous-by
|
||||
- [fsm-error](./src/errors/fsm-error.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
|
||||
- [context-helpers](./src/utils/context-helpers.md) — Provides ergonomic factory helpers for building context-update and side-effect actions that can be slotted directly int…
|
||||
- [definition-validator](./src/utils/definition-validator.md) — Validates an `FSMDefinition` object before the interpreter begins executing. Early structural validation prevents subtl…
|
||||
|
||||
<!-- end:auto-docs --> - [fsm-interpreter](./src/core/interpreter/fsm-interpreter.md) — <!-- begin:auto-docs --> _(no submodules)_- [errors](./src/errors/fsm-error.md) — <!-- begin:auto-docs --> _(no submodules)_- [fsm-error](./src/errors/fsm-error.md) — <!-- begin:auto-docs --> _(no submodules)_- [context-helpers](./src/utils/context-helpers.md) — Provides ergonomic factory helpers for building context-update and side-effect actions that can be slotted directly int…
|
||||
|
||||
- [definition-validator](./src/utils/definition-validator.md) — Validates an `FSMDefinition` object before the interpreter begins executing. Early structural validation prevents subtl…
|
||||
- [fsm-interpreter](./src/core/interpreter/fsm-interpreter.md) — <!-- begin:auto-docs --> _(no submodules)_- [errors](./src/errors/fsm-error.md) — <!-- begin:auto-docs --> _(no submodules)_- [fsm-error](./src/errors/fsm-error.md) — <!-- begin:auto-docs --> _(no submodules)_- [context-helpers](./src/utils/context-helpers.md) — Provides ergonomic factory helpers for building context-update and side-effect actions that can be slotted directly int…
|
||||
- [definition-validator](./src/utils/definition-validator.md) — Validates an `FSMDefinition` object before the interpreter begins executing. Early structural validation prevents subtl…
|
||||
|
||||
@@ -26,7 +26,9 @@ Idempotency key guard and deduplication for async operations. The package provid
|
||||
- [redis-store](./src/store/redis-store.md) — Provides a Redis-backed `IdempotencyStoreAdapter<T>` with automatic key expiration using Redis native TTL. By accepting…
|
||||
- [versioning](./src/versioning/key-versioning.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
|
||||
- [key-versioning](./src/versioning/key-versioning.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
|
||||
|
||||
<!-- end:auto-docs --> - [idempotency-error](./src/errors/idempotency-error.md) — <!-- begin:auto-docs --> _(no submodules)_- [guard](./src/guard/idempotency-guard.md) — <!-- begin:auto-docs --> _(no submodules)_- [idempotency-guard](./src/guard/idempotency-guard.md) — <!-- begin:auto-docs --> _(no submodules)_- [key](./src/key/key-generator.md) — <!-- begin:auto-docs --> _(no submodules)_- [key-generator](./src/key/key-generator.md) — <!-- begin:auto-docs --> _(no submodules)_- [metrics](./src/metrics/idempotency-metrics.md) — <!-- begin:auto-docs --> _(no submodules)_- [idempotency-metrics](./src/metrics/idempotency-metrics.md) — <!-- begin:auto-docs --> _(no submodules)_- [middleware](./src/middleware/idempotency-middleware.md) — <!-- begin:auto-docs --> _(no submodules)_- [idempotency-middleware](./src/middleware/idempotency-middleware.md) — <!-- begin:auto-docs --> _(no submodules)_- [database-store](./src/store/database-store.md) — Provides a persistent `IdempotencyStoreAdapter<T>` backed by any relational database via the `IdempotencyDatabaseAdapte…
|
||||
|
||||
- [memory-store](./src/store/memory-store.md) — Provides an in-memory implementation of `IdempotencyStoreAdapter<T>` backed by a `Map`. It is the default store for uni…
|
||||
- [redis-store](./src/store/redis-store.md) — Provides a Redis-backed `IdempotencyStoreAdapter<T>` with automatic key expiration using Redis native TTL. By accepting…
|
||||
- [versioning](./src/versioning/key-versioning.md) — <!-- begin:auto-docs --> _(no submodules)_- [key-versioning](./src/versioning/key-versioning.md) — <!-- begin:auto-docs --> _(no submodules)_- [error-codes](./src/error-codes.md) — Defines and registers all stable error codes and their HTTP/retry metadata for the `@suaveplan/idempotency` package. Ev…
|
||||
@@ -92,4 +94,3 @@ const safe = await guard.execute(
|
||||
| [src/versioning/key-versioning.md](src/versioning/key-versioning.md) | `KeyVersioning` — multi-version record management with `reject`, `overwrite`, and `version` conflict strategies |
|
||||
| [src/errors/idempotency-error.md](src/errors/idempotency-error.md) | Structured error hierarchy: `IdempotencyConflictError`, `IdempotencyMismatchError`, `LockAcquisitionError`, `VersionConflictError` |
|
||||
| [src/error-codes.md](src/error-codes.md) | Stable `IDEMPOTENCY.*` error code registry |
|
||||
|
||||
|
||||
@@ -20,10 +20,12 @@ Universal in-process job scheduler with cron + queued runs, retries, timeouts, c
|
||||
- [registry](./src/registry/job-registry.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
|
||||
- [job-registry](./src/registry/job-registry.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
|
||||
- [cron-rescheduler](./src/scheduler/cron-rescheduler.md) — Houses the two cron-related decisions the scheduler makes after a job settles or resumes: (1) compute the next fire fro…
|
||||
- [job-dispatcher](./src/scheduler/job-dispatcher.md) — Encapsulates the "hand a job to the executor" step of the scheduler's tick loop. The scheduler core decides *which* job…
|
||||
- [job-dispatcher](./src/scheduler/job-dispatcher.md) — Encapsulates the "hand a job to the executor" step of the scheduler's tick loop. The scheduler core decides _which_ job…
|
||||
- [job-scheduler](./src/scheduler/job-scheduler.md) — The `JobScheduler` class is the package's main entry point: it accepts both cron-scheduled and one-shot queued jobs, ru…
|
||||
- [job-snapshot](./src/scheduler/job-snapshot.md) — Projects a mutable `ScheduledJob` record (the scheduler's internal data structure) to an immutable `JobStatus` snapshot…
|
||||
|
||||
<!-- end:auto-docs --> - [job-scheduler-error](./src/errors/job-scheduler-error.md) — <!-- begin:auto-docs --> _(no submodules)_- [events](./src/events/events.md) — <!-- begin:auto-docs --> _(no submodules)_- [events](./src/events/events.md) — <!-- begin:auto-docs --> _(no submodules)_- [execution](./src/execution/job-execution.md) — <!-- begin:auto-docs --> _(no submodules)_- [job-execution](./src/execution/job-execution.md) — <!-- begin:auto-docs --> _(no submodules)_- [otel](./src/otel/job-scheduler-otel.md) — <!-- begin:auto-docs --> _(no submodules)_- [job-scheduler-otel](./src/otel/job-scheduler-otel.md) — <!-- begin:auto-docs --> _(no submodules)_- [queue](./src/queue/job-queue.md) — <!-- begin:auto-docs --> _(no submodules)_- [job-queue](./src/queue/job-queue.md) — <!-- begin:auto-docs --> _(no submodules)_- [registry](./src/registry/job-registry.md) — <!-- begin:auto-docs --> _(no submodules)_- [job-registry](./src/registry/job-registry.md) — <!-- begin:auto-docs --> _(no submodules)_- [cron-rescheduler](./src/scheduler/cron-rescheduler.md) — Houses the two cron-related decisions the scheduler makes after a job settles or resumes: (1) compute the next fire fro…
|
||||
|
||||
- [job-dispatcher](./src/scheduler/job-dispatcher.md) — Encapsulates the "hand a job to the executor" step of the scheduler's tick loop. The scheduler core decides _which_ job…
|
||||
- [job-scheduler](./src/scheduler/job-scheduler.md) — The `JobScheduler` class is the package's main entry point: it accepts both cron-scheduled and one-shot queued jobs, ru…
|
||||
- [job-snapshot](./src/scheduler/job-snapshot.md) — Projects a mutable `ScheduledJob` record (the scheduler's internal data structure) to an immutable `JobStatus` snapshot…
|
||||
|
||||
@@ -19,7 +19,9 @@ The plugin lives separately from the core distribution package so browser-only a
|
||||
- [instruments](./src/observability/instruments.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
|
||||
- [remote](./src/worker/remote/remote.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
|
||||
- [remote](./src/worker/remote/remote.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
|
||||
|
||||
<!-- end:auto-docs --> - [base](./src/communicator/base/base.md) — <!-- begin:auto-docs --> _(no submodules)_- [http](./src/communicator/http/http-communicator.md) — <!-- begin:auto-docs --> _(no submodules)_- [http-communicator](./src/communicator/http/http-communicator.md) — <!-- begin:auto-docs --> _(no submodules)_- [errors](./src/errors/queue-distribution-http-error.md) — <!-- begin:auto-docs --> _(no submodules)_- [queue-distribution-http-error](./src/errors/queue-distribution-http-error.md) — <!-- begin:auto-docs --> _(no submodules)_- [observability](./src/observability/instruments.md) — <!-- begin:auto-docs --> _(no submodules)_- [instruments](./src/observability/instruments.md) — <!-- begin:auto-docs --> _(no submodules)_- [remote](./src/worker/remote/remote.md) — <!-- begin:auto-docs --> _(no submodules)_- [remote](./src/worker/remote/remote.md) — <!-- begin:auto-docs --> _(no submodules)_- [base](./src/communicator/base/base.md) — <!-- begin:auto-docs --> _(no submodules)_- [http](./src/communicator/http/http-communicator.md) — <!-- begin:auto-docs --> _(no submodules)_- [http-communicator](./src/communicator/http/http-communicator.md) — <!-- begin:auto-docs --> _(no submodules)_- [errors](./src/errors/queue-distribution-http-error.md) — <!-- begin:auto-docs --> _(no submodules)_- [queue-distribution-http-error](./src/errors/queue-distribution-http-error.md) — <!-- begin:auto-docs --> _(no submodules)_- [observability](./src/observability/instruments.md) — <!-- begin:auto-docs --> _(no submodules)_- [instruments](./src/observability/instruments.md) — <!-- begin:auto-docs --> _(no submodules)_- [remote](./src/worker/remote/remote.md) — <!-- begin:auto-docs --> _(no submodules)_- [remote](./src/worker/remote/remote.md) — <!-- begin:auto-docs --> _(no submodules)_- [base](./src/communicator/base/base.md) — <!-- begin:auto-docs --> _(no submodules)_- [http](./src/communicator/http/http-communicator.md) — <!-- begin:auto-docs --> _(no submodules)_- [http-communicator](./src/communicator/http/http-communicator.md) — <!-- begin:auto-docs --> _(no submodules)_- [errors](./src/errors/queue-distribution-http-error.md) — <!-- begin:auto-docs --> _(no submodules)_- [queue-distribution-http-error](./src/errors/queue-distribution-http-error.md) — <!-- begin:auto-docs --> _(no submodules)_- [observability](./src/observability/instruments.md) — <!-- begin:auto-docs --> _(no submodules)_- [instruments](./src/observability/instruments.md) — <!-- begin:auto-docs --> _(no submodules)_- [remote](./src/worker/remote/remote.md) — <!-- begin:auto-docs --> _(no submodules)_- [remote](./src/worker/remote/remote.md) — <!-- begin:auto-docs --> _(no submodules)_- [base](./src/communicator/base/base.md) — <!-- begin:auto-docs --> _(no submodules)_- [http](./src/communicator/http/http-communicator.md) — <!-- begin:auto-docs --> _(no submodules)_ - [http-communicator](./src/communicator/http/http-communicator.md) — <!-- begin:auto-docs --> _(no submodules)_- [errors](./src/errors/queue-distribution-http-error.md) — <!-- begin:auto-docs --> _(no submodules)_ - [queue-distribution-http-error](./src/errors/queue-distribution-http-error.md) — <!-- begin:auto-docs --> _(no submodules)_- [observability](./src/observability/instruments.md) — <!-- begin:auto-docs --> _(no submodules)_ - [instruments](./src/observability/instruments.md) — <!-- begin:auto-docs --> _(no submodules)_- [remote](./src/worker/remote/remote.md) — <!-- begin:auto-docs --> _(no submodules)_ - [remote](./src/worker/remote/remote.md) — <!-- begin:auto-docs --> _(no submodules)_
|
||||
|
||||
## Install
|
||||
|
||||
```bash
|
||||
@@ -72,4 +74,3 @@ const result = await manager.assignTask(task, input);
|
||||
## Status
|
||||
|
||||
Part of the rebuild — version `0.1.0` while it stabilises.
|
||||
|
||||
|
||||
@@ -17,7 +17,9 @@ The plugin lives separately from the core distribution package so HTTP-only and
|
||||
- [instruments](./src/observability/instruments.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
|
||||
- [worker-thread](./src/worker/worker-thread/worker-thread-worker.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
|
||||
- [worker-thread-worker](./src/worker/worker-thread/worker-thread-worker.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
|
||||
|
||||
<!-- end:auto-docs --> - [worker-thread-communicator](./src/communicator/worker-thread/worker-thread-communicator.md) — <!-- begin:auto-docs --> _(no submodules)_- [errors](./src/errors/queue-distribution-worker-thread-error.md) — <!-- begin:auto-docs --> _(no submodules)_- [queue-distribution-worker-thread-error](./src/errors/queue-distribution-worker-thread-error.md) — <!-- begin:auto-docs --> _(no submodules)_- [observability](./src/observability/instruments.md) — <!-- begin:auto-docs --> _(no submodules)_- [instruments](./src/observability/instruments.md) — <!-- begin:auto-docs --> _(no submodules)_- [worker-thread](./src/worker/worker-thread/worker-thread-worker.md) — <!-- begin:auto-docs --> _(no submodules)_- [worker-thread-worker](./src/worker/worker-thread/worker-thread-worker.md) — <!-- begin:auto-docs --> _(no submodules)_- [worker-thread-communicator](./src/communicator/worker-thread/worker-thread-communicator.md) — <!-- begin:auto-docs --> _(no submodules)_- [errors](./src/errors/queue-distribution-worker-thread-error.md) — <!-- begin:auto-docs --> _(no submodules)_- [queue-distribution-worker-thread-error](./src/errors/queue-distribution-worker-thread-error.md) — <!-- begin:auto-docs --> _(no submodules)_- [observability](./src/observability/instruments.md) — <!-- begin:auto-docs --> _(no submodules)_- [instruments](./src/observability/instruments.md) — <!-- begin:auto-docs --> _(no submodules)_- [worker-thread](./src/worker/worker-thread/worker-thread-worker.md) — <!-- begin:auto-docs --> _(no submodules)_- [worker-thread-worker](./src/worker/worker-thread/worker-thread-worker.md) — <!-- begin:auto-docs --> _(no submodules)_- [worker-thread-communicator](./src/communicator/worker-thread/worker-thread-communicator.md) — <!-- begin:auto-docs --> _(no submodules)_- [errors](./src/errors/queue-distribution-worker-thread-error.md) — <!-- begin:auto-docs --> _(no submodules)_- [queue-distribution-worker-thread-error](./src/errors/queue-distribution-worker-thread-error.md) — <!-- begin:auto-docs --> _(no submodules)_- [observability](./src/observability/instruments.md) — <!-- begin:auto-docs --> _(no submodules)_- [instruments](./src/observability/instruments.md) — <!-- begin:auto-docs --> _(no submodules)_- [worker-thread](./src/worker/worker-thread/worker-thread-worker.md) — <!-- begin:auto-docs --> _(no submodules)_- [worker-thread-worker](./src/worker/worker-thread/worker-thread-worker.md) — <!-- begin:auto-docs --> _(no submodules)_- [worker-thread-communicator](./src/communicator/worker-thread/worker-thread-communicator.md) — <!-- begin:auto-docs --> _(no submodules)_- [errors](./src/errors/queue-distribution-worker-thread-error.md) — <!-- begin:auto-docs --> _(no submodules)_ - [queue-distribution-worker-thread-error](./src/errors/queue-distribution-worker-thread-error.md) — <!-- begin:auto-docs --> _(no submodules)_- [observability](./src/observability/instruments.md) — <!-- begin:auto-docs --> _(no submodules)_ - [instruments](./src/observability/instruments.md) — <!-- begin:auto-docs --> _(no submodules)_- [worker-thread](./src/worker/worker-thread/worker-thread-worker.md) — <!-- begin:auto-docs --> _(no submodules)_ - [worker-thread-worker](./src/worker/worker-thread/worker-thread-worker.md) — <!-- begin:auto-docs --> _(no submodules)_
|
||||
|
||||
## Install
|
||||
|
||||
```bash
|
||||
@@ -65,4 +67,3 @@ const result = await manager.assignTask(task, input);
|
||||
## Status
|
||||
|
||||
Part of the rebuild — version `0.1.0` while it stabilises.
|
||||
|
||||
|
||||
@@ -40,14 +40,16 @@ The package is decoupled from `@suaveplan/queue`'s `Job` model: distribution ope
|
||||
- [local](./src/worker/local/local.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
|
||||
- [local](./src/worker/local/local.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
|
||||
- [worker](./src/worker/worker.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->- abstract-metrics — `abstract-metrics.ts` carries the…
|
||||
|
||||
<!-- end:auto-docs --> - [queue-distribution-error](./src/errors/queue-distribution-error.md) — <!-- begin:auto-docs --> _(no submodules)_- [health-checker](./src/health-checker/health-checker.md) — <!-- begin:auto-docs --> _(no submodules)_- [health-checker](./src/health-checker/health-checker.md) — <!-- begin:auto-docs --> _(no submodules)_- [internal](./src/internal/noop-logger.md) — <!-- begin:auto-docs --> _(no submodules)_- [noop-logger](./src/internal/noop-logger.md) — <!-- begin:auto-docs --> _(no submodules)_- [base](./src/load-balancing/base/base.md) — <!-- begin:auto-docs --> _(no submodules)_- [base](./src/load-balancing/base/base.md) — <!-- begin:auto-docs --> _(no submodules)_- [least-busy](./src/load-balancing/least-busy/least-busy.md) — <!-- begin:auto-docs --> _(no submodules)_- [least-busy](./src/load-balancing/least-busy/least-busy.md) — <!-- begin:auto-docs --> _(no submodules)_- [round-robin](./src/load-balancing/round-robin/round-robin.md) — <!-- begin:auto-docs --> _(no submodules)_- [round-robin](./src/load-balancing/round-robin/round-robin.md) — <!-- begin:auto-docs --> _(no submodules)_- [weighted](./src/load-balancing/weighted/weighted.md) — <!-- begin:auto-docs --> _(no submodules)_- [weighted](./src/load-balancing/weighted/weighted.md) — <!-- begin:auto-docs --> _(no submodules)_- [manager](./src/manager/manager.md) — <!-- begin:auto-docs --> _(no submodules)_- [manager-dispatch](./src/manager/manager-dispatch.md) — `dispatchWithRetry` is the core retry loop of `WorkerManager`. It picks a worker via the configured `LoadBalancingStrat…
|
||||
- [manager-supervisor](./src/manager/manager-supervisor.md) — The supervisor module owns the worker-health side of `WorkerManager`: periodic health checks across the pool, restart o…
|
||||
- [manager](./src/manager/manager.md) — <!-- begin:auto-docs --> _(no submodules)_- [observability](./src/observability/instruments.md) — <!-- begin:auto-docs --> _(no submodules)_- [instruments](./src/observability/instruments.md) — <!-- begin:auto-docs --> _(no submodules)_- [pool](./src/pool/pool.md) — <!-- begin:auto-docs --> _(no submodules)_- [pool](./src/pool/pool.md) — <!-- begin:auto-docs --> _(no submodules)_- [task](./src/task/task.md) — <!-- begin:auto-docs --> _(no submodules)_- [task](./src/task/task.md) — <!-- begin:auto-docs --> _(no submodules)_- [worker](./src/worker/worker.md) — <!-- begin:auto-docs --> _(no submodules)_- [abstract](./src/worker/abstract/abstract.md) — <!-- begin:auto-docs --> _(no submodules)_- [abstract-metrics](./src/worker/abstract/abstract-metrics.md) — `abstract-metrics.ts` carries the pure functions and the small accumulator used by `AbstractWorker` to track success/fa…
|
||||
- [abstract](./src/worker/abstract/abstract.md) — <!-- begin:auto-docs --> _(no submodules)_- [local](./src/worker/local/local.md) — <!-- begin:auto-docs --> _(no submodules)_- [local](./src/worker/local/local.md) — <!-- begin:auto-docs --> _(no submodules)_- [worker](./src/worker/worker.md) — <!-- begin:auto-docs --> _(no submodules)_- [queue-distribution-error](./src/errors/queue-distribution-error.md) — <!-- begin:auto-docs --> _(no submodules)_- [health-checker](./src/health-checker/health-checker.md) — <!-- begin:auto-docs --> _(no submodules)_- [health-checker](./src/health-checker/health-checker.md) — <!-- begin:auto-docs --> _(no submodules)_- [internal](./src/internal/noop-logger.md) — <!-- begin:auto-docs --> _(no submodules)_- [noop-logger](./src/internal/noop-logger.md) — <!-- begin:auto-docs --> _(no submodules)_- [base](./src/load-balancing/base/base.md) — <!-- begin:auto-docs --> _(no submodules)_- [base](./src/load-balancing/base/base.md) — <!-- begin:auto-docs --> _(no submodules)_- [least-busy](./src/load-balancing/least-busy/least-busy.md) — <!-- begin:auto-docs --> _(no submodules)_- [least-busy](./src/load-balancing/least-busy/least-busy.md) — <!-- begin:auto-docs --> _(no submodules)_- [round-robin](./src/load-balancing/round-robin/round-robin.md) — <!-- begin:auto-docs --> _(no submodules)_- [round-robin](./src/load-balancing/round-robin/round-robin.md) — <!-- begin:auto-docs --> _(no submodules)_- [weighted](./src/load-balancing/weighted/weighted.md) — <!-- begin:auto-docs --> _(no submodules)_- [weighted](./src/load-balancing/weighted/weighted.md) — <!-- begin:auto-docs --> _(no submodules)_- [manager](./src/manager/manager.md) — <!-- begin:auto-docs --> _(no submodules)_- [manager-dispatch](./src/manager/manager-dispatch.md) — `dispatchWithRetry` is the core retry loop of `WorkerManager`. It picks a worker via the configured `LoadBalancingStrat…
|
||||
- [manager-supervisor](./src/manager/manager-supervisor.md) — The supervisor module owns the worker-health side of `WorkerManager`: periodic health checks across the pool, restart o…
|
||||
- [manager](./src/manager/manager.md) — <!-- begin:auto-docs --> _(no submodules)_- [observability](./src/observability/instruments.md) — <!-- begin:auto-docs --> _(no submodules)_- [instruments](./src/observability/instruments.md) — <!-- begin:auto-docs --> _(no submodules)_- [pool](./src/pool/pool.md) — <!-- begin:auto-docs --> _(no submodules)_- [pool](./src/pool/pool.md) — <!-- begin:auto-docs --> _(no submodules)_- [task](./src/task/task.md) — <!-- begin:auto-docs --> _(no submodules)_- [task](./src/task/task.md) — <!-- begin:auto-docs --> _(no submodules)_- [worker](./src/worker/worker.md) — <!-- begin:auto-docs --> _(no submodules)_- abstract-metrics — `abstract…
|
||||
- [abstract](./src/worker/abstract/abstract.md) — <!-- begin:auto-docs --> _(no submodules)_- [abstract-metrics](./src/worker/abstract/abstract-metrics.md) — `abstract-metrics.ts` carries the pure functions and the small accumulator used by `AbstractWorker` to track success/fa…
|
||||
- [abstract](./src/worker/abstract/abstract.md) — <!-- begin:auto-docs --> _(no submodules)_- [local](./src/worker/local/local.md) — <!-- begin:auto-docs --> _(no submodules)_- [local](./src/worker/local/local.md) — <!-- begin:auto-docs --> _(no submodules)_- [worker](./src/worker/worker.md) — <!-- begin:auto-docs --> _(no submodules)_- abstract-metrics — `abstract…
|
||||
|
||||
- [manager-supervisor](./src/manager/manager-supervisor.md) — The supervisor module owns the worker-health side of `WorkerManager`: periodic health checks across the pool, restart o…
|
||||
- [manager](./src/manager/manager.md) — <!-- begin:auto-docs --> _(no submodules)_- [observability](./src/observability/instruments.md) — <!-- begin:auto-docs --> _(no submodules)_- [instruments](./src/observability/instruments.md) — <!-- begin:auto-docs --> _(no submodules)_- [pool](./src/pool/pool.md) — <!-- begin:auto-docs --> _(no submodules)_- [pool](./src/pool/pool.md) — <!-- begin:auto-docs --> _(no submodules)_- [task](./src/task/task.md) — <!-- begin:auto-docs --> _(no submodules)_- [task](./src/task/task.md) — <!-- begin:auto-docs --> _(no submodules)_- [worker](./src/worker/worker.md) — <!-- begin:auto-docs --> _(no submodules)_- [abstract](./src/worker/abstract/abstract.md) — <!-- begin:auto-docs --> _(no submodules)_- [abstract-metrics](./src/worker/abstract/abstract-metrics.md) — `abstract-metrics.ts` carries the pure functions and the small accumulator used by `AbstractWorker` to track success/fa…
|
||||
- [abstract](./src/worker/abstract/abstract.md) — <!-- begin:auto-docs --> _(no submodules)_- [local](./src/worker/local/local.md) — <!-- begin:auto-docs --> _(no submodules)_- [local](./src/worker/local/local.md) — <!-- begin:auto-docs --> _(no submodules)_- [worker](./src/worker/worker.md) — <!-- begin:auto-docs --> _(no submodules)_- [queue-distribution-error](./src/errors/queue-distribution-error.md) — <!-- begin:auto-docs --> _(no submodules)_- [health-checker](./src/health-checker/health-checker.md) — <!-- begin:auto-docs --> _(no submodules)_- [health-checker](./src/health-checker/health-checker.md) — <!-- begin:auto-docs --> _(no submodules)_- [internal](./src/internal/noop-logger.md) — <!-- begin:auto-docs --> _(no submodules)_- [noop-logger](./src/internal/noop-logger.md) — <!-- begin:auto-docs --> _(no submodules)_- [base](./src/load-balancing/base/base.md) — <!-- begin:auto-docs --> _(no submodules)_- [base](./src/load-balancing/base/base.md) — <!-- begin:auto-docs --> _(no submodules)_- [least-busy](./src/load-balancing/least-busy/least-busy.md) — <!-- begin:auto-docs --> _(no submodules)_- [least-busy](./src/load-balancing/least-busy/least-busy.md) — <!-- begin:auto-docs --> _(no submodules)_- [round-robin](./src/load-balancing/round-robin/round-robin.md) — <!-- begin:auto-docs --> _(no submodules)_- [round-robin](./src/load-balancing/round-robin/round-robin.md) — <!-- begin:auto-docs --> _(no submodules)_- [weighted](./src/load-balancing/weighted/weighted.md) — <!-- begin:auto-docs --> _(no submodules)_- [weighted](./src/load-balancing/weighted/weighted.md) — <!-- begin:auto-docs --> _(no submodules)_- [manager](./src/manager/manager.md) — <!-- begin:auto-docs --> _(no submodules)_- [manager-dispatch](./src/manager/manager-dispatch.md) — `dispatchWithRetry` is the core retry loop of `WorkerManager`. It picks a worker via the configured `LoadBalancingStrat…
|
||||
- [manager-supervisor](./src/manager/manager-supervisor.md) — The supervisor module owns the worker-health side of `WorkerManager`: periodic health checks across the pool, restart o…
|
||||
- [manager](./src/manager/manager.md) — <!-- begin:auto-docs --> _(no submodules)_- [observability](./src/observability/instruments.md) — <!-- begin:auto-docs --> _(no submodules)_- [instruments](./src/observability/instruments.md) — <!-- begin:auto-docs --> _(no submodules)_- [pool](./src/pool/pool.md) — <!-- begin:auto-docs --> _(no submodules)_- [pool](./src/pool/pool.md) — <!-- begin:auto-docs --> _(no submodules)_- [task](./src/task/task.md) — <!-- begin:auto-docs --> _(no submodules)_- [task](./src/task/task.md) — <!-- begin:auto-docs --> _(no submodules)_- [worker](./src/worker/worker.md) — <!-- begin:auto-docs --> _(no submodules)_- abstract-metrics — `abstract…
|
||||
- [abstract](./src/worker/abstract/abstract.md) — <!-- begin:auto-docs --> _(no submodules)_- [abstract-metrics](./src/worker/abstract/abstract-metrics.md) — `abstract-metrics.ts` carries the pure functions and the small accumulator used by `AbstractWorker` to track success/fa…
|
||||
- [abstract](./src/worker/abstract/abstract.md) — <!-- begin:auto-docs --> _(no submodules)_- [local](./src/worker/local/local.md) — <!-- begin:auto-docs --> _(no submodules)_- [local](./src/worker/local/local.md) — <!-- begin:auto-docs --> _(no submodules)_- [worker](./src/worker/worker.md) — <!-- begin:auto-docs --> _(no submodules)_- abstract-metrics — `abstract…
|
||||
- [queue-distribution-error](./src/errors/queue-distribution-error.md) — <!-- begin:auto-docs --> _(no submodules)_- [health-checker](./src/health-checker/health-checker.md) — <!-- begin:auto-docs --> _(no submodules)_- [health-checker](./src/health-checker/health-checker.md) — <!-- begin:auto-docs --> _(no submodules)_- [internal](./src/internal/noop-logger.md) — <!-- begin:auto-docs --> _(no submodules)_- [noop-logger](./src/internal/noop-logger.md) — <!-- begin:auto-docs --> _(no submodules)_- [base](./src/load-balancing/base/base.md) — <!-- begin:auto-docs --> _(no submodules)_- [base](./src/load-balancing/base/base.md) — <!-- begin:auto-docs --> _(no submodules)_- [least-busy](./src/load-balancing/least-busy/least-busy.md) — <!-- begin:auto-docs --> _(no submodules)_- [least-busy](./src/load-balancing/least-busy/least-busy.md) — <!-- begin:auto-docs --> _(no submodules)_- [round-robin](./src/load-balancing/round-robin/round-robin.md) — <!-- begin:auto-docs --> _(no submodules)_- [round-robin](./src/load-balancing/round-robin/round-robin.md) — <!-- begin:auto-docs --> _(no submodules)_- [weighted](./src/load-balancing/weighted/weighted.md) — <!-- begin:auto-docs --> _(no submodules)_- [weighted](./src/load-balancing/weighted/weighted.md) — <!-- begin:auto-docs --> _(no submodules)_- [manager](./src/manager/manager.md) — <!-- begin:auto-docs --> _(no submodules)_- [manager-dispatch](./src/manager/manager-dispatch.md) — `dispatchWithRetry` is the core retry loop of `WorkerManager`. It picks a worker via the configured `LoadBalancingStrat…
|
||||
- [manager-supervisor](./src/manager/manager-supervisor.md) — The supervisor module owns the worker-health side of `WorkerManager`: periodic health checks across the pool, restart o…
|
||||
- [manager](./src/manager/manager.md) — <!-- begin:auto-docs --> _(no submodules)_- [observability](./src/observability/instruments.md) — <!-- begin:auto-docs --> _(no submodules)_- [instruments](./src/observability/instruments.md) — <!-- begin:auto-docs --> _(no submodules)_- [pool](./src/pool/pool.md) — <!-- begin:auto-docs --> _(no submodules)_- [pool](./src/pool/pool.md) — <!-- begin:auto-docs --> _(no submodules)_- [task](./src/task/task.md) — <!-- begin:auto-docs --> _(no submodules)_- [task](./src/task/task.md) — <!-- begin:auto-docs --> _(no submodules)_- [worker](./src/worker/worker.md) — <!-- begin:auto-docs --> _(no submodules)_- abstract-metrics — `abstract-metrics.ts` carries th…
|
||||
@@ -57,6 +59,7 @@ The package is decoupled from `@suaveplan/queue`'s `Job` model: distribution ope
|
||||
- [manager-supervisor](./src/manager/manager-supervisor.md) — The supervisor module owns the worker-health side of `WorkerManager`: periodic health checks across the pool, restart o…
|
||||
- [manager](./src/manager/manager.md) — <!-- begin:auto-docs --> _(no submodules)_- [observability](./src/observability/instruments.md) — <!-- begin:auto-docs --> _(no submodules)_ - [instruments](./src/observability/instruments.md) — <!-- begin:auto-docs --> _(no submodules)_- [pool](./src/pool/pool.md) — <!-- begin:auto-docs --> _(no submodules)_ - [pool](./src/pool/pool.md) — <!-- begin:auto-docs --> _(no submodules)_- [task](./src/task/task.md) — <!-- begin:auto-docs --> _(no submodules)_ - [task](./src/task/task.md) — <!-- begin:auto-docs --> _(no submodules)_- [worker](./src/worker/worker.md) — <!-- begin:auto-docs --> _(no submodules)_ - [abstract](./src/worker/abstract/abstract.md) — <!-- begin:auto-docs --> _(no submodules)_ - [abstract-metrics](./src/worker/abstract/abstract-metrics.md) — `abstract-metrics.ts` carries the pure functions and the small accumulator used by `AbstractWorker` to track success/fa…
|
||||
- [abstract](./src/worker/abstract/abstract.md) — <!-- begin:auto-docs --> _(no submodules)_ - [local](./src/worker/local/local.md) — <!-- begin:auto-docs --> _(no submodules)_ - [local](./src/worker/local/local.md) — <!-- begin:auto-docs --> _(no submodules)_ - [worker](./src/worker/worker.md) — <!-- begin:auto-docs --> _(no submodules)_
|
||||
|
||||
## Install
|
||||
|
||||
```bash
|
||||
@@ -126,4 +129,3 @@ console.log(await manager.assignTask(task, 21)); // 42
|
||||
## Status
|
||||
|
||||
This package is part of the rebuild — version `0.1.0` while it stabilises. Graduation to `1.0.0` follows the criteria in [`CLAUDE.md`](../../../CLAUDE.md).
|
||||
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
- [abstract-metrics](./abstract/abstract-metrics.md) — `abstract-metrics.ts` carries the pure functions and the small accumulator used by `AbstractWorker` to track success/fa…
|
||||
- [abstract](./abstract/abstract.md) — <!-- begin:auto-docs --> _(no submodules)_- [local](./local/local.md) — <!-- begin:auto-docs --> _(no submodules)_ - [local](./local/local.md) — <!-- begin:auto-docs --> _(no submodules)_- [worker](./worker.md) — <!-- begin:auto-docs --> _(no submodules)_- abstract-metrics — `abstract-metrics.ts` carries the…
|
||||
- [abstract-metrics](./abstract/abstract-metrics.md) — `abstract-metrics.ts` carries the pure functions and the small accumulator used by `AbstractWorker` to track success/fa…
|
||||
- [abstract](./abstract/abstract.md) — <!-- begin:auto-docs --> _(no submodules)_- [local](./local/local.md) — <!-- begin:auto-docs --> _(no submodules)_ - [local](./local/local.md) — <!-- begin:auto-docs --> _(no submodules)_- [worker](./worker.md) — <!-- begin:auto-docs --> _(no submodules)_- abstract-metrics — `abstract-metrics.ts` carries the…
|
||||
- [abstract-metrics](./abstract/abstract-metrics.md) — `abstract-metrics.ts` carries the pure functions and the small accumulator used by `AbstractWorker` to track success/fa…
|
||||
- [abstract](./abstract/abstract.md) — <!-- begin:auto-docs --> _(no submodules)_- [local](./local/local.md) — <!-- begin:auto-docs --> _(no submodules)_- [local](./local/local.md) — <!-- begin:auto-docs --> _(no submodules)_- [worker](./worker.md) — <!-- begin:auto-docs --> _(no submodules)_- [abstract-metrics](./abstract/abstract-metrics.md) — `abstract-metrics.ts` carries the pure functions and the small accumulator used by `AbstractWorker` to track success/fa…
|
||||
- [abstract](./abstract/abstract.md) — <!-- begin:auto-docs --> _(no submodules)_- [local](./local/local.md) — <!-- begin:auto-docs --> _(no submodules)_- [local](./local/local.md) — <!-- begin:auto-docs --> _(no submodules)_- [worker](./worker.md) — <!-- begin:auto-docs --> _(no submodules)_- abstract-metrics — `abstract…
|
||||
@@ -12,6 +12,7 @@ _(no submodules)_
|
||||
- [abstract](./abstract/abstract.md) — <!-- begin:auto-docs --> _(no submodules)_- [local](./local/local.md) — <!-- begin:auto-docs --> _(no submodules)_- [local](./local/local.md) — <!-- begin:auto-docs --> _(no submodules)_- [worker](./worker.md) — <!-- begin:auto-docs --> _(no submodules)_- abstract-metrics — `abstract-metrics.ts` carries th…
|
||||
- [abstract-metrics](./abstract/abstract-metrics.md) — `abstract-metrics.ts` carries the pure functions and the small accumulator used by `AbstractWorker` to track success/fa…
|
||||
- [abstract](./abstract/abstract.md) — <!-- begin:auto-docs --> _(no submodules)_- [local](./local/local.md) — <!-- begin:auto-docs --> _(no submodules)_ - [local](./local/local.md) — <!-- begin:auto-docs --> _(no submodules)_- [worker](./worker.md) — <!-- begin:auto-docs --> _(no submodules)_
|
||||
|
||||
## Purpose
|
||||
|
||||
`Worker` is the structural interface every distribution-tier executor implements. It describes lifecycle (`start`, `stop`), task execution (`executeTask`), health probing (`getHealth`, `canAcceptTask`), and introspection (`getInfo`). Concrete workers (`LocalWorker`, `RemoteWorker` from the HTTP plugin, `WorkerThreadWorker` from the worker-thread plugin) extend `AbstractWorker`, but downstream code should program against `Worker` so transport choices stay swappable.
|
||||
|
||||
@@ -18,7 +18,9 @@ Enterprise-grade queue management: memory/priority/delayed queues, concurrent wo
|
||||
- [queue-types](./src/types/queue-types.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
|
||||
- [worker](./src/worker/queue-worker.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
|
||||
- [queue-worker](./src/worker/queue-worker.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
|
||||
|
||||
<!-- end:auto-docs --> - [queue-error](./src/errors/queue-error.md) — <!-- begin:auto-docs --> _(no submodules)_- [job](./src/job/job.md) — <!-- begin:auto-docs --> _(no submodules)_- [job](./src/job/job.md) — <!-- begin:auto-docs --> _(no submodules)_- [delayed-queue](./src/queue/delayed-queue.md) — `DelayedQueue<T>` is an explicit alias for a delay-aware FIFO queue. It extends `MemoryQueue<T>` to make the delay inte…
|
||||
|
||||
- [memory-queue](./src/queue/memory-queue.md) — `MemoryQueue<T>` is the foundational FIFO queue for `@suaveplan/queue`. It stores jobs in memory in insertion order and…
|
||||
- [priority-queue](./src/queue/priority-queue.md) — `PriorityQueue<T>` extends `MemoryQueue<T>` to provide priority-ordered job scheduling. Jobs with higher `priority` val…
|
||||
- [types](./src/types/queue-types.md) — <!-- begin:auto-docs --> _(no submodules)_- [queue-types](./src/types/queue-types.md) — <!-- begin:auto-docs --> _(no submodules)_- [worker](./src/worker/queue-worker.md) — <!-- begin:auto-docs --> _(no submodules)_- [queue-worker](./src/worker/queue-worker.md) — <!-- begin:auto-docs --> _(no submodules)_- [queue-error](./src/errors/queue-error.md) — <!-- begin:auto-docs --> _(no submodules)_- [job](./src/job/job.md) — <!-- begin:auto-docs --> _(no submodules)_- [job](./src/job/job.md) — <!-- begin:auto-docs --> _(no submodules)_- [delayed-queue](./src/queue/delayed-queue.md) — `DelayedQueue<T>` is an explicit alias for a delay-aware FIFO queue. It extends `MemoryQueue<T>` to make the delay inte…
|
||||
@@ -31,6 +33,7 @@ Enterprise-grade queue management: memory/priority/delayed queues, concurrent wo
|
||||
- [memory-queue](./src/queue/memory-queue.md) — `MemoryQueue<T>` is the foundational FIFO queue for `@suaveplan/queue`. It stores jobs in memory in insertion order and…
|
||||
- [priority-queue](./src/queue/priority-queue.md) — `PriorityQueue<T>` extends `MemoryQueue<T>` to provide priority-ordered job scheduling. Jobs with higher `priority` val…
|
||||
- [types](./src/types/queue-types.md) — <!-- begin:auto-docs --> _(no submodules)_ - [queue-types](./src/types/queue-types.md) — <!-- begin:auto-docs --> _(no submodules)_- [worker](./src/worker/queue-worker.md) — <!-- begin:auto-docs --> _(no submodules)_ - [queue-worker](./src/worker/queue-worker.md) — <!-- begin:auto-docs --> _(no submodules)_
|
||||
|
||||
## Packages
|
||||
|
||||
| Module | Description |
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -21,7 +21,9 @@ Retry strategies and circuit-breaker for async operations. The package provides
|
||||
- [exponential](./src/strategies/exponential/exponential.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
|
||||
- [linear](./src/strategies/linear/linear.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
|
||||
- [linear](./src/strategies/linear/linear.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
|
||||
|
||||
<!-- end:auto-docs --> - [circuit-breaker](./src/circuit-breaker/circuit-breaker.md) — <!-- begin:auto-docs --> _(no submodules)_- [errors](./src/errors/retry-error.md) — <!-- begin:auto-docs --> _(no submodules)_- [retry-error](./src/errors/retry-error.md) — <!-- begin:auto-docs --> _(no submodules)_- [otel](./src/otel/retry-otel.md) — <!-- begin:auto-docs --> _(no submodules)_- [retry-otel](./src/otel/retry-otel.md) — <!-- begin:auto-docs --> _(no submodules)_- [retry](./src/retry/retry.md) — <!-- begin:auto-docs --> _(no submodules)_- [retry](./src/retry/retry.md) — <!-- begin:auto-docs --> _(no submodules)_- [constant](./src/strategies/constant/constant.md) — <!-- begin:auto-docs --> _(no submodules)_- [constant](./src/strategies/constant/constant.md) — <!-- begin:auto-docs --> _(no submodules)_- [exponential](./src/strategies/exponential/exponential.md) — <!-- begin:auto-docs --> _(no submodules)_- [exponential](./src/strategies/exponential/exponential.md) — <!-- begin:auto-docs --> _(no submodules)_- [linear](./src/strategies/linear/linear.md) — <!-- begin:auto-docs --> _(no submodules)_- [linear](./src/strategies/linear/linear.md) — <!-- begin:auto-docs --> _(no submodules)_- [circuit-breaker](./src/circuit-breaker/circuit-breaker.md) — <!-- begin:auto-docs --> _(no submodules)_- [errors](./src/errors/retry-error.md) — <!-- begin:auto-docs --> _(no submodules)_- [retry-error](./src/errors/retry-error.md) — <!-- begin:auto-docs --> _(no submodules)_- [otel](./src/otel/retry-otel.md) — <!-- begin:auto-docs --> _(no submodules)_- [retry-otel](./src/otel/retry-otel.md) — <!-- begin:auto-docs --> _(no submodules)_- [retry](./src/retry/retry.md) — <!-- begin:auto-docs --> _(no submodules)_- [retry](./src/retry/retry.md) — <!-- begin:auto-docs --> _(no submodules)_- [constant](./src/strategies/constant/constant.md) — <!-- begin:auto-docs --> _(no submodules)_- [constant](./src/strategies/constant/constant.md) — <!-- begin:auto-docs --> _(no submodules)_- [exponential](./src/strategies/exponential/exponential.md) — <!-- begin:auto-docs --> _(no submodules)_- [exponential](./src/strategies/exponential/exponential.md) — <!-- begin:auto-docs --> _(no submodules)_- [linear](./src/strategies/linear/linear.md) — <!-- begin:auto-docs --> _(no submodules)_- [linear](./src/strategies/linear/linear.md) — <!-- begin:auto-docs --> _(no submodules)_- [circuit-breaker](./src/circuit-breaker/circuit-breaker.md) — <!-- begin:auto-docs --> _(no submodules)_- [errors](./src/errors/retry-error.md) — <!-- begin:auto-docs --> _(no submodules)_- [retry-error](./src/errors/retry-error.md) — <!-- begin:auto-docs --> _(no submodules)_- [otel](./src/otel/retry-otel.md) — <!-- begin:auto-docs --> _(no submodules)_- [retry-otel](./src/otel/retry-otel.md) — <!-- begin:auto-docs --> _(no submodules)_- [retry](./src/retry/retry.md) — <!-- begin:auto-docs --> _(no submodules)_- [retry](./src/retry/retry.md) — <!-- begin:auto-docs --> _(no submodules)_- [constant](./src/strategies/constant/constant.md) — <!-- begin:auto-docs --> _(no submodules)_- [constant](./src/strategies/constant/constant.md) — <!-- begin:auto-docs --> _(no submodules)_- [exponential](./src/strategies/exponential/exponential.md) — <!-- begin:auto-docs --> _(no submodules)_- [exponential](./src/strategies/exponential/exponential.md) — <!-- begin:auto-docs --> _(no submodules)_- [linear](./src/strategies/linear/linear.md) — <!-- begin:auto-docs --> _(no submodules)_- [linear](./src/strategies/linear/linear.md) — <!-- begin:auto-docs --> _(no submodules)_- [circuit-breaker](./src/circuit-breaker/circuit-breaker.md) — <!-- begin:auto-docs --> _(no submodules)_- [errors](./src/errors/retry-error.md) — <!-- begin:auto-docs --> _(no submodules)_ - [retry-error](./src/errors/retry-error.md) — <!-- begin:auto-docs --> _(no submodules)_- [otel](./src/otel/retry-otel.md) — <!-- begin:auto-docs --> _(no submodules)_ - [retry-otel](./src/otel/retry-otel.md) — <!-- begin:auto-docs --> _(no submodules)_- [retry](./src/retry/retry.md) — <!-- begin:auto-docs --> _(no submodules)_ - [retry](./src/retry/retry.md) — <!-- begin:auto-docs --> _(no submodules)_- [constant](./src/strategies/constant/constant.md) — <!-- begin:auto-docs --> _(no submodules)_ - [constant](./src/strategies/constant/constant.md) — <!-- begin:auto-docs --> _(no submodules)_- [exponential](./src/strategies/exponential/exponential.md) — <!-- begin:auto-docs --> _(no submodules)_ - [exponential](./src/strategies/exponential/exponential.md) — <!-- begin:auto-docs --> _(no submodules)_- [linear](./src/strategies/linear/linear.md) — <!-- begin:auto-docs --> _(no submodules)_ - [linear](./src/strategies/linear/linear.md) — <!-- begin:auto-docs --> _(no submodules)_
|
||||
|
||||
## Install
|
||||
|
||||
```sh
|
||||
@@ -75,4 +77,3 @@ const value = await breaker.call(async (signal) => {
|
||||
| `circuit-breaker` — `CircuitBreaker` class, `CircuitBreakerConfig`, event map | [`src/circuit-breaker/circuit-breaker.md`](src/circuit-breaker/circuit-breaker.md) |
|
||||
| `retry-error` — `RetryExhaustedError`, `RetryAbortedError`, `AttemptTimeoutError`, `CircuitOpenError` | [`src/errors/retry-error.md`](src/errors/retry-error.md) |
|
||||
| `retry-otel` — OpenTelemetry span/event helpers | [`src/otel/retry-otel.md`](src/otel/retry-otel.md) |
|
||||
|
||||
|
||||
@@ -11,7 +11,9 @@ Hono middleware wrapping [`@suaveplan/rules`](../rules/README.md). Six middlewar
|
||||
- [default](./src/engine/default.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
|
||||
- [middleware](./src/middleware/middleware.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
|
||||
- [middleware](./src/middleware/middleware.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
|
||||
|
||||
<!-- end:auto-docs --> - [default](./src/engine/default.md) — <!-- begin:auto-docs --> _(no submodules)_- [middleware](./src/middleware/middleware.md) — <!-- begin:auto-docs --> _(no submodules)_- [middleware](./src/middleware/middleware.md) — <!-- begin:auto-docs --> _(no submodules)_- [default](./src/engine/default.md) — <!-- begin:auto-docs --> _(no submodules)_- [middleware](./src/middleware/middleware.md) — <!-- begin:auto-docs --> _(no submodules)_- [middleware](./src/middleware/middleware.md) — <!-- begin:auto-docs --> _(no submodules)_- [default](./src/engine/default.md) — <!-- begin:auto-docs --> _(no submodules)_- [middleware](./src/middleware/middleware.md) — <!-- begin:auto-docs --> _(no submodules)_- [middleware](./src/middleware/middleware.md) — <!-- begin:auto-docs --> _(no submodules)_- [default](./src/engine/default.md) — <!-- begin:auto-docs --> _(no submodules)_- [middleware](./src/middleware/middleware.md) — <!-- begin:auto-docs --> _(no submodules)_ - [middleware](./src/middleware/middleware.md) — <!-- begin:auto-docs --> _(no submodules)_
|
||||
|
||||
## Install
|
||||
|
||||
```bash
|
||||
@@ -38,4 +40,3 @@ app.get("/", (c) => c.json({ ok: true }));
|
||||
## See also
|
||||
|
||||
- [`@suaveplan/rules`](../rules/README.md) — the underlying engine, builder, operators, validators, serializer, optimizer.
|
||||
|
||||
|
||||
@@ -35,9 +35,11 @@ Universal rule engine + validation system. Comprehensive `RuleEngine` with async
|
||||
- [schema](./src/validators/schema/schema.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
|
||||
- [schema](./src/validators/schema/schema.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
|
||||
- [constants](./src/constants.md) — This module exposes the package-level constants consumed across the rules engine, validators, action executor, and even…
|
||||
|
||||
<!-- end:auto-docs --> - [actions](./src/actions/actions.md) — <!-- begin:auto-docs --> _(no submodules)_- [builder](./src/builder/builder.md) — <!-- begin:auto-docs --> _(no submodules)_- [builder](./src/builder/builder.md) — <!-- begin:auto-docs --> _(no submodules)_- [context](./src/context/context.md) — <!-- begin:auto-docs --> _(no submodules)_- [context](./src/context/context.md) — <!-- begin:auto-docs --> _(no submodules)_- [engine](./src/engine/engine.md) — <!-- begin:auto-docs --> _(no submodules)_- operators-internal — `compareValues(actual, operato…
|
||||
- [engine](./src/engine/engine.md) — <!-- begin:auto-docs --> _(no submodules)_- operators-internal — `compareValues(actual, operato…
|
||||
- [operators-internal](./src/engine/operators-internal.md) — `compareValues(actual, operator, expected)` is the built-in comparison dispatcher used by `RuleEngine` until the dedica…
|
||||
|
||||
- [engine](./src/engine/engine.md) — <!-- begin:auto-docs --> _(no submodules)_- operators-internal — `compareValues(actual, operato…
|
||||
- [operators-internal](./src/engine/operators-internal.md) — `compareValues(actual, operator, expected)` is the built-in comparison dispatcher used by `RuleEngine` until the dedica…
|
||||
- [errors](./src/errors/rules-error.md) — <!-- begin:auto-docs --> _(no submodules)_- [rules-error](./src/errors/rules-error.md) — <!-- begin:auto-docs --> _(no submodules)_- [events](./src/events/event-bus.md) — <!-- begin:auto-docs --> _(no submodules)_- [event-bus](./src/events/event-bus.md) — <!-- begin:auto-docs --> _(no submodules)_- [operators](./src/operators/operators.md) — <!-- begin:auto-docs --> _(no submodules)_- [operators](./src/operators/operators.md) — <!-- begin:auto-docs --> _(no submodules)_- [optimizer](./src/optimizer/optimizer.md) — <!-- begin:auto-docs --> _(no submodules)_- [optimizer](./src/optimizer/optimizer.md) — <!-- begin:auto-docs --> _(no submodules)_- [otel](./src/otel/otel.md) — <!-- begin:auto-docs --> _(no submodules)_- [otel](./src/otel/otel.md) — <!-- begin:auto-docs --> _(no submodules)_- [serializer](./src/serializer/serializer.md) — <!-- begin:auto-docs --> _(no submodules)_- [serializer](./src/serializer/serializer.md) — <!-- begin:auto-docs --> _(no submodules)_- [custom](./src/validators/custom/custom.md) — <!-- begin:auto-docs --> _(no submodules)_- [custom](./src/validators/custom/custom.md) — <!-- begin:auto-docs --> _(no submodules)_- [field](./src/validators/field/field.md) — <!-- begin:auto-docs --> _(no submodules)_- [field](./src/validators/field/field.md) — <!-- begin:auto-docs --> _(no submodules)_- [schema](./src/validators/schema/schema.md) — <!-- begin:auto-docs --> _(no submodules)_- [schema](./src/validators/schema/schema.md) — <!-- begin:auto-docs --> _(no submodules)_- [constants](./src/constants.md) — This module exposes the package-level constants consumed across the rules engine, validators, action executor, and even…
|
||||
- [actions](./src/actions/actions.md) — <!-- begin:auto-docs --> _(no submodules)_- [builder](./src/builder/builder.md) — <!-- begin:auto-docs --> _(no submodules)_- [builder](./src/builder/builder.md) — <!-- begin:auto-docs --> _(no submodules)_- [context](./src/context/context.md) — <!-- begin:auto-docs --> _(no submodules)_- [context](./src/context/context.md) — <!-- begin:auto-docs --> _(no submodules)_- [engine](./src/engine/engine.md) — <!-- begin:auto-docs --> _(no submodules)_- operators-internal — `compareValues(actual, operato…
|
||||
- [engine](./src/engine/engine.md) — <!-- begin:auto-docs --> _(no submodules)_- operators-internal — `compareValues(actual, operato…
|
||||
@@ -108,4 +110,3 @@ const result = await engine.evaluate(policy, context({ age: 21, region: "US" }))
|
||||
## See also
|
||||
|
||||
- [`@suaveplan/rules-hono`](../rules-hono/README.md) — Hono middleware wrapping this package.
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs --> - operators-internal — `compareValues(actual, operato…
|
||||
|
||||
- [operators-internal](./operators-internal.md) — `compareValues(actual, operator, expected)` is the built-in comparison dispatcher used by `RuleEngine` until the dedica…
|
||||
- operators-internal — `compareValues(actual, operato…
|
||||
|
||||
|
||||
@@ -97,4 +97,3 @@ await queue.getStream().pipeTo(destination);
|
||||
## License
|
||||
|
||||
MIT
|
||||
|
||||
|
||||
@@ -85,4 +85,3 @@ wraps every reader exception as `ExifFailedError`
|
||||
- [`src/types/process-options.md`](src/types/process-options.md) — runtime
|
||||
`ProcessOptions` shape (signal / clock / logger) accepted by every
|
||||
transform.
|
||||
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -54,19 +54,19 @@ bun add @suaveplan/workflow
|
||||
import { createWorkflow } from "@suaveplan/workflow";
|
||||
|
||||
interface OrderData {
|
||||
orderId: string;
|
||||
items: string[];
|
||||
total: number;
|
||||
orderId: string;
|
||||
items: string[];
|
||||
total: number;
|
||||
}
|
||||
|
||||
const result = await createWorkflow<OrderData>("process-order")
|
||||
.addStep("validate", (ctx) => {
|
||||
if (ctx.items.length === 0) throw new Error("No items");
|
||||
})
|
||||
.build()
|
||||
.run({
|
||||
initialData: { orderId: "ord-1", items: ["widget"], total: 29.99 },
|
||||
});
|
||||
.addStep("validate", (ctx) => {
|
||||
if (ctx.items.length === 0) throw new Error("No items");
|
||||
})
|
||||
.build()
|
||||
.run({
|
||||
initialData: { orderId: "ord-1", items: ["widget"], total: 29.99 },
|
||||
});
|
||||
```
|
||||
|
||||
## Modules
|
||||
@@ -103,15 +103,15 @@ builder.onError("continue"); // log + skip the failure, run the rest
|
||||
import { RetryPresets } from "@suaveplan/workflow";
|
||||
|
||||
builder
|
||||
.addStep("flaky-api", async (ctx) => callExternalApi(ctx))
|
||||
.withRetry(RetryPresets.standard());
|
||||
.addStep("flaky-api", async (ctx) => callExternalApi(ctx))
|
||||
.withRetry(RetryPresets.standard());
|
||||
|
||||
builder.addStep("custom", (ctx) => callApi(ctx)).withRetry({
|
||||
maxAttempts: 5,
|
||||
strategy: "exponential",
|
||||
delayMs: 500,
|
||||
maxDelayMs: 10_000,
|
||||
backoffMultiplier: 2,
|
||||
maxAttempts: 5,
|
||||
strategy: "exponential",
|
||||
delayMs: 500,
|
||||
maxDelayMs: 10_000,
|
||||
backoffMultiplier: 2,
|
||||
});
|
||||
```
|
||||
|
||||
@@ -121,13 +121,13 @@ builder.addStep("custom", (ctx) => callApi(ctx)).withRetry({
|
||||
const engine = createWorkflowEngine(config);
|
||||
|
||||
engine.hooks.on("workflow:start", (p) =>
|
||||
console.log(`Workflow ${p.workflowId} started`),
|
||||
console.log(`Workflow ${p.workflowId} started`),
|
||||
);
|
||||
engine.hooks.on("step:complete", (p) =>
|
||||
console.log(`Step ${p.stepId} completed`),
|
||||
console.log(`Step ${p.stepId} completed`),
|
||||
);
|
||||
engine.hooks.on("step:fail", (p) =>
|
||||
console.error(`Step ${p.stepId} failed:`, p.error),
|
||||
console.error(`Step ${p.stepId} failed:`, p.error),
|
||||
);
|
||||
```
|
||||
|
||||
@@ -139,18 +139,18 @@ import { createSaga, createStep } from "@suaveplan/workflow";
|
||||
const saga = createSaga({ id: "booking", name: "Booking Saga" });
|
||||
|
||||
saga
|
||||
.addStep(createStep({
|
||||
id: "reserve",
|
||||
name: "Reserve Seats",
|
||||
execute: (ctx) => reserveSeats(ctx),
|
||||
compensate: (ctx) => releaseSeats(ctx),
|
||||
}))
|
||||
.addStep(createStep({
|
||||
id: "charge",
|
||||
name: "Charge Payment",
|
||||
execute: (ctx) => chargeCard(ctx),
|
||||
compensate: (ctx) => refundCard(ctx),
|
||||
}));
|
||||
.addStep(createStep({
|
||||
id: "reserve",
|
||||
name: "Reserve Seats",
|
||||
execute: (ctx) => reserveSeats(ctx),
|
||||
compensate: (ctx) => releaseSeats(ctx),
|
||||
}))
|
||||
.addStep(createStep({
|
||||
id: "charge",
|
||||
name: "Charge Payment",
|
||||
execute: (ctx) => chargeCard(ctx),
|
||||
compensate: (ctx) => refundCard(ctx),
|
||||
}));
|
||||
|
||||
const result = await saga.execute(context);
|
||||
// If "charge" fails, "reserve" is automatically compensated.
|
||||
@@ -159,4 +159,3 @@ const result = await saga.execute(context);
|
||||
## License
|
||||
|
||||
MIT
|
||||
|
||||
|
||||
@@ -49,12 +49,12 @@ await comp.compensateAll({ orderId: "ord-42" });
|
||||
import { createCompensator } from "@suaveplan/workflow";
|
||||
|
||||
const comp = createCompensator<{ id: string }>({
|
||||
continueOnError: false,
|
||||
clock: () => 0,
|
||||
continueOnError: false,
|
||||
clock: () => 0,
|
||||
});
|
||||
|
||||
comp.register("a", () => {
|
||||
throw new Error("a failed");
|
||||
throw new Error("a failed");
|
||||
});
|
||||
comp.register("b", () => undefined);
|
||||
|
||||
@@ -62,7 +62,7 @@ await comp.compensateAll({ id: "x" });
|
||||
// Records contain a single entry for "b" with status "compensated"
|
||||
// then the loop breaks because continueOnError is false.
|
||||
if (comp.hasFailures === false) {
|
||||
// Re-attempt the failed compensations later.
|
||||
// Re-attempt the failed compensations later.
|
||||
}
|
||||
```
|
||||
|
||||
@@ -86,7 +86,7 @@ The internal `_runEntry` helper is shared by `compensateAll` and
|
||||
`continueOnError` is false) so the callers can short-circuit without
|
||||
duplicating the predicate.
|
||||
|
||||
`compensateSteps` filters by id *before* reversing, so the relative order
|
||||
`compensateSteps` filters by id _before_ reversing, so the relative order
|
||||
between matching ids is preserved as the inverse of registration order —
|
||||
mirroring the `compensateAll` contract.
|
||||
|
||||
|
||||
@@ -34,27 +34,27 @@ leaking state across runs.
|
||||
import { createSaga, createStep } from "@suaveplan/workflow";
|
||||
|
||||
const saga = createSaga<{ orderId: string }>({
|
||||
id: "booking",
|
||||
name: "Booking saga",
|
||||
id: "booking",
|
||||
name: "Booking saga",
|
||||
});
|
||||
|
||||
saga
|
||||
.addStep(
|
||||
createStep({
|
||||
id: "reserve",
|
||||
name: "Reserve seats",
|
||||
execute: (ctx) => reserveSeats(ctx),
|
||||
compensate: (ctx) => releaseSeats(ctx),
|
||||
}),
|
||||
)
|
||||
.addStep(
|
||||
createStep({
|
||||
id: "charge",
|
||||
name: "Charge payment",
|
||||
execute: (ctx) => chargeCard(ctx),
|
||||
compensate: (ctx) => refundCard(ctx),
|
||||
}),
|
||||
);
|
||||
.addStep(
|
||||
createStep({
|
||||
id: "reserve",
|
||||
name: "Reserve seats",
|
||||
execute: (ctx) => reserveSeats(ctx),
|
||||
compensate: (ctx) => releaseSeats(ctx),
|
||||
}),
|
||||
)
|
||||
.addStep(
|
||||
createStep({
|
||||
id: "charge",
|
||||
name: "Charge payment",
|
||||
execute: (ctx) => chargeCard(ctx),
|
||||
compensate: (ctx) => refundCard(ctx),
|
||||
}),
|
||||
);
|
||||
|
||||
const result = await saga.execute({ orderId: "ord-1" });
|
||||
```
|
||||
@@ -66,28 +66,28 @@ import { createSaga, createStep } from "@suaveplan/workflow";
|
||||
|
||||
const fakeClock = () => 1_000;
|
||||
const saga = createSaga<{ id: string }>({
|
||||
id: "two-phase",
|
||||
name: "Two-phase commit",
|
||||
continueCompensationOnError: false,
|
||||
clock: fakeClock,
|
||||
id: "two-phase",
|
||||
name: "Two-phase commit",
|
||||
continueCompensationOnError: false,
|
||||
clock: fakeClock,
|
||||
});
|
||||
|
||||
saga.addStep(
|
||||
createStep({
|
||||
id: "lock",
|
||||
name: "Lock resource",
|
||||
execute: (ctx) => acquireLock(ctx.id),
|
||||
compensate: (ctx) => releaseLock(ctx.id),
|
||||
}),
|
||||
createStep({
|
||||
id: "lock",
|
||||
name: "Lock resource",
|
||||
execute: (ctx) => acquireLock(ctx.id),
|
||||
compensate: (ctx) => releaseLock(ctx.id),
|
||||
}),
|
||||
);
|
||||
saga.addStep(
|
||||
createStep({
|
||||
id: "commit",
|
||||
name: "Commit",
|
||||
execute: () => {
|
||||
throw new Error("conflict");
|
||||
},
|
||||
}),
|
||||
createStep({
|
||||
id: "commit",
|
||||
name: "Commit",
|
||||
execute: () => {
|
||||
throw new Error("conflict");
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
const result = await saga.execute({ id: "row-7" });
|
||||
|
||||
@@ -36,13 +36,13 @@ the live context mutating beneath the caller.
|
||||
import { createContext } from "@suaveplan/workflow";
|
||||
|
||||
interface OrderData {
|
||||
orderId: string;
|
||||
total: number;
|
||||
orderId: string;
|
||||
total: number;
|
||||
}
|
||||
|
||||
const ctx = createContext<OrderData>("wf-1", {
|
||||
orderId: "ord-1",
|
||||
total: 9.99,
|
||||
orderId: "ord-1",
|
||||
total: 9.99,
|
||||
});
|
||||
ctx.setStatus("running");
|
||||
ctx.updateData({ total: 14.99 });
|
||||
@@ -56,21 +56,21 @@ import { createContext } from "@suaveplan/workflow";
|
||||
const ctx = createContext("wf-1", { user: "alice" });
|
||||
ctx.setMeta("traceId", "00-abc-def");
|
||||
ctx.addStepResult({
|
||||
stepId: "validate",
|
||||
status: "completed",
|
||||
startedAt: 0,
|
||||
completedAt: 1,
|
||||
attempts: 1,
|
||||
stepId: "validate",
|
||||
status: "completed",
|
||||
startedAt: 0,
|
||||
completedAt: 1,
|
||||
attempts: 1,
|
||||
});
|
||||
|
||||
const before = ctx.snapshot();
|
||||
ctx.updateData({ user: "bob" });
|
||||
ctx.addStepResult({
|
||||
stepId: "charge",
|
||||
status: "completed",
|
||||
startedAt: 1,
|
||||
completedAt: 2,
|
||||
attempts: 1,
|
||||
stepId: "charge",
|
||||
status: "completed",
|
||||
startedAt: 1,
|
||||
completedAt: 2,
|
||||
attempts: 1,
|
||||
});
|
||||
|
||||
console.log(before.data.user); // "alice" — snapshot is unaffected
|
||||
|
||||
@@ -33,11 +33,11 @@ so callers can subscribe to lifecycle events before the engine is built.
|
||||
import { createWorkflow } from "@suaveplan/workflow";
|
||||
|
||||
const result = await createWorkflow<{ orderId: string }>("checkout")
|
||||
.addStep("validate", (ctx) => {
|
||||
if (!ctx.orderId) throw new Error("missing");
|
||||
})
|
||||
.build()
|
||||
.run({ initialData: { orderId: "ord-1" } });
|
||||
.addStep("validate", (ctx) => {
|
||||
if (!ctx.orderId) throw new Error("missing");
|
||||
})
|
||||
.build()
|
||||
.run({ initialData: { orderId: "ord-1" } });
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
@@ -46,21 +46,21 @@ const result = await createWorkflow<{ orderId: string }>("checkout")
|
||||
import { createWorkflow, RetryPresets } from "@suaveplan/workflow";
|
||||
|
||||
const engine = createWorkflow<{ user: string }>("onboarding", {
|
||||
clock: () => 1_700_000_000_000,
|
||||
clock: () => 1_700_000_000_000,
|
||||
})
|
||||
.withTimeout(30_000)
|
||||
.onError("compensate")
|
||||
.on("step:fail", (p) => console.error("failed:", p.stepId));
|
||||
.withTimeout(30_000)
|
||||
.onError("compensate")
|
||||
.on("step:fail", (p) => console.error("failed:", p.stepId));
|
||||
|
||||
engine
|
||||
.addStep("create", (ctx) => createAccount(ctx.user))
|
||||
.withCompensation((ctx) => deleteAccount(ctx.user))
|
||||
.withRetry({ maxAttempts: 2, strategy: "exponential" });
|
||||
.addStep("create", (ctx) => createAccount(ctx.user))
|
||||
.withCompensation((ctx) => deleteAccount(ctx.user))
|
||||
.withRetry({ maxAttempts: 2, strategy: "exponential" });
|
||||
|
||||
engine
|
||||
.addStep("notify", (ctx) => sendWelcome(ctx.user))
|
||||
.withGuard((ctx) => ctx.user.startsWith("@"))
|
||||
.withTimeout(5_000);
|
||||
.addStep("notify", (ctx) => sendWelcome(ctx.user))
|
||||
.withGuard((ctx) => ctx.user.startsWith("@"))
|
||||
.withTimeout(5_000);
|
||||
|
||||
const result = await engine.run({ initialData: { user: "@alice" } });
|
||||
```
|
||||
|
||||
@@ -29,7 +29,7 @@ import { createWorkflowEngine } from "@suaveplan/workflow";
|
||||
|
||||
const engine = createWorkflowEngine({ id: "w", name: "n", steps: [] });
|
||||
engine.transitionHooks.hook((data) => {
|
||||
console.log("entering", data.stepName);
|
||||
console.log("entering", data.stepName);
|
||||
});
|
||||
```
|
||||
|
||||
@@ -37,18 +37,18 @@ engine.transitionHooks.hook((data) => {
|
||||
|
||||
```ts
|
||||
const engine = createWorkflowEngine<{ region: string }>({
|
||||
id: "w",
|
||||
name: "checkout",
|
||||
steps,
|
||||
id: "w",
|
||||
name: "checkout",
|
||||
steps,
|
||||
});
|
||||
|
||||
const unsubscribe = engine.transitionHooks.hook(async (data) => {
|
||||
if (data.context.region === "EU") {
|
||||
return {
|
||||
...data,
|
||||
context: { ...data.context, region: "EU-norm" },
|
||||
};
|
||||
}
|
||||
if (data.context.region === "EU") {
|
||||
return {
|
||||
...data,
|
||||
context: { ...data.context, region: "EU-norm" },
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
await engine.run({ initialData: { region: "EU" } });
|
||||
|
||||
@@ -40,18 +40,18 @@ codes.
|
||||
import { createWorkflowEngine } from "@suaveplan/workflow";
|
||||
|
||||
const engine = createWorkflowEngine<{ orderId: string }>({
|
||||
id: "wf-1",
|
||||
name: "checkout",
|
||||
onError: "stop",
|
||||
steps: [
|
||||
{
|
||||
id: "validate",
|
||||
name: "Validate",
|
||||
execute: (ctx) => {
|
||||
if (!ctx.orderId) throw new Error("missing");
|
||||
},
|
||||
},
|
||||
],
|
||||
id: "wf-1",
|
||||
name: "checkout",
|
||||
onError: "stop",
|
||||
steps: [
|
||||
{
|
||||
id: "validate",
|
||||
name: "Validate",
|
||||
execute: (ctx) => {
|
||||
if (!ctx.orderId) throw new Error("missing");
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
const result = await engine.run({ initialData: { orderId: "ord-1" } });
|
||||
@@ -63,31 +63,31 @@ const result = await engine.run({ initialData: { orderId: "ord-1" } });
|
||||
import { createWorkflowEngine } from "@suaveplan/workflow";
|
||||
|
||||
const engine = createWorkflowEngine<{ user: string }>({
|
||||
id: "wf-1",
|
||||
name: "onboarding",
|
||||
timeout: 30_000,
|
||||
onError: "compensate",
|
||||
steps: [
|
||||
{
|
||||
id: "create-account",
|
||||
name: "Create account",
|
||||
execute: (ctx) => createAccount(ctx.user),
|
||||
compensate: (ctx) => deleteAccount(ctx.user),
|
||||
},
|
||||
{
|
||||
id: "send-welcome",
|
||||
name: "Send welcome email",
|
||||
execute: (ctx) => sendWelcome(ctx.user),
|
||||
},
|
||||
],
|
||||
id: "wf-1",
|
||||
name: "onboarding",
|
||||
timeout: 30_000,
|
||||
onError: "compensate",
|
||||
steps: [
|
||||
{
|
||||
id: "create-account",
|
||||
name: "Create account",
|
||||
execute: (ctx) => createAccount(ctx.user),
|
||||
compensate: (ctx) => deleteAccount(ctx.user),
|
||||
},
|
||||
{
|
||||
id: "send-welcome",
|
||||
name: "Send welcome email",
|
||||
execute: (ctx) => sendWelcome(ctx.user),
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
engine.hooks.on("step:fail", (p) => {
|
||||
console.error("step failed", p.stepId, p.error?.message);
|
||||
console.error("step failed", p.stepId, p.error?.message);
|
||||
});
|
||||
|
||||
engine.transitionHooks.hook((data) => {
|
||||
return { ...data, context: { ...data.context, user: data.context.user.trim() } };
|
||||
return { ...data, context: { ...data.context, user: data.context.user.trim() } };
|
||||
});
|
||||
|
||||
const run = engine.run({ initialData: { user: " alice " } });
|
||||
@@ -121,7 +121,7 @@ while still satisfying the registry's reachability gate.
|
||||
is the original step error, so callers walking the cause chain still see
|
||||
the real underlying error.
|
||||
|
||||
The `workflow:cancel` emit happens during the loop *and* the status is
|
||||
The `workflow:cancel` emit happens during the loop _and_ the status is
|
||||
re-set to `"cancelled"` after the loop returns; the second assignment is
|
||||
a guard for the rare case where the loop returns immediately because the
|
||||
flag was set before the first iteration.
|
||||
|
||||
@@ -40,20 +40,20 @@ throw new WorkflowError(WORKFLOW_CODES.TIMEOUT, 'Workflow "checkout" timed out')
|
||||
import { WORKFLOW_CODES, WorkflowError } from "@suaveplan/workflow/errors";
|
||||
|
||||
try {
|
||||
await engine.run({ initialData });
|
||||
await engine.run({ initialData });
|
||||
} catch (error) {
|
||||
if (error instanceof WorkflowError) {
|
||||
switch (error.code) {
|
||||
case WORKFLOW_CODES.TIMEOUT:
|
||||
metrics.record("workflow.timeout");
|
||||
break;
|
||||
case WORKFLOW_CODES.COMPENSATION_FAILED:
|
||||
alerts.fire({ severity: "critical", error });
|
||||
break;
|
||||
default:
|
||||
logger.error("workflow failed", { code: error.code });
|
||||
}
|
||||
}
|
||||
if (error instanceof WorkflowError) {
|
||||
switch (error.code) {
|
||||
case WORKFLOW_CODES.TIMEOUT:
|
||||
metrics.record("workflow.timeout");
|
||||
break;
|
||||
case WORKFLOW_CODES.COMPENSATION_FAILED:
|
||||
alerts.fire({ severity: "critical", error });
|
||||
break;
|
||||
default:
|
||||
logger.error("workflow failed", { code: error.code });
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ that aren't built in, push-notifications, audit trails). It deliberately
|
||||
keeps the surface to four operations — `on`, `once`, `off`, `emit` — so
|
||||
the engine and its consumers depend on a stable contract.
|
||||
|
||||
Every other module in the package emits *through* this class rather than
|
||||
Every other module in the package emits _through_ this class rather than
|
||||
acquiring its own listeners-map, so the registry is the canonical extension
|
||||
point for downstream tooling.
|
||||
|
||||
@@ -34,12 +34,12 @@ import { createHookRegistry } from "@suaveplan/workflow";
|
||||
|
||||
const hooks = createHookRegistry<{ orderId: string }>();
|
||||
hooks.on("workflow:start", (p) => {
|
||||
console.log("starting", p.workflowId);
|
||||
console.log("starting", p.workflowId);
|
||||
});
|
||||
|
||||
await hooks.emit("workflow:start", {
|
||||
workflowId: "wf-1",
|
||||
context: { orderId: "ord-1" },
|
||||
workflowId: "wf-1",
|
||||
context: { orderId: "ord-1" },
|
||||
});
|
||||
```
|
||||
|
||||
@@ -47,34 +47,34 @@ await hooks.emit("workflow:start", {
|
||||
|
||||
```ts
|
||||
import {
|
||||
buildStepPayload,
|
||||
createHookRegistry,
|
||||
buildStepPayload,
|
||||
createHookRegistry,
|
||||
} from "@suaveplan/workflow";
|
||||
|
||||
const hooks = createHookRegistry<{ user: string }>();
|
||||
|
||||
const unsubscribe = hooks.on("step:fail", async (p) => {
|
||||
if (p.stepResult && p.stepResult.attempts >= 3) {
|
||||
await pageOnCall(p.error);
|
||||
}
|
||||
if (p.stepResult && p.stepResult.attempts >= 3) {
|
||||
await pageOnCall(p.error);
|
||||
}
|
||||
});
|
||||
|
||||
hooks.once("workflow:complete", (p) => {
|
||||
console.log("done", p.workflowId, p.timestamp);
|
||||
console.log("done", p.workflowId, p.timestamp);
|
||||
});
|
||||
|
||||
await hooks.emit("step:fail", buildStepPayload(
|
||||
"wf-1",
|
||||
{ user: "alice" },
|
||||
"step-1",
|
||||
{
|
||||
stepId: "step-1",
|
||||
status: "failed",
|
||||
startedAt: 0,
|
||||
completedAt: 100,
|
||||
attempts: 3,
|
||||
},
|
||||
new Error("boom"),
|
||||
"wf-1",
|
||||
{ user: "alice" },
|
||||
"step-1",
|
||||
{
|
||||
stepId: "step-1",
|
||||
status: "failed",
|
||||
startedAt: 0,
|
||||
completedAt: 100,
|
||||
attempts: 3,
|
||||
},
|
||||
new Error("boom"),
|
||||
));
|
||||
|
||||
unsubscribe();
|
||||
|
||||
@@ -35,8 +35,8 @@ the numbers in every consumer.
|
||||
import { withRetry, RetryPresets } from "@suaveplan/workflow";
|
||||
|
||||
const result = await withRetry(
|
||||
() => fetch("/api/data").then((r) => r.json()),
|
||||
RetryPresets.standard(),
|
||||
() => fetch("/api/data").then((r) => r.json()),
|
||||
RetryPresets.standard(),
|
||||
);
|
||||
```
|
||||
|
||||
@@ -46,14 +46,14 @@ const result = await withRetry(
|
||||
import { createRetryWrapper } from "@suaveplan/workflow";
|
||||
|
||||
const wrap = createRetryWrapper({
|
||||
maxAttempts: 4,
|
||||
strategy: "exponential",
|
||||
delayMs: 100,
|
||||
maxDelayMs: 2_000,
|
||||
shouldRetry: (err) => !(err instanceof TypeError),
|
||||
onRetry: (err, attempt) => {
|
||||
console.warn(`attempt ${attempt} failed:`, err.message);
|
||||
},
|
||||
maxAttempts: 4,
|
||||
strategy: "exponential",
|
||||
delayMs: 100,
|
||||
maxDelayMs: 2_000,
|
||||
shouldRetry: (err) => !(err instanceof TypeError),
|
||||
onRetry: (err, attempt) => {
|
||||
console.warn(`attempt ${attempt} failed:`, err.message);
|
||||
},
|
||||
});
|
||||
|
||||
const json = await wrap(() => fetch("/api/v1").then((r) => r.json()));
|
||||
@@ -72,7 +72,7 @@ const json = await wrap(() => fetch("/api/v1").then((r) => r.json()));
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
`withRetry` always rethrows the *last* error, not the *first*. Callers
|
||||
`withRetry` always rethrows the _last_ error, not the _first_. Callers
|
||||
that want the first-error semantics can capture it inside an `onRetry`
|
||||
closure. The loop checks `attempt >= maxAttempts` before `shouldRetry` so
|
||||
the final attempt always actually runs even if `shouldRetry` would have
|
||||
|
||||
@@ -52,7 +52,7 @@ await engine.run({ initialData });
|
||||
|
||||
const snapshot = await reader.collect();
|
||||
const stepCounter = snapshot.resourceMetrics.scopeMetrics[0].metrics.find(
|
||||
(m) => m.descriptor.name === "workflow.step.executed_total",
|
||||
(m) => m.descriptor.name === "workflow.step.executed_total",
|
||||
);
|
||||
expect(stepCounter?.dataPoints.length).toBeGreaterThan(0);
|
||||
|
||||
|
||||
@@ -34,19 +34,19 @@ concerns. `compensate` rolls back only the branch that actually ran.
|
||||
import { createConditionalStep, createStep } from "@suaveplan/workflow";
|
||||
|
||||
const branch = createConditionalStep<{ premium: boolean }>({
|
||||
id: "discount",
|
||||
name: "Discount",
|
||||
condition: (ctx) => ctx.premium,
|
||||
onTrue: createStep({
|
||||
id: "premium",
|
||||
name: "Premium discount",
|
||||
execute: () => ({ discountPct: 30 }),
|
||||
}),
|
||||
onFalse: createStep({
|
||||
id: "standard",
|
||||
name: "Standard discount",
|
||||
execute: () => ({ discountPct: 5 }),
|
||||
}),
|
||||
id: "discount",
|
||||
name: "Discount",
|
||||
condition: (ctx) => ctx.premium,
|
||||
onTrue: createStep({
|
||||
id: "premium",
|
||||
name: "Premium discount",
|
||||
execute: () => ({ discountPct: 30 }),
|
||||
}),
|
||||
onFalse: createStep({
|
||||
id: "standard",
|
||||
name: "Standard discount",
|
||||
execute: () => ({ discountPct: 5 }),
|
||||
}),
|
||||
});
|
||||
const result = await branch.run({ premium: true });
|
||||
```
|
||||
@@ -57,21 +57,21 @@ const result = await branch.run({ premium: true });
|
||||
import { createConditionalStep, createStep } from "@suaveplan/workflow";
|
||||
|
||||
const ship = createStep<{ shipped: boolean }>({
|
||||
id: "ship",
|
||||
name: "Ship",
|
||||
execute: async (ctx) => {
|
||||
await dispatch(ctx);
|
||||
},
|
||||
compensate: async (ctx) => {
|
||||
await recall(ctx);
|
||||
},
|
||||
id: "ship",
|
||||
name: "Ship",
|
||||
execute: async (ctx) => {
|
||||
await dispatch(ctx);
|
||||
},
|
||||
compensate: async (ctx) => {
|
||||
await recall(ctx);
|
||||
},
|
||||
});
|
||||
|
||||
const guard = createConditionalStep<{ ready: boolean }>({
|
||||
id: "maybe-ship",
|
||||
name: "Maybe ship",
|
||||
condition: (ctx) => ctx.ready,
|
||||
onTrue: ship,
|
||||
id: "maybe-ship",
|
||||
name: "Maybe ship",
|
||||
condition: (ctx) => ctx.ready,
|
||||
onTrue: ship,
|
||||
});
|
||||
|
||||
await guard.run({ ready: true });
|
||||
|
||||
@@ -36,13 +36,13 @@ failure.
|
||||
import { createParallelStep, createStep } from "@suaveplan/workflow";
|
||||
|
||||
const group = createParallelStep<{ url: string }>({
|
||||
groupId: "fetch-group",
|
||||
name: "Fetch resources",
|
||||
groupId: "fetch-group",
|
||||
name: "Fetch resources",
|
||||
});
|
||||
|
||||
group
|
||||
.addStep(createStep({ id: "a", name: "A", execute: () => "a" }))
|
||||
.addStep(createStep({ id: "b", name: "B", execute: () => "b" }));
|
||||
.addStep(createStep({ id: "a", name: "A", execute: () => "a" }))
|
||||
.addStep(createStep({ id: "b", name: "B", execute: () => "b" }));
|
||||
|
||||
const result = await group.run({ url: "https://example.com" });
|
||||
```
|
||||
@@ -53,31 +53,31 @@ const result = await group.run({ url: "https://example.com" });
|
||||
import { createParallelStep, createStep } from "@suaveplan/workflow";
|
||||
|
||||
const group = createParallelStep<{ jobIds: string[] }>({
|
||||
groupId: "process",
|
||||
name: "Process jobs",
|
||||
maxConcurrency: 2,
|
||||
failFast: true,
|
||||
groupId: "process",
|
||||
name: "Process jobs",
|
||||
maxConcurrency: 2,
|
||||
failFast: true,
|
||||
});
|
||||
|
||||
for (const id of ["a", "b", "c", "d"]) {
|
||||
group.addStep(
|
||||
createStep({
|
||||
id,
|
||||
name: `process-${id}`,
|
||||
execute: async (ctx) => {
|
||||
if (id === "b") throw new Error("nope");
|
||||
return id;
|
||||
},
|
||||
compensate: async () => {
|
||||
await unprocess(id);
|
||||
},
|
||||
}),
|
||||
);
|
||||
group.addStep(
|
||||
createStep({
|
||||
id,
|
||||
name: `process-${id}`,
|
||||
execute: async (ctx) => {
|
||||
if (id === "b") throw new Error("nope");
|
||||
return id;
|
||||
},
|
||||
compensate: async () => {
|
||||
await unprocess(id);
|
||||
},
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
const result = await group.run({ jobIds: [] });
|
||||
if (result.status === "failed") {
|
||||
await group.compensateAll({ jobIds: [] });
|
||||
await group.compensateAll({ jobIds: [] });
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@@ -37,9 +37,9 @@ unless they want to.
|
||||
import { createStep } from "@suaveplan/workflow";
|
||||
|
||||
const charge = createStep<{ total: number }>({
|
||||
id: "charge",
|
||||
name: "Charge card",
|
||||
execute: (ctx) => ({ chargedAt: Date.now(), amount: ctx.total }),
|
||||
id: "charge",
|
||||
name: "Charge card",
|
||||
execute: (ctx) => ({ chargedAt: Date.now(), amount: ctx.total }),
|
||||
});
|
||||
|
||||
const result = await charge.run({ total: 9.99 });
|
||||
@@ -52,20 +52,20 @@ import { createStep, RetryPresets } from "@suaveplan/workflow";
|
||||
|
||||
const fakeClock = () => 0;
|
||||
const flaky = createStep<{ url: string }>({
|
||||
id: "fetch",
|
||||
name: "Fetch resource",
|
||||
execute: async (ctx) => {
|
||||
const response = await fetch(ctx.url);
|
||||
if (!response.ok) throw new Error(`status ${response.status}`);
|
||||
return response.json();
|
||||
},
|
||||
guard: (ctx) => ctx.url.startsWith("https://"),
|
||||
retry: RetryPresets.standard(),
|
||||
timeout: 5_000,
|
||||
compensate: async () => {
|
||||
await releaseLock();
|
||||
},
|
||||
clock: fakeClock,
|
||||
id: "fetch",
|
||||
name: "Fetch resource",
|
||||
execute: async (ctx) => {
|
||||
const response = await fetch(ctx.url);
|
||||
if (!response.ok) throw new Error(`status ${response.status}`);
|
||||
return response.json();
|
||||
},
|
||||
guard: (ctx) => ctx.url.startsWith("https://"),
|
||||
retry: RetryPresets.standard(),
|
||||
timeout: 5_000,
|
||||
compensate: async () => {
|
||||
await releaseLock();
|
||||
},
|
||||
clock: fakeClock,
|
||||
});
|
||||
```
|
||||
|
||||
|
||||
@@ -26,18 +26,18 @@ package root barrel and reason about wire shapes without chasing imports.
|
||||
import type { WorkflowConfig, StepDefinition } from "@suaveplan/workflow";
|
||||
|
||||
const validate: StepDefinition<{ items: string[] }> = {
|
||||
id: "validate",
|
||||
name: "Validate",
|
||||
execute: (ctx) => {
|
||||
if (ctx.items.length === 0) throw new Error("empty");
|
||||
},
|
||||
id: "validate",
|
||||
name: "Validate",
|
||||
execute: (ctx) => {
|
||||
if (ctx.items.length === 0) throw new Error("empty");
|
||||
},
|
||||
};
|
||||
|
||||
const config: WorkflowConfig<{ items: string[] }> = {
|
||||
id: "wf-1",
|
||||
name: "checkout",
|
||||
steps: [validate],
|
||||
onError: "stop",
|
||||
id: "wf-1",
|
||||
name: "checkout",
|
||||
steps: [validate],
|
||||
onError: "stop",
|
||||
};
|
||||
```
|
||||
|
||||
@@ -45,35 +45,35 @@ const config: WorkflowConfig<{ items: string[] }> = {
|
||||
|
||||
```ts
|
||||
import type {
|
||||
HookListener,
|
||||
StepResult,
|
||||
RetryConfig,
|
||||
HookListener,
|
||||
StepResult,
|
||||
RetryConfig,
|
||||
} from "@suaveplan/workflow";
|
||||
|
||||
const auditLog: HookListener<{ orderId: string }> = (payload) => {
|
||||
if (payload.event === "step:fail" && payload.stepResult) {
|
||||
console.error("step failed", {
|
||||
id: payload.stepResult.stepId,
|
||||
attempts: payload.stepResult.attempts,
|
||||
error: payload.stepResult.error?.message,
|
||||
});
|
||||
}
|
||||
if (payload.event === "step:fail" && payload.stepResult) {
|
||||
console.error("step failed", {
|
||||
id: payload.stepResult.stepId,
|
||||
attempts: payload.stepResult.attempts,
|
||||
error: payload.stepResult.error?.message,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const exponential: RetryConfig = {
|
||||
maxAttempts: 5,
|
||||
strategy: "exponential",
|
||||
delayMs: 100,
|
||||
backoffMultiplier: 2,
|
||||
maxDelayMs: 5000,
|
||||
maxAttempts: 5,
|
||||
strategy: "exponential",
|
||||
delayMs: 100,
|
||||
backoffMultiplier: 2,
|
||||
maxDelayMs: 5000,
|
||||
};
|
||||
|
||||
const skipped: StepResult = {
|
||||
stepId: "skip",
|
||||
status: "skipped",
|
||||
startedAt: 0,
|
||||
completedAt: 0,
|
||||
attempts: 0,
|
||||
stepId: "skip",
|
||||
status: "skipped",
|
||||
startedAt: 0,
|
||||
completedAt: 0,
|
||||
attempts: 0,
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@ changing the handler signatures.
|
||||
import type { WorkflowTransitioningHandler } from "@suaveplan/workflow";
|
||||
|
||||
const audit: WorkflowTransitioningHandler<{ userId: string }> = (data) => {
|
||||
console.log("entering step", data.stepName, "for", data.context.userId);
|
||||
console.log("entering step", data.stepName, "for", data.context.userId);
|
||||
};
|
||||
```
|
||||
|
||||
@@ -40,25 +40,25 @@ const audit: WorkflowTransitioningHandler<{ userId: string }> = (data) => {
|
||||
|
||||
```ts
|
||||
import type {
|
||||
WorkflowTransitionData,
|
||||
WorkflowTransitioningHandler,
|
||||
WorkflowTransitionData,
|
||||
WorkflowTransitioningHandler,
|
||||
} from "@suaveplan/workflow";
|
||||
|
||||
interface OrderCtx {
|
||||
orderId: string;
|
||||
region: string;
|
||||
taxRate?: number;
|
||||
orderId: string;
|
||||
region: string;
|
||||
taxRate?: number;
|
||||
}
|
||||
|
||||
// Enrich context with region-specific tax rate before each step runs.
|
||||
const enrichTax: WorkflowTransitioningHandler<OrderCtx> = async (data) => {
|
||||
if (data.context.taxRate !== undefined) return;
|
||||
const rate = await fetchTaxRate(data.context.region);
|
||||
const next: WorkflowTransitionData<OrderCtx> = {
|
||||
...data,
|
||||
context: { ...data.context, taxRate: rate },
|
||||
};
|
||||
return next;
|
||||
if (data.context.taxRate !== undefined) return;
|
||||
const rate = await fetchTaxRate(data.context.region);
|
||||
const next: WorkflowTransitionData<OrderCtx> = {
|
||||
...data,
|
||||
context: { ...data.context, taxRate: rate },
|
||||
};
|
||||
return next;
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
Reference in New Issue
Block a user