@dudousxd/nestjs-durable 0.48.2 → 0.49.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/dist/index.d.cts CHANGED
@@ -1,4 +1,4 @@
1
- import { WorkflowCtx, SearchAttributes, WorkflowClass, StepError, OnErrorInput, WorkflowRef, SingletonConfig, ConcurrencyConfig, SearchAttributesSchema, StateStore, Transport, NamedTransport, ControlPlane, ScheduledWorkflow, RetentionPolicy, QueueConfig, AdmissionBackend, WorkflowInputOf, StartOptions, RunResult, RunGateway, WorkflowEngine, EntityHandler, DurableTopology, RunDetailOptions, RunDetail, FanPageQuery, FanPage, RunQuery, RunListItem, RunFacetQuery, RunFacetRow, RunValueAxis, RunValueFacetOptions, RunValueFacetRow, GroupHealth, RunWaiting, EngineEvent, RunRequest, RunReply, StepInvocation, WorkflowRun, TenantEvent } from '@dudousxd/nestjs-durable-core';
1
+ import { WorkflowCtx, SearchAttributes, WorkflowClass, StandardSchemaV1, StepError, OnErrorInput, WorkflowRef, SingletonConfig, ConcurrencyConfig, SearchAttributesSchema, StateStore, Transport, NamedTransport, ControlPlane, ScheduledWorkflow, RetentionPolicy, QueueConfig, AdmissionBackend, WorkflowInputOf, StartOptions, RunResult, RunGateway, WorkflowEngine, EntityHandler, DurableTopology, RunDetailOptions, RunDetail, FanPageQuery, FanPage, RunQuery, RunListItem, RunFacetQuery, RunFacetRow, RunValueAxis, RunValueFacetOptions, RunValueFacetRow, GroupHealth, RunWaiting, EngineEvent, RunRequest, RunReply, StepInvocation, WorkflowRun, TenantEvent } from '@dudousxd/nestjs-durable-core';
2
2
  export { AttributeFilter, DynamicWorkflowCtx, EngineEvent, InferSearchAttributes, OnErrorInput, RunDetail, RunGateway, RunListItem, RunQuery, RunStatus, RunWaiting, SearchAttributes, SearchAttributesSchema, StepCheckpoint, StepEvent, StepLogger, WorkflowCtx, WorkflowEngine, WorkflowHandler, WorkflowRun, readSearchAttributes } from '@dudousxd/nestjs-durable-core';
3
3
  import { z } from 'zod';
4
4
  import { ConcurrencyOption, StartRunDeps, RunRedisWorkerOptions, RunningWorker, DurableWorkerRuntime } from '@dudousxd/durable-worker';
@@ -102,6 +102,17 @@ interface ContextAccessor {
102
102
  get(): ContextStore | undefined;
103
103
  }
104
104
 
105
+ /** A class-validator DTO class — validated with the same `plainToInstance` + `validate` NestJS's
106
+ * `ValidationPipe` runs. */
107
+ type DtoClass = new (...args: any[]) => object;
108
+ /**
109
+ * What `@Workflow({ input, output })` and `@Step({ input, output })` accept: a **Standard Schema**
110
+ * (https://standardschema.dev — zod 3.24+, valibot, arktype, …) or a **class-validator DTO class**.
111
+ * The two are told apart at runtime: a value with a `~standard` property is a Standard Schema, a
112
+ * constructor is a DTO. A zod schema older than 3.24 (no `~standard`) still works through its `parse`.
113
+ */
114
+ type IoSchema = StandardSchemaV1 | DtoClass | z.ZodType;
115
+
105
116
  declare const WORKFLOW_METADATA: unique symbol;
106
117
  interface WorkflowMeta {
107
118
  name: string;
@@ -121,8 +132,11 @@ interface WorkflowMeta {
121
132
  concurrency?: ConcurrencyConfig | undefined;
122
133
  /** Max wall-clock lifetime before a run is cancelled (e.g. `'2h'`). See `WorkflowOptions`. */
123
134
  executionTimeout?: string | number | undefined;
124
- /** class-validator DTO validated at start. See `WorkflowOptions`. */
125
- inputSchema?: (new (...args: any[]) => object) | undefined;
135
+ /** Input schema (Standard Schema or DTO class), parsed at start and before every turn. Holds
136
+ * `input` or its `inputSchema` alias. See `WorkflowOptions`. */
137
+ input?: IoSchema | undefined;
138
+ /** Output schema, parsed before the run completes. See `WorkflowOptions`. */
139
+ output?: IoSchema | undefined;
126
140
  /** Custom input validator (throws on invalid). See `WorkflowOptions`. */
127
141
  validateInput?: ((input: unknown) => void | Promise<void>) | undefined;
128
142
  /** Typed/validated `searchAttributes` shape. See `WorkflowOptions`. */
@@ -183,9 +197,11 @@ interface WorkflowOptions {
183
197
  */
184
198
  concurrency?: ConcurrencyConfig;
185
199
  /**
186
- * Max wall-clock lifetime for a run of this workflow (e.g. `'2h'`, `'7 days'`, or ms). A run that
187
- * outlives it is moved to `cancelled` (`execution_timeout`) by the timer poller — a backstop for
188
- * runs that get stuck or loop forever. Omit for no limit.
200
+ * Max wall-clock lifetime for a run of this workflow (e.g. `'2h'`, `'7 days'`, or ms), counted from
201
+ * the run's creation. A run that outlives it is moved to `cancelled` (`execution_timeout`) by the
202
+ * timer poller — whether it is `pending`, `blocked`, `running` or `suspended` — a backstop for runs
203
+ * that get stuck, loop forever or are never picked up. A run in `cancelling` finishes its saga undo
204
+ * instead. Omit for no limit.
189
205
  */
190
206
  executionTimeout?: string | number;
191
207
  /**
@@ -197,15 +213,36 @@ interface WorkflowOptions {
197
213
  */
198
214
  requires?: string[];
199
215
  /**
200
- * Validate the workflow input at `start` against a **class-validator DTO** (the same
201
- * `plainToInstance` + `validate` NestJS runs in controllers) — invalid input is rejected before any
202
- * run is created. Needs the optional peers `class-validator` + `class-transformer`.
203
- * `@Workflow({ inputSchema: CheckoutInput })`.
216
+ * The run's input schema — a **Standard Schema** (zod 3.24+, valibot, arktype, …) or a
217
+ * **class-validator DTO class** (the same `plainToInstance` + `validate` NestJS's `ValidationPipe`
218
+ * runs; needs the optional peers `class-validator` + `class-transformer`). The same option
219
+ * `@Step({ input })` takes.
220
+ *
221
+ * Parsed at `start`, so invalid input is rejected before any run is created. The run stores the
222
+ * input **as it was sent**; `run(ctx, input)` receives the **parsed** value (coerced/transformed —
223
+ * a `Date` from `z.coerce.date()`, a DTO instance), re-parsed from the stored input before every
224
+ * turn, so a replay sees the same value the first execution did. The schema must therefore be
225
+ * deterministic (no `default(() => Date.now())`), and changing it is a breaking change to the
226
+ * workflow — bump `version`. A stored input that no longer parses fails the run with
227
+ * `code: 'invalid_input'`.
228
+ *
229
+ * `@Workflow({ name: 'checkout', input: CheckoutInput })` or `input: z.object({ … })`.
230
+ */
231
+ input?: IoSchema;
232
+ /**
233
+ * The run's output schema (same forms as {@link input}). The value `run()` returns is parsed before
234
+ * the run completes, and the parsed value is what the run stores as its output. A mismatch fails the
235
+ * run with `code: 'invalid_output'`. The same option `@Step({ output })` takes.
236
+ */
237
+ output?: IoSchema;
238
+ /**
239
+ * Alias of {@link input}, kept for workflows declared before `input` existed. Setting both is an
240
+ * error.
204
241
  */
205
- inputSchema?: new (...args: any[]) => object;
242
+ inputSchema?: IoSchema;
206
243
  /**
207
- * Custom input validator (throws on invalid) — an escape hatch for zod/yup/etc. instead of
208
- * `inputSchema`. Takes precedence over `inputSchema` if both are set.
244
+ * A custom check at `start` (throw to reject) for a rule a schema can't express — a cross-field
245
+ * invariant, an async lookup. It sees the input as it was sent, and runs after `input` parses.
209
246
  */
210
247
  validateInput?: (input: unknown) => void | Promise<void>;
211
248
  /**
@@ -268,9 +305,9 @@ interface DurableStepMeta {
268
305
  name: string;
269
306
  /** Opt-in runtime input schema, validated when the handler is served (see `scanSteps`). Absent on
270
307
  * a bare `@Step()` — compile-time types from the method signature only, no runtime check. */
271
- input?: z.ZodType | undefined;
308
+ input?: IoSchema | undefined;
272
309
  /** Opt-in runtime output schema, validated before the handler's result is handed back. */
273
- output?: z.ZodType | undefined;
310
+ output?: IoSchema | undefined;
274
311
  }
275
312
  /**
276
313
  * `@Step({ name?, input?, output?, retries?, backoff?, backoffMs?, backoffMaxMs?, jitter?,
@@ -279,10 +316,12 @@ interface DurableStepMeta {
279
316
  interface StepOptions {
280
317
  /** Explicit routing name, overriding the derived `Class.method`. */
281
318
  name?: string;
282
- /** Runtime input schema, validated at the serve boundary (opt-in — a bare `@Step()` skips it). */
283
- input?: z.ZodType;
319
+ /** Runtime input schema, validated at the serve boundary (opt-in — a bare `@Step()` skips it). A
320
+ * Standard Schema (zod, valibot, …) or a class-validator DTO class — the same forms
321
+ * `@Workflow({ input })` takes. The method receives the parsed value. */
322
+ input?: IoSchema;
284
323
  /** Runtime output schema, validated before the result is returned (opt-in). */
285
- output?: z.ZodType;
324
+ output?: IoSchema;
286
325
  /**
287
326
  * Def-level durable-dispatch policy stamped under `DURABLE_STEP_CONFIG` and read by `ctx.step` at
288
327
  * the dispatch boundary (see `stepConfigOf`) — a per-call `ctx.step(ref, input, opts)` overrides
@@ -328,8 +367,8 @@ interface StepOptions {
328
367
  * - `@Step('custom:name')` — explicit name override (stable across refactors, or a cross-runtime
329
368
  * contract with a non-JS worker that has no `@Step` of its own).
330
369
  * - `@Step({ name?, input?, output?, retries?, backoff?, backoffMs?, backoffMaxMs?, jitter?,
331
- * timeoutMs? })` — optional name override, opt-in RUNTIME zod schemas, and a def-level
332
- * durable-retry/liveness-timeout policy. `input` validates before the method runs; `output`
370
+ * timeoutMs? })` — optional name override, opt-in RUNTIME schemas (a Standard Schema or a DTO
371
+ * class), and a def-level durable-retry/liveness-timeout policy. `input` validates before the method runs; `output`
333
372
  * validates its return value before it's handed back (see `scanSteps`). `retries`/`backoff`/
334
373
  * `backoffMs`/`backoffMaxMs`/`jitter`/`timeoutMs` are the policy `ctx.step` reads off this method
335
374
  * reference (via `stepConfigOf`) to build the dispatched `StepDef`'s durable retry/backoff and
@@ -1321,4 +1360,4 @@ declare class WorkflowService {
1321
1360
  }): Promise<number>;
1322
1361
  }
1323
1362
 
1324
- export { CONTEXT_ACCESSOR, type ContextAccessor, type ContextStore, DEAD_LETTER_METADATA, DURABLE_STEP_METADATA, DURABLE_WORKER_RUNNERS, DURABLE_WORKER_RUNTIME, DeadLetter, DurableModule, type DurableModuleAsyncOptions, type DurableModuleOptions, type DurableRetentionOptions, DurableStartClient, DurableStep, type DurableStepInterceptor, type DurableStepMeta, ENTITY_METADATA, ENTITY_ON_METADATA, Entity, EntityService, type ErrorClassWithCode, IN_APP_RUN_REDIS_WORKER, IN_APP_WORKER_BINDING, IN_APP_WORKER_RUNNERS, IN_APP_WORKER_RUNTIME, type InAppWorkerBinding, InAppWorkerBootstrap, ON_ERROR_METADATA, ON_EVENT_METADATA, On, OnDurableEvent, OnError, type OnErrorMethod, OnEvent, ProxyRunGateway, RUN_GATEWAY, RUN_REDIS_WORKER, type RunLookupStore, type RunRedisWorkerFn, RunRequestResponder, type RunRequestTransport, STEP_INTERCEPTOR_METADATA, Step, StepInterceptor, type StepOptions, StoreRunGateway, TenantEventRepublisher, ThinStepRegistrar, ThinWorkerBootstrap, ThinWorkflowRegistrar, UNSUPPORTED_RUN_REQUEST, type UserRef, WORKFLOW_METADATA, Workflow, type WorkflowAttributesOf, type WorkflowMeta, type WorkflowOptions, WorkflowService, attributesOf, entityConfigFor, getDurableStepMeta, getEntityMeta, getOnEvents, getWorkflowMeta, inAppWorkerProviders, isDeadLetterHandler, isDrivingOperator, isOperatorRole, isStepInterceptor, onErrorCodesOf, thinWorkerProviders, unavailableRunGateway };
1363
+ export { CONTEXT_ACCESSOR, type ContextAccessor, type ContextStore, DEAD_LETTER_METADATA, DURABLE_STEP_METADATA, DURABLE_WORKER_RUNNERS, DURABLE_WORKER_RUNTIME, DeadLetter, type DtoClass, DurableModule, type DurableModuleAsyncOptions, type DurableModuleOptions, type DurableRetentionOptions, DurableStartClient, DurableStep, type DurableStepInterceptor, type DurableStepMeta, ENTITY_METADATA, ENTITY_ON_METADATA, Entity, EntityService, type ErrorClassWithCode, IN_APP_RUN_REDIS_WORKER, IN_APP_WORKER_BINDING, IN_APP_WORKER_RUNNERS, IN_APP_WORKER_RUNTIME, type InAppWorkerBinding, InAppWorkerBootstrap, type IoSchema, ON_ERROR_METADATA, ON_EVENT_METADATA, On, OnDurableEvent, OnError, type OnErrorMethod, OnEvent, ProxyRunGateway, RUN_GATEWAY, RUN_REDIS_WORKER, type RunLookupStore, type RunRedisWorkerFn, RunRequestResponder, type RunRequestTransport, STEP_INTERCEPTOR_METADATA, Step, StepInterceptor, type StepOptions, StoreRunGateway, TenantEventRepublisher, ThinStepRegistrar, ThinWorkerBootstrap, ThinWorkflowRegistrar, UNSUPPORTED_RUN_REQUEST, type UserRef, WORKFLOW_METADATA, Workflow, type WorkflowAttributesOf, type WorkflowMeta, type WorkflowOptions, WorkflowService, attributesOf, entityConfigFor, getDurableStepMeta, getEntityMeta, getOnEvents, getWorkflowMeta, inAppWorkerProviders, isDeadLetterHandler, isDrivingOperator, isOperatorRole, isStepInterceptor, onErrorCodesOf, thinWorkerProviders, unavailableRunGateway };
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { WorkflowCtx, SearchAttributes, WorkflowClass, StepError, OnErrorInput, WorkflowRef, SingletonConfig, ConcurrencyConfig, SearchAttributesSchema, StateStore, Transport, NamedTransport, ControlPlane, ScheduledWorkflow, RetentionPolicy, QueueConfig, AdmissionBackend, WorkflowInputOf, StartOptions, RunResult, RunGateway, WorkflowEngine, EntityHandler, DurableTopology, RunDetailOptions, RunDetail, FanPageQuery, FanPage, RunQuery, RunListItem, RunFacetQuery, RunFacetRow, RunValueAxis, RunValueFacetOptions, RunValueFacetRow, GroupHealth, RunWaiting, EngineEvent, RunRequest, RunReply, StepInvocation, WorkflowRun, TenantEvent } from '@dudousxd/nestjs-durable-core';
1
+ import { WorkflowCtx, SearchAttributes, WorkflowClass, StandardSchemaV1, StepError, OnErrorInput, WorkflowRef, SingletonConfig, ConcurrencyConfig, SearchAttributesSchema, StateStore, Transport, NamedTransport, ControlPlane, ScheduledWorkflow, RetentionPolicy, QueueConfig, AdmissionBackend, WorkflowInputOf, StartOptions, RunResult, RunGateway, WorkflowEngine, EntityHandler, DurableTopology, RunDetailOptions, RunDetail, FanPageQuery, FanPage, RunQuery, RunListItem, RunFacetQuery, RunFacetRow, RunValueAxis, RunValueFacetOptions, RunValueFacetRow, GroupHealth, RunWaiting, EngineEvent, RunRequest, RunReply, StepInvocation, WorkflowRun, TenantEvent } from '@dudousxd/nestjs-durable-core';
2
2
  export { AttributeFilter, DynamicWorkflowCtx, EngineEvent, InferSearchAttributes, OnErrorInput, RunDetail, RunGateway, RunListItem, RunQuery, RunStatus, RunWaiting, SearchAttributes, SearchAttributesSchema, StepCheckpoint, StepEvent, StepLogger, WorkflowCtx, WorkflowEngine, WorkflowHandler, WorkflowRun, readSearchAttributes } from '@dudousxd/nestjs-durable-core';
3
3
  import { z } from 'zod';
4
4
  import { ConcurrencyOption, StartRunDeps, RunRedisWorkerOptions, RunningWorker, DurableWorkerRuntime } from '@dudousxd/durable-worker';
@@ -102,6 +102,17 @@ interface ContextAccessor {
102
102
  get(): ContextStore | undefined;
103
103
  }
104
104
 
105
+ /** A class-validator DTO class — validated with the same `plainToInstance` + `validate` NestJS's
106
+ * `ValidationPipe` runs. */
107
+ type DtoClass = new (...args: any[]) => object;
108
+ /**
109
+ * What `@Workflow({ input, output })` and `@Step({ input, output })` accept: a **Standard Schema**
110
+ * (https://standardschema.dev — zod 3.24+, valibot, arktype, …) or a **class-validator DTO class**.
111
+ * The two are told apart at runtime: a value with a `~standard` property is a Standard Schema, a
112
+ * constructor is a DTO. A zod schema older than 3.24 (no `~standard`) still works through its `parse`.
113
+ */
114
+ type IoSchema = StandardSchemaV1 | DtoClass | z.ZodType;
115
+
105
116
  declare const WORKFLOW_METADATA: unique symbol;
106
117
  interface WorkflowMeta {
107
118
  name: string;
@@ -121,8 +132,11 @@ interface WorkflowMeta {
121
132
  concurrency?: ConcurrencyConfig | undefined;
122
133
  /** Max wall-clock lifetime before a run is cancelled (e.g. `'2h'`). See `WorkflowOptions`. */
123
134
  executionTimeout?: string | number | undefined;
124
- /** class-validator DTO validated at start. See `WorkflowOptions`. */
125
- inputSchema?: (new (...args: any[]) => object) | undefined;
135
+ /** Input schema (Standard Schema or DTO class), parsed at start and before every turn. Holds
136
+ * `input` or its `inputSchema` alias. See `WorkflowOptions`. */
137
+ input?: IoSchema | undefined;
138
+ /** Output schema, parsed before the run completes. See `WorkflowOptions`. */
139
+ output?: IoSchema | undefined;
126
140
  /** Custom input validator (throws on invalid). See `WorkflowOptions`. */
127
141
  validateInput?: ((input: unknown) => void | Promise<void>) | undefined;
128
142
  /** Typed/validated `searchAttributes` shape. See `WorkflowOptions`. */
@@ -183,9 +197,11 @@ interface WorkflowOptions {
183
197
  */
184
198
  concurrency?: ConcurrencyConfig;
185
199
  /**
186
- * Max wall-clock lifetime for a run of this workflow (e.g. `'2h'`, `'7 days'`, or ms). A run that
187
- * outlives it is moved to `cancelled` (`execution_timeout`) by the timer poller — a backstop for
188
- * runs that get stuck or loop forever. Omit for no limit.
200
+ * Max wall-clock lifetime for a run of this workflow (e.g. `'2h'`, `'7 days'`, or ms), counted from
201
+ * the run's creation. A run that outlives it is moved to `cancelled` (`execution_timeout`) by the
202
+ * timer poller — whether it is `pending`, `blocked`, `running` or `suspended` — a backstop for runs
203
+ * that get stuck, loop forever or are never picked up. A run in `cancelling` finishes its saga undo
204
+ * instead. Omit for no limit.
189
205
  */
190
206
  executionTimeout?: string | number;
191
207
  /**
@@ -197,15 +213,36 @@ interface WorkflowOptions {
197
213
  */
198
214
  requires?: string[];
199
215
  /**
200
- * Validate the workflow input at `start` against a **class-validator DTO** (the same
201
- * `plainToInstance` + `validate` NestJS runs in controllers) — invalid input is rejected before any
202
- * run is created. Needs the optional peers `class-validator` + `class-transformer`.
203
- * `@Workflow({ inputSchema: CheckoutInput })`.
216
+ * The run's input schema — a **Standard Schema** (zod 3.24+, valibot, arktype, …) or a
217
+ * **class-validator DTO class** (the same `plainToInstance` + `validate` NestJS's `ValidationPipe`
218
+ * runs; needs the optional peers `class-validator` + `class-transformer`). The same option
219
+ * `@Step({ input })` takes.
220
+ *
221
+ * Parsed at `start`, so invalid input is rejected before any run is created. The run stores the
222
+ * input **as it was sent**; `run(ctx, input)` receives the **parsed** value (coerced/transformed —
223
+ * a `Date` from `z.coerce.date()`, a DTO instance), re-parsed from the stored input before every
224
+ * turn, so a replay sees the same value the first execution did. The schema must therefore be
225
+ * deterministic (no `default(() => Date.now())`), and changing it is a breaking change to the
226
+ * workflow — bump `version`. A stored input that no longer parses fails the run with
227
+ * `code: 'invalid_input'`.
228
+ *
229
+ * `@Workflow({ name: 'checkout', input: CheckoutInput })` or `input: z.object({ … })`.
230
+ */
231
+ input?: IoSchema;
232
+ /**
233
+ * The run's output schema (same forms as {@link input}). The value `run()` returns is parsed before
234
+ * the run completes, and the parsed value is what the run stores as its output. A mismatch fails the
235
+ * run with `code: 'invalid_output'`. The same option `@Step({ output })` takes.
236
+ */
237
+ output?: IoSchema;
238
+ /**
239
+ * Alias of {@link input}, kept for workflows declared before `input` existed. Setting both is an
240
+ * error.
204
241
  */
205
- inputSchema?: new (...args: any[]) => object;
242
+ inputSchema?: IoSchema;
206
243
  /**
207
- * Custom input validator (throws on invalid) — an escape hatch for zod/yup/etc. instead of
208
- * `inputSchema`. Takes precedence over `inputSchema` if both are set.
244
+ * A custom check at `start` (throw to reject) for a rule a schema can't express — a cross-field
245
+ * invariant, an async lookup. It sees the input as it was sent, and runs after `input` parses.
209
246
  */
210
247
  validateInput?: (input: unknown) => void | Promise<void>;
211
248
  /**
@@ -268,9 +305,9 @@ interface DurableStepMeta {
268
305
  name: string;
269
306
  /** Opt-in runtime input schema, validated when the handler is served (see `scanSteps`). Absent on
270
307
  * a bare `@Step()` — compile-time types from the method signature only, no runtime check. */
271
- input?: z.ZodType | undefined;
308
+ input?: IoSchema | undefined;
272
309
  /** Opt-in runtime output schema, validated before the handler's result is handed back. */
273
- output?: z.ZodType | undefined;
310
+ output?: IoSchema | undefined;
274
311
  }
275
312
  /**
276
313
  * `@Step({ name?, input?, output?, retries?, backoff?, backoffMs?, backoffMaxMs?, jitter?,
@@ -279,10 +316,12 @@ interface DurableStepMeta {
279
316
  interface StepOptions {
280
317
  /** Explicit routing name, overriding the derived `Class.method`. */
281
318
  name?: string;
282
- /** Runtime input schema, validated at the serve boundary (opt-in — a bare `@Step()` skips it). */
283
- input?: z.ZodType;
319
+ /** Runtime input schema, validated at the serve boundary (opt-in — a bare `@Step()` skips it). A
320
+ * Standard Schema (zod, valibot, …) or a class-validator DTO class — the same forms
321
+ * `@Workflow({ input })` takes. The method receives the parsed value. */
322
+ input?: IoSchema;
284
323
  /** Runtime output schema, validated before the result is returned (opt-in). */
285
- output?: z.ZodType;
324
+ output?: IoSchema;
286
325
  /**
287
326
  * Def-level durable-dispatch policy stamped under `DURABLE_STEP_CONFIG` and read by `ctx.step` at
288
327
  * the dispatch boundary (see `stepConfigOf`) — a per-call `ctx.step(ref, input, opts)` overrides
@@ -328,8 +367,8 @@ interface StepOptions {
328
367
  * - `@Step('custom:name')` — explicit name override (stable across refactors, or a cross-runtime
329
368
  * contract with a non-JS worker that has no `@Step` of its own).
330
369
  * - `@Step({ name?, input?, output?, retries?, backoff?, backoffMs?, backoffMaxMs?, jitter?,
331
- * timeoutMs? })` — optional name override, opt-in RUNTIME zod schemas, and a def-level
332
- * durable-retry/liveness-timeout policy. `input` validates before the method runs; `output`
370
+ * timeoutMs? })` — optional name override, opt-in RUNTIME schemas (a Standard Schema or a DTO
371
+ * class), and a def-level durable-retry/liveness-timeout policy. `input` validates before the method runs; `output`
333
372
  * validates its return value before it's handed back (see `scanSteps`). `retries`/`backoff`/
334
373
  * `backoffMs`/`backoffMaxMs`/`jitter`/`timeoutMs` are the policy `ctx.step` reads off this method
335
374
  * reference (via `stepConfigOf`) to build the dispatched `StepDef`'s durable retry/backoff and
@@ -1321,4 +1360,4 @@ declare class WorkflowService {
1321
1360
  }): Promise<number>;
1322
1361
  }
1323
1362
 
1324
- export { CONTEXT_ACCESSOR, type ContextAccessor, type ContextStore, DEAD_LETTER_METADATA, DURABLE_STEP_METADATA, DURABLE_WORKER_RUNNERS, DURABLE_WORKER_RUNTIME, DeadLetter, DurableModule, type DurableModuleAsyncOptions, type DurableModuleOptions, type DurableRetentionOptions, DurableStartClient, DurableStep, type DurableStepInterceptor, type DurableStepMeta, ENTITY_METADATA, ENTITY_ON_METADATA, Entity, EntityService, type ErrorClassWithCode, IN_APP_RUN_REDIS_WORKER, IN_APP_WORKER_BINDING, IN_APP_WORKER_RUNNERS, IN_APP_WORKER_RUNTIME, type InAppWorkerBinding, InAppWorkerBootstrap, ON_ERROR_METADATA, ON_EVENT_METADATA, On, OnDurableEvent, OnError, type OnErrorMethod, OnEvent, ProxyRunGateway, RUN_GATEWAY, RUN_REDIS_WORKER, type RunLookupStore, type RunRedisWorkerFn, RunRequestResponder, type RunRequestTransport, STEP_INTERCEPTOR_METADATA, Step, StepInterceptor, type StepOptions, StoreRunGateway, TenantEventRepublisher, ThinStepRegistrar, ThinWorkerBootstrap, ThinWorkflowRegistrar, UNSUPPORTED_RUN_REQUEST, type UserRef, WORKFLOW_METADATA, Workflow, type WorkflowAttributesOf, type WorkflowMeta, type WorkflowOptions, WorkflowService, attributesOf, entityConfigFor, getDurableStepMeta, getEntityMeta, getOnEvents, getWorkflowMeta, inAppWorkerProviders, isDeadLetterHandler, isDrivingOperator, isOperatorRole, isStepInterceptor, onErrorCodesOf, thinWorkerProviders, unavailableRunGateway };
1363
+ export { CONTEXT_ACCESSOR, type ContextAccessor, type ContextStore, DEAD_LETTER_METADATA, DURABLE_STEP_METADATA, DURABLE_WORKER_RUNNERS, DURABLE_WORKER_RUNTIME, DeadLetter, type DtoClass, DurableModule, type DurableModuleAsyncOptions, type DurableModuleOptions, type DurableRetentionOptions, DurableStartClient, DurableStep, type DurableStepInterceptor, type DurableStepMeta, ENTITY_METADATA, ENTITY_ON_METADATA, Entity, EntityService, type ErrorClassWithCode, IN_APP_RUN_REDIS_WORKER, IN_APP_WORKER_BINDING, IN_APP_WORKER_RUNNERS, IN_APP_WORKER_RUNTIME, type InAppWorkerBinding, InAppWorkerBootstrap, type IoSchema, ON_ERROR_METADATA, ON_EVENT_METADATA, On, OnDurableEvent, OnError, type OnErrorMethod, OnEvent, ProxyRunGateway, RUN_GATEWAY, RUN_REDIS_WORKER, type RunLookupStore, type RunRedisWorkerFn, RunRequestResponder, type RunRequestTransport, STEP_INTERCEPTOR_METADATA, Step, StepInterceptor, type StepOptions, StoreRunGateway, TenantEventRepublisher, ThinStepRegistrar, ThinWorkerBootstrap, ThinWorkflowRegistrar, UNSUPPORTED_RUN_REQUEST, type UserRef, WORKFLOW_METADATA, Workflow, type WorkflowAttributesOf, type WorkflowMeta, type WorkflowOptions, WorkflowService, attributesOf, entityConfigFor, getDurableStepMeta, getEntityMeta, getOnEvents, getWorkflowMeta, inAppWorkerProviders, isDeadLetterHandler, isDrivingOperator, isOperatorRole, isStepInterceptor, onErrorCodesOf, thinWorkerProviders, unavailableRunGateway };
package/dist/index.js CHANGED
@@ -119,6 +119,9 @@ __name(fileOfFrame, "fileOfFrame");
119
119
  var WORKFLOW_METADATA = Symbol("nestjs-durable:workflow");
120
120
  function Workflow(options) {
121
121
  const declaredIn = declaringFile(Workflow);
122
+ if (options.input !== void 0 && options.inputSchema !== void 0) {
123
+ throw new Error(`@Workflow ${options.name}: set either \`input\` or its alias \`inputSchema\`, not both`);
124
+ }
122
125
  return (target) => {
123
126
  rememberDeclaringFile(target, declaredIn);
124
127
  const meta = {
@@ -130,7 +133,8 @@ function Workflow(options) {
130
133
  singleton: options.singleton,
131
134
  concurrency: options.concurrency,
132
135
  executionTimeout: options.executionTimeout,
133
- inputSchema: options.inputSchema,
136
+ input: options.input ?? options.inputSchema,
137
+ output: options.output,
134
138
  validateInput: options.validateInput,
135
139
  searchAttributes: options.searchAttributes,
136
140
  onEvent: options.onEvent,
@@ -388,6 +392,94 @@ import { DURABLE_OPTIONS_CANONICAL } from "@dudousxd/nestjs-durable-core";
388
392
  import { Inject, Injectable as Injectable2 } from "@nestjs/common";
389
393
  import { DiscoveryService, MetadataScanner } from "@nestjs/core";
390
394
 
395
+ // src/schema-parse.ts
396
+ import { FatalError } from "@dudousxd/nestjs-durable-core";
397
+ function schemaParser(schema, kind, label) {
398
+ const code = kind === "input" ? "invalid_input" : "invalid_output";
399
+ if (isStandardSchema(schema)) {
400
+ return async (value) => {
401
+ const result = await schema["~standard"].validate(value);
402
+ if (result.issues) {
403
+ const issues = result.issues.map((i) => ({
404
+ message: i.message,
405
+ ...i.path ? {
406
+ path: i.path.map((p) => String(typeof p === "object" ? p.key : p))
407
+ } : {}
408
+ }));
409
+ throw new FatalError(`invalid ${label}: ${formatIssues(issues)}`, code, issues);
410
+ }
411
+ return result.value;
412
+ };
413
+ }
414
+ if (isZodLike(schema)) {
415
+ return async (value) => {
416
+ const result = schema.safeParse(value);
417
+ if (!result.success) {
418
+ const issues = result.error.issues.map((i) => ({
419
+ message: i.message,
420
+ path: i.path.map(String)
421
+ }));
422
+ throw new FatalError(`invalid ${label}: ${formatIssues(issues)}`, code, issues);
423
+ }
424
+ return result.data;
425
+ };
426
+ }
427
+ if (typeof schema === "function") return classValidatorParser(schema, code, label);
428
+ throw new Error(`${label}: expected a Standard Schema (zod, valibot, \u2026) or a class-validator DTO class`);
429
+ }
430
+ __name(schemaParser, "schemaParser");
431
+ function classValidatorParser(cls, code, label) {
432
+ return async (value) => {
433
+ let cv;
434
+ let ct;
435
+ try {
436
+ cv = await import("class-validator");
437
+ ct = await import("class-transformer");
438
+ } catch {
439
+ throw new Error(`${label} uses a class-validator DTO, which needs the optional peers "class-validator" and "class-transformer" \u2014 install them, or use a Standard Schema (zod, valibot, \u2026) instead.`);
440
+ }
441
+ if (value === null || typeof value !== "object" || Array.isArray(value)) {
442
+ const got = value === null ? "null" : Array.isArray(value) ? "an array" : typeof value;
443
+ const message = `expected an object, got ${got}`;
444
+ throw new FatalError(`invalid ${label}: ${message}`, code, [
445
+ {
446
+ message
447
+ }
448
+ ]);
449
+ }
450
+ const instance = ct.plainToInstance(cls, value);
451
+ const errors = await cv.validate(instance, {
452
+ whitelist: true
453
+ });
454
+ if (errors.length > 0) {
455
+ const issues = errors.map((e) => ({
456
+ message: Object.values(e.constraints ?? {
457
+ _: `${e.property} is invalid`
458
+ }).join(", "),
459
+ path: [
460
+ String(e.property)
461
+ ]
462
+ }));
463
+ const message = issues.map((i) => i.message).join("; ");
464
+ throw new FatalError(`invalid ${label}: ${message}`, code, issues);
465
+ }
466
+ return instance;
467
+ };
468
+ }
469
+ __name(classValidatorParser, "classValidatorParser");
470
+ function isStandardSchema(schema) {
471
+ return (typeof schema === "object" || typeof schema === "function") && schema !== null && "~standard" in schema;
472
+ }
473
+ __name(isStandardSchema, "isStandardSchema");
474
+ function isZodLike(schema) {
475
+ return typeof schema === "object" && schema !== null && typeof schema.safeParse === "function";
476
+ }
477
+ __name(isZodLike, "isZodLike");
478
+ function formatIssues(issues) {
479
+ return issues.map((i) => i.path && i.path.length > 0 ? `${i.path.join(".")}: ${i.message}` : i.message).join("; ");
480
+ }
481
+ __name(formatIssues, "formatIssues");
482
+
391
483
  // src/discovery-helpers.ts
392
484
  function scanWorkflows(discovery, register) {
393
485
  for (const wrapper of discovery.getProviders()) {
@@ -414,10 +506,12 @@ function scanSteps(discovery, scanner, register) {
414
506
  const meta = getDurableStepMeta(method);
415
507
  if (!meta) continue;
416
508
  const boundMethod = method;
509
+ const parseInput = meta.input && schemaParser(meta.input, "input", `step "${meta.name}" input`);
510
+ const parseOutput = meta.output && schemaParser(meta.output, "output", `step "${meta.name}" output`);
417
511
  const handler = /* @__PURE__ */ __name(async (input, log) => {
418
- const validInput = meta.input ? meta.input.parse(input) : input;
512
+ const validInput = parseInput ? await parseInput(input) : input;
419
513
  const output = await boundMethod.call(instance, validInput, log);
420
- return meta.output ? meta.output.parse(output) : output;
514
+ return parseOutput ? parseOutput(output) : output;
421
515
  }, "handler");
422
516
  register(meta, handler);
423
517
  }
@@ -457,6 +551,24 @@ function onErrorHandlersOf(meta, instance, scanner) {
457
551
  }
458
552
  __name(onErrorHandlersOf, "onErrorHandlersOf");
459
553
 
554
+ // src/workflow-io.ts
555
+ function workflowIo(meta) {
556
+ const parseInput = meta.input && schemaParser(meta.input, "input", `workflow "${meta.name}" input`);
557
+ const parseOutput = meta.output && schemaParser(meta.output, "output", `workflow "${meta.name}" output`);
558
+ const validateInput = meta.validateInput;
559
+ return {
560
+ validateAtStart: parseInput || validateInput ? async (input) => {
561
+ if (parseInput) await parseInput(input);
562
+ await validateInput?.(input);
563
+ } : void 0,
564
+ wrap: /* @__PURE__ */ __name((body) => !parseInput && !parseOutput ? body : async (ctx, input) => {
565
+ const output = await body(ctx, parseInput ? await parseInput(input) : input);
566
+ return parseOutput ? parseOutput(output) : output;
567
+ }, "wrap")
568
+ };
569
+ }
570
+ __name(workflowIo, "workflowIo");
571
+
460
572
  // src/runtime-registration.ts
461
573
  function registerOnRuntime(runtime, scanner, meta, instance, origin) {
462
574
  const { handlers, routes } = onErrorHandlersOf(meta, instance, scanner);
@@ -466,7 +578,8 @@ function registerOnRuntime(runtime, scanner, meta, instance, origin) {
466
578
  origin
467
579
  } : {}
468
580
  };
469
- runtime.registerWorkflow(meta.name, (ctx, input) => instance.run(ctx, input), {
581
+ const body = workflowIo(meta).wrap((ctx, input) => instance.run(ctx, input));
582
+ runtime.registerWorkflow(meta.name, body, {
470
583
  ...announced,
471
584
  ...meta.requires !== void 0 ? {
472
585
  requires: meta.requires
@@ -2111,31 +2224,6 @@ import { DURABLE_OPTIONS_CANONICAL as DURABLE_OPTIONS_CANONICAL6, RemoteWorkflow
2111
2224
  import { Inject as Inject7, Injectable as Injectable10 } from "@nestjs/common";
2112
2225
  import { DiscoveryService as DiscoveryService4, MetadataScanner as MetadataScanner4 } from "@nestjs/core";
2113
2226
 
2114
- // src/input-validation.ts
2115
- function classValidatorInput(cls) {
2116
- return async (input) => {
2117
- let cv;
2118
- let ct;
2119
- try {
2120
- cv = await import("class-validator");
2121
- ct = await import("class-transformer");
2122
- } catch {
2123
- throw new Error('@Workflow({ inputSchema }) needs the optional peers "class-validator" and "class-transformer" \u2014 install them, or pass a `validateInput` function instead.');
2124
- }
2125
- const instance = ct.plainToInstance(cls, input);
2126
- const errors = await cv.validate(instance, {
2127
- whitelist: true
2128
- });
2129
- if (errors.length > 0) {
2130
- const message = errors.map((e) => Object.values(e.constraints ?? {
2131
- _: e.property
2132
- }).join(", ")).join("; ");
2133
- throw new Error(`invalid input for workflow: ${message}`);
2134
- }
2135
- };
2136
- }
2137
- __name(classValidatorInput, "classValidatorInput");
2138
-
2139
2227
  // src/step-interceptor.ts
2140
2228
  import "reflect-metadata";
2141
2229
  var STEP_INTERCEPTOR_METADATA = Symbol("nestjs-durable:step-interceptor");
@@ -2238,7 +2326,8 @@ var WorkflowRegistrar = class {
2238
2326
  const unattributed = [];
2239
2327
  scanWorkflows(this.discovery, (meta, workflow, origin) => {
2240
2328
  if (origin === void 0) unattributed.push(meta.name);
2241
- const validateInput = meta.validateInput ?? (meta.inputSchema ? classValidatorInput(meta.inputSchema) : void 0);
2329
+ const io = workflowIo(meta);
2330
+ const body = io.wrap((ctx, input) => workflow.run(ctx, input));
2242
2331
  const eventBatch = meta.debounce ? {
2243
2332
  mode: "debounce",
2244
2333
  windowMs: parseDuration2(meta.debounce)
@@ -2254,7 +2343,7 @@ var WorkflowRegistrar = class {
2254
2343
  origin
2255
2344
  });
2256
2345
  }
2257
- this.engine.register(meta.name, meta.version, (ctx, input) => workflow.run(ctx, input), {
2346
+ this.engine.register(meta.name, meta.version, body, {
2258
2347
  // Stamped onto every run this workflow starts (see `WorkflowRun.origin`). Derived from the
2259
2348
  // class's declaring file, so a lib gets attributed without opting in; `undefined` when it
2260
2349
  // could not be derived, which the engine records as unknown rather than defaulting.
@@ -2264,7 +2353,7 @@ var WorkflowRegistrar = class {
2264
2353
  concurrency: meta.concurrency,
2265
2354
  executionTimeout: meta.executionTimeout,
2266
2355
  requires: meta.requires,
2267
- validateInput,
2356
+ validateInput: io.validateAtStart,
2268
2357
  searchAttributesSchema: meta.searchAttributes,
2269
2358
  onEvent: getOnEvents(meta, workflowCtor),
2270
2359
  eventBatch,