@adonis-agora/durable 0.7.0 → 0.8.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 +64 -0
- package/README.md +1 -1
- package/dist/commands/main.d.ts +3 -2
- package/dist/commands/main.d.ts.map +1 -1
- package/dist/commands/main.js +3 -2
- package/dist/commands/main.js.map +1 -1
- package/dist/commands/make_workflow.d.ts +4 -3
- package/dist/commands/make_workflow.d.ts.map +1 -1
- package/dist/commands/make_workflow.js +4 -3
- package/dist/commands/make_workflow.js.map +1 -1
- package/dist/configure.d.ts +6 -5
- package/dist/configure.d.ts.map +1 -1
- package/dist/configure.js +9 -6
- package/dist/configure.js.map +1 -1
- package/dist/providers/dashboard_provider.d.ts +34 -3
- package/dist/providers/dashboard_provider.d.ts.map +1 -1
- package/dist/providers/dashboard_provider.js +110 -6
- package/dist/providers/dashboard_provider.js.map +1 -1
- package/dist/src/base-workflow.d.ts +78 -0
- package/dist/src/base-workflow.d.ts.map +1 -0
- package/dist/src/base-workflow.js +106 -0
- package/dist/src/base-workflow.js.map +1 -0
- package/dist/src/codec-state-store.d.ts +13 -0
- package/dist/src/codec-state-store.d.ts.map +1 -1
- package/dist/src/codec-state-store.js +13 -0
- package/dist/src/codec-state-store.js.map +1 -1
- package/dist/src/control-flow-signal.d.ts +50 -0
- package/dist/src/control-flow-signal.d.ts.map +1 -0
- package/dist/src/control-flow-signal.js +54 -0
- package/dist/src/control-flow-signal.js.map +1 -0
- package/dist/src/control-plane-redis/redis-control-plane.d.ts +57 -5
- package/dist/src/control-plane-redis/redis-control-plane.d.ts.map +1 -1
- package/dist/src/control-plane-redis/redis-control-plane.js +100 -2
- package/dist/src/control-plane-redis/redis-control-plane.js.map +1 -1
- package/dist/src/dashboard/auth.d.ts +89 -0
- package/dist/src/dashboard/auth.d.ts.map +1 -0
- package/dist/src/dashboard/auth.js +113 -0
- package/dist/src/dashboard/auth.js.map +1 -0
- package/dist/src/dashboard/define_config.d.ts +17 -0
- package/dist/src/dashboard/define_config.d.ts.map +1 -1
- package/dist/src/dashboard/define_config.js +23 -1
- package/dist/src/dashboard/define_config.js.map +1 -1
- package/dist/src/dashboard/index.d.ts +5 -0
- package/dist/src/dashboard/index.d.ts.map +1 -1
- package/dist/src/dashboard/index.js +4 -0
- package/dist/src/dashboard/index.js.map +1 -1
- package/dist/src/dashboard/login_page.d.ts +23 -0
- package/dist/src/dashboard/login_page.d.ts.map +1 -0
- package/dist/src/dashboard/login_page.js +170 -0
- package/dist/src/dashboard/login_page.js.map +1 -0
- package/dist/src/dashboard/session_cookie.d.ts +42 -0
- package/dist/src/dashboard/session_cookie.d.ts.map +1 -0
- package/dist/src/dashboard/session_cookie.js +97 -0
- package/dist/src/dashboard/session_cookie.js.map +1 -0
- package/dist/src/define_config.d.ts +3 -2
- package/dist/src/define_config.d.ts.map +1 -1
- package/dist/src/define_config.js.map +1 -1
- package/dist/src/engine.d.ts +51 -1
- package/dist/src/engine.d.ts.map +1 -1
- package/dist/src/engine.js +213 -27
- package/dist/src/engine.js.map +1 -1
- package/dist/src/errors.d.ts +6 -1
- package/dist/src/errors.d.ts.map +1 -1
- package/dist/src/errors.js +7 -2
- package/dist/src/errors.js.map +1 -1
- package/dist/src/event-accumulators.d.ts +1 -1
- package/dist/src/event-accumulators.d.ts.map +1 -1
- package/dist/src/events.d.ts +16 -0
- package/dist/src/events.d.ts.map +1 -1
- package/dist/src/events.js +26 -1
- package/dist/src/events.js.map +1 -1
- package/dist/src/hooks/workflows.d.ts +3 -2
- package/dist/src/hooks/workflows.d.ts.map +1 -1
- package/dist/src/hooks/workflows.js +3 -2
- package/dist/src/hooks/workflows.js.map +1 -1
- package/dist/src/index.d.ts +3 -0
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +3 -0
- package/dist/src/index.js.map +1 -1
- package/dist/src/interfaces.d.ts +44 -1
- package/dist/src/interfaces.d.ts.map +1 -1
- package/dist/src/services/main.d.ts +13 -0
- package/dist/src/services/main.d.ts.map +1 -0
- package/dist/src/services/main.js +17 -0
- package/dist/src/services/main.js.map +1 -0
- package/dist/src/step-name-symbol.d.ts +2 -2
- package/dist/src/step-name-symbol.js +2 -2
- package/dist/src/stores/lucid-schema.d.ts +1 -0
- package/dist/src/stores/lucid-schema.d.ts.map +1 -1
- package/dist/src/stores/lucid-schema.js +15 -0
- package/dist/src/stores/lucid-schema.js.map +1 -1
- package/dist/src/stores/lucid.d.ts +13 -0
- package/dist/src/stores/lucid.d.ts.map +1 -1
- package/dist/src/stores/lucid.js +43 -0
- package/dist/src/stores/lucid.js.map +1 -1
- package/dist/src/testing/in-memory-state-store.d.ts +16 -0
- package/dist/src/testing/in-memory-state-store.d.ts.map +1 -1
- package/dist/src/testing/in-memory-state-store.js +29 -0
- package/dist/src/testing/in-memory-state-store.js.map +1 -1
- package/dist/src/testing-kit/state-store-conformance.d.ts.map +1 -1
- package/dist/src/testing-kit/state-store-conformance.js +31 -0
- package/dist/src/testing-kit/state-store-conformance.js.map +1 -1
- package/dist/src/workflow-als.d.ts +21 -0
- package/dist/src/workflow-als.d.ts.map +1 -0
- package/dist/src/workflow-als.js +22 -0
- package/dist/src/workflow-als.js.map +1 -0
- package/dist/src/workflow-ctx.d.ts.map +1 -1
- package/dist/src/workflow-ctx.js +83 -4
- package/dist/src/workflow-ctx.js.map +1 -1
- package/dist/src/workflow-discovery.d.ts +21 -15
- package/dist/src/workflow-discovery.d.ts.map +1 -1
- package/dist/src/workflow-discovery.js +17 -13
- package/dist/src/workflow-discovery.js.map +1 -1
- package/dist/src/workflow-ref.d.ts +9 -27
- package/dist/src/workflow-ref.d.ts.map +1 -1
- package/dist/src/workflow-ref.js +12 -34
- package/dist/src/workflow-ref.js.map +1 -1
- package/dist/stubs/database/migrations/create_durable_tables.stub +13 -1
- package/dist/stubs/make/workflow/main.stub +4 -3
- package/package.json +6 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,69 @@
|
|
|
1
1
|
# @adonis-agora/durable
|
|
2
2
|
|
|
3
|
+
## 0.8.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- [`86819e0`](https://github.com/DavideCarvalho/adonis-durable/commit/86819e08666a307046e8845a7d9b9ed3685d7c53) - BaseWorkflow is the sole authoring form; `services/main`; dashboard login; signal/child/subscriber fixes
|
|
8
|
+
|
|
9
|
+
**BREAKING — the `@Workflow` decorator is removed.** Author workflows with a
|
|
10
|
+
`BaseWorkflow` subclass plus `static workflow = { name, version }`:
|
|
11
|
+
|
|
12
|
+
```ts
|
|
13
|
+
// before
|
|
14
|
+
@Workflow({ name: "charge", version: "1" })
|
|
15
|
+
class ChargeWorkflow {
|
|
16
|
+
async run(ctx: WorkflowCtx, input: Input) {}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// after
|
|
20
|
+
export default class ChargeWorkflow extends BaseWorkflow {
|
|
21
|
+
static workflow = { name: "charge", version: "1" };
|
|
22
|
+
async run(ctx: WorkflowCtx, input: Input) {}
|
|
23
|
+
}
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
`workflowMeta()` now reads only the `static workflow` config; normalization is
|
|
27
|
+
unchanged (version defaults to `'1'`). One authoring form means one thing to
|
|
28
|
+
document, one thing to discover, and no decorator/metadata runtime.
|
|
29
|
+
|
|
30
|
+
**Features**
|
|
31
|
+
|
|
32
|
+
- `BaseWorkflow` with context-aware static `start`/`dispatch`. Call
|
|
33
|
+
`ChargeWorkflow.start(input)` and it does the right thing by context: outside a
|
|
34
|
+
workflow it enqueues on the engine and blocks until the run reaches a terminal
|
|
35
|
+
state; inside one it starts a linked child and suspends the parent. `.dispatch`
|
|
36
|
+
is the fire-and-forget twin, returning `{ runId }` without waiting.
|
|
37
|
+
- `@adonis-agora/durable/services/main` — an idiomatic singleton import, so app
|
|
38
|
+
code reaches the engine the way it reaches any other Adonis service.
|
|
39
|
+
- Control-flow signal marker plus `isWorkflowControlFlowSignal`, so a workflow
|
|
40
|
+
can tell a control-flow signal apart from a domain one.
|
|
41
|
+
- Buffered events are now reliable: an event delivered before its waiter exists is
|
|
42
|
+
no longer lost.
|
|
43
|
+
- Dashboard built-in login screen via `dashboardAuth`.
|
|
44
|
+
|
|
45
|
+
**Fixes**
|
|
46
|
+
|
|
47
|
+
- Closed a lost-wake race in the signal waiter, and added the
|
|
48
|
+
`removeSignalWaiter` SPI so a waiter can be torn down deterministically.
|
|
49
|
+
- A child that fails to _start_ now surfaces the failure to the parent instead of
|
|
50
|
+
stranding it. The parent used to wait forever on a child that never existed:
|
|
51
|
+
the start was fire-and-forget, so nothing ever notified the parent. The child
|
|
52
|
+
start is now deferred and its rejection is reported to the parent as a failed
|
|
53
|
+
child result.
|
|
54
|
+
- The Redis control plane now heals a silently-dead subscriber connection. A
|
|
55
|
+
subscriber whose socket died without an error event stopped delivering messages
|
|
56
|
+
while still looking healthy, and every wake-up routed through it was lost. A
|
|
57
|
+
ping watchdog now detects the dead connection and forces a reconnect.
|
|
58
|
+
- `BaseWorkflow.start` waits for terminal (matching the linked-child path), the
|
|
59
|
+
steps hook is configurable, and the dashboard token comparison is
|
|
60
|
+
constant-time.
|
|
61
|
+
|
|
62
|
+
**eslint-plugin** — `no-nondeterminism` identifies a workflow's `run` body by the
|
|
63
|
+
new authoring form (a `BaseWorkflow` subclass, or any class with a
|
|
64
|
+
`static workflow` config) instead of the removed `@Workflow` decorator. Without
|
|
65
|
+
this the rule would silently stop guarding every workflow in a 0.8 codebase.
|
|
66
|
+
|
|
3
67
|
## 0.6.1
|
|
4
68
|
|
|
5
69
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -53,7 +53,7 @@ export default defineConfig({
|
|
|
53
53
|
`node ace configure` registers an AdonisJS **Assembler `init` hook** that generates
|
|
54
54
|
a typed barrel of `app/workflows/` at build/dev time — exactly how core generates
|
|
55
55
|
the controllers/events/listeners barrels. The provider imports the generated
|
|
56
|
-
`.adonisjs/durable/workflows.ts` at boot and registers every
|
|
56
|
+
`.adonisjs/durable/workflows.ts` at boot and registers every `BaseWorkflow` class,
|
|
57
57
|
instead of scanning the directory with `readdir` at runtime. The file watcher
|
|
58
58
|
regenerates the barrel whenever a workflow file changes.
|
|
59
59
|
|
package/dist/commands/main.d.ts
CHANGED
|
@@ -6,8 +6,9 @@ import DurableWork from './work.js';
|
|
|
6
6
|
/**
|
|
7
7
|
* The commands barrel for `@adonis-agora/durable`. Registered in an app's `adonisrc` via
|
|
8
8
|
* `rcFile.addCommand('@adonis-agora/durable/commands')` (done by this package's `configure`). The ace
|
|
9
|
-
* kernel imports this module and treats it as a commands loader: a {@link ListLoader} over the
|
|
10
|
-
* durable commands
|
|
9
|
+
* kernel imports this module and treats it as a commands loader: a {@link ListLoader} over the four
|
|
10
|
+
* durable commands (`durable:work`, `durable:runs`, `durable:retry`, `make:workflow`) provides their
|
|
11
|
+
* metadata and constructors.
|
|
11
12
|
*/
|
|
12
13
|
declare const loader: ListLoader<typeof MakeWorkflow | typeof DurableRetry | typeof DurableRuns | typeof DurableWork>;
|
|
13
14
|
export declare const getMetaData: () => Promise<import("@adonisjs/core/types/ace").CommandMetaData[]>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../../commands/main.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAChD,OAAO,YAAY,MAAM,oBAAoB,CAAC;AAC9C,OAAO,YAAY,MAAM,YAAY,CAAC;AACtC,OAAO,WAAW,MAAM,WAAW,CAAC;AACpC,OAAO,WAAW,MAAM,WAAW,CAAC;AAEpC
|
|
1
|
+
{"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../../commands/main.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAChD,OAAO,YAAY,MAAM,oBAAoB,CAAC;AAC9C,OAAO,YAAY,MAAM,YAAY,CAAC;AACtC,OAAO,WAAW,MAAM,WAAW,CAAC;AACpC,OAAO,WAAW,MAAM,WAAW,CAAC;AAEpC;;;;;;GAMG;AACH,QAAA,MAAM,MAAM,iGAAyE,CAAC;AAEtF,eAAO,MAAM,WAAW,qEAAkC,CAAC;AAC3D,eAAO,MAAM,UAAU,uKAAiC,CAAC;AAEzD,eAAe,MAAM,CAAC"}
|
package/dist/commands/main.js
CHANGED
|
@@ -6,8 +6,9 @@ import DurableWork from './work.js';
|
|
|
6
6
|
/**
|
|
7
7
|
* The commands barrel for `@adonis-agora/durable`. Registered in an app's `adonisrc` via
|
|
8
8
|
* `rcFile.addCommand('@adonis-agora/durable/commands')` (done by this package's `configure`). The ace
|
|
9
|
-
* kernel imports this module and treats it as a commands loader: a {@link ListLoader} over the
|
|
10
|
-
* durable commands
|
|
9
|
+
* kernel imports this module and treats it as a commands loader: a {@link ListLoader} over the four
|
|
10
|
+
* durable commands (`durable:work`, `durable:runs`, `durable:retry`, `make:workflow`) provides their
|
|
11
|
+
* metadata and constructors.
|
|
11
12
|
*/
|
|
12
13
|
const loader = new ListLoader([DurableWork, DurableRuns, DurableRetry, MakeWorkflow]);
|
|
13
14
|
export const getMetaData = loader.getMetaData.bind(loader);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"main.js","sourceRoot":"","sources":["../../commands/main.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAChD,OAAO,YAAY,MAAM,oBAAoB,CAAC;AAC9C,OAAO,YAAY,MAAM,YAAY,CAAC;AACtC,OAAO,WAAW,MAAM,WAAW,CAAC;AACpC,OAAO,WAAW,MAAM,WAAW,CAAC;AAEpC
|
|
1
|
+
{"version":3,"file":"main.js","sourceRoot":"","sources":["../../commands/main.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAChD,OAAO,YAAY,MAAM,oBAAoB,CAAC;AAC9C,OAAO,YAAY,MAAM,YAAY,CAAC;AACtC,OAAO,WAAW,MAAM,WAAW,CAAC;AACpC,OAAO,WAAW,MAAM,WAAW,CAAC;AAEpC;;;;;;GAMG;AACH,MAAM,MAAM,GAAG,IAAI,UAAU,CAAC,CAAC,WAAW,EAAE,WAAW,EAAE,YAAY,EAAE,YAAY,CAAC,CAAC,CAAC;AAEtF,MAAM,CAAC,MAAM,WAAW,GAAG,MAAM,CAAC,WAAW,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;AAC3D,MAAM,CAAC,MAAM,UAAU,GAAG,MAAM,CAAC,UAAU,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;AAEzD,eAAe,MAAM,CAAC"}
|
|
@@ -2,9 +2,10 @@ import { BaseCommand } from '@adonisjs/core/ace';
|
|
|
2
2
|
import type { CommandOptions } from '@adonisjs/core/types/ace';
|
|
3
3
|
/**
|
|
4
4
|
* `node ace make:workflow <name>` — scaffold a durable workflow class under `app/workflows/`, the
|
|
5
|
-
* parallel to `@adonisjs/queue`'s `make:job` (which scaffolds `app/jobs/`). The generated class
|
|
6
|
-
*
|
|
7
|
-
*
|
|
5
|
+
* parallel to `@adonisjs/queue`'s `make:job` (which scaffolds `app/jobs/`). The generated class
|
|
6
|
+
* extends `BaseWorkflow` (the blessed authoring form) with a `static workflow = { name, version }`
|
|
7
|
+
* identity and a `run(ctx, input)` method, and is auto-registered on the engine at boot by the
|
|
8
|
+
* durable provider — no manual `engine.register(...)`.
|
|
8
9
|
*/
|
|
9
10
|
export default class MakeWorkflow extends BaseCommand {
|
|
10
11
|
static commandName: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"make_workflow.d.ts","sourceRoot":"","sources":["../../commands/make_workflow.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAQ,MAAM,oBAAoB,CAAC;AACvD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAG/D
|
|
1
|
+
{"version":3,"file":"make_workflow.d.ts","sourceRoot":"","sources":["../../commands/make_workflow.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAQ,MAAM,oBAAoB,CAAC;AACvD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAG/D;;;;;;GAMG;AACH,MAAM,CAAC,OAAO,OAAO,YAAa,SAAQ,WAAW;IACnD,OAAgB,WAAW,SAAmB;IAC9C,OAAgB,WAAW,SAAyC;IACpE,OAAgB,OAAO,EAAE,cAAc,CAA+B;IAG9D,IAAI,EAAE,MAAM,CAAC;IAEN,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC;CAOpC"}
|
|
@@ -8,9 +8,10 @@ import { BaseCommand, args } from '@adonisjs/core/ace';
|
|
|
8
8
|
import { stubsRoot } from '../stubs/main.js';
|
|
9
9
|
/**
|
|
10
10
|
* `node ace make:workflow <name>` — scaffold a durable workflow class under `app/workflows/`, the
|
|
11
|
-
* parallel to `@adonisjs/queue`'s `make:job` (which scaffolds `app/jobs/`). The generated class
|
|
12
|
-
*
|
|
13
|
-
*
|
|
11
|
+
* parallel to `@adonisjs/queue`'s `make:job` (which scaffolds `app/jobs/`). The generated class
|
|
12
|
+
* extends `BaseWorkflow` (the blessed authoring form) with a `static workflow = { name, version }`
|
|
13
|
+
* identity and a `run(ctx, input)` method, and is auto-registered on the engine at boot by the
|
|
14
|
+
* durable provider — no manual `engine.register(...)`.
|
|
14
15
|
*/
|
|
15
16
|
export default class MakeWorkflow extends BaseCommand {
|
|
16
17
|
static commandName = 'make:workflow';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"make_workflow.js","sourceRoot":"","sources":["../../commands/make_workflow.ts"],"names":[],"mappings":";;;;;;AAAA,OAAO,EAAE,WAAW,EAAE,IAAI,EAAE,MAAM,oBAAoB,CAAC;AAEvD,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAE7C
|
|
1
|
+
{"version":3,"file":"make_workflow.js","sourceRoot":"","sources":["../../commands/make_workflow.ts"],"names":[],"mappings":";;;;;;AAAA,OAAO,EAAE,WAAW,EAAE,IAAI,EAAE,MAAM,oBAAoB,CAAC;AAEvD,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAE7C;;;;;;GAMG;AACH,MAAM,CAAC,OAAO,OAAO,YAAa,SAAQ,WAAW;IACnD,MAAM,CAAU,WAAW,GAAG,eAAe,CAAC;IAC9C,MAAM,CAAU,WAAW,GAAG,qCAAqC,CAAC;IACpE,MAAM,CAAU,OAAO,GAAmB,EAAE,iBAAiB,EAAE,IAAI,EAAE,CAAC;IAK7D,KAAK,CAAC,GAAG;QAChB,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,cAAc,EAAE,CAAC;QAC7C,MAAM,QAAQ,CAAC,aAAa,CAAC,SAAS,EAAE,yBAAyB,EAAE;YACjE,KAAK,EAAE,IAAI,CAAC,MAAM,CAAC,KAAK;YACxB,MAAM,EAAE,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC;SACpD,CAAC,CAAC;IACL,CAAC;;AARO;IADP,IAAI,CAAC,MAAM,CAAC,EAAE,WAAW,EAAE,sBAAsB,EAAE,CAAC;0CAChC"}
|
package/dist/configure.d.ts
CHANGED
|
@@ -3,12 +3,13 @@ import type Configure from '@adonisjs/core/commands/configure';
|
|
|
3
3
|
* `node ace configure @adonis-agora/durable` — auto-wires the package:
|
|
4
4
|
*
|
|
5
5
|
* 1. registers the core service provider in `adonisrc.ts`;
|
|
6
|
-
* 2. registers the ace commands barrel (
|
|
7
|
-
* `durable:retry`);
|
|
6
|
+
* 2. registers the ace commands barrel (four commands: `durable:work`,
|
|
7
|
+
* `durable:runs`, `durable:retry`, `make:workflow`);
|
|
8
8
|
* 3. registers the optional dashboard provider;
|
|
9
|
-
* 4. registers the Assembler `init`
|
|
10
|
-
*
|
|
11
|
-
* runtime;
|
|
9
|
+
* 4. registers the Assembler `init` hooks that generate the typed `app/workflows`
|
|
10
|
+
* and `app/steps` barrels at build/dev time (the provider imports them instead
|
|
11
|
+
* of scanning at runtime; each falls back to the runtime scan when its barrel
|
|
12
|
+
* is absent);
|
|
12
13
|
* 5. publishes `config/durable.ts` + `config/durable_dashboard.ts`;
|
|
13
14
|
* 6. publishes the Lucid migrations for the optional `lucid` store and `db`
|
|
14
15
|
* transport drivers (run `node ace migration:run`, and delete the transport
|
package/dist/configure.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"configure.d.ts","sourceRoot":"","sources":["../configure.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,SAAS,MAAM,mCAAmC,CAAC;AAG/D
|
|
1
|
+
{"version":3,"file":"configure.d.ts","sourceRoot":"","sources":["../configure.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,SAAS,MAAM,mCAAmC,CAAC;AAG/D;;;;;;;;;;;;;;;GAeG;AACH,wBAAsB,SAAS,CAAC,OAAO,EAAE,SAAS,iBAqBjD"}
|
package/dist/configure.js
CHANGED
|
@@ -3,12 +3,13 @@ import { stubsRoot } from './stubs/main.js';
|
|
|
3
3
|
* `node ace configure @adonis-agora/durable` — auto-wires the package:
|
|
4
4
|
*
|
|
5
5
|
* 1. registers the core service provider in `adonisrc.ts`;
|
|
6
|
-
* 2. registers the ace commands barrel (
|
|
7
|
-
* `durable:retry`);
|
|
6
|
+
* 2. registers the ace commands barrel (four commands: `durable:work`,
|
|
7
|
+
* `durable:runs`, `durable:retry`, `make:workflow`);
|
|
8
8
|
* 3. registers the optional dashboard provider;
|
|
9
|
-
* 4. registers the Assembler `init`
|
|
10
|
-
*
|
|
11
|
-
* runtime;
|
|
9
|
+
* 4. registers the Assembler `init` hooks that generate the typed `app/workflows`
|
|
10
|
+
* and `app/steps` barrels at build/dev time (the provider imports them instead
|
|
11
|
+
* of scanning at runtime; each falls back to the runtime scan when its barrel
|
|
12
|
+
* is absent);
|
|
12
13
|
* 5. publishes `config/durable.ts` + `config/durable_dashboard.ts`;
|
|
13
14
|
* 6. publishes the Lucid migrations for the optional `lucid` store and `db`
|
|
14
15
|
* transport drivers (run `node ace migration:run`, and delete the transport
|
|
@@ -20,8 +21,10 @@ export async function configure(command) {
|
|
|
20
21
|
rcFile.addProvider('@adonis-agora/durable/durable_provider');
|
|
21
22
|
rcFile.addProvider('@adonis-agora/durable/dashboard_provider');
|
|
22
23
|
rcFile.addCommand('@adonis-agora/durable/commands');
|
|
23
|
-
// Generate the typed app/workflows
|
|
24
|
+
// Generate the typed app/workflows + app/steps barrels at build/dev time (each replaces its
|
|
25
|
+
// runtime readdir scan; the provider falls back to the scan when a barrel is absent).
|
|
24
26
|
rcFile.addAssemblerHook('init', '@adonis-agora/durable/hooks/workflows');
|
|
27
|
+
rcFile.addAssemblerHook('init', '@adonis-agora/durable/hooks/steps');
|
|
25
28
|
});
|
|
26
29
|
await codemods.makeUsingStub(stubsRoot, 'config/durable.stub', {});
|
|
27
30
|
await codemods.makeUsingStub(stubsRoot, 'config/durable_dashboard.stub', {});
|
package/dist/configure.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"configure.js","sourceRoot":"","sources":["../configure.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAE5C
|
|
1
|
+
{"version":3,"file":"configure.js","sourceRoot":"","sources":["../configure.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAE5C;;;;;;;;;;;;;;;GAeG;AACH,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,OAAkB;IAChD,MAAM,QAAQ,GAAG,MAAM,OAAO,CAAC,cAAc,EAAE,CAAC;IAEhD,MAAM,QAAQ,CAAC,YAAY,CAAC,CAAC,MAAM,EAAE,EAAE;QACrC,MAAM,CAAC,WAAW,CAAC,wCAAwC,CAAC,CAAC;QAC7D,MAAM,CAAC,WAAW,CAAC,0CAA0C,CAAC,CAAC;QAC/D,MAAM,CAAC,UAAU,CAAC,gCAAgC,CAAC,CAAC;QACpD,4FAA4F;QAC5F,sFAAsF;QACtF,MAAM,CAAC,gBAAgB,CAAC,MAAM,EAAE,uCAAuC,CAAC,CAAC;QACzE,MAAM,CAAC,gBAAgB,CAAC,MAAM,EAAE,mCAAmC,CAAC,CAAC;IACvE,CAAC,CAAC,CAAC;IAEH,MAAM,QAAQ,CAAC,aAAa,CAAC,SAAS,EAAE,qBAAqB,EAAE,EAAE,CAAC,CAAC;IACnE,MAAM,QAAQ,CAAC,aAAa,CAAC,SAAS,EAAE,+BAA+B,EAAE,EAAE,CAAC,CAAC;IAC7E,MAAM,QAAQ,CAAC,aAAa,CAAC,SAAS,EAAE,gDAAgD,EAAE,EAAE,CAAC,CAAC;IAC9F,MAAM,QAAQ,CAAC,aAAa,CAC1B,SAAS,EACT,0DAA0D,EAC1D,EAAE,CACH,CAAC;AACJ,CAAC"}
|
|
@@ -16,12 +16,43 @@ import type { ApplicationService } from '@adonisjs/core/types';
|
|
|
16
16
|
export default class DashboardProvider {
|
|
17
17
|
protected app: ApplicationService;
|
|
18
18
|
constructor(app: ApplicationService);
|
|
19
|
+
/** Warn once so a throwing `login` hook doesn't spam the logs on every failed attempt. */
|
|
20
|
+
private warnedOnHookThrow;
|
|
19
21
|
boot(): Promise<void>;
|
|
20
22
|
private registerRoutes;
|
|
21
23
|
/**
|
|
22
|
-
*
|
|
23
|
-
* the
|
|
24
|
+
* Mount the built-in `dashboardAuth` endpoints under `basePath`. All three are public (no guard):
|
|
25
|
+
* they create/destroy the session the {@link enforce} guard checks for.
|
|
26
|
+
*
|
|
27
|
+
* - `GET <base>/login` → the server-rendered login page (never varies per request beyond the
|
|
28
|
+
* developer-controlled `basePath`; `returnTo`/`error` are read client-side from the query).
|
|
29
|
+
* - `POST <base>/login` → verifies credentials via the host `login` hook and mints the cookie.
|
|
30
|
+
* Called by the login page's own `fetch` (JSON body). Uniform `401` on ANY failure — unknown
|
|
31
|
+
* user, wrong password, or a throwing hook — so there is no user-enumeration.
|
|
32
|
+
* - `GET <base>/logout` → clears the cookie and redirects back to the login page. A plain `GET`
|
|
33
|
+
* (idempotent, only ever destroys the caller's own session) so a simple `<a href>` works.
|
|
24
34
|
*/
|
|
25
|
-
private
|
|
35
|
+
private registerAuthRoutes;
|
|
36
|
+
/**
|
|
37
|
+
* Run the guards for a dashboard resource. Composes the existing `authorize` hook (bearer
|
|
38
|
+
* token/custom) with the optional `dashboardAuth` session guard — BOTH must pass:
|
|
39
|
+
*
|
|
40
|
+
* 1. `authorize` fails → `403` (unchanged behavior).
|
|
41
|
+
* 2. `dashboardAuth` configured AND no valid session → for a `page` request, redirect `302` to
|
|
42
|
+
* the login page carrying a sanitized `returnTo`; for an `api` request, a plain `401`.
|
|
43
|
+
*
|
|
44
|
+
* When `dashboardAuth` is unconfigured only step 1 runs, so behavior is byte-for-byte unchanged.
|
|
45
|
+
* Returns `false` (and has already written the response) when the request must short-circuit.
|
|
46
|
+
*/
|
|
47
|
+
private enforce;
|
|
48
|
+
/** Read the raw (unencoded) session cookie value, or `undefined` when absent. */
|
|
49
|
+
private readSessionCookie;
|
|
50
|
+
/**
|
|
51
|
+
* Write the signed session as an unsigned (`plainCookie`, `encode: false`) cookie — the value
|
|
52
|
+
* carries its own HMAC signature, so AdonisJS's cookie signing would be redundant double-wrapping.
|
|
53
|
+
* `HttpOnly` + `SameSite=Lax` (blocks cross-site POSTs carrying the cookie — CSRF coverage) +
|
|
54
|
+
* `Secure` on https. `Path=/` so it reaches both the UI and API mounts regardless of prefix.
|
|
55
|
+
*/
|
|
56
|
+
private writeSessionCookie;
|
|
26
57
|
}
|
|
27
58
|
//# sourceMappingURL=dashboard_provider.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dashboard_provider.d.ts","sourceRoot":"","sources":["../../providers/dashboard_provider.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"dashboard_provider.d.ts","sourceRoot":"","sources":["../../providers/dashboard_provider.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AA0B/D;;;;;;;;;;;;;GAaG;AACH,MAAM,CAAC,OAAO,OAAO,iBAAiB;IACxB,SAAS,CAAC,GAAG,EAAE,kBAAkB;gBAAvB,GAAG,EAAE,kBAAkB;IAE7C,0FAA0F;IAC1F,OAAO,CAAC,iBAAiB,CAAS;IAE5B,IAAI;IASV,OAAO,CAAC,cAAc;IAwDtB;;;;;;;;;;;OAWG;IACH,OAAO,CAAC,kBAAkB;IAgD1B;;;;;;;;;;OAUG;YACW,OAAO;IA0BrB,iFAAiF;IACjF,OAAO,CAAC,iBAAiB;IAKzB;;;;;OAKG;IACH,OAAO,CAAC,kBAAkB;CAU3B"}
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import router from '@adonisjs/core/services/router';
|
|
2
|
+
import { SESSION_COOKIE_NAME, performLogin, readSession, } from '../src/dashboard/auth.js';
|
|
2
3
|
import { resolveConfig, } from '../src/dashboard/define_config.js';
|
|
3
4
|
import { cancelRun, getRun, health, listRuns, retryRun, } from '../src/dashboard/handlers.js';
|
|
4
5
|
import { renderDashboard } from '../src/dashboard/html.js';
|
|
6
|
+
import { renderLoginPage } from '../src/dashboard/login_page.js';
|
|
5
7
|
import { WorkflowEngine } from '../src/index.js';
|
|
6
8
|
/**
|
|
7
9
|
* Mounts the durable dashboard into an AdonisJS app: a JSON API over the
|
|
@@ -22,6 +24,8 @@ export default class DashboardProvider {
|
|
|
22
24
|
constructor(app) {
|
|
23
25
|
this.app = app;
|
|
24
26
|
}
|
|
27
|
+
/** Warn once so a throwing `login` hook doesn't spam the logs on every failed attempt. */
|
|
28
|
+
warnedOnHookThrow = false;
|
|
25
29
|
async boot() {
|
|
26
30
|
const config = resolveConfig(this.app.config.get('durable_dashboard', {}));
|
|
27
31
|
if (!config.enabled)
|
|
@@ -37,10 +41,18 @@ export default class DashboardProvider {
|
|
|
37
41
|
const engine = await this.app.container.make(WorkflowEngine);
|
|
38
42
|
return { engine };
|
|
39
43
|
};
|
|
44
|
+
// Built-in `dashboardAuth` login screen (opt-in). Registered ONLY when configured, so omitting
|
|
45
|
+
// `dashboardAuth` leaves route registration byte-for-byte as it was. These endpoints are public
|
|
46
|
+
// (behind NEITHER guard): they MINT the session the guard checks for, and the login page must
|
|
47
|
+
// stay reachable while unauthenticated — including in production, where the default `authorize`
|
|
48
|
+
// hook would otherwise deny it.
|
|
49
|
+
if (config.dashboardAuth) {
|
|
50
|
+
this.registerAuthRoutes(config, config.dashboardAuth);
|
|
51
|
+
}
|
|
40
52
|
// The HTML page.
|
|
41
53
|
router
|
|
42
54
|
.get(config.path === '' ? '/' : config.path, async (ctx) => {
|
|
43
|
-
if (!(await this.
|
|
55
|
+
if (!(await this.enforce(config, ctx, 'page')))
|
|
44
56
|
return;
|
|
45
57
|
ctx.response.header('content-type', 'text/html; charset=utf-8');
|
|
46
58
|
return ctx.response.send(renderDashboard(apiBase));
|
|
@@ -49,7 +61,7 @@ export default class DashboardProvider {
|
|
|
49
61
|
// JSON API.
|
|
50
62
|
const json = (handler) => {
|
|
51
63
|
return async (ctx) => {
|
|
52
|
-
if (!(await this.
|
|
64
|
+
if (!(await this.enforce(config, ctx, 'api')))
|
|
53
65
|
return;
|
|
54
66
|
try {
|
|
55
67
|
const result = await handler(await deps(), toApiRequest(ctx));
|
|
@@ -71,16 +83,108 @@ export default class DashboardProvider {
|
|
|
71
83
|
.as('durable_dashboard.health');
|
|
72
84
|
}
|
|
73
85
|
/**
|
|
74
|
-
*
|
|
75
|
-
* the
|
|
86
|
+
* Mount the built-in `dashboardAuth` endpoints under `basePath`. All three are public (no guard):
|
|
87
|
+
* they create/destroy the session the {@link enforce} guard checks for.
|
|
88
|
+
*
|
|
89
|
+
* - `GET <base>/login` → the server-rendered login page (never varies per request beyond the
|
|
90
|
+
* developer-controlled `basePath`; `returnTo`/`error` are read client-side from the query).
|
|
91
|
+
* - `POST <base>/login` → verifies credentials via the host `login` hook and mints the cookie.
|
|
92
|
+
* Called by the login page's own `fetch` (JSON body). Uniform `401` on ANY failure — unknown
|
|
93
|
+
* user, wrong password, or a throwing hook — so there is no user-enumeration.
|
|
94
|
+
* - `GET <base>/logout` → clears the cookie and redirects back to the login page. A plain `GET`
|
|
95
|
+
* (idempotent, only ever destroys the caller's own session) so a simple `<a href>` works.
|
|
96
|
+
*/
|
|
97
|
+
registerAuthRoutes(config, auth) {
|
|
98
|
+
const loginPath = `${config.path}/login`;
|
|
99
|
+
const logoutPath = `${config.path}/logout`;
|
|
100
|
+
router
|
|
101
|
+
.get(loginPath, async (ctx) => {
|
|
102
|
+
ctx.response.header('content-type', 'text/html; charset=utf-8');
|
|
103
|
+
ctx.response.header('cache-control', 'no-store, must-revalidate');
|
|
104
|
+
return ctx.response.send(renderLoginPage(config.path));
|
|
105
|
+
})
|
|
106
|
+
.as('durable_dashboard.login.page');
|
|
107
|
+
router
|
|
108
|
+
.post(loginPath, async (ctx) => {
|
|
109
|
+
const outcome = await performLogin(auth, ctx.request.body(), config.path);
|
|
110
|
+
if (outcome.kind === 'bad-request') {
|
|
111
|
+
return ctx.response.status(400).json({ error: outcome.message });
|
|
112
|
+
}
|
|
113
|
+
if (outcome.kind === 'unauthorized') {
|
|
114
|
+
// A throwing hook is a denial (never a 500), warn-logged once so a buggy hook doesn't
|
|
115
|
+
// flood the logs. The client sees the same uniform 401 either way.
|
|
116
|
+
if (outcome.hookError !== undefined && !this.warnedOnHookThrow) {
|
|
117
|
+
this.warnedOnHookThrow = true;
|
|
118
|
+
const message = outcome.hookError instanceof Error
|
|
119
|
+
? outcome.hookError.message
|
|
120
|
+
: String(outcome.hookError);
|
|
121
|
+
const logger = await this.app.container.make('logger');
|
|
122
|
+
logger.warn(`dashboardAuth login hook threw; treating as denial. ${message}`);
|
|
123
|
+
}
|
|
124
|
+
return ctx.response.status(401).json({ error: outcome.message });
|
|
125
|
+
}
|
|
126
|
+
this.writeSessionCookie(ctx, auth, outcome.cookieValue);
|
|
127
|
+
return ctx.response.status(200).json({ redirectTo: outcome.redirectTo });
|
|
128
|
+
})
|
|
129
|
+
.as('durable_dashboard.login.submit');
|
|
130
|
+
router
|
|
131
|
+
.get(logoutPath, async (ctx) => {
|
|
132
|
+
ctx.response.clearCookie(SESSION_COOKIE_NAME, { path: '/' });
|
|
133
|
+
return ctx.response.redirect().toPath(loginPath);
|
|
134
|
+
})
|
|
135
|
+
.as('durable_dashboard.logout');
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Run the guards for a dashboard resource. Composes the existing `authorize` hook (bearer
|
|
139
|
+
* token/custom) with the optional `dashboardAuth` session guard — BOTH must pass:
|
|
140
|
+
*
|
|
141
|
+
* 1. `authorize` fails → `403` (unchanged behavior).
|
|
142
|
+
* 2. `dashboardAuth` configured AND no valid session → for a `page` request, redirect `302` to
|
|
143
|
+
* the login page carrying a sanitized `returnTo`; for an `api` request, a plain `401`.
|
|
144
|
+
*
|
|
145
|
+
* When `dashboardAuth` is unconfigured only step 1 runs, so behavior is byte-for-byte unchanged.
|
|
146
|
+
* Returns `false` (and has already written the response) when the request must short-circuit.
|
|
76
147
|
*/
|
|
77
|
-
async
|
|
148
|
+
async enforce(config, ctx, mode) {
|
|
78
149
|
const allowed = await config.authorize(ctx);
|
|
79
150
|
if (!allowed) {
|
|
80
151
|
ctx.response.status(403).json({ error: 'forbidden' });
|
|
81
152
|
return false;
|
|
82
153
|
}
|
|
83
|
-
|
|
154
|
+
const auth = config.dashboardAuth;
|
|
155
|
+
if (!auth)
|
|
156
|
+
return true;
|
|
157
|
+
const session = readSession(auth, this.readSessionCookie(ctx));
|
|
158
|
+
if (session)
|
|
159
|
+
return true;
|
|
160
|
+
if (mode === 'page') {
|
|
161
|
+
const returnTo = ctx.request.url(true);
|
|
162
|
+
ctx.response.redirect().withQs('returnTo', returnTo).toPath(`${config.path}/login`);
|
|
163
|
+
return false;
|
|
164
|
+
}
|
|
165
|
+
ctx.response.status(401).json({ error: 'unauthorized' });
|
|
166
|
+
return false;
|
|
167
|
+
}
|
|
168
|
+
/** Read the raw (unencoded) session cookie value, or `undefined` when absent. */
|
|
169
|
+
readSessionCookie(ctx) {
|
|
170
|
+
const value = ctx.request.plainCookie(SESSION_COOKIE_NAME, undefined, false);
|
|
171
|
+
return typeof value === 'string' && value !== '' ? value : undefined;
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Write the signed session as an unsigned (`plainCookie`, `encode: false`) cookie — the value
|
|
175
|
+
* carries its own HMAC signature, so AdonisJS's cookie signing would be redundant double-wrapping.
|
|
176
|
+
* `HttpOnly` + `SameSite=Lax` (blocks cross-site POSTs carrying the cookie — CSRF coverage) +
|
|
177
|
+
* `Secure` on https. `Path=/` so it reaches both the UI and API mounts regardless of prefix.
|
|
178
|
+
*/
|
|
179
|
+
writeSessionCookie(ctx, auth, value) {
|
|
180
|
+
ctx.response.plainCookie(SESSION_COOKIE_NAME, value, {
|
|
181
|
+
httpOnly: true,
|
|
182
|
+
sameSite: 'lax',
|
|
183
|
+
secure: ctx.request.secure(),
|
|
184
|
+
path: '/',
|
|
185
|
+
maxAge: Math.floor(auth.ttlMs / 1000),
|
|
186
|
+
encode: false,
|
|
187
|
+
});
|
|
84
188
|
}
|
|
85
189
|
}
|
|
86
190
|
/** Adapt an AdonisJS `HttpContext` to the framework-light {@link ApiRequest}. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dashboard_provider.js","sourceRoot":"","sources":["../../providers/dashboard_provider.ts"],"names":[],"mappings":"AACA,OAAO,MAAM,MAAM,gCAAgC,CAAC;AAEpD,OAAO,EAGL,aAAa,GACd,MAAM,mCAAmC,CAAC;AAC3C,OAAO,EAIL,SAAS,EACT,MAAM,EACN,MAAM,EACN,QAAQ,EACR,QAAQ,GACT,MAAM,8BAA8B,CAAC;AACtC,OAAO,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAC3D,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAEjD;;;;;;;;;;;;;GAaG;AACH,MAAM,CAAC,OAAO,OAAO,iBAAiB;IACd;IAAtB,YAAsB,GAAuB;QAAvB,QAAG,GAAH,GAAG,CAAoB;IAAG,CAAC;IAEjD,KAAK,CAAC,IAAI;QACR,MAAM,MAAM,GAAG,aAAa,CAC1B,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,CAAyB,mBAAmB,EAAE,EAAE,CAAC,CACrE,CAAC;QACF,IAAI,CAAC,MAAM,CAAC,OAAO;YAAE,OAAO;QAE5B,IAAI,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC;IAC9B,CAAC;IAEO,cAAc,CAAC,MAAsC;QAC3D,MAAM,OAAO,GAAG,GAAG,MAAM,CAAC,IAAI,MAAM,CAAC;QAErC,mEAAmE;QACnE,kFAAkF;QAClF,8EAA8E;QAC9E,MAAM,IAAI,GAAG,KAAK,IAAmB,EAAE;YACrC,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;YAC7D,OAAO,EAAE,MAAM,EAAE,CAAC;QACpB,CAAC,CAAC;QAEF,iBAAiB;QACjB,MAAM;aACH,GAAG,CAAC,MAAM,CAAC,IAAI,KAAK,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE;YACzD,IAAI,CAAC,CAAC,MAAM,IAAI,CAAC,
|
|
1
|
+
{"version":3,"file":"dashboard_provider.js","sourceRoot":"","sources":["../../providers/dashboard_provider.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { WorkflowEngine } from './engine.js';
|
|
2
|
+
import type { StartOptions } from './engine.js';
|
|
3
|
+
import type { WorkflowCtx } from './interfaces.js';
|
|
4
|
+
import type { WorkflowClass, WorkflowInputOf, WorkflowOptions, WorkflowOutputOf } from './workflow-ref.js';
|
|
5
|
+
/**
|
|
6
|
+
* Options for `BaseWorkflow`'s static `start`/`dispatch`. A superset of {@link StartOptions} (priority,
|
|
7
|
+
* namespace, tags, searchAttributes) plus an explicit `runId`:
|
|
8
|
+
* - **Outside** a running workflow, `runId` sets the new run's id; omitted → a fresh `randomUUID()`.
|
|
9
|
+
* - **Inside** a running workflow, `runId` is the deterministic child id; omitted → the ctx derives a
|
|
10
|
+
* replay-stable id from the call position (DO NOT pass a random one — it breaks determinism).
|
|
11
|
+
*/
|
|
12
|
+
export type WorkflowDispatchOptions = {
|
|
13
|
+
runId?: string | undefined;
|
|
14
|
+
} & StartOptions;
|
|
15
|
+
/**
|
|
16
|
+
* How `BaseWorkflow`'s OUTSIDE static path locates the {@link WorkflowEngine}. Defaults to resolving
|
|
17
|
+
* the engine from the AdonisJS container (a module-singleton locator, exactly how a queue `Job`
|
|
18
|
+
* resolves its manager). Tests (or advanced embeddings) override it with {@link setWorkflowEngineResolver}
|
|
19
|
+
* to inject a specific engine without booting an application.
|
|
20
|
+
*/
|
|
21
|
+
export type WorkflowEngineResolver = () => WorkflowEngine | Promise<WorkflowEngine>;
|
|
22
|
+
/**
|
|
23
|
+
* Override how `BaseWorkflow.start`/`dispatch` resolve the engine when called OUTSIDE a running
|
|
24
|
+
* workflow. Pass a resolver returning the engine to use; pass `undefined` to restore the default
|
|
25
|
+
* (container lookup). Primarily a testing seam — inject an in-memory engine, then reset in teardown.
|
|
26
|
+
*/
|
|
27
|
+
export declare function setWorkflowEngineResolver(resolver: WorkflowEngineResolver | undefined): void;
|
|
28
|
+
/**
|
|
29
|
+
* Base class for a durable workflow. Declare the workflow's identity as a
|
|
30
|
+
* `static workflow = { name, version }` config and implement `run(ctx, input)`:
|
|
31
|
+
*
|
|
32
|
+
* ```ts
|
|
33
|
+
* export default class CheckoutWorkflow extends BaseWorkflow {
|
|
34
|
+
* static workflow = { name: 'checkout', version: '1' }
|
|
35
|
+
* async run(ctx: WorkflowCtx, order: Order) { … }
|
|
36
|
+
* }
|
|
37
|
+
* ```
|
|
38
|
+
*
|
|
39
|
+
* The static `workflow` config is resolved through `workflowName()` and registered via
|
|
40
|
+
* `app/workflows` auto-discovery.
|
|
41
|
+
*
|
|
42
|
+
* ## Context-aware dispatch
|
|
43
|
+
* The statics behave differently depending on whether they're called inside or outside a running
|
|
44
|
+
* workflow body (detected via the ambient ctx — see {@link getCurrentWorkflowCtx}):
|
|
45
|
+
*
|
|
46
|
+
* | | OUTSIDE (controller/service/script) | INSIDE a running workflow body |
|
|
47
|
+
* |------------|----------------------------------------------------------|---------------------------------------|
|
|
48
|
+
* | `.start` | `engine.start` + `waitForRun` → blocks, returns result | `ctx.child` → linked child, awaits it |
|
|
49
|
+
* | `.dispatch`| `engine.start` → fire-and-forget, returns `{ runId }` | `ctx.startChild` → fire-and-forget |
|
|
50
|
+
*
|
|
51
|
+
* `.start` always means "I want the result"; `.dispatch` always means "fire and forget" — in both
|
|
52
|
+
* contexts. Inside a workflow, routing through `ctx.child`/`ctx.startChild` (never the engine) is what
|
|
53
|
+
* keeps the run deterministic and replay-safe.
|
|
54
|
+
*/
|
|
55
|
+
export declare abstract class BaseWorkflow {
|
|
56
|
+
/**
|
|
57
|
+
* The workflow's identity + options. Read by `workflowMeta()`/`workflowName()` and by
|
|
58
|
+
* `app/workflows` auto-discovery. Omit it and the class is not a registrable workflow.
|
|
59
|
+
*/
|
|
60
|
+
static workflow?: WorkflowOptions;
|
|
61
|
+
/** The workflow body. `ctx` is the durable context; `input` is the run's typed input. */
|
|
62
|
+
abstract run(ctx: WorkflowCtx, input: unknown): unknown;
|
|
63
|
+
/**
|
|
64
|
+
* Start a run and **wait for its result**. Outside a workflow, enqueues via the engine and blocks
|
|
65
|
+
* until the run settles, returning its output. Inside a running workflow, starts a **linked child**
|
|
66
|
+
* (`ctx.child`) — the parent suspends until the child settles — and returns the child's output.
|
|
67
|
+
*/
|
|
68
|
+
static start<C extends WorkflowClass>(this: C, input: WorkflowInputOf<C>, opts?: WorkflowDispatchOptions): Promise<WorkflowOutputOf<C>>;
|
|
69
|
+
/**
|
|
70
|
+
* Start a run **fire-and-forget** and return its `{ runId }` without waiting. Outside a workflow,
|
|
71
|
+
* enqueues via the engine and returns immediately. Inside a running workflow, kicks off a
|
|
72
|
+
* fire-and-forget child (`ctx.startChild`) — the parent keeps running — and returns the child id.
|
|
73
|
+
*/
|
|
74
|
+
static dispatch<C extends WorkflowClass>(this: C, input: WorkflowInputOf<C>, opts?: WorkflowDispatchOptions): Promise<{
|
|
75
|
+
runId: string;
|
|
76
|
+
}>;
|
|
77
|
+
}
|
|
78
|
+
//# sourceMappingURL=base-workflow.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"base-workflow.d.ts","sourceRoot":"","sources":["../../src/base-workflow.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAEnD,OAAO,KAAK,EACV,aAAa,EACb,eAAe,EACf,eAAe,EACf,gBAAgB,EACjB,MAAM,mBAAmB,CAAC;AAE3B;;;;;;GAMG;AACH,MAAM,MAAM,uBAAuB,GAAG;IAAE,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;CAAE,GAAG,YAAY,CAAC;AAEpF;;;;;GAKG;AACH,MAAM,MAAM,sBAAsB,GAAG,MAAM,cAAc,GAAG,OAAO,CAAC,cAAc,CAAC,CAAC;AAIpF;;;;GAIG;AACH,wBAAgB,yBAAyB,CAAC,QAAQ,EAAE,sBAAsB,GAAG,SAAS,GAAG,IAAI,CAE5F;AAWD;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,8BAAsB,YAAY;IAChC;;;OAGG;IACH,MAAM,CAAC,QAAQ,CAAC,EAAE,eAAe,CAAC;IAElC,yFAAyF;IACzF,QAAQ,CAAC,GAAG,CAAC,GAAG,EAAE,WAAW,EAAE,KAAK,EAAE,OAAO,GAAG,OAAO;IAEvD;;;;OAIG;IACH,MAAM,CAAC,KAAK,CAAC,CAAC,SAAS,aAAa,EAClC,IAAI,EAAE,CAAC,EACP,KAAK,EAAE,eAAe,CAAC,CAAC,CAAC,EACzB,IAAI,CAAC,EAAE,uBAAuB,GAC7B,OAAO,CAAC,gBAAgB,CAAC,CAAC,CAAC,CAAC;IAwB/B;;;;OAIG;IACH,MAAM,CAAC,QAAQ,CAAC,CAAC,SAAS,aAAa,EACrC,IAAI,EAAE,CAAC,EACP,KAAK,EAAE,eAAe,CAAC,CAAC,CAAC,EACzB,IAAI,CAAC,EAAE,uBAAuB,GAC7B,OAAO,CAAC;QAAE,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;CAkB9B"}
|