@hatchet-dev/typescript-sdk 1.12.0 → 1.13.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.
Files changed (92) hide show
  1. package/clients/dispatcher/dispatcher-client.d.ts +14 -1
  2. package/clients/dispatcher/dispatcher-client.js +25 -2
  3. package/clients/hatchet-client/client-config.d.ts +59 -0
  4. package/clients/hatchet-client/client-config.js +7 -0
  5. package/clients/rest/generated/Api.d.ts +9 -1
  6. package/clients/rest/generated/data-contracts.d.ts +43 -7
  7. package/clients/rest/generated/data-contracts.js +13 -2
  8. package/clients/worker/worker.js +25 -1
  9. package/package.json +1 -1
  10. package/protoc/dispatcher/dispatcher.d.ts +46 -1
  11. package/protoc/dispatcher/dispatcher.js +214 -2
  12. package/protoc/v1/workflows.d.ts +11 -0
  13. package/protoc/v1/workflows.js +122 -2
  14. package/step.d.ts +24 -18
  15. package/step.js +9 -1
  16. package/v1/client/client.d.ts +30 -14
  17. package/v1/client/client.js +33 -5
  18. package/v1/client/worker/context.d.ts +6 -0
  19. package/v1/client/worker/context.js +8 -0
  20. package/v1/client/worker/deprecated/deprecation.d.ts +44 -0
  21. package/v1/client/worker/deprecated/deprecation.js +95 -0
  22. package/v1/client/worker/deprecated/index.d.ts +4 -0
  23. package/v1/client/worker/deprecated/index.js +15 -0
  24. package/v1/client/worker/deprecated/legacy-registration.d.ts +18 -0
  25. package/v1/client/worker/deprecated/legacy-registration.js +35 -0
  26. package/v1/client/worker/deprecated/legacy-v1-worker.d.ts +15 -0
  27. package/v1/client/worker/deprecated/legacy-v1-worker.js +39 -0
  28. package/v1/client/worker/deprecated/legacy-worker.d.ts +41 -0
  29. package/v1/client/worker/deprecated/legacy-worker.js +148 -0
  30. package/v1/client/worker/slot-utils.d.ts +21 -0
  31. package/v1/client/worker/slot-utils.js +73 -0
  32. package/v1/client/worker/worker-internal.d.ts +14 -3
  33. package/v1/client/worker/worker-internal.js +53 -13
  34. package/v1/client/worker/worker.d.ts +12 -15
  35. package/v1/client/worker/worker.js +45 -49
  36. package/v1/declaration.d.ts +63 -15
  37. package/v1/declaration.js +66 -2
  38. package/v1/examples/cancellations/workflow.d.ts +2 -2
  39. package/v1/examples/child_workflows/workflow.d.ts +4 -4
  40. package/v1/examples/concurrency-rr/workflow.d.ts +2 -2
  41. package/v1/examples/dag/interface-workflow.d.ts +1 -1
  42. package/v1/examples/dag/workflow.d.ts +1 -1
  43. package/v1/examples/dag_match_condition/complex-workflow.d.ts +1 -1
  44. package/v1/examples/dag_match_condition/workflow.d.ts +1 -1
  45. package/v1/examples/deep/workflow.d.ts +6 -6
  46. package/v1/examples/durable-event/workflow.d.ts +2 -2
  47. package/v1/examples/durable-sleep/workflow.d.ts +1 -1
  48. package/v1/examples/hatchet-client.d.ts +1 -1
  49. package/v1/examples/high-memory/workflow-with-child.d.ts +2 -2
  50. package/v1/examples/inferred-typing/workflow.d.ts +4 -4
  51. package/v1/examples/landing_page/event-signaling.d.ts +1 -1
  52. package/v1/examples/landing_page/flow-control.d.ts +1 -1
  53. package/v1/examples/landing_page/task-routing.d.ts +1 -1
  54. package/v1/examples/middleware/client.d.ts +26 -0
  55. package/v1/examples/middleware/client.js +39 -0
  56. package/v1/examples/middleware/recipes.d.ts +1 -0
  57. package/v1/examples/middleware/recipes.js +95 -0
  58. package/v1/examples/middleware/run.d.ts +1 -0
  59. package/v1/examples/middleware/run.js +29 -0
  60. package/v1/examples/middleware/worker.d.ts +1 -0
  61. package/v1/examples/middleware/worker.js +24 -0
  62. package/v1/examples/middleware/workflow.d.ts +15 -0
  63. package/v1/examples/middleware/workflow.js +18 -0
  64. package/v1/examples/migration-guides/hatchet-client.d.ts +1 -1
  65. package/v1/examples/migration-guides/mergent.d.ts +1 -1
  66. package/v1/examples/multiple_wf_concurrency/workflow.d.ts +1 -1
  67. package/v1/examples/non_retryable/workflow.d.ts +1 -1
  68. package/v1/examples/on_cron/workflow.d.ts +1 -1
  69. package/v1/examples/on_event/workflow.d.ts +3 -3
  70. package/v1/examples/on_failure/workflow.d.ts +1 -1
  71. package/v1/examples/on_success/workflow.d.ts +1 -1
  72. package/v1/examples/priority/workflow.d.ts +4 -4
  73. package/v1/examples/quickstart/hatchet-client.d.ts +1 -1
  74. package/v1/examples/quickstart/workflows/first-task.d.ts +1 -1
  75. package/v1/examples/retries/workflow.d.ts +3 -3
  76. package/v1/examples/simple/stub-workflow.d.ts +1 -1
  77. package/v1/examples/simple/workflow-with-child.d.ts +2 -2
  78. package/v1/examples/simple/workflow.d.ts +1 -1
  79. package/v1/examples/simple/zod.d.ts +1 -1
  80. package/v1/examples/sticky/workflow.d.ts +1 -1
  81. package/v1/examples/streaming/workflow.d.ts +1 -1
  82. package/v1/examples/timeouts/workflow.d.ts +1 -1
  83. package/v1/examples/with_timeouts/workflow.d.ts +2 -2
  84. package/v1/index.d.ts +1 -0
  85. package/v1/index.js +1 -0
  86. package/v1/slot-types.d.ts +5 -0
  87. package/v1/slot-types.js +9 -0
  88. package/v1/task.d.ts +2 -0
  89. package/v1/types.d.ts +10 -1
  90. package/version.d.ts +1 -1
  91. package/version.js +1 -1
  92. package/workflow.d.ts +58 -58
@@ -0,0 +1,148 @@
1
+ "use strict";
2
+ /**
3
+ * Legacy dual-worker implementation for pre-slot-config engines.
4
+ *
5
+ * When connected to an older Hatchet engine that does not support multiple slot types,
6
+ * this module provides the old worker start flow which creates separate durable and
7
+ * non-durable workers, each registered with the legacy `slots` proto field.
8
+ */
9
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
10
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
11
+ return new (P || (P = Promise))(function (resolve, reject) {
12
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
13
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
14
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
15
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
16
+ });
17
+ };
18
+ Object.defineProperty(exports, "__esModule", { value: true });
19
+ exports.LegacyDualWorker = void 0;
20
+ exports.isLegacyEngine = isLegacyEngine;
21
+ const nice_grpc_1 = require("nice-grpc");
22
+ const declaration_1 = require("../../../declaration");
23
+ const legacy_v1_worker_1 = require("./legacy-v1-worker");
24
+ const deprecation_1 = require("./deprecation");
25
+ const DEFAULT_DEFAULT_SLOTS = 100;
26
+ const DEFAULT_DURABLE_SLOTS = 1000;
27
+ /** The date when slot_config support was released. */
28
+ const LEGACY_ENGINE_START = new Date('2026-02-12T00:00:00Z');
29
+ /** Minimum engine version that supports multiple slot types. */
30
+ const MIN_SLOT_CONFIG_VERSION = 'v0.78.23';
31
+ const LEGACY_ENGINE_MESSAGE = 'Connected to an older Hatchet engine that does not support multiple slot types. ' +
32
+ 'Falling back to legacy worker registration. ' +
33
+ 'Please upgrade your Hatchet engine to the latest version.';
34
+ /**
35
+ * Checks if the connected engine is legacy by comparing its semantic version
36
+ * against the minimum required version for slot_config support.
37
+ * Returns true if the engine is legacy, false otherwise.
38
+ * Emits a time-aware deprecation notice when a legacy engine is detected.
39
+ */
40
+ function isLegacyEngine(v1) {
41
+ return __awaiter(this, void 0, void 0, function* () {
42
+ try {
43
+ const version = yield v1._v0.dispatcher.getVersion();
44
+ // If the version is empty or older than the minimum, treat as legacy
45
+ if (!version || (0, deprecation_1.semverLessThan)(version, MIN_SLOT_CONFIG_VERSION)) {
46
+ const logger = v1._v0.config.logger('Worker', v1._v0.config.log_level);
47
+ (0, deprecation_1.emitDeprecationNotice)('legacy-engine', LEGACY_ENGINE_MESSAGE, LEGACY_ENGINE_START, logger, {
48
+ errorDays: 180,
49
+ });
50
+ return true;
51
+ }
52
+ return false;
53
+ }
54
+ catch (e) {
55
+ if ((e === null || e === void 0 ? void 0 : e.code) === nice_grpc_1.Status.UNIMPLEMENTED) {
56
+ const logger = v1._v0.config.logger('Worker', v1._v0.config.log_level);
57
+ (0, deprecation_1.emitDeprecationNotice)('legacy-engine', LEGACY_ENGINE_MESSAGE, LEGACY_ENGINE_START, logger, {
58
+ errorDays: 180,
59
+ });
60
+ return true;
61
+ }
62
+ // For other errors, assume new engine and let registration fail naturally
63
+ return false;
64
+ }
65
+ });
66
+ }
67
+ /**
68
+ * LegacyDualWorker manages two V1Worker instances (nonDurable + durable)
69
+ * for engines that don't support slot_config.
70
+ * Uses the legacy `slots` proto field (maxRuns) instead of `slotConfig`.
71
+ */
72
+ class LegacyDualWorker {
73
+ constructor(name, nonDurable, durable) {
74
+ this.name = name;
75
+ this.nonDurable = nonDurable;
76
+ this.durable = durable;
77
+ }
78
+ /**
79
+ * Creates a legacy dual-worker setup from the given options.
80
+ * Workers are created with legacy registration (old `slots` proto field).
81
+ */
82
+ static create(v1, name, options) {
83
+ return __awaiter(this, void 0, void 0, function* () {
84
+ const defaultSlots = options.slots || options.maxRuns || DEFAULT_DEFAULT_SLOTS;
85
+ const durableSlots = options.durableSlots || DEFAULT_DURABLE_SLOTS;
86
+ // Create the non-durable worker with legacy registration
87
+ const nonDurable = new legacy_v1_worker_1.LegacyV1Worker(v1, { name, labels: options.labels, handleKill: options.handleKill }, defaultSlots);
88
+ // Check if any workflows have durable tasks
89
+ let hasDurableTasks = false;
90
+ for (const wf of options.workflows || []) {
91
+ if (wf instanceof declaration_1.BaseWorkflowDeclaration) {
92
+ if (wf.definition._durableTasks.length > 0) {
93
+ hasDurableTasks = true;
94
+ break;
95
+ }
96
+ }
97
+ }
98
+ let durableWorker;
99
+ if (hasDurableTasks) {
100
+ // Create the durable worker with legacy registration
101
+ durableWorker = new legacy_v1_worker_1.LegacyV1Worker(v1, { name: `${name}-durable`, labels: options.labels, handleKill: options.handleKill }, durableSlots);
102
+ }
103
+ const legacyWorker = new LegacyDualWorker(name, nonDurable, durableWorker);
104
+ // Register workflows on appropriate workers
105
+ for (const wf of options.workflows || []) {
106
+ if (wf instanceof declaration_1.BaseWorkflowDeclaration) {
107
+ if (wf.definition._durableTasks.length > 0 && durableWorker) {
108
+ yield durableWorker.registerWorkflowV1(wf);
109
+ durableWorker.registerDurableActionsV1(wf.definition);
110
+ }
111
+ else {
112
+ yield nonDurable.registerWorkflowV1(wf);
113
+ }
114
+ }
115
+ else {
116
+ // fallback to v0 client for backwards compatibility
117
+ yield nonDurable.registerWorkflow(wf);
118
+ }
119
+ }
120
+ return legacyWorker;
121
+ });
122
+ }
123
+ /**
124
+ * Starts both workers using Promise.all.
125
+ */
126
+ start() {
127
+ return __awaiter(this, void 0, void 0, function* () {
128
+ const promises = [this.nonDurable.start()];
129
+ if (this.durable) {
130
+ promises.push(this.durable.start());
131
+ }
132
+ yield Promise.all(promises);
133
+ });
134
+ }
135
+ /**
136
+ * Stops both workers.
137
+ */
138
+ stop() {
139
+ return __awaiter(this, void 0, void 0, function* () {
140
+ const promises = [this.nonDurable.stop()];
141
+ if (this.durable) {
142
+ promises.push(this.durable.stop());
143
+ }
144
+ yield Promise.all(promises);
145
+ });
146
+ }
147
+ }
148
+ exports.LegacyDualWorker = LegacyDualWorker;
@@ -0,0 +1,21 @@
1
+ import { Workflow as V0Workflow } from '../../../workflow';
2
+ import { BaseWorkflowDeclaration } from '../../declaration';
3
+ import { SlotConfig } from '../../slot-types';
4
+ export interface WorkerSlotOptions {
5
+ /** (optional) Maximum number of concurrent runs on this worker, defaults to 100 */
6
+ slots?: number;
7
+ /** (optional) Maximum number of concurrent durable tasks, defaults to 1,000 */
8
+ durableSlots?: number;
9
+ /** (optional) Array of workflows to register */
10
+ workflows?: BaseWorkflowDeclaration<any, any>[] | V0Workflow[];
11
+ /** @deprecated Use slots instead */
12
+ maxRuns?: number;
13
+ }
14
+ export declare function resolveWorkerOptions<T extends WorkerSlotOptions>(options: T): T & {
15
+ slots?: number;
16
+ durableSlots?: number;
17
+ slotConfig: SlotConfig;
18
+ };
19
+ export declare const testingExports: {
20
+ resolveWorkerOptions: typeof resolveWorkerOptions;
21
+ };
@@ -0,0 +1,73 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.testingExports = void 0;
4
+ exports.resolveWorkerOptions = resolveWorkerOptions;
5
+ const declaration_1 = require("../../declaration");
6
+ const slot_types_1 = require("../../slot-types");
7
+ const DEFAULT_DEFAULT_SLOTS = 100;
8
+ const DEFAULT_DURABLE_SLOTS = 1000;
9
+ function resolveWorkerOptions(options) {
10
+ const requiredSlotTypes = options.workflows
11
+ ? getRequiredSlotTypes(options.workflows)
12
+ : new Set();
13
+ const slotConfig = options.slots || options.durableSlots || options.maxRuns
14
+ ? Object.assign(Object.assign({}, (options.slots || options.maxRuns
15
+ ? { [slot_types_1.SlotType.Default]: options.slots || options.maxRuns || 0 }
16
+ : {})), (options.durableSlots ? { [slot_types_1.SlotType.Durable]: options.durableSlots } : {})) : {};
17
+ if (requiredSlotTypes.has(slot_types_1.SlotType.Default) && slotConfig[slot_types_1.SlotType.Default] == null) {
18
+ slotConfig[slot_types_1.SlotType.Default] = DEFAULT_DEFAULT_SLOTS;
19
+ }
20
+ if (requiredSlotTypes.has(slot_types_1.SlotType.Durable) && slotConfig[slot_types_1.SlotType.Durable] == null) {
21
+ slotConfig[slot_types_1.SlotType.Durable] = DEFAULT_DURABLE_SLOTS;
22
+ }
23
+ if (Object.keys(slotConfig).length === 0) {
24
+ slotConfig[slot_types_1.SlotType.Default] = DEFAULT_DEFAULT_SLOTS;
25
+ }
26
+ return Object.assign(Object.assign({}, options), { slots: options.slots ||
27
+ options.maxRuns ||
28
+ (slotConfig[slot_types_1.SlotType.Default] != null ? slotConfig[slot_types_1.SlotType.Default] : undefined), durableSlots: options.durableSlots ||
29
+ (slotConfig[slot_types_1.SlotType.Durable] != null ? slotConfig[slot_types_1.SlotType.Durable] : undefined), slotConfig });
30
+ }
31
+ // eslint-disable-next-line @typescript-eslint/naming-convention
32
+ exports.testingExports = {
33
+ resolveWorkerOptions,
34
+ };
35
+ function getRequiredSlotTypes(workflows) {
36
+ const required = new Set();
37
+ const addFromRequests = (requests, fallbackType) => {
38
+ if (requests && Object.keys(requests).length > 0) {
39
+ if (requests[slot_types_1.SlotType.Default] !== undefined) {
40
+ required.add(slot_types_1.SlotType.Default);
41
+ }
42
+ if (requests[slot_types_1.SlotType.Durable] !== undefined) {
43
+ required.add(slot_types_1.SlotType.Durable);
44
+ }
45
+ }
46
+ else {
47
+ required.add(fallbackType);
48
+ }
49
+ };
50
+ for (const wf of workflows) {
51
+ if (wf instanceof declaration_1.BaseWorkflowDeclaration) {
52
+ // eslint-disable-next-line dot-notation
53
+ const tasks = wf.definition['_tasks'];
54
+ for (const task of tasks) {
55
+ addFromRequests(task.slotRequests, slot_types_1.SlotType.Default);
56
+ }
57
+ // eslint-disable-next-line dot-notation
58
+ const durableTasks = wf.definition['_durableTasks'];
59
+ if (durableTasks.length > 0) {
60
+ required.add(slot_types_1.SlotType.Durable);
61
+ }
62
+ if (wf.definition.onFailure) {
63
+ const opts = typeof wf.definition.onFailure === 'object' ? wf.definition.onFailure : undefined;
64
+ addFromRequests(opts === null || opts === void 0 ? void 0 : opts.slotRequests, slot_types_1.SlotType.Default);
65
+ }
66
+ if (wf.definition.onSuccess) {
67
+ const opts = typeof wf.definition.onSuccess === 'object' ? wf.definition.onSuccess : undefined;
68
+ addFromRequests(opts === null || opts === void 0 ? void 0 : opts.slotRequests, slot_types_1.SlotType.Default);
69
+ }
70
+ }
71
+ }
72
+ return required;
73
+ }
@@ -8,11 +8,13 @@ import { BaseWorkflowDeclaration, WorkflowDefinition, HatchetClient } from '../.
8
8
  import { WorkerLabels } from '../../../clients/dispatcher/dispatcher-client';
9
9
  import { StepRunFunction } from '../../../step';
10
10
  import { Context } from './context';
11
+ import { SlotConfig } from '../../slot-types';
11
12
  export type ActionRegistry = Record<Action['actionId'], Function>;
12
13
  export interface WorkerOpts {
13
14
  name: string;
14
15
  handleKill?: boolean;
15
- maxRuns?: number;
16
+ slots?: number;
17
+ durableSlots?: number;
16
18
  labels?: WorkerLabels;
17
19
  healthPort?: number;
18
20
  enableHealthServer?: boolean;
@@ -28,7 +30,9 @@ export declare class V1Worker {
28
30
  listener: ActionListener | undefined;
29
31
  futures: Record<Action['taskRunExternalId'], HatchetPromise<any>>;
30
32
  contexts: Record<Action['taskRunExternalId'], Context<any, any>>;
31
- maxRuns?: number;
33
+ slots?: number;
34
+ durableSlots?: number;
35
+ slotConfig: SlotConfig;
32
36
  logger: Logger;
33
37
  registeredWorkflowPromises: Array<Promise<any>>;
34
38
  labels: WorkerLabels;
@@ -39,7 +43,9 @@ export declare class V1Worker {
39
43
  constructor(client: HatchetClient, options: {
40
44
  name: string;
41
45
  handleKill?: boolean;
42
- maxRuns?: number;
46
+ slots?: number;
47
+ durableSlots?: number;
48
+ slotConfig?: SlotConfig;
43
49
  labels?: WorkerLabels;
44
50
  });
45
51
  private initializeHealthServer;
@@ -66,6 +72,11 @@ export declare class V1Worker {
66
72
  handleCancelStepRun(action: Action): Promise<void>;
67
73
  stop(): Promise<void>;
68
74
  exitGracefully(handleKill: boolean): Promise<void>;
75
+ /**
76
+ * Creates an action listener by registering the worker with the dispatcher.
77
+ * Override in subclasses to change registration behavior (e.g. legacy engines).
78
+ */
79
+ protected createListener(): Promise<ActionListener>;
69
80
  start(): Promise<void>;
70
81
  handleAction(action: Action): Promise<void>;
71
82
  upsertLabels(labels: WorkerLabels): Promise<WorkerLabels>;
@@ -46,7 +46,9 @@ class V1Worker {
46
46
  this.client = client;
47
47
  this.name = (0, apply_namespace_1.applyNamespace)(options.name, this.client.config.namespace);
48
48
  this.action_registry = {};
49
- this.maxRuns = options.maxRuns;
49
+ this.slots = options.slots;
50
+ this.durableSlots = options.durableSlots;
51
+ this.slotConfig = options.slotConfig || {};
50
52
  this.labels = options.labels || {};
51
53
  this.enableHealthServer = (_b = (_a = client.config.healthcheck) === null || _a === void 0 ? void 0 : _a.enabled) !== null && _b !== void 0 ? _b : false;
52
54
  this.healthPort = (_d = (_c = client.config.healthcheck) === null || _c === void 0 ? void 0 : _c.port) !== null && _d !== void 0 ? _d : 8001;
@@ -67,11 +69,10 @@ class V1Worker {
67
69
  this.healthServer = new health_server_1.HealthServer(this.healthPort, () => this.status, this.name, () => this.getAvailableSlots(), () => this.getRegisteredActions(), () => this.getFilteredLabels(), this.logger);
68
70
  }
69
71
  getAvailableSlots() {
70
- if (!this.maxRuns) {
71
- return 0;
72
- }
72
+ // sum all the slots in the slot config
73
+ const totalSlots = Object.values(this.slotConfig).reduce((acc, curr) => acc + curr, 0);
73
74
  const currentRuns = Object.keys(this.futures).length;
74
- return Math.max(0, this.maxRuns - currentRuns);
75
+ return Math.max(0, totalSlots - currentRuns);
75
76
  }
76
77
  getRegisteredActions() {
77
78
  return Object.keys(this.action_registry);
@@ -177,6 +178,8 @@ class V1Worker {
177
178
  rateLimits: [],
178
179
  workerLabels: {},
179
180
  concurrency: [],
181
+ isDurable: false,
182
+ slotRequests: { default: 1 },
180
183
  };
181
184
  }
182
185
  if (workflow.onFailure && typeof workflow.onFailure === 'object') {
@@ -194,6 +197,8 @@ class V1Worker {
194
197
  concurrency: [],
195
198
  backoffFactor: ((_f = onFailure.backoff) === null || _f === void 0 ? void 0 : _f.factor) || ((_h = (_g = workflow.taskDefaults) === null || _g === void 0 ? void 0 : _g.backoff) === null || _h === void 0 ? void 0 : _h.factor),
196
199
  backoffMaxSeconds: ((_j = onFailure.backoff) === null || _j === void 0 ? void 0 : _j.maxSeconds) || ((_l = (_k = workflow.taskDefaults) === null || _k === void 0 ? void 0 : _k.backoff) === null || _l === void 0 ? void 0 : _l.maxSeconds),
200
+ isDurable: false,
201
+ slotRequests: { default: 1 },
197
202
  };
198
203
  }
199
204
  let onSuccessTask;
@@ -252,6 +257,7 @@ class V1Worker {
252
257
  const jsonSchema = (0, zod_to_json_schema_1.zodToJsonSchema)(workflow.inputValidator);
253
258
  inputJsonSchema = new TextEncoder().encode(JSON.stringify(jsonSchema));
254
259
  }
260
+ const durableTaskSet = new Set(workflow._durableTasks);
255
261
  const registeredWorkflow = this.client._v0.admin.putWorkflowV1({
256
262
  name: workflow.name,
257
263
  description: workflow.description || '',
@@ -282,6 +288,8 @@ class V1Worker {
282
288
  backoffFactor: ((_h = task.backoff) === null || _h === void 0 ? void 0 : _h.factor) || ((_k = (_j = workflow.taskDefaults) === null || _j === void 0 ? void 0 : _j.backoff) === null || _k === void 0 ? void 0 : _k.factor),
283
289
  backoffMaxSeconds: ((_l = task.backoff) === null || _l === void 0 ? void 0 : _l.maxSeconds) || ((_o = (_m = workflow.taskDefaults) === null || _m === void 0 ? void 0 : _m.backoff) === null || _o === void 0 ? void 0 : _o.maxSeconds),
284
290
  conditions: (0, transformer_1.taskConditionsToPb)(task),
291
+ isDurable: durableTaskSet.has(task),
292
+ slotRequests: task.slotRequests || (durableTaskSet.has(task) ? { durable: 1 } : { default: 1 }),
285
293
  concurrency: task.concurrency
286
294
  ? Array.isArray(task.concurrency)
287
295
  ? task.concurrency
@@ -416,7 +424,30 @@ class V1Worker {
416
424
  childIndex: 0,
417
425
  desiredWorkerId: this.workerId || '',
418
426
  });
419
- return step(context);
427
+ const { middleware } = this.client.config;
428
+ if (middleware === null || middleware === void 0 ? void 0 : middleware.before) {
429
+ const hooks = Array.isArray(middleware.before) ? middleware.before : [middleware.before];
430
+ for (const hook of hooks) {
431
+ const returned = yield hook(context.input, context);
432
+ if (returned !== undefined) {
433
+ context.input = returned;
434
+ if (context.data && typeof context.data === 'object') {
435
+ context.data.input = returned;
436
+ }
437
+ }
438
+ }
439
+ }
440
+ let result = yield step(context);
441
+ if (middleware === null || middleware === void 0 ? void 0 : middleware.after) {
442
+ const hooks = Array.isArray(middleware.after) ? middleware.after : [middleware.after];
443
+ for (const hook of hooks) {
444
+ const returned = yield hook(result, context, context.input);
445
+ if (returned !== undefined) {
446
+ result = returned;
447
+ }
448
+ }
449
+ }
450
+ return result;
420
451
  });
421
452
  const success = (result) => __awaiter(this, void 0, void 0, function* () {
422
453
  try {
@@ -671,6 +702,21 @@ class V1Worker {
671
702
  }
672
703
  });
673
704
  }
705
+ /**
706
+ * Creates an action listener by registering the worker with the dispatcher.
707
+ * Override in subclasses to change registration behavior (e.g. legacy engines).
708
+ */
709
+ createListener() {
710
+ return __awaiter(this, void 0, void 0, function* () {
711
+ return this.client._v0.dispatcher.getActionListener({
712
+ workerName: this.name,
713
+ services: ['default'],
714
+ actions: Object.keys(this.action_registry),
715
+ slotConfig: this.slotConfig,
716
+ labels: this.labels,
717
+ });
718
+ });
719
+ }
674
720
  start() {
675
721
  return __awaiter(this, void 0, void 0, function* () {
676
722
  var _a, e_1, _b, _c;
@@ -691,13 +737,7 @@ class V1Worker {
691
737
  return;
692
738
  }
693
739
  try {
694
- this.listener = yield this.client._v0.dispatcher.getActionListener({
695
- workerName: this.name,
696
- services: ['default'],
697
- actions: Object.keys(this.action_registry),
698
- maxRuns: this.maxRuns,
699
- labels: this.labels,
700
- });
740
+ this.listener = yield this.createListener();
701
741
  this.workerId = this.listener.workerId;
702
742
  this.setStatus(health_server_1.workerStatus.HEALTHY);
703
743
  const generator = this.listener.actions();
@@ -5,23 +5,16 @@ import { WebhookWorkerCreateRequest } from '../../../clients/rest/generated/data
5
5
  import { BaseWorkflowDeclaration } from '../../declaration';
6
6
  import { HatchetClient } from '../..';
7
7
  import { V1Worker } from './worker-internal';
8
+ import { type WorkerSlotOptions } from './slot-utils';
8
9
  /**
9
10
  * Options for creating a new hatchet worker
10
11
  * @interface CreateWorkerOpts
11
12
  */
12
- export interface CreateWorkerOpts {
13
- /** (optional) Maximum number of concurrent runs on this worker, defaults to 100 */
14
- slots?: number;
15
- /** (optional) Array of workflows to register */
16
- workflows?: BaseWorkflowDeclaration<any, any>[] | V0Workflow[];
13
+ export interface CreateWorkerOpts extends WorkerSlotOptions {
17
14
  /** (optional) Worker labels for affinity-based assignment */
18
15
  labels?: WorkerLabels;
19
16
  /** (optional) Whether to handle kill signals */
20
17
  handleKill?: boolean;
21
- /** @deprecated Use slots instead */
22
- maxRuns?: number;
23
- /** (optional) Maximum number of concurrent runs on the durable worker, defaults to 1,000 */
24
- durableSlots?: number;
25
18
  }
26
19
  /**
27
20
  * HatchetWorker class for workflow execution runtime
@@ -32,8 +25,11 @@ export declare class Worker {
32
25
  _v1: HatchetClient;
33
26
  _v0: LegacyHatchetClient;
34
27
  /** Internal reference to the underlying V0 worker implementation */
35
- nonDurable: V1Worker;
36
- durable?: V1Worker;
28
+ _internal: V1Worker;
29
+ /** Set when connected to a legacy engine that needs dual-worker architecture */
30
+ private _legacyWorker;
31
+ /** Tracks all workflows registered after construction (via registerWorkflow/registerWorkflows) */
32
+ private _registeredWorkflows;
37
33
  /**
38
34
  * Creates a new HatchetWorker instance
39
35
  * @param nonDurable - The V0 worker implementation
@@ -63,12 +59,12 @@ export declare class Worker {
63
59
  * Starts the worker
64
60
  * @returns Promise that resolves when the worker is stopped or killed
65
61
  */
66
- start(): Promise<void[]>;
62
+ start(): Promise<void>;
67
63
  /**
68
64
  * Stops the worker
69
65
  * @returns Promise that resolves when the worker stops
70
66
  */
71
- stop(): Promise<void[]>;
67
+ stop(): Promise<void>;
72
68
  /**
73
69
  * Updates or inserts worker labels
74
70
  * @param labels - Worker labels to update
@@ -87,6 +83,7 @@ export declare class Worker {
87
83
  */
88
84
  registerWebhook(webhook: WebhookWorkerCreateRequest): Promise<import("axios").AxiosResponse<import("../../../clients/rest/generated/data-contracts").WebhookWorkerCreated, any, {}>>;
89
85
  isPaused(): Promise<boolean>;
90
- pause(): Promise<any[]>;
91
- unpause(): Promise<any[]>;
86
+ pause(): Promise<void> | Promise<import("../../../clients/rest/generated/data-contracts").Worker>;
87
+ unpause(): Promise<void> | Promise<import("../../../clients/rest/generated/data-contracts").Worker>;
92
88
  }
89
+ export { testingExports as __testing } from './slot-utils';
@@ -9,10 +9,11 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
9
9
  });
10
10
  };
11
11
  Object.defineProperty(exports, "__esModule", { value: true });
12
- exports.Worker = void 0;
12
+ exports.__testing = exports.Worker = void 0;
13
13
  const declaration_1 = require("../../declaration");
14
14
  const worker_internal_1 = require("./worker-internal");
15
- const DEFAULT_DURABLE_SLOTS = 1000;
15
+ const slot_utils_1 = require("./slot-utils");
16
+ const deprecated_1 = require("./deprecated");
16
17
  /**
17
18
  * HatchetWorker class for workflow execution runtime
18
19
  */
@@ -22,9 +23,11 @@ class Worker {
22
23
  * @param nonDurable - The V0 worker implementation
23
24
  */
24
25
  constructor(v1, v0, nonDurable, config, name) {
26
+ /** Tracks all workflows registered after construction (via registerWorkflow/registerWorkflows) */
27
+ this._registeredWorkflows = [];
25
28
  this._v1 = v1;
26
29
  this._v0 = v0;
27
- this.nonDurable = nonDurable;
30
+ this._internal = nonDurable;
28
31
  this.config = config;
29
32
  this.name = name;
30
33
  }
@@ -36,7 +39,8 @@ class Worker {
36
39
  */
37
40
  static create(v1, v0, name, options) {
38
41
  return __awaiter(this, void 0, void 0, function* () {
39
- const opts = Object.assign(Object.assign({ name }, options), { maxRuns: options.slots || options.maxRuns });
42
+ const resolvedOptions = (0, slot_utils_1.resolveWorkerOptions)(options);
43
+ const opts = Object.assign({ name }, resolvedOptions);
40
44
  const internalWorker = new worker_internal_1.V1Worker(v1, opts);
41
45
  const worker = new Worker(v1, v0, internalWorker, options, name);
42
46
  yield worker.registerWorkflows(options.workflows);
@@ -51,21 +55,17 @@ class Worker {
51
55
  registerWorkflows(workflows) {
52
56
  return __awaiter(this, void 0, void 0, function* () {
53
57
  for (const wf of workflows || []) {
58
+ this._registeredWorkflows.push(wf);
54
59
  if (wf instanceof declaration_1.BaseWorkflowDeclaration) {
55
60
  // TODO check if tenant is V1
56
- yield this.nonDurable.registerWorkflowV1(wf);
61
+ yield this._internal.registerWorkflowV1(wf);
57
62
  if (wf.definition._durableTasks.length > 0) {
58
- if (!this.durable) {
59
- const opts = Object.assign(Object.assign({ name: `${this.name}-durable` }, this.config), { maxRuns: this.config.durableSlots || DEFAULT_DURABLE_SLOTS });
60
- this.durable = new worker_internal_1.V1Worker(this._v1, opts);
61
- yield this.durable.registerWorkflowV1(wf, true);
62
- }
63
- this.durable.registerDurableActionsV1(wf.definition);
63
+ this._internal.registerDurableActionsV1(wf.definition);
64
64
  }
65
65
  }
66
66
  else {
67
67
  // fallback to v0 client for backwards compatibility
68
- yield this.nonDurable.registerWorkflow(wf);
68
+ yield this._internal.registerWorkflow(wf);
69
69
  }
70
70
  }
71
71
  });
@@ -84,22 +84,29 @@ class Worker {
84
84
  * @returns Promise that resolves when the worker is stopped or killed
85
85
  */
86
86
  start() {
87
- const workers = [this.nonDurable];
88
- if (this.durable) {
89
- workers.push(this.durable);
90
- }
91
- return Promise.all(workers.map((w) => w.start()));
87
+ return __awaiter(this, void 0, void 0, function* () {
88
+ // Check engine version and fall back to legacy dual-worker mode if needed
89
+ if (yield (0, deprecated_1.isLegacyEngine)(this._v1)) {
90
+ // Include workflows registered after construction (via registerWorkflow/registerWorkflows)
91
+ // so the legacy worker picks them up.
92
+ const legacyConfig = Object.assign(Object.assign({}, this.config), { workflows: this._registeredWorkflows.length
93
+ ? this._registeredWorkflows
94
+ : this.config.workflows });
95
+ this._legacyWorker = yield deprecated_1.LegacyDualWorker.create(this._v1, this.name, legacyConfig);
96
+ return this._legacyWorker.start();
97
+ }
98
+ return this._internal.start();
99
+ });
92
100
  }
93
101
  /**
94
102
  * Stops the worker
95
103
  * @returns Promise that resolves when the worker stops
96
104
  */
97
105
  stop() {
98
- const workers = [this.nonDurable];
99
- if (this.durable) {
100
- workers.push(this.durable);
106
+ if (this._legacyWorker) {
107
+ return this._legacyWorker.stop();
101
108
  }
102
- return Promise.all(workers.map((w) => w.stop()));
109
+ return this._internal.stop();
103
110
  }
104
111
  /**
105
112
  * Updates or inserts worker labels
@@ -107,14 +114,14 @@ class Worker {
107
114
  * @returns Promise that resolves when labels are updated
108
115
  */
109
116
  upsertLabels(labels) {
110
- return this.nonDurable.upsertLabels(labels);
117
+ return this._internal.upsertLabels(labels);
111
118
  }
112
119
  /**
113
120
  * Get the labels for the worker
114
121
  * @returns The labels for the worker
115
122
  */
116
123
  getLabels() {
117
- return this.nonDurable.labels;
124
+ return this._internal.labels;
118
125
  }
119
126
  /**
120
127
  * Register a webhook with the worker
@@ -122,44 +129,33 @@ class Worker {
122
129
  * @returns A promise that resolves when the webhook is registered
123
130
  */
124
131
  registerWebhook(webhook) {
125
- return this.nonDurable.registerWebhook(webhook);
132
+ return this._internal.registerWebhook(webhook);
126
133
  }
127
134
  isPaused() {
128
135
  return __awaiter(this, void 0, void 0, function* () {
129
- var _a, _b;
130
- const promises = [];
131
- if ((_a = this.nonDurable) === null || _a === void 0 ? void 0 : _a.workerId) {
132
- promises.push(this._v1.workers.isPaused(this.nonDurable.workerId));
136
+ var _a;
137
+ if (!((_a = this._internal) === null || _a === void 0 ? void 0 : _a.workerId)) {
138
+ return false;
133
139
  }
134
- if ((_b = this.durable) === null || _b === void 0 ? void 0 : _b.workerId) {
135
- promises.push(this._v1.workers.isPaused(this.durable.workerId));
136
- }
137
- const res = yield Promise.all(promises);
138
- return !res.includes(false);
140
+ return this._v1.workers.isPaused(this._internal.workerId);
139
141
  });
140
142
  }
141
143
  // TODO docstrings
142
144
  pause() {
143
- var _a, _b;
144
- const promises = [];
145
- if ((_a = this.nonDurable) === null || _a === void 0 ? void 0 : _a.workerId) {
146
- promises.push(this._v1.workers.pause(this.nonDurable.workerId));
147
- }
148
- if ((_b = this.durable) === null || _b === void 0 ? void 0 : _b.workerId) {
149
- promises.push(this._v1.workers.pause(this.durable.workerId));
145
+ var _a;
146
+ if (!((_a = this._internal) === null || _a === void 0 ? void 0 : _a.workerId)) {
147
+ return Promise.resolve();
150
148
  }
151
- return Promise.all(promises);
149
+ return this._v1.workers.pause(this._internal.workerId);
152
150
  }
153
151
  unpause() {
154
- var _a, _b;
155
- const promises = [];
156
- if ((_a = this.nonDurable) === null || _a === void 0 ? void 0 : _a.workerId) {
157
- promises.push(this._v1.workers.unpause(this.nonDurable.workerId));
158
- }
159
- if ((_b = this.durable) === null || _b === void 0 ? void 0 : _b.workerId) {
160
- promises.push(this._v1.workers.unpause(this.durable.workerId));
152
+ var _a;
153
+ if (!((_a = this._internal) === null || _a === void 0 ? void 0 : _a.workerId)) {
154
+ return Promise.resolve();
161
155
  }
162
- return Promise.all(promises);
156
+ return this._v1.workers.unpause(this._internal.workerId);
163
157
  }
164
158
  }
165
159
  exports.Worker = Worker;
160
+ var slot_utils_2 = require("./slot-utils");
161
+ Object.defineProperty(exports, "__testing", { enumerable: true, get: function () { return slot_utils_2.testingExports; } });