@dudousxd/nestjs-durable-core 0.82.0 → 0.83.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
@@ -2877,8 +2877,8 @@ interface WorkflowCtx<A extends SearchAttributes = SearchAttributes> {
2877
2877
  *
2878
2878
  * When the workflow declares a `@Workflow({ searchAttributes })` schema, the MERGED result (this
2879
2879
  * run's existing `searchAttributes` shallow-merged with `attrs`) is validated against it before the
2880
- * write — an invalid merge throws, naming the workflow, the offending key(s), and the schema's
2881
- * issues. Validation runs once, on the same first-run-only position as the write itself (skipped
2880
+ * write — an invalid merge throws a `SchemaValidationError` (code `schema_validation`, never
2881
+ * retried) naming the workflow, with the schema's issues. Validation runs once, on the same first-run-only position as the write itself (skipped
2882
2882
  * entirely on replay, so it's never a source of nondeterminism); it must be synchronous — a schema
2883
2883
  * whose `validate` returns a `Promise` throws instead of being awaited. No schema declared ⇒
2884
2884
  * unchanged, unvalidated behavior.
@@ -3827,7 +3827,10 @@ declare function liveHandoff<C extends {
3827
3827
  seq: number;
3828
3828
  name: string;
3829
3829
  }>(runId: string, checkpoints: readonly C[]): C | undefined;
3830
- /** The route a failure takes: the one for its exact code, else the catch-all, else none. */
3830
+ /**
3831
+ * The route a failure takes: the one for its exact code, else (for an `invalid_input` /
3832
+ * `invalid_output` failure) a `schema_validation` route, else the catch-all, else none.
3833
+ */
3831
3834
  declare function routeFor(routes: OnErrorRoute[] | undefined, error: StepError): OnErrorRoute | undefined;
3832
3835
  /** Reject two routes for one code, or two catch-alls — ambiguous config, caught at registration. */
3833
3836
  declare function assertRoutes(workflow: string, routes: OnErrorRoute[]): void;
@@ -4847,6 +4850,8 @@ declare class WorkflowEngine {
4847
4850
  * between this call and `drain()` (other hooks, the HTTP server closing) never is. Calls add up.
4848
4851
  */
4849
4852
  beginDrain(spentMs?: number): void;
4853
+ /** True once {@link beginDrain} (or {@link drain}) has been called. */
4854
+ isDraining(): boolean;
4850
4855
  /**
4851
4856
  * Graceful shutdown: stop picking up new runs (recovery/timer become no-ops) and wait for
4852
4857
  * in-flight executions to settle, up to `timeoutMs`. Call from your app's shutdown hook so a
@@ -5966,6 +5971,44 @@ declare class FatalError extends Error {
5966
5971
  readonly retryable = false;
5967
5972
  constructor(message: string, code?: string, details?: unknown);
5968
5973
  }
5974
+ /** One schema issue in JSON-safe form: its message and the path segments, as strings. */
5975
+ interface SchemaIssue {
5976
+ message: string;
5977
+ path: string[];
5978
+ }
5979
+ /**
5980
+ * The code a {@link SchemaValidationError} carries: `invalid_input` for an input schema (a
5981
+ * workflow's or a step's), `invalid_output` for an output schema, `schema_validation` for anything
5982
+ * else (a typed `searchAttributes` write). The same codes `@adonis-agora/durable` uses.
5983
+ */
5984
+ type SchemaValidationCode = 'invalid_input' | 'invalid_output' | 'schema_validation';
5985
+ /**
5986
+ * Thrown when a value fails a schema the app declared: `@Workflow({ input, output })`,
5987
+ * `@Step({ input, output })`, or the workflow's typed `searchAttributes`. A {@link FatalError}, so it
5988
+ * is NOT retried — retrying cannot fix a value that doesn't match its schema. Its `code` says which
5989
+ * schema rejected the value ({@link SchemaValidationCode}). `issues` (also `details.issues`, so it
5990
+ * survives the transport to the run's error and an onError handler's input) lists every issue with
5991
+ * a JSON-safe `path`. Route one code with `onError: [{ code: 'invalid_input', workflow }]`, or all
5992
+ * three with `onError: [{ code: SchemaValidationError, workflow }]`: its `static code`,
5993
+ * `schema_validation`, also catches an `invalid_input` or `invalid_output` no route of its own
5994
+ * matches. Same class, codes, message and `details` as `@adonis-agora/durable`.
5995
+ */
5996
+ declare class SchemaValidationError extends FatalError {
5997
+ static readonly code = "schema_validation";
5998
+ readonly issues: SchemaIssue[];
5999
+ /**
6000
+ * @param label What was validated, e.g. `workflow "checkout" input`; the message is
6001
+ * `invalid <label>: <path>: <message>; …`.
6002
+ * @param summary Overrides the `<path>: <message>; …` part, for issues whose messages already
6003
+ * name their field (class-validator's `orderId must be a string`).
6004
+ */
6005
+ constructor(label: string, issues: ReadonlyArray<{
6006
+ readonly message: string;
6007
+ readonly path?: ReadonlyArray<PropertyKey | {
6008
+ readonly key: PropertyKey;
6009
+ }> | undefined;
6010
+ }>, code?: SchemaValidationCode, summary?: string);
6011
+ }
5969
6012
  /**
5970
6013
  * Thrown by `start` when a run would exceed its concurrency quota: `active` runs sharing the quota
5971
6014
  * `key` are already in flight (in the quota's counted statuses) and the `limit` is `limit`. Nothing
@@ -6634,11 +6677,20 @@ type Subscribable = {
6634
6677
  */
6635
6678
  declare function collectMetrics(engine: Subscribable): MetricsCollector;
6636
6679
 
6637
- interface ScheduledWorkflow {
6680
+ /**
6681
+ * A recurring start of `workflow`, fired on a fixed interval or a cron.
6682
+ *
6683
+ * `workflow` is a registered name or a `@Workflow` class. A class types `input` against its `run`;
6684
+ * annotate the schedule to get that check, as in
6685
+ * `const nightly: ScheduledWorkflow<typeof ReportWorkflow> = { workflow: ReportWorkflow, … }`.
6686
+ */
6687
+ interface ScheduledWorkflow<W extends WorkflowRef = WorkflowRef> {
6638
6688
  /** Stable key identifying this schedule — part of the deterministic run id. */
6639
6689
  key: string;
6640
- workflow: string;
6641
- input?: unknown;
6690
+ /** The workflow to start: a registered name (any runtime) or a `@Workflow` class. */
6691
+ workflow: W;
6692
+ /** The input every run starts with — typed against `run` when `workflow` is a class. */
6693
+ input?: WorkflowInputOf<W>;
6642
6694
  /** Start one run every `everyMs`. Mutually exclusive with `cron`. */
6643
6695
  everyMs?: number;
6644
6696
  /**
@@ -6676,6 +6728,16 @@ interface ScheduledWorkflow {
6676
6728
  backfill?: {
6677
6729
  maxCatchup: number;
6678
6730
  };
6731
+ /**
6732
+ * The namespace this schedule's runs are stamped with, overriding the ticking engine's own. Opt-in;
6733
+ * absent = the run gets the namespace of whichever engine fired it.
6734
+ *
6735
+ * The run id is a time bucket, so the instance that wins the race to fire a window decides which
6736
+ * namespace (worker pool) the run lands in. When the workflow can only run in one pool — its steps
6737
+ * need a capability only that pool's workers have — pin it here, so whoever fires the window, the
6738
+ * run is stamped for (and only picked up by) that pool.
6739
+ */
6740
+ namespace?: string;
6679
6741
  }
6680
6742
  /** Injectable dispatch-path effects (kept out of the deterministic run-id logic). Defaults to real
6681
6743
  * `Math.random` + `setTimeout`; overridden in tests so jitter is deterministic and instant. */
@@ -6697,14 +6759,23 @@ declare function prevCronFireMs(expr: string, nowMs: number, timezone?: string):
6697
6759
  * schedule is next due.
6698
6760
  */
6699
6761
  declare function nextCronFireMs(expr: string, afterMs: number, timezone?: string): number;
6762
+ /**
6763
+ * The deterministic, idempotent run id for a schedule at `nowMs` — its current fire window
6764
+ * (`sched:<key>:<bucket>`, the bucket being a fixed-interval index or the cron fire time). Pass the
6765
+ * same `ScheduledWorkflow` you registered to look a window's run up, e.g.
6766
+ * `engine.getRun(scheduleRunIdAt(dailyReport, Date.now()))`, rather than rebuilding the id by hand.
6767
+ */
6768
+ declare function scheduleRunIdAt(s: ScheduledWorkflow, nowMs: number): string;
6700
6769
  /**
6701
6770
  * Start each schedule's current-window run. The run id is the time bucket (a fixed-interval index,
6702
6771
  * or the cron fire time) and `engine.start` is idempotent, so firing this on an interval — or
6703
6772
  * racing two instances on the same tick — starts **each window exactly once**. Wire it to a
6704
- * `setInterval`, the durable timer poller, or `@nestjs/schedule`. Returns the run ids for the
6705
- * current windows.
6773
+ * `setInterval`, the durable timer poller, or `@nestjs/schedule`. Returns the run ids this call
6774
+ * started: a window that already has a run is not started or returned again, and once this process
6775
+ * has seen a window's run exist, later calls in that window skip it without a store read or a
6776
+ * jitter sleep.
6706
6777
  */
6707
- declare function runSchedules(engine: Pick<WorkflowEngine, 'start' | 'getRun'>, schedules: readonly ScheduledWorkflow[], nowMs: number, opts?: RunSchedulesOptions): Promise<string[]>;
6778
+ declare function runSchedules(engine: Pick<WorkflowEngine, 'start' | 'getRun'> & Partial<Pick<WorkflowEngine, 'isDraining'>>, schedules: readonly ScheduledWorkflow[], nowMs: number, opts?: RunSchedulesOptions): Promise<string[]>;
6708
6779
 
6709
6780
  /**
6710
6781
  * A {@link StepLogger} that appends to `events`, stamping each line with `now()`. Shared by the
@@ -7117,4 +7188,4 @@ declare function singletonAdmissionDenialPatch(runs: WorkflowRun[], runId: strin
7117
7188
  updatedAt: Date;
7118
7189
  } | undefined;
7119
7190
 
7120
- export { type AccumulatorHost, type Admission, type AdmissionBackend, type AdmissionItem, type AllOptions, type AllSettledOptions, type AllStepsOptions, type AllStepsSettledOptions, type AnnouncedWorkflow, type AnnouncementEvidence, type AttributeFilter, type AttributeOp, type AttributePredicateOperand, type AttributeValue, type BackoffStrategy, type BlockedDispatch, type BlockedResolution, CHILD_EDGE_PREFIXES, CONTROL_FLOW_SIGNAL, CURRENT_PROTOCOL_VERSION, type CapabilityRequirement, type CheckpointAggregatingStore, type CheckpointNameKind, type CheckpointQuery, type CheckpointStat, type ChildCallOptions, type ChildWait, CodecStateStore, type CodecStateStoreOptions, type ConcurrencyConfig, ConcurrencyLimitError, type ConcurrencyQuota, ContinueAsNew, type ControlMessage, type ControlPlane, DEFAULT_CONCURRENCY_STATUSES, DEFAULT_PARTITION, DEFAULT_STALLED_AFTER_MS, DURABLE_OPTIONS, DURABLE_OPTIONS_CANONICAL, DURABLE_STEP_CONFIG, DURABLE_STEP_NAME, type Disagreement, type DispatchDiagnostics, type DispatchPlan, type DurableExecution, type DurableExecutionWrapper, type DurableTopology, type DurableWebhook, DurableWorkflow, type DynamicWorkflowCtx, ENGINE_MINTED_TAG_PREFIXES, ENTITY_WORKFLOW, EVENT_BUFFER_SCAN_LIMIT, EVENT_TOKEN_PREFIX, type EngineEvent, type EngineEventType, type EngineListener, Entities, type EntityConfig, type EntityHandler, type EntityHost, type EntityWorkflowHost, EventAccumulators, type EventBatchConfig, FAN_SUMMARY_FAILURES, type FanJoin, type FanPage, type FanPageQuery, type FanSummary, type FanWait, FatalError, type FindStalledRunsOptions, GatherError, type GatherItemFailure, type GroupHealth, HANDOFF_SEQ_BASE, type Heartbeat, type HeartbeatSighting, type HeartbeatStatus, type HistoryEvent, InMemoryAdmissionBackend, InMemoryStateStore, InMemoryTransport, type InferSearchAttributes, LEGACY_V1_CAPABILITIES, LEGACY_V1_PROTOCOL, type ListStalledRunsOptions, type MetricsCollector, type MetricsSnapshot, type NamedTransport, type NegotiateOptions, type NegotiationOutcome, type NegotiationReason, type NegotiationResult, NestedWorkflowCallError, NonDeterminismError, type OnErrorInput, type OnErrorRoute, type OwnedLeaseReclaim, type PartitionSighting, type PayloadCodec, type QueueConfig, QueueController, RUN_HISTORY_FIXTURE_VERSION, RUN_VALUE_FACET_LIMIT, RUN_VALUE_FACET_SCAN, type RawWorkerDescriptor, RemoteStepError, RemoteStepTimeout, type RemoteTask, RemoteWorkflowExecutor, RemoteWorkflowTimeout, type RetentionPolicy, type RoutableDispatch, type RoutableResolution, type RoutingResolution, type RunAttributeRow, type RunDetail, type RunDetailOptions, type RunDispatcher, type RunFacetQuery, type RunFacetRow, RunGateway, type RunListItem, type RunQuery, type RunReply, type RunReplyResult, type RunRequest, type RunRequestKind, type RunResult, type RunSchedulesOptions, type RunScope, type RunStatus, type RunValueAxis, type RunValueFacetOptions, type RunValueFacetOrder, type RunValueFacetRow, type RunWaiting, SINGLETON_ACTIVE_STATUSES, SINGLETON_ADMITTED_TAG, SINGLETON_RESERVED_TAGS, SINGLETON_TRACKED_TAG, STATE_STORE, STATE_STORE_CANONICAL, SUMMARY_CHILD_LIMIT, type ScalarAttributeFilter, ScheduleAlreadyExistsError, ScheduleClient, type ScheduleClientOptions, type ScheduleDescription, type ScheduleHost, type ScheduleListQuery, ScheduleNotFoundError, type ScheduleOptions, type ScheduleQuery, type ScheduleRecord, type ScheduledWorkflow, type SearchAttributes, type SearchAttributesSchema, type Settled, SignalTimeoutError, type SignalWaiter, type SingletonConfig, SingletonQueueFullError, type StalePendingStep, type StalledRunInfo, StandardSchemaV1, type StartOptions, type StartRunMessage, type StateStore, type StepCheckpoint, type StepConfig, type StepDef, type StepDispatchOpts, type StepError, type StepEvent, type StepHandler, type StepInterceptor, type StepInvocation, type StepKind, type StepLogger, type StepOptions, type StepRef, type StepResult, type StepScope, type StepUndo, type StoreTransaction, type SubProcessHandle, TERMINAL_RUN_STATUSES, TRANSPORT, TRANSPORT_CANONICAL, type TenantEvent, type Transport, type UndoOf, type UpdateResult, type UpdateValidator, VERSION_UNDECLARED_TAG, WORKFLOW_NAME_KEY, type WireDates, type WorkerAdjust, type WorkerConcurrencyStatus, type WorkerDescriptor, type WorkerHeartbeat, type WorkerLifecycle, type WorkerStatus, type WorkflowAnnouncement, WorkflowBlocked, type WorkflowClass, type WorkflowCommand, type WorkflowCtx, type WorkflowDecision, type WorkflowDirectory, WorkflowEngine, type WorkflowEngineDeps, type WorkflowExecutor, type WorkflowHandler, type WorkflowInputOf, type WorkflowOutputOf, type WorkflowRef, type WorkflowRegistration, type WorkflowRun, type WorkflowStartClient, type WorkflowStepEvent, WorkflowSuspended, type WorkflowTask, aggregateAnnouncements, aggregatesCheckpoints, announcementsOf, applyAttributeQuery, assertConcurrency, assertRoutes, attributeColumnFor, attributeOperand, attributePredicateOperands, axisIsRunColumn, bindWorkflowClass, breakpointToken, buildWorkflowDirectory, canRoute, capableWorkers, checkpointNameKind, checkpointStatsOf, childIdsOf, classifyWaiterToken, clearDurableExecutionWrappers, clearTerminalSingletonAdmission, codeAndDetails, codeOf, collectMetrics, concurrencyTag, controlPlaneDescriptor, countRuns, coveredTokens, createStepLogger, currentStep, currentStepScope, currentWorkflowCtx, debug, describeWorker, descriptorHash, detailsOf, error, etagMatches, eventMatchOf, eventMatches, eventNameOf, eventPrefix, eventToken, facetOrigin, fanPageBounds, fanPageEtag, findStalledRuns, foldedCount, handoffHandlerId, handoffName, hasDurableExecutionWrappers, heartbeatStatus, indexWaitersByRun, info, isEngineMintedTag, isEventToken, isFailedMember, isLegacyDescriptor, isNonEmptyRunScope, isWorkflowControlFlowSignal, legacySingletonHolders, listSignalWaitersOfRuns, liveHandoff, log, matchesAttributes, mergeRunFacetRows, mergeRunValueFacetRows, missingCapabilities, negotiate, nextCronFireMs, normalizeAttributeRows, normalizeDescriptor, observedAnnouncements, onErrorRunId, pageFan, pageFanFromStore, parentRunIdOf, parseDuration, parseWebhookToken, partitionOfToken, planDispatch, poolNamespace, prevCronFireMs, queryCheckpointsOf, readSearchAttributes, requiredCapabilities, resolveConcurrency, resolveRouting, resolveRunWaiting, retiredHandoffName, routeFor, runDetailEtag, runDurableExecution, runInStepLogger, runInStepScope, runInWorkflowCtx, runOutsideWorkflowCtx, runSchedules, runStepHandler, runValueFacetsFromRuns, sanitizeQueueToken, scheduledRunId, singletonAdmissionDenialPatch, singletonAdmissionTags, sqlComparator, stepConfigOf, stepId, stepNameOf, sub, subEvent, subProcess, summarizeFan, summarizeRunFromStore, summarizeTimeline, tenantGroup, useDurableExecution, warn, withSingletonTracked, withoutSingletonMarkers, workflowName };
7191
+ export { type AccumulatorHost, type Admission, type AdmissionBackend, type AdmissionItem, type AllOptions, type AllSettledOptions, type AllStepsOptions, type AllStepsSettledOptions, type AnnouncedWorkflow, type AnnouncementEvidence, type AttributeFilter, type AttributeOp, type AttributePredicateOperand, type AttributeValue, type BackoffStrategy, type BlockedDispatch, type BlockedResolution, CHILD_EDGE_PREFIXES, CONTROL_FLOW_SIGNAL, CURRENT_PROTOCOL_VERSION, type CapabilityRequirement, type CheckpointAggregatingStore, type CheckpointNameKind, type CheckpointQuery, type CheckpointStat, type ChildCallOptions, type ChildWait, CodecStateStore, type CodecStateStoreOptions, type ConcurrencyConfig, ConcurrencyLimitError, type ConcurrencyQuota, ContinueAsNew, type ControlMessage, type ControlPlane, DEFAULT_CONCURRENCY_STATUSES, DEFAULT_PARTITION, DEFAULT_STALLED_AFTER_MS, DURABLE_OPTIONS, DURABLE_OPTIONS_CANONICAL, DURABLE_STEP_CONFIG, DURABLE_STEP_NAME, type Disagreement, type DispatchDiagnostics, type DispatchPlan, type DurableExecution, type DurableExecutionWrapper, type DurableTopology, type DurableWebhook, DurableWorkflow, type DynamicWorkflowCtx, ENGINE_MINTED_TAG_PREFIXES, ENTITY_WORKFLOW, EVENT_BUFFER_SCAN_LIMIT, EVENT_TOKEN_PREFIX, type EngineEvent, type EngineEventType, type EngineListener, Entities, type EntityConfig, type EntityHandler, type EntityHost, type EntityWorkflowHost, EventAccumulators, type EventBatchConfig, FAN_SUMMARY_FAILURES, type FanJoin, type FanPage, type FanPageQuery, type FanSummary, type FanWait, FatalError, type FindStalledRunsOptions, GatherError, type GatherItemFailure, type GroupHealth, HANDOFF_SEQ_BASE, type Heartbeat, type HeartbeatSighting, type HeartbeatStatus, type HistoryEvent, InMemoryAdmissionBackend, InMemoryStateStore, InMemoryTransport, type InferSearchAttributes, LEGACY_V1_CAPABILITIES, LEGACY_V1_PROTOCOL, type ListStalledRunsOptions, type MetricsCollector, type MetricsSnapshot, type NamedTransport, type NegotiateOptions, type NegotiationOutcome, type NegotiationReason, type NegotiationResult, NestedWorkflowCallError, NonDeterminismError, type OnErrorInput, type OnErrorRoute, type OwnedLeaseReclaim, type PartitionSighting, type PayloadCodec, type QueueConfig, QueueController, RUN_HISTORY_FIXTURE_VERSION, RUN_VALUE_FACET_LIMIT, RUN_VALUE_FACET_SCAN, type RawWorkerDescriptor, RemoteStepError, RemoteStepTimeout, type RemoteTask, RemoteWorkflowExecutor, RemoteWorkflowTimeout, type RetentionPolicy, type RoutableDispatch, type RoutableResolution, type RoutingResolution, type RunAttributeRow, type RunDetail, type RunDetailOptions, type RunDispatcher, type RunFacetQuery, type RunFacetRow, RunGateway, type RunListItem, type RunQuery, type RunReply, type RunReplyResult, type RunRequest, type RunRequestKind, type RunResult, type RunSchedulesOptions, type RunScope, type RunStatus, type RunValueAxis, type RunValueFacetOptions, type RunValueFacetOrder, type RunValueFacetRow, type RunWaiting, SINGLETON_ACTIVE_STATUSES, SINGLETON_ADMITTED_TAG, SINGLETON_RESERVED_TAGS, SINGLETON_TRACKED_TAG, STATE_STORE, STATE_STORE_CANONICAL, SUMMARY_CHILD_LIMIT, type ScalarAttributeFilter, ScheduleAlreadyExistsError, ScheduleClient, type ScheduleClientOptions, type ScheduleDescription, type ScheduleHost, type ScheduleListQuery, ScheduleNotFoundError, type ScheduleOptions, type ScheduleQuery, type ScheduleRecord, type ScheduledWorkflow, type SchemaIssue, type SchemaValidationCode, SchemaValidationError, type SearchAttributes, type SearchAttributesSchema, type Settled, SignalTimeoutError, type SignalWaiter, type SingletonConfig, SingletonQueueFullError, type StalePendingStep, type StalledRunInfo, StandardSchemaV1, type StartOptions, type StartRunMessage, type StateStore, type StepCheckpoint, type StepConfig, type StepDef, type StepDispatchOpts, type StepError, type StepEvent, type StepHandler, type StepInterceptor, type StepInvocation, type StepKind, type StepLogger, type StepOptions, type StepRef, type StepResult, type StepScope, type StepUndo, type StoreTransaction, type SubProcessHandle, TERMINAL_RUN_STATUSES, TRANSPORT, TRANSPORT_CANONICAL, type TenantEvent, type Transport, type UndoOf, type UpdateResult, type UpdateValidator, VERSION_UNDECLARED_TAG, WORKFLOW_NAME_KEY, type WireDates, type WorkerAdjust, type WorkerConcurrencyStatus, type WorkerDescriptor, type WorkerHeartbeat, type WorkerLifecycle, type WorkerStatus, type WorkflowAnnouncement, WorkflowBlocked, type WorkflowClass, type WorkflowCommand, type WorkflowCtx, type WorkflowDecision, type WorkflowDirectory, WorkflowEngine, type WorkflowEngineDeps, type WorkflowExecutor, type WorkflowHandler, type WorkflowInputOf, type WorkflowOutputOf, type WorkflowRef, type WorkflowRegistration, type WorkflowRun, type WorkflowStartClient, type WorkflowStepEvent, WorkflowSuspended, type WorkflowTask, aggregateAnnouncements, aggregatesCheckpoints, announcementsOf, applyAttributeQuery, assertConcurrency, assertRoutes, attributeColumnFor, attributeOperand, attributePredicateOperands, axisIsRunColumn, bindWorkflowClass, breakpointToken, buildWorkflowDirectory, canRoute, capableWorkers, checkpointNameKind, checkpointStatsOf, childIdsOf, classifyWaiterToken, clearDurableExecutionWrappers, clearTerminalSingletonAdmission, codeAndDetails, codeOf, collectMetrics, concurrencyTag, controlPlaneDescriptor, countRuns, coveredTokens, createStepLogger, currentStep, currentStepScope, currentWorkflowCtx, debug, describeWorker, descriptorHash, detailsOf, error, etagMatches, eventMatchOf, eventMatches, eventNameOf, eventPrefix, eventToken, facetOrigin, fanPageBounds, fanPageEtag, findStalledRuns, foldedCount, handoffHandlerId, handoffName, hasDurableExecutionWrappers, heartbeatStatus, indexWaitersByRun, info, isEngineMintedTag, isEventToken, isFailedMember, isLegacyDescriptor, isNonEmptyRunScope, isWorkflowControlFlowSignal, legacySingletonHolders, listSignalWaitersOfRuns, liveHandoff, log, matchesAttributes, mergeRunFacetRows, mergeRunValueFacetRows, missingCapabilities, negotiate, nextCronFireMs, normalizeAttributeRows, normalizeDescriptor, observedAnnouncements, onErrorRunId, pageFan, pageFanFromStore, parentRunIdOf, parseDuration, parseWebhookToken, partitionOfToken, planDispatch, poolNamespace, prevCronFireMs, queryCheckpointsOf, readSearchAttributes, requiredCapabilities, resolveConcurrency, resolveRouting, resolveRunWaiting, retiredHandoffName, routeFor, runDetailEtag, runDurableExecution, runInStepLogger, runInStepScope, runInWorkflowCtx, runOutsideWorkflowCtx, runSchedules, runStepHandler, runValueFacetsFromRuns, sanitizeQueueToken, scheduleRunIdAt, scheduledRunId, singletonAdmissionDenialPatch, singletonAdmissionTags, sqlComparator, stepConfigOf, stepId, stepNameOf, sub, subEvent, subProcess, summarizeFan, summarizeRunFromStore, summarizeTimeline, tenantGroup, useDurableExecution, warn, withSingletonTracked, withoutSingletonMarkers, workflowName };
package/dist/index.d.ts CHANGED
@@ -2877,8 +2877,8 @@ interface WorkflowCtx<A extends SearchAttributes = SearchAttributes> {
2877
2877
  *
2878
2878
  * When the workflow declares a `@Workflow({ searchAttributes })` schema, the MERGED result (this
2879
2879
  * run's existing `searchAttributes` shallow-merged with `attrs`) is validated against it before the
2880
- * write — an invalid merge throws, naming the workflow, the offending key(s), and the schema's
2881
- * issues. Validation runs once, on the same first-run-only position as the write itself (skipped
2880
+ * write — an invalid merge throws a `SchemaValidationError` (code `schema_validation`, never
2881
+ * retried) naming the workflow, with the schema's issues. Validation runs once, on the same first-run-only position as the write itself (skipped
2882
2882
  * entirely on replay, so it's never a source of nondeterminism); it must be synchronous — a schema
2883
2883
  * whose `validate` returns a `Promise` throws instead of being awaited. No schema declared ⇒
2884
2884
  * unchanged, unvalidated behavior.
@@ -3827,7 +3827,10 @@ declare function liveHandoff<C extends {
3827
3827
  seq: number;
3828
3828
  name: string;
3829
3829
  }>(runId: string, checkpoints: readonly C[]): C | undefined;
3830
- /** The route a failure takes: the one for its exact code, else the catch-all, else none. */
3830
+ /**
3831
+ * The route a failure takes: the one for its exact code, else (for an `invalid_input` /
3832
+ * `invalid_output` failure) a `schema_validation` route, else the catch-all, else none.
3833
+ */
3831
3834
  declare function routeFor(routes: OnErrorRoute[] | undefined, error: StepError): OnErrorRoute | undefined;
3832
3835
  /** Reject two routes for one code, or two catch-alls — ambiguous config, caught at registration. */
3833
3836
  declare function assertRoutes(workflow: string, routes: OnErrorRoute[]): void;
@@ -4847,6 +4850,8 @@ declare class WorkflowEngine {
4847
4850
  * between this call and `drain()` (other hooks, the HTTP server closing) never is. Calls add up.
4848
4851
  */
4849
4852
  beginDrain(spentMs?: number): void;
4853
+ /** True once {@link beginDrain} (or {@link drain}) has been called. */
4854
+ isDraining(): boolean;
4850
4855
  /**
4851
4856
  * Graceful shutdown: stop picking up new runs (recovery/timer become no-ops) and wait for
4852
4857
  * in-flight executions to settle, up to `timeoutMs`. Call from your app's shutdown hook so a
@@ -5966,6 +5971,44 @@ declare class FatalError extends Error {
5966
5971
  readonly retryable = false;
5967
5972
  constructor(message: string, code?: string, details?: unknown);
5968
5973
  }
5974
+ /** One schema issue in JSON-safe form: its message and the path segments, as strings. */
5975
+ interface SchemaIssue {
5976
+ message: string;
5977
+ path: string[];
5978
+ }
5979
+ /**
5980
+ * The code a {@link SchemaValidationError} carries: `invalid_input` for an input schema (a
5981
+ * workflow's or a step's), `invalid_output` for an output schema, `schema_validation` for anything
5982
+ * else (a typed `searchAttributes` write). The same codes `@adonis-agora/durable` uses.
5983
+ */
5984
+ type SchemaValidationCode = 'invalid_input' | 'invalid_output' | 'schema_validation';
5985
+ /**
5986
+ * Thrown when a value fails a schema the app declared: `@Workflow({ input, output })`,
5987
+ * `@Step({ input, output })`, or the workflow's typed `searchAttributes`. A {@link FatalError}, so it
5988
+ * is NOT retried — retrying cannot fix a value that doesn't match its schema. Its `code` says which
5989
+ * schema rejected the value ({@link SchemaValidationCode}). `issues` (also `details.issues`, so it
5990
+ * survives the transport to the run's error and an onError handler's input) lists every issue with
5991
+ * a JSON-safe `path`. Route one code with `onError: [{ code: 'invalid_input', workflow }]`, or all
5992
+ * three with `onError: [{ code: SchemaValidationError, workflow }]`: its `static code`,
5993
+ * `schema_validation`, also catches an `invalid_input` or `invalid_output` no route of its own
5994
+ * matches. Same class, codes, message and `details` as `@adonis-agora/durable`.
5995
+ */
5996
+ declare class SchemaValidationError extends FatalError {
5997
+ static readonly code = "schema_validation";
5998
+ readonly issues: SchemaIssue[];
5999
+ /**
6000
+ * @param label What was validated, e.g. `workflow "checkout" input`; the message is
6001
+ * `invalid <label>: <path>: <message>; …`.
6002
+ * @param summary Overrides the `<path>: <message>; …` part, for issues whose messages already
6003
+ * name their field (class-validator's `orderId must be a string`).
6004
+ */
6005
+ constructor(label: string, issues: ReadonlyArray<{
6006
+ readonly message: string;
6007
+ readonly path?: ReadonlyArray<PropertyKey | {
6008
+ readonly key: PropertyKey;
6009
+ }> | undefined;
6010
+ }>, code?: SchemaValidationCode, summary?: string);
6011
+ }
5969
6012
  /**
5970
6013
  * Thrown by `start` when a run would exceed its concurrency quota: `active` runs sharing the quota
5971
6014
  * `key` are already in flight (in the quota's counted statuses) and the `limit` is `limit`. Nothing
@@ -6634,11 +6677,20 @@ type Subscribable = {
6634
6677
  */
6635
6678
  declare function collectMetrics(engine: Subscribable): MetricsCollector;
6636
6679
 
6637
- interface ScheduledWorkflow {
6680
+ /**
6681
+ * A recurring start of `workflow`, fired on a fixed interval or a cron.
6682
+ *
6683
+ * `workflow` is a registered name or a `@Workflow` class. A class types `input` against its `run`;
6684
+ * annotate the schedule to get that check, as in
6685
+ * `const nightly: ScheduledWorkflow<typeof ReportWorkflow> = { workflow: ReportWorkflow, … }`.
6686
+ */
6687
+ interface ScheduledWorkflow<W extends WorkflowRef = WorkflowRef> {
6638
6688
  /** Stable key identifying this schedule — part of the deterministic run id. */
6639
6689
  key: string;
6640
- workflow: string;
6641
- input?: unknown;
6690
+ /** The workflow to start: a registered name (any runtime) or a `@Workflow` class. */
6691
+ workflow: W;
6692
+ /** The input every run starts with — typed against `run` when `workflow` is a class. */
6693
+ input?: WorkflowInputOf<W>;
6642
6694
  /** Start one run every `everyMs`. Mutually exclusive with `cron`. */
6643
6695
  everyMs?: number;
6644
6696
  /**
@@ -6676,6 +6728,16 @@ interface ScheduledWorkflow {
6676
6728
  backfill?: {
6677
6729
  maxCatchup: number;
6678
6730
  };
6731
+ /**
6732
+ * The namespace this schedule's runs are stamped with, overriding the ticking engine's own. Opt-in;
6733
+ * absent = the run gets the namespace of whichever engine fired it.
6734
+ *
6735
+ * The run id is a time bucket, so the instance that wins the race to fire a window decides which
6736
+ * namespace (worker pool) the run lands in. When the workflow can only run in one pool — its steps
6737
+ * need a capability only that pool's workers have — pin it here, so whoever fires the window, the
6738
+ * run is stamped for (and only picked up by) that pool.
6739
+ */
6740
+ namespace?: string;
6679
6741
  }
6680
6742
  /** Injectable dispatch-path effects (kept out of the deterministic run-id logic). Defaults to real
6681
6743
  * `Math.random` + `setTimeout`; overridden in tests so jitter is deterministic and instant. */
@@ -6697,14 +6759,23 @@ declare function prevCronFireMs(expr: string, nowMs: number, timezone?: string):
6697
6759
  * schedule is next due.
6698
6760
  */
6699
6761
  declare function nextCronFireMs(expr: string, afterMs: number, timezone?: string): number;
6762
+ /**
6763
+ * The deterministic, idempotent run id for a schedule at `nowMs` — its current fire window
6764
+ * (`sched:<key>:<bucket>`, the bucket being a fixed-interval index or the cron fire time). Pass the
6765
+ * same `ScheduledWorkflow` you registered to look a window's run up, e.g.
6766
+ * `engine.getRun(scheduleRunIdAt(dailyReport, Date.now()))`, rather than rebuilding the id by hand.
6767
+ */
6768
+ declare function scheduleRunIdAt(s: ScheduledWorkflow, nowMs: number): string;
6700
6769
  /**
6701
6770
  * Start each schedule's current-window run. The run id is the time bucket (a fixed-interval index,
6702
6771
  * or the cron fire time) and `engine.start` is idempotent, so firing this on an interval — or
6703
6772
  * racing two instances on the same tick — starts **each window exactly once**. Wire it to a
6704
- * `setInterval`, the durable timer poller, or `@nestjs/schedule`. Returns the run ids for the
6705
- * current windows.
6773
+ * `setInterval`, the durable timer poller, or `@nestjs/schedule`. Returns the run ids this call
6774
+ * started: a window that already has a run is not started or returned again, and once this process
6775
+ * has seen a window's run exist, later calls in that window skip it without a store read or a
6776
+ * jitter sleep.
6706
6777
  */
6707
- declare function runSchedules(engine: Pick<WorkflowEngine, 'start' | 'getRun'>, schedules: readonly ScheduledWorkflow[], nowMs: number, opts?: RunSchedulesOptions): Promise<string[]>;
6778
+ declare function runSchedules(engine: Pick<WorkflowEngine, 'start' | 'getRun'> & Partial<Pick<WorkflowEngine, 'isDraining'>>, schedules: readonly ScheduledWorkflow[], nowMs: number, opts?: RunSchedulesOptions): Promise<string[]>;
6708
6779
 
6709
6780
  /**
6710
6781
  * A {@link StepLogger} that appends to `events`, stamping each line with `now()`. Shared by the
@@ -7117,4 +7188,4 @@ declare function singletonAdmissionDenialPatch(runs: WorkflowRun[], runId: strin
7117
7188
  updatedAt: Date;
7118
7189
  } | undefined;
7119
7190
 
7120
- export { type AccumulatorHost, type Admission, type AdmissionBackend, type AdmissionItem, type AllOptions, type AllSettledOptions, type AllStepsOptions, type AllStepsSettledOptions, type AnnouncedWorkflow, type AnnouncementEvidence, type AttributeFilter, type AttributeOp, type AttributePredicateOperand, type AttributeValue, type BackoffStrategy, type BlockedDispatch, type BlockedResolution, CHILD_EDGE_PREFIXES, CONTROL_FLOW_SIGNAL, CURRENT_PROTOCOL_VERSION, type CapabilityRequirement, type CheckpointAggregatingStore, type CheckpointNameKind, type CheckpointQuery, type CheckpointStat, type ChildCallOptions, type ChildWait, CodecStateStore, type CodecStateStoreOptions, type ConcurrencyConfig, ConcurrencyLimitError, type ConcurrencyQuota, ContinueAsNew, type ControlMessage, type ControlPlane, DEFAULT_CONCURRENCY_STATUSES, DEFAULT_PARTITION, DEFAULT_STALLED_AFTER_MS, DURABLE_OPTIONS, DURABLE_OPTIONS_CANONICAL, DURABLE_STEP_CONFIG, DURABLE_STEP_NAME, type Disagreement, type DispatchDiagnostics, type DispatchPlan, type DurableExecution, type DurableExecutionWrapper, type DurableTopology, type DurableWebhook, DurableWorkflow, type DynamicWorkflowCtx, ENGINE_MINTED_TAG_PREFIXES, ENTITY_WORKFLOW, EVENT_BUFFER_SCAN_LIMIT, EVENT_TOKEN_PREFIX, type EngineEvent, type EngineEventType, type EngineListener, Entities, type EntityConfig, type EntityHandler, type EntityHost, type EntityWorkflowHost, EventAccumulators, type EventBatchConfig, FAN_SUMMARY_FAILURES, type FanJoin, type FanPage, type FanPageQuery, type FanSummary, type FanWait, FatalError, type FindStalledRunsOptions, GatherError, type GatherItemFailure, type GroupHealth, HANDOFF_SEQ_BASE, type Heartbeat, type HeartbeatSighting, type HeartbeatStatus, type HistoryEvent, InMemoryAdmissionBackend, InMemoryStateStore, InMemoryTransport, type InferSearchAttributes, LEGACY_V1_CAPABILITIES, LEGACY_V1_PROTOCOL, type ListStalledRunsOptions, type MetricsCollector, type MetricsSnapshot, type NamedTransport, type NegotiateOptions, type NegotiationOutcome, type NegotiationReason, type NegotiationResult, NestedWorkflowCallError, NonDeterminismError, type OnErrorInput, type OnErrorRoute, type OwnedLeaseReclaim, type PartitionSighting, type PayloadCodec, type QueueConfig, QueueController, RUN_HISTORY_FIXTURE_VERSION, RUN_VALUE_FACET_LIMIT, RUN_VALUE_FACET_SCAN, type RawWorkerDescriptor, RemoteStepError, RemoteStepTimeout, type RemoteTask, RemoteWorkflowExecutor, RemoteWorkflowTimeout, type RetentionPolicy, type RoutableDispatch, type RoutableResolution, type RoutingResolution, type RunAttributeRow, type RunDetail, type RunDetailOptions, type RunDispatcher, type RunFacetQuery, type RunFacetRow, RunGateway, type RunListItem, type RunQuery, type RunReply, type RunReplyResult, type RunRequest, type RunRequestKind, type RunResult, type RunSchedulesOptions, type RunScope, type RunStatus, type RunValueAxis, type RunValueFacetOptions, type RunValueFacetOrder, type RunValueFacetRow, type RunWaiting, SINGLETON_ACTIVE_STATUSES, SINGLETON_ADMITTED_TAG, SINGLETON_RESERVED_TAGS, SINGLETON_TRACKED_TAG, STATE_STORE, STATE_STORE_CANONICAL, SUMMARY_CHILD_LIMIT, type ScalarAttributeFilter, ScheduleAlreadyExistsError, ScheduleClient, type ScheduleClientOptions, type ScheduleDescription, type ScheduleHost, type ScheduleListQuery, ScheduleNotFoundError, type ScheduleOptions, type ScheduleQuery, type ScheduleRecord, type ScheduledWorkflow, type SearchAttributes, type SearchAttributesSchema, type Settled, SignalTimeoutError, type SignalWaiter, type SingletonConfig, SingletonQueueFullError, type StalePendingStep, type StalledRunInfo, StandardSchemaV1, type StartOptions, type StartRunMessage, type StateStore, type StepCheckpoint, type StepConfig, type StepDef, type StepDispatchOpts, type StepError, type StepEvent, type StepHandler, type StepInterceptor, type StepInvocation, type StepKind, type StepLogger, type StepOptions, type StepRef, type StepResult, type StepScope, type StepUndo, type StoreTransaction, type SubProcessHandle, TERMINAL_RUN_STATUSES, TRANSPORT, TRANSPORT_CANONICAL, type TenantEvent, type Transport, type UndoOf, type UpdateResult, type UpdateValidator, VERSION_UNDECLARED_TAG, WORKFLOW_NAME_KEY, type WireDates, type WorkerAdjust, type WorkerConcurrencyStatus, type WorkerDescriptor, type WorkerHeartbeat, type WorkerLifecycle, type WorkerStatus, type WorkflowAnnouncement, WorkflowBlocked, type WorkflowClass, type WorkflowCommand, type WorkflowCtx, type WorkflowDecision, type WorkflowDirectory, WorkflowEngine, type WorkflowEngineDeps, type WorkflowExecutor, type WorkflowHandler, type WorkflowInputOf, type WorkflowOutputOf, type WorkflowRef, type WorkflowRegistration, type WorkflowRun, type WorkflowStartClient, type WorkflowStepEvent, WorkflowSuspended, type WorkflowTask, aggregateAnnouncements, aggregatesCheckpoints, announcementsOf, applyAttributeQuery, assertConcurrency, assertRoutes, attributeColumnFor, attributeOperand, attributePredicateOperands, axisIsRunColumn, bindWorkflowClass, breakpointToken, buildWorkflowDirectory, canRoute, capableWorkers, checkpointNameKind, checkpointStatsOf, childIdsOf, classifyWaiterToken, clearDurableExecutionWrappers, clearTerminalSingletonAdmission, codeAndDetails, codeOf, collectMetrics, concurrencyTag, controlPlaneDescriptor, countRuns, coveredTokens, createStepLogger, currentStep, currentStepScope, currentWorkflowCtx, debug, describeWorker, descriptorHash, detailsOf, error, etagMatches, eventMatchOf, eventMatches, eventNameOf, eventPrefix, eventToken, facetOrigin, fanPageBounds, fanPageEtag, findStalledRuns, foldedCount, handoffHandlerId, handoffName, hasDurableExecutionWrappers, heartbeatStatus, indexWaitersByRun, info, isEngineMintedTag, isEventToken, isFailedMember, isLegacyDescriptor, isNonEmptyRunScope, isWorkflowControlFlowSignal, legacySingletonHolders, listSignalWaitersOfRuns, liveHandoff, log, matchesAttributes, mergeRunFacetRows, mergeRunValueFacetRows, missingCapabilities, negotiate, nextCronFireMs, normalizeAttributeRows, normalizeDescriptor, observedAnnouncements, onErrorRunId, pageFan, pageFanFromStore, parentRunIdOf, parseDuration, parseWebhookToken, partitionOfToken, planDispatch, poolNamespace, prevCronFireMs, queryCheckpointsOf, readSearchAttributes, requiredCapabilities, resolveConcurrency, resolveRouting, resolveRunWaiting, retiredHandoffName, routeFor, runDetailEtag, runDurableExecution, runInStepLogger, runInStepScope, runInWorkflowCtx, runOutsideWorkflowCtx, runSchedules, runStepHandler, runValueFacetsFromRuns, sanitizeQueueToken, scheduledRunId, singletonAdmissionDenialPatch, singletonAdmissionTags, sqlComparator, stepConfigOf, stepId, stepNameOf, sub, subEvent, subProcess, summarizeFan, summarizeRunFromStore, summarizeTimeline, tenantGroup, useDurableExecution, warn, withSingletonTracked, withoutSingletonMarkers, workflowName };
7191
+ export { type AccumulatorHost, type Admission, type AdmissionBackend, type AdmissionItem, type AllOptions, type AllSettledOptions, type AllStepsOptions, type AllStepsSettledOptions, type AnnouncedWorkflow, type AnnouncementEvidence, type AttributeFilter, type AttributeOp, type AttributePredicateOperand, type AttributeValue, type BackoffStrategy, type BlockedDispatch, type BlockedResolution, CHILD_EDGE_PREFIXES, CONTROL_FLOW_SIGNAL, CURRENT_PROTOCOL_VERSION, type CapabilityRequirement, type CheckpointAggregatingStore, type CheckpointNameKind, type CheckpointQuery, type CheckpointStat, type ChildCallOptions, type ChildWait, CodecStateStore, type CodecStateStoreOptions, type ConcurrencyConfig, ConcurrencyLimitError, type ConcurrencyQuota, ContinueAsNew, type ControlMessage, type ControlPlane, DEFAULT_CONCURRENCY_STATUSES, DEFAULT_PARTITION, DEFAULT_STALLED_AFTER_MS, DURABLE_OPTIONS, DURABLE_OPTIONS_CANONICAL, DURABLE_STEP_CONFIG, DURABLE_STEP_NAME, type Disagreement, type DispatchDiagnostics, type DispatchPlan, type DurableExecution, type DurableExecutionWrapper, type DurableTopology, type DurableWebhook, DurableWorkflow, type DynamicWorkflowCtx, ENGINE_MINTED_TAG_PREFIXES, ENTITY_WORKFLOW, EVENT_BUFFER_SCAN_LIMIT, EVENT_TOKEN_PREFIX, type EngineEvent, type EngineEventType, type EngineListener, Entities, type EntityConfig, type EntityHandler, type EntityHost, type EntityWorkflowHost, EventAccumulators, type EventBatchConfig, FAN_SUMMARY_FAILURES, type FanJoin, type FanPage, type FanPageQuery, type FanSummary, type FanWait, FatalError, type FindStalledRunsOptions, GatherError, type GatherItemFailure, type GroupHealth, HANDOFF_SEQ_BASE, type Heartbeat, type HeartbeatSighting, type HeartbeatStatus, type HistoryEvent, InMemoryAdmissionBackend, InMemoryStateStore, InMemoryTransport, type InferSearchAttributes, LEGACY_V1_CAPABILITIES, LEGACY_V1_PROTOCOL, type ListStalledRunsOptions, type MetricsCollector, type MetricsSnapshot, type NamedTransport, type NegotiateOptions, type NegotiationOutcome, type NegotiationReason, type NegotiationResult, NestedWorkflowCallError, NonDeterminismError, type OnErrorInput, type OnErrorRoute, type OwnedLeaseReclaim, type PartitionSighting, type PayloadCodec, type QueueConfig, QueueController, RUN_HISTORY_FIXTURE_VERSION, RUN_VALUE_FACET_LIMIT, RUN_VALUE_FACET_SCAN, type RawWorkerDescriptor, RemoteStepError, RemoteStepTimeout, type RemoteTask, RemoteWorkflowExecutor, RemoteWorkflowTimeout, type RetentionPolicy, type RoutableDispatch, type RoutableResolution, type RoutingResolution, type RunAttributeRow, type RunDetail, type RunDetailOptions, type RunDispatcher, type RunFacetQuery, type RunFacetRow, RunGateway, type RunListItem, type RunQuery, type RunReply, type RunReplyResult, type RunRequest, type RunRequestKind, type RunResult, type RunSchedulesOptions, type RunScope, type RunStatus, type RunValueAxis, type RunValueFacetOptions, type RunValueFacetOrder, type RunValueFacetRow, type RunWaiting, SINGLETON_ACTIVE_STATUSES, SINGLETON_ADMITTED_TAG, SINGLETON_RESERVED_TAGS, SINGLETON_TRACKED_TAG, STATE_STORE, STATE_STORE_CANONICAL, SUMMARY_CHILD_LIMIT, type ScalarAttributeFilter, ScheduleAlreadyExistsError, ScheduleClient, type ScheduleClientOptions, type ScheduleDescription, type ScheduleHost, type ScheduleListQuery, ScheduleNotFoundError, type ScheduleOptions, type ScheduleQuery, type ScheduleRecord, type ScheduledWorkflow, type SchemaIssue, type SchemaValidationCode, SchemaValidationError, type SearchAttributes, type SearchAttributesSchema, type Settled, SignalTimeoutError, type SignalWaiter, type SingletonConfig, SingletonQueueFullError, type StalePendingStep, type StalledRunInfo, StandardSchemaV1, type StartOptions, type StartRunMessage, type StateStore, type StepCheckpoint, type StepConfig, type StepDef, type StepDispatchOpts, type StepError, type StepEvent, type StepHandler, type StepInterceptor, type StepInvocation, type StepKind, type StepLogger, type StepOptions, type StepRef, type StepResult, type StepScope, type StepUndo, type StoreTransaction, type SubProcessHandle, TERMINAL_RUN_STATUSES, TRANSPORT, TRANSPORT_CANONICAL, type TenantEvent, type Transport, type UndoOf, type UpdateResult, type UpdateValidator, VERSION_UNDECLARED_TAG, WORKFLOW_NAME_KEY, type WireDates, type WorkerAdjust, type WorkerConcurrencyStatus, type WorkerDescriptor, type WorkerHeartbeat, type WorkerLifecycle, type WorkerStatus, type WorkflowAnnouncement, WorkflowBlocked, type WorkflowClass, type WorkflowCommand, type WorkflowCtx, type WorkflowDecision, type WorkflowDirectory, WorkflowEngine, type WorkflowEngineDeps, type WorkflowExecutor, type WorkflowHandler, type WorkflowInputOf, type WorkflowOutputOf, type WorkflowRef, type WorkflowRegistration, type WorkflowRun, type WorkflowStartClient, type WorkflowStepEvent, WorkflowSuspended, type WorkflowTask, aggregateAnnouncements, aggregatesCheckpoints, announcementsOf, applyAttributeQuery, assertConcurrency, assertRoutes, attributeColumnFor, attributeOperand, attributePredicateOperands, axisIsRunColumn, bindWorkflowClass, breakpointToken, buildWorkflowDirectory, canRoute, capableWorkers, checkpointNameKind, checkpointStatsOf, childIdsOf, classifyWaiterToken, clearDurableExecutionWrappers, clearTerminalSingletonAdmission, codeAndDetails, codeOf, collectMetrics, concurrencyTag, controlPlaneDescriptor, countRuns, coveredTokens, createStepLogger, currentStep, currentStepScope, currentWorkflowCtx, debug, describeWorker, descriptorHash, detailsOf, error, etagMatches, eventMatchOf, eventMatches, eventNameOf, eventPrefix, eventToken, facetOrigin, fanPageBounds, fanPageEtag, findStalledRuns, foldedCount, handoffHandlerId, handoffName, hasDurableExecutionWrappers, heartbeatStatus, indexWaitersByRun, info, isEngineMintedTag, isEventToken, isFailedMember, isLegacyDescriptor, isNonEmptyRunScope, isWorkflowControlFlowSignal, legacySingletonHolders, listSignalWaitersOfRuns, liveHandoff, log, matchesAttributes, mergeRunFacetRows, mergeRunValueFacetRows, missingCapabilities, negotiate, nextCronFireMs, normalizeAttributeRows, normalizeDescriptor, observedAnnouncements, onErrorRunId, pageFan, pageFanFromStore, parentRunIdOf, parseDuration, parseWebhookToken, partitionOfToken, planDispatch, poolNamespace, prevCronFireMs, queryCheckpointsOf, readSearchAttributes, requiredCapabilities, resolveConcurrency, resolveRouting, resolveRunWaiting, retiredHandoffName, routeFor, runDetailEtag, runDurableExecution, runInStepLogger, runInStepScope, runInWorkflowCtx, runOutsideWorkflowCtx, runSchedules, runStepHandler, runValueFacetsFromRuns, sanitizeQueueToken, scheduleRunIdAt, scheduledRunId, singletonAdmissionDenialPatch, singletonAdmissionTags, sqlComparator, stepConfigOf, stepId, stepNameOf, sub, subEvent, subProcess, summarizeFan, summarizeRunFromStore, summarizeTimeline, tenantGroup, useDurableExecution, warn, withSingletonTracked, withoutSingletonMarkers, workflowName };
package/dist/index.js CHANGED
@@ -277,6 +277,31 @@ var FatalError = class extends Error {
277
277
  this.details = details;
278
278
  }
279
279
  };
280
+ var SchemaValidationError = class extends FatalError {
281
+ static {
282
+ __name(this, "SchemaValidationError");
283
+ }
284
+ static code = "schema_validation";
285
+ issues;
286
+ /**
287
+ * @param label What was validated, e.g. `workflow "checkout" input`; the message is
288
+ * `invalid <label>: <path>: <message>; …`.
289
+ * @param summary Overrides the `<path>: <message>; …` part, for issues whose messages already
290
+ * name their field (class-validator's `orderId must be a string`).
291
+ */
292
+ constructor(label, issues, code = "schema_validation", summary) {
293
+ const normalized = issues.map((issue) => ({
294
+ message: issue.message,
295
+ path: (issue.path ?? []).map((segment) => String(typeof segment === "object" && segment !== null ? segment.key : segment))
296
+ }));
297
+ const text = summary ?? normalized.map((issue) => issue.path.length > 0 ? `${issue.path.join(".")}: ${issue.message}` : issue.message).join("; ");
298
+ super(`invalid ${label}: ${text}`, code, {
299
+ issues: normalized
300
+ });
301
+ this.name = "SchemaValidationError";
302
+ this.issues = normalized;
303
+ }
304
+ };
280
305
  var ConcurrencyLimitError = class extends Error {
281
306
  static {
282
307
  __name(this, "ConcurrencyLimitError");
@@ -2008,9 +2033,14 @@ function liveHandoff(runId, checkpoints) {
2008
2033
  return live;
2009
2034
  }
2010
2035
  __name(liveHandoff, "liveHandoff");
2036
+ var SCHEMA_VALIDATION_CODES = /* @__PURE__ */ new Set([
2037
+ "invalid_input",
2038
+ "invalid_output"
2039
+ ]);
2011
2040
  function routeFor(routes, error2) {
2012
2041
  if (!routes?.length || error2.code && UNROUTABLE_CODES.has(error2.code)) return void 0;
2013
- return (error2.code ? routes.find((r) => r.code === error2.code) : void 0) ?? routes.find((r) => r.code === void 0);
2042
+ const { code } = error2;
2043
+ return (code ? routes.find((r) => r.code === code) : void 0) ?? (code && SCHEMA_VALIDATION_CODES.has(code) ? routes.find((r) => r.code === "schema_validation") : void 0) ?? routes.find((r) => r.code === void 0);
2014
2044
  }
2015
2045
  __name(routeFor, "routeFor");
2016
2046
  function assertRoutes(workflow, routes) {
@@ -2282,9 +2312,13 @@ __name(scheduleRunIdsAt, "scheduleRunIdsAt");
2282
2312
  async function runSchedules(engine, schedules, nowMs, opts) {
2283
2313
  const random = opts?.random ?? Math.random;
2284
2314
  const sleep = opts?.sleep ?? defaultSleep;
2315
+ const settled = settledIdsFor(engine);
2285
2316
  const ids = [];
2286
2317
  for (const s of schedules) {
2318
+ if (engine.isDraining?.()) break;
2287
2319
  if (s.paused) continue;
2320
+ const candidates = scheduleRunIdsAt(s, nowMs).filter((id) => !settled.get(s.key)?.has(id));
2321
+ if (candidates.length === 0) continue;
2288
2322
  if (s.overlap === "skip" && s.everyMs) {
2289
2323
  const prevBucket = Math.floor(nowMs / s.everyMs) - 1;
2290
2324
  if (prevBucket >= 0) {
@@ -2293,14 +2327,44 @@ async function runSchedules(engine, schedules, nowMs, opts) {
2293
2327
  }
2294
2328
  }
2295
2329
  if (s.jitter && s.jitter > 0) await sleep(Math.floor(random() * s.jitter));
2296
- for (const runId of scheduleRunIdsAt(s, nowMs)) {
2297
- await engine.start(s.workflow, s.input, runId);
2330
+ for (const runId of candidates) {
2331
+ if (engine.isDraining?.()) break;
2332
+ const existing = await engine.getRun(runId);
2333
+ remember(settled, s.key, runId);
2334
+ if (existing) continue;
2335
+ await engine.start(workflowName(s.workflow), s.input, runId, {
2336
+ namespace: s.namespace
2337
+ });
2298
2338
  ids.push(runId);
2299
2339
  }
2300
2340
  }
2301
2341
  return ids;
2302
2342
  }
2303
2343
  __name(runSchedules, "runSchedules");
2344
+ var settledRunIds = /* @__PURE__ */ new WeakMap();
2345
+ function settledIdsFor(engine) {
2346
+ let memo = settledRunIds.get(engine);
2347
+ if (memo === void 0) {
2348
+ memo = /* @__PURE__ */ new Map();
2349
+ settledRunIds.set(engine, memo);
2350
+ }
2351
+ return memo;
2352
+ }
2353
+ __name(settledIdsFor, "settledIdsFor");
2354
+ function remember(memo, key, runId) {
2355
+ const seen = memo.get(key);
2356
+ if (seen === void 0) {
2357
+ memo.set(key, /* @__PURE__ */ new Set([
2358
+ runId
2359
+ ]));
2360
+ return;
2361
+ }
2362
+ seen.add(runId);
2363
+ if (seen.size > 64) memo.set(key, /* @__PURE__ */ new Set([
2364
+ runId
2365
+ ]));
2366
+ }
2367
+ __name(remember, "remember");
2304
2368
 
2305
2369
  // src/schedules.ts
2306
2370
  var ScheduleAlreadyExistsError = class extends Error {
@@ -3099,11 +3163,6 @@ function normalizeChildOptions(options) {
3099
3163
  } : options ?? {};
3100
3164
  }
3101
3165
  __name(normalizeChildOptions, "normalizeChildOptions");
3102
- function formatIssuePath(path) {
3103
- if (!path || path.length === 0) return "(root)";
3104
- return path.map((segment) => String(typeof segment === "object" ? segment.key : segment)).join(".");
3105
- }
3106
- __name(formatIssuePath, "formatIssuePath");
3107
3166
  function resolveStepPolicy(config, opts) {
3108
3167
  const policy = {};
3109
3168
  const retries = opts?.retries ?? config?.retries;
@@ -3935,11 +3994,7 @@ function createWorkflowCtx(host, runId, compensations, workflow = "", searchAttr
3935
3994
  throw new Error(`workflow "${workflow}": ctx.upsertSearchAttributes schema validation must be synchronous (the searchAttributes schema's \`~standard.validate\` returned a Promise) \u2014 replay-determinism requires a sync validator; drop any async refinement from the schema passed to @Workflow({ searchAttributes }).`);
3936
3995
  }
3937
3996
  if (result.issues) {
3938
- const offendingKeys = [
3939
- ...new Set(result.issues.map((issue) => formatIssuePath(issue.path)))
3940
- ];
3941
- const details = result.issues.map((issue) => `${formatIssuePath(issue.path)}: ${issue.message}`).join("; ");
3942
- throw new Error(`workflow "${workflow}": ctx.upsertSearchAttributes rejected by its searchAttributes schema \u2014 offending key(s) ${offendingKeys.join(", ")} \u2014 ${details}`);
3997
+ throw new SchemaValidationError(`workflow "${workflow}" ctx.upsertSearchAttributes`, result.issues);
3943
3998
  }
3944
3999
  }
3945
4000
  await host.upsertSearchAttributes(runId, attrs);
@@ -5145,6 +5200,10 @@ var WorkflowEngine = class {
5145
5200
  for (const rearm of this.drainRearms) rearm();
5146
5201
  }
5147
5202
  }
5203
+ /** True once {@link beginDrain} (or {@link drain}) has been called. */
5204
+ isDraining() {
5205
+ return this.draining;
5206
+ }
5148
5207
  /**
5149
5208
  * Graceful shutdown: stop picking up new runs (recovery/timer become no-ops) and wait for
5150
5209
  * in-flight executions to settle, up to `timeoutMs`. Call from your app's shutdown hook so a
@@ -10735,6 +10794,7 @@ export {
10735
10794
  ScheduleAlreadyExistsError,
10736
10795
  ScheduleClient,
10737
10796
  ScheduleNotFoundError,
10797
+ SchemaValidationError,
10738
10798
  SignalTimeoutError,
10739
10799
  SingletonQueueFullError,
10740
10800
  TERMINAL_RUN_STATUSES,
@@ -10846,6 +10906,7 @@ export {
10846
10906
  runStepHandler,
10847
10907
  runValueFacetsFromRuns,
10848
10908
  sanitizeQueueToken,
10909
+ scheduleRunIdAt,
10849
10910
  scheduledRunId,
10850
10911
  singletonAdmissionDenialPatch,
10851
10912
  singletonAdmissionTags,