@voyant-travel/workflows-orchestrator 0.113.0 → 0.114.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.
@@ -1,5 +1,6 @@
1
1
  import type { EnvironmentName } from "@voyant-travel/workflows";
2
2
  import { type DriverFactory } from "@voyant-travel/workflows/driver";
3
+ import { type WorkflowResolver } from "@voyant-travel/workflows/handler";
3
4
  import type { RunRecord, StepHandler } from "./types.js";
4
5
  export interface InMemoryDriverOptions {
5
6
  /** Default environment for `trigger()` calls that don't specify one. */
@@ -8,6 +9,13 @@ export interface InMemoryDriverOptions {
8
9
  tenantMeta?: RunRecord["tenantMeta"];
9
10
  /** Injectable clock; defaults to `Date.now`. */
10
11
  now?: () => number;
12
+ /**
13
+ * Executable workflow lookup for this driver instance.
14
+ *
15
+ * Omitting this dependency is deprecated and falls back to the process-global
16
+ * authoring registry for backward compatibility with existing consumers.
17
+ */
18
+ workflowResolver?: WorkflowResolver;
11
19
  /** Step handler override — defaults to in-process `handleStepRequest`. */
12
20
  handler?: StepHandler;
13
21
  /** Schedule runner tick interval. Defaults to 1_000 ms. */
@@ -1 +1 @@
1
- {"version":3,"file":"driver-inmemory.d.ts","sourceRoot":"","sources":["../src/driver-inmemory.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EACV,eAAe,EAMhB,MAAM,0BAA0B,CAAA;AACjC,OAAO,EACL,KAAK,aAAa,EAQnB,MAAM,iCAAiC,CAAA;AAkBxC,OAAO,KAAK,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA;AAIxD,MAAM,WAAW,qBAAqB;IACpC,wEAAwE;IACxE,kBAAkB,CAAC,EAAE,eAAe,CAAA;IACpC,wDAAwD;IACxD,UAAU,CAAC,EAAE,SAAS,CAAC,YAAY,CAAC,CAAA;IACpC,gDAAgD;IAChD,GAAG,CAAC,EAAE,MAAM,MAAM,CAAA;IAClB,0EAA0E;IAC1E,OAAO,CAAC,EAAE,WAAW,CAAA;IACrB,2DAA2D;IAC3D,sBAAsB,CAAC,EAAE,MAAM,CAAA;IAC/B,2EAA2E;IAC3E,qBAAqB,CAAC,EAAE,OAAO,CAAA;CAChC;AAQD;;;;;;;;;;GAUG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,GAAE,qBAA0B,GAAG,aAAa,CA+TpF"}
1
+ {"version":3,"file":"driver-inmemory.d.ts","sourceRoot":"","sources":["../src/driver-inmemory.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EACV,eAAe,EAMhB,MAAM,0BAA0B,CAAA;AAEjC,OAAO,EACL,KAAK,aAAa,EAQnB,MAAM,iCAAiC,CAAA;AAExC,OAAO,EAEL,KAAK,gBAAgB,EAEtB,MAAM,kCAAkC,CAAA;AAgBzC,OAAO,KAAK,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA;AAIxD,MAAM,WAAW,qBAAqB;IACpC,wEAAwE;IACxE,kBAAkB,CAAC,EAAE,eAAe,CAAA;IACpC,wDAAwD;IACxD,UAAU,CAAC,EAAE,SAAS,CAAC,YAAY,CAAC,CAAA;IACpC,gDAAgD;IAChD,GAAG,CAAC,EAAE,MAAM,MAAM,CAAA;IAClB;;;;;OAKG;IACH,gBAAgB,CAAC,EAAE,gBAAgB,CAAA;IACnC,0EAA0E;IAC1E,OAAO,CAAC,EAAE,WAAW,CAAA;IACrB,2DAA2D;IAC3D,sBAAsB,CAAC,EAAE,MAAM,CAAA;IAC/B,2EAA2E;IAC3E,qBAAqB,CAAC,EAAE,OAAO,CAAA;CAChC;AAYD;;;;;;;;;;GAUG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,GAAE,qBAA0B,GAAG,aAAa,CAgUpF"}
@@ -11,9 +11,10 @@
11
11
  // call to the factory yields a fresh, isolated driver. No global state.
12
12
  //
13
13
  // Authoritative architecture: docs/architecture/workflows-runtime-architecture.md §6.
14
+ import { getWorkflow } from "@voyant-travel/workflows";
14
15
  import { ManifestNotRegisteredError, } from "@voyant-travel/workflows/driver";
15
16
  import { deriveStableEventId } from "@voyant-travel/workflows/events";
16
- import { handleStepRequest } from "@voyant-travel/workflows/handler";
17
+ import { handleStepRequest, } from "@voyant-travel/workflows/handler";
17
18
  import { createInProcessConcurrencyCoordinator, } from "./concurrency.js";
18
19
  import { routeEvent } from "./event-router.js";
19
20
  import { createInMemoryRunStore } from "./in-memory-store.js";
@@ -24,6 +25,9 @@ const DEFAULT_TENANT_META = {
24
25
  projectId: "default",
25
26
  organizationId: "default",
26
27
  };
28
+ const legacyWorkflowResolver = {
29
+ resolve: getWorkflow,
30
+ };
27
31
  /**
28
32
  * Build an in-memory driver factory. The factory closes over its
29
33
  * options and returns a fresh `WorkflowDriver` when `createApp()`
@@ -43,11 +47,12 @@ export function createInMemoryDriver(opts = {}) {
43
47
  const now = opts.now ?? deps.now ?? (() => Date.now());
44
48
  const tenantMeta = opts.tenantMeta ?? DEFAULT_TENANT_META;
45
49
  const defaultEnv = opts.defaultEnvironment ?? "development";
50
+ const workflowResolver = opts.workflowResolver ?? legacyWorkflowResolver;
46
51
  // Wire the framework-supplied service container through to step bodies.
47
52
  // The handler closes over `deps.services` so every step invocation
48
53
  // surfaces it as `ctx.services` inside the workflow body.
49
54
  const handler = opts.handler ??
50
- (async (req, stepOpts) => handleStepRequest(req, { services: deps.services }, stepOpts));
55
+ (async (req, stepOpts) => handleStepRequest(req, { workflowResolver, services: deps.services }, stepOpts));
51
56
  let shuttingDown = false;
52
57
  const concurrency = createInProcessConcurrencyCoordinator({
53
58
  async cancelRun(runId, reason) {
@@ -1 +1 @@
1
- {"version":3,"file":"node-selfhost-deps.d.ts","sourceRoot":"","sources":["../../src/node/node-selfhost-deps.ts"],"names":[],"mappings":"AAoBA,OAAO,KAAK,EAEV,qBAAqB,EACrB,SAAS,EACT,WAAW,EACZ,MAAM,sBAAsB,CAAA;AAwB7B,wBAAsB,mBAAmB,CAAC,IAAI,EAAE,qBAAqB,GAAG,OAAO,CAAC,WAAW,CAAC,CAS3F;AAED,wBAAsB,kBAAkB,CACtC,IAAI,EAAE,IAAI,CACR,qBAAqB,EACnB,WAAW,GACX,WAAW,GACX,gBAAgB,GAChB,UAAU,GACV,OAAO,GACP,aAAa,GACb,sBAAsB,GACtB,eAAe,GACf,kBAAkB,CACrB,GACA,OAAO,CAAC,SAAS,CAAC,CAkapB"}
1
+ {"version":3,"file":"node-selfhost-deps.d.ts","sourceRoot":"","sources":["../../src/node/node-selfhost-deps.ts"],"names":[],"mappings":"AAoBA,OAAO,KAAK,EAEV,qBAAqB,EACrB,SAAS,EACT,WAAW,EACZ,MAAM,sBAAsB,CAAA;AAwB7B,wBAAsB,mBAAmB,CAAC,IAAI,EAAE,qBAAqB,GAAG,OAAO,CAAC,WAAW,CAAC,CAS3F;AAED,wBAAsB,kBAAkB,CACtC,IAAI,EAAE,IAAI,CACR,qBAAqB,EACnB,WAAW,GACX,WAAW,GACX,gBAAgB,GAChB,UAAU,GACV,OAAO,GACP,aAAa,GACb,sBAAsB,GACtB,eAAe,GACf,kBAAkB,CACrB,GACA,OAAO,CAAC,SAAS,CAAC,CAuapB"}
@@ -47,12 +47,17 @@ export async function createSelfHostDeps(opts) {
47
47
  await assertReadableFile(entryAbs, "workflow entry");
48
48
  await loadEntryFile(entryAbs, { cacheBust: opts.cacheBustEntry });
49
49
  const _handlerMod = await import("@voyant-travel/workflows/handler");
50
+ const workflowDefinitions = wfMod.__listRegisteredWorkflows();
51
+ const workflowsById = new Map(workflowDefinitions.map((workflow) => [workflow.id, workflow]));
52
+ const workflowResolver = {
53
+ resolve: (workflowId) => workflowsById.get(workflowId),
54
+ };
50
55
  const rateLimiter = createInMemoryRateLimiter();
51
56
  const chunkBus = createChunkBus();
52
- const stepHandler = async (req, stepOpts) => handleStepRequest(req, { rateLimiter, services: opts.services }, stepOpts);
57
+ const stepHandler = async (req, stepOpts) => handleStepRequest(req, { workflowResolver, rateLimiter, services: opts.services }, stepOpts);
53
58
  const wakeupStore = pg ? createPostgresWakeupStore({ db: pg.db }) : createFsWakeupStore();
54
59
  const leaseOwner = opts.wakeupLeaseOwner ?? createDefaultWakeupLeaseOwner();
55
- const listWorkflows = () => wfMod.__listRegisteredWorkflows().map((workflow) => ({
60
+ const listWorkflows = () => workflowDefinitions.map((workflow) => ({
56
61
  id: workflow.id,
57
62
  description: workflow.config.description,
58
63
  }));
@@ -162,7 +167,7 @@ export async function createSelfHostDeps(opts) {
162
167
  return { ok: true, saved };
163
168
  };
164
169
  const triggerRun = async ({ workflowId, input, runId, tags, triggeredByUserId, }) => {
165
- const workflow = wfMod.__listRegisteredWorkflows().find((entry) => entry.id === workflowId);
170
+ const workflow = workflowResolver.resolve(workflowId);
166
171
  if (!workflow) {
167
172
  return {
168
173
  ok: false,
@@ -231,7 +236,7 @@ export async function createSelfHostDeps(opts) {
231
236
  };
232
237
  }
233
238
  const workflowId = parent?.workflowId ?? requestedWorkflowId;
234
- const workflow = wfMod.__listRegisteredWorkflows().find((entry) => entry.id === workflowId);
239
+ const workflow = workflowResolver.resolve(workflowId);
235
240
  if (!workflow) {
236
241
  return {
237
242
  ok: false,
@@ -1,4 +1,5 @@
1
1
  import type { DriverFactory } from "@voyant-travel/workflows/driver";
2
+ import { type WorkflowResolver } from "@voyant-travel/workflows/handler";
2
3
  import type { drizzle } from "drizzle-orm/node-postgres";
3
4
  import { type RunRecord, type StepHandler } from "./core.js";
4
5
  type Db = ReturnType<typeof drizzle>;
@@ -11,6 +12,13 @@ export interface StandaloneDriverOptions {
11
12
  tenantMeta?: RunRecord["tenantMeta"];
12
13
  /** Injectable clock; defaults to `Date.now`. */
13
14
  now?: () => number;
15
+ /**
16
+ * Executable workflow lookup for this driver instance.
17
+ *
18
+ * Omitting this dependency is deprecated and falls back to the process-global
19
+ * authoring registry for backward compatibility with existing consumers.
20
+ */
21
+ workflowResolver?: WorkflowResolver;
14
22
  /**
15
23
  * Step handler override. Defaults to in-process `handleStepRequest`
16
24
  * with the framework-supplied `services` container plumbed through
@@ -1 +1 @@
1
- {"version":3,"file":"node-standalone-driver.d.ts","sourceRoot":"","sources":["../../src/node/node-standalone-driver.ts"],"names":[],"mappings":"AA2BA,OAAO,KAAK,EACV,aAAa,EAOd,MAAM,iCAAiC,CAAA;AAIxC,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,2BAA2B,CAAA;AACxD,OAAO,EAML,KAAK,SAAS,EAId,KAAK,WAAW,EAEjB,MAAM,WAAW,CAAA;AAWlB,KAAK,EAAE,GAAG,UAAU,CAAC,OAAO,OAAO,CAAC,CAAA;AAIpC,MAAM,WAAW,uBAAuB;IACtC,4EAA4E;IAC5E,EAAE,EAAE,EAAE,CAAA;IACN,wEAAwE;IACxE,kBAAkB,CAAC,EAAE,YAAY,GAAG,SAAS,GAAG,aAAa,CAAA;IAC7D,wDAAwD;IACxD,UAAU,CAAC,EAAE,SAAS,CAAC,YAAY,CAAC,CAAA;IACpC,gDAAgD;IAChD,GAAG,CAAC,EAAE,MAAM,MAAM,CAAA;IAClB;;;;OAIG;IACH,OAAO,CAAC,EAAE,WAAW,CAAA;IACrB;;;;OAIG;IACH,sBAAsB,CAAC,EAAE,MAAM,CAAA;IAC/B;;;;;OAKG;IACH,oBAAoB,CAAC,EAAE,MAAM,CAAA;IAC7B;;;;;OAKG;IACH,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB;;;OAGG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB;;;;;OAKG;IACH,gBAAgB,CAAC,EAAE,OAAO,CAAA;IAC1B,2DAA2D;IAC3D,sBAAsB,CAAC,EAAE,MAAM,CAAA;IAC/B,2EAA2E;IAC3E,qBAAqB,CAAC,EAAE,OAAO,CAAA;CAChC;AA+FD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,uBAAuB,GAAG,aAAa,CAuXnF"}
1
+ {"version":3,"file":"node-standalone-driver.d.ts","sourceRoot":"","sources":["../../src/node/node-standalone-driver.ts"],"names":[],"mappings":"AA4BA,OAAO,KAAK,EACV,aAAa,EAOd,MAAM,iCAAiC,CAAA;AAExC,OAAO,EAEL,KAAK,gBAAgB,EAEtB,MAAM,kCAAkC,CAAA;AAEzC,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,2BAA2B,CAAA;AACxD,OAAO,EAML,KAAK,SAAS,EAId,KAAK,WAAW,EAEjB,MAAM,WAAW,CAAA;AAWlB,KAAK,EAAE,GAAG,UAAU,CAAC,OAAO,OAAO,CAAC,CAAA;AAIpC,MAAM,WAAW,uBAAuB;IACtC,4EAA4E;IAC5E,EAAE,EAAE,EAAE,CAAA;IACN,wEAAwE;IACxE,kBAAkB,CAAC,EAAE,YAAY,GAAG,SAAS,GAAG,aAAa,CAAA;IAC7D,wDAAwD;IACxD,UAAU,CAAC,EAAE,SAAS,CAAC,YAAY,CAAC,CAAA;IACpC,gDAAgD;IAChD,GAAG,CAAC,EAAE,MAAM,MAAM,CAAA;IAClB;;;;;OAKG;IACH,gBAAgB,CAAC,EAAE,gBAAgB,CAAA;IACnC;;;;OAIG;IACH,OAAO,CAAC,EAAE,WAAW,CAAA;IACrB;;;;OAIG;IACH,sBAAsB,CAAC,EAAE,MAAM,CAAA;IAC/B;;;;;OAKG;IACH,oBAAoB,CAAC,EAAE,MAAM,CAAA;IAC7B;;;;;OAKG;IACH,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB;;;OAGG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB;;;;;OAKG;IACH,gBAAgB,CAAC,EAAE,OAAO,CAAA;IAC1B,2DAA2D;IAC3D,sBAAsB,CAAC,EAAE,MAAM,CAAA;IAC/B,2EAA2E;IAC3E,qBAAqB,CAAC,EAAE,OAAO,CAAA;CAChC;AAmGD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,uBAAuB,GAAG,aAAa,CAwXnF"}
@@ -17,8 +17,9 @@
17
17
  // `createApp()` — by the framework's bootstrap.
18
18
  //
19
19
  // See docs/architecture/workflows-runtime-architecture.md.
20
+ import { getWorkflow } from "@voyant-travel/workflows";
20
21
  import { deriveStableEventId } from "@voyant-travel/workflows/events";
21
- import { handleStepRequest } from "@voyant-travel/workflows/handler";
22
+ import { handleStepRequest, } from "@voyant-travel/workflows/handler";
22
23
  import { createInProcessConcurrencyCoordinator, createScheduler, manifestScheduleSources, cancel as orchestratorCancel, trigger as orchestratorTrigger, routeEvent, } from "./core.js";
23
24
  import { createPersistentWakeupManager, } from "./persistent-wakeup-manager.js";
24
25
  import { createPostgresManifestStore } from "./postgres-manifest-store.js";
@@ -31,6 +32,9 @@ const DEFAULT_TENANT_META = {
31
32
  organizationId: "default",
32
33
  };
33
34
  const DEFAULT_MANIFEST_KEEP = 3;
35
+ const legacyWorkflowResolver = {
36
+ resolve: getWorkflow,
37
+ };
34
38
  function serializeWorkflowManifest(manifest) {
35
39
  return { ...manifest };
36
40
  }
@@ -121,11 +125,12 @@ export function createStandaloneDriver(opts) {
121
125
  const defaultEnv = opts.defaultEnvironment ?? "development";
122
126
  const keep = opts.manifestVersionsToKeep ?? DEFAULT_MANIFEST_KEEP;
123
127
  const leaseOwner = opts.wakeupLeaseOwner ?? `selfhost-standalone-${randomToken()}`;
128
+ const workflowResolver = opts.workflowResolver ?? legacyWorkflowResolver;
124
129
  // Wire the framework-supplied service container through to step bodies.
125
130
  // The handler closes over `deps.services` so every step invocation
126
131
  // surfaces it as `ctx.services` inside the workflow body.
127
132
  const handler = opts.handler ??
128
- (async (req, stepOpts) => handleStepRequest(req, { services: deps.services }, stepOpts));
133
+ (async (req, stepOpts) => handleStepRequest(req, { workflowResolver, services: deps.services }, stepOpts));
129
134
  // Persistent wakeup manager — polls `voyant_wakeups` for runs
130
135
  // parked on DATETIME waitpoints and resumes them via the orchestrator's
131
136
  // `resumeDueAlarms`. This is what makes `ctx.sleep(...)` actually
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@voyant-travel/workflows-orchestrator",
3
- "version": "0.113.0",
3
+ "version": "0.114.0",
4
4
  "description": "Postgres self-host orchestrator runtime for Voyant Workflows — drives runs through the tenant step handler over the v1 wire protocol.",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -49,7 +49,7 @@
49
49
  "dependencies": {
50
50
  "drizzle-orm": "^0.45.2",
51
51
  "pg": "^8.22.0",
52
- "@voyant-travel/workflows": "^0.113.0"
52
+ "@voyant-travel/workflows": "^0.114.0"
53
53
  },
54
54
  "devDependencies": {
55
55
  "@types/node": "^25.5.2",
@@ -1,10 +1,18 @@
1
- import { __resetRegistry } from "@voyant-travel/workflows"
2
- import { handleStepRequest, type WorkflowStepRequest } from "@voyant-travel/workflows/handler"
1
+ import { __resetRegistry, getWorkflow } from "@voyant-travel/workflows"
2
+ import {
3
+ handleStepRequest,
4
+ type WorkflowResolver,
5
+ type WorkflowStepRequest,
6
+ } from "@voyant-travel/workflows/handler"
3
7
  import { beforeEach } from "vitest"
4
8
  import type { StepHandler } from "../index.js"
5
9
 
10
+ export const workflowResolver: WorkflowResolver = {
11
+ resolve: (workflowId) => getWorkflow(workflowId),
12
+ }
13
+
6
14
  export const handler: StepHandler = async (req: WorkflowStepRequest, opts) => {
7
- return await handleStepRequest(req, {}, opts)
15
+ return await handleStepRequest(req, { workflowResolver }, opts)
8
16
  }
9
17
 
10
18
  export const tenantMeta = {
@@ -20,6 +20,7 @@ import type {
20
20
  RunSummary,
21
21
  TriggerOptions,
22
22
  } from "@voyant-travel/workflows"
23
+ import { getWorkflow } from "@voyant-travel/workflows"
23
24
  import {
24
25
  type DriverFactory,
25
26
  type DriverFactoryDeps,
@@ -31,7 +32,11 @@ import {
31
32
  type WorkflowDriver,
32
33
  } from "@voyant-travel/workflows/driver"
33
34
  import { deriveStableEventId } from "@voyant-travel/workflows/events"
34
- import { handleStepRequest, type WorkflowStepRequest } from "@voyant-travel/workflows/handler"
35
+ import {
36
+ handleStepRequest,
37
+ type WorkflowResolver,
38
+ type WorkflowStepRequest,
39
+ } from "@voyant-travel/workflows/handler"
35
40
  import type { WorkflowManifest } from "@voyant-travel/workflows/protocol"
36
41
 
37
42
  import {
@@ -58,6 +63,13 @@ export interface InMemoryDriverOptions {
58
63
  tenantMeta?: RunRecord["tenantMeta"]
59
64
  /** Injectable clock; defaults to `Date.now`. */
60
65
  now?: () => number
66
+ /**
67
+ * Executable workflow lookup for this driver instance.
68
+ *
69
+ * Omitting this dependency is deprecated and falls back to the process-global
70
+ * authoring registry for backward compatibility with existing consumers.
71
+ */
72
+ workflowResolver?: WorkflowResolver
61
73
  /** Step handler override — defaults to in-process `handleStepRequest`. */
62
74
  handler?: StepHandler
63
75
  /** Schedule runner tick interval. Defaults to 1_000 ms. */
@@ -72,6 +84,10 @@ const DEFAULT_TENANT_META: RunRecord["tenantMeta"] = {
72
84
  organizationId: "default",
73
85
  }
74
86
 
87
+ const legacyWorkflowResolver: WorkflowResolver = {
88
+ resolve: getWorkflow,
89
+ }
90
+
75
91
  /**
76
92
  * Build an in-memory driver factory. The factory closes over its
77
93
  * options and returns a fresh `WorkflowDriver` when `createApp()`
@@ -91,13 +107,14 @@ export function createInMemoryDriver(opts: InMemoryDriverOptions = {}): DriverFa
91
107
  const now = opts.now ?? deps.now ?? (() => Date.now())
92
108
  const tenantMeta = opts.tenantMeta ?? DEFAULT_TENANT_META
93
109
  const defaultEnv: EnvironmentName = opts.defaultEnvironment ?? "development"
110
+ const workflowResolver = opts.workflowResolver ?? legacyWorkflowResolver
94
111
  // Wire the framework-supplied service container through to step bodies.
95
112
  // The handler closes over `deps.services` so every step invocation
96
113
  // surfaces it as `ctx.services` inside the workflow body.
97
114
  const handler: StepHandler =
98
115
  opts.handler ??
99
116
  (async (req: WorkflowStepRequest, stepOpts) =>
100
- handleStepRequest(req, { services: deps.services }, stepOpts))
117
+ handleStepRequest(req, { workflowResolver, services: deps.services }, stepOpts))
101
118
 
102
119
  let shuttingDown = false
103
120
  const concurrency = createInProcessConcurrencyCoordinator({
@@ -1,6 +1,6 @@
1
1
  import { createServer } from "node:http"
2
2
  import { resolve as resolvePath } from "node:path"
3
- import { handleStepRequest } from "@voyant-travel/workflows/handler"
3
+ import { handleStepRequest, type WorkflowResolver } from "@voyant-travel/workflows/handler"
4
4
  import { createInMemoryRateLimiter } from "@voyant-travel/workflows/rate-limit"
5
5
  import {
6
6
  createInMemoryRunStore,
@@ -94,17 +94,22 @@ export async function createSelfHostDeps(
94
94
  await loadEntryFile(entryAbs, { cacheBust: opts.cacheBustEntry })
95
95
 
96
96
  const _handlerMod = await import("@voyant-travel/workflows/handler")
97
+ const workflowDefinitions = wfMod.__listRegisteredWorkflows()
98
+ const workflowsById = new Map(workflowDefinitions.map((workflow) => [workflow.id, workflow]))
99
+ const workflowResolver: WorkflowResolver = {
100
+ resolve: (workflowId) => workflowsById.get(workflowId),
101
+ }
97
102
  const rateLimiter = createInMemoryRateLimiter()
98
103
  const chunkBus = createChunkBus()
99
104
 
100
105
  const stepHandler: StepHandler = async (req, stepOpts) =>
101
- handleStepRequest(req, { rateLimiter, services: opts.services }, stepOpts)
106
+ handleStepRequest(req, { workflowResolver, rateLimiter, services: opts.services }, stepOpts)
102
107
 
103
108
  const wakeupStore = pg ? createPostgresWakeupStore({ db: pg.db }) : createFsWakeupStore()
104
109
  const leaseOwner = opts.wakeupLeaseOwner ?? createDefaultWakeupLeaseOwner()
105
110
 
106
111
  const listWorkflows = () =>
107
- wfMod.__listRegisteredWorkflows().map((workflow) => ({
112
+ workflowDefinitions.map((workflow) => ({
108
113
  id: workflow.id,
109
114
  description: workflow.config.description,
110
115
  }))
@@ -227,7 +232,7 @@ export async function createSelfHostDeps(
227
232
  tags,
228
233
  triggeredByUserId,
229
234
  }) => {
230
- const workflow = wfMod.__listRegisteredWorkflows().find((entry) => entry.id === workflowId)
235
+ const workflow = workflowResolver.resolve(workflowId)
231
236
  if (!workflow) {
232
237
  return {
233
238
  ok: false,
@@ -309,7 +314,7 @@ export async function createSelfHostDeps(
309
314
  }
310
315
 
311
316
  const workflowId = parent?.workflowId ?? requestedWorkflowId!
312
- const workflow = wfMod.__listRegisteredWorkflows().find((entry) => entry.id === workflowId)
317
+ const workflow = workflowResolver.resolve(workflowId)
313
318
  if (!workflow) {
314
319
  return {
315
320
  ok: false,
@@ -25,6 +25,7 @@ import type {
25
25
  RunSummary,
26
26
  TriggerOptions,
27
27
  } from "@voyant-travel/workflows"
28
+ import { getWorkflow } from "@voyant-travel/workflows"
28
29
  import type {
29
30
  DriverFactory,
30
31
  DriverFactoryDeps,
@@ -35,7 +36,11 @@ import type {
35
36
  WorkflowDriver,
36
37
  } from "@voyant-travel/workflows/driver"
37
38
  import { deriveStableEventId } from "@voyant-travel/workflows/events"
38
- import { handleStepRequest, type WorkflowStepRequest } from "@voyant-travel/workflows/handler"
39
+ import {
40
+ handleStepRequest,
41
+ type WorkflowResolver,
42
+ type WorkflowStepRequest,
43
+ } from "@voyant-travel/workflows/handler"
39
44
  import type { WorkflowManifest } from "@voyant-travel/workflows/protocol"
40
45
  import type { drizzle } from "drizzle-orm/node-postgres"
41
46
  import {
@@ -74,6 +79,13 @@ export interface StandaloneDriverOptions {
74
79
  tenantMeta?: RunRecord["tenantMeta"]
75
80
  /** Injectable clock; defaults to `Date.now`. */
76
81
  now?: () => number
82
+ /**
83
+ * Executable workflow lookup for this driver instance.
84
+ *
85
+ * Omitting this dependency is deprecated and falls back to the process-global
86
+ * authoring registry for backward compatibility with existing consumers.
87
+ */
88
+ workflowResolver?: WorkflowResolver
77
89
  /**
78
90
  * Step handler override. Defaults to in-process `handleStepRequest`
79
91
  * with the framework-supplied `services` container plumbed through
@@ -126,6 +138,10 @@ const DEFAULT_TENANT_META: RunRecord["tenantMeta"] = {
126
138
 
127
139
  const DEFAULT_MANIFEST_KEEP = 3
128
140
 
141
+ const legacyWorkflowResolver: WorkflowResolver = {
142
+ resolve: getWorkflow,
143
+ }
144
+
129
145
  function serializeWorkflowManifest(manifest: WorkflowManifest): Record<string, unknown> {
130
146
  return { ...manifest }
131
147
  }
@@ -238,6 +254,7 @@ export function createStandaloneDriver(opts: StandaloneDriverOptions): DriverFac
238
254
  const defaultEnv = opts.defaultEnvironment ?? "development"
239
255
  const keep = opts.manifestVersionsToKeep ?? DEFAULT_MANIFEST_KEEP
240
256
  const leaseOwner = opts.wakeupLeaseOwner ?? `selfhost-standalone-${randomToken()}`
257
+ const workflowResolver = opts.workflowResolver ?? legacyWorkflowResolver
241
258
 
242
259
  // Wire the framework-supplied service container through to step bodies.
243
260
  // The handler closes over `deps.services` so every step invocation
@@ -245,7 +262,7 @@ export function createStandaloneDriver(opts: StandaloneDriverOptions): DriverFac
245
262
  const handler: StepHandler =
246
263
  opts.handler ??
247
264
  (async (req: WorkflowStepRequest, stepOpts) =>
248
- handleStepRequest(req, { services: deps.services }, stepOpts))
265
+ handleStepRequest(req, { workflowResolver, services: deps.services }, stepOpts))
249
266
 
250
267
  // Persistent wakeup manager — polls `voyant_wakeups` for runs
251
268
  // parked on DATETIME waitpoints and resumes them via the orchestrator's