@hatchet-dev/typescript-sdk 1.16.0 → 1.17.1

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 (97) hide show
  1. package/README.md +14 -2
  2. package/clients/admin/admin-client.d.ts +2 -2
  3. package/clients/dispatcher/action-listener.d.ts +3 -6
  4. package/clients/dispatcher/action-listener.js +12 -17
  5. package/clients/listeners/durable-listener/durable-listener-client.d.ts +116 -15
  6. package/clients/listeners/durable-listener/durable-listener-client.js +770 -19
  7. package/clients/listeners/durable-listener/pooled-durable-listener-client.js +1 -14
  8. package/clients/listeners/run-listener/pooled-child-listener-client.js +1 -14
  9. package/clients/rest/generated/Api.d.ts +5 -1
  10. package/clients/rest/generated/data-contracts.d.ts +16 -2
  11. package/clients/rest/generated/data-contracts.js +7 -3
  12. package/legacy/examples/affinity-workers.js +2 -2
  13. package/legacy/legacy-client.js +1 -1
  14. package/legacy/step.d.ts +2 -2
  15. package/legacy/step.js +3 -2
  16. package/legacy/workflow.d.ts +25 -25
  17. package/package.json +1 -1
  18. package/protoc/dispatcher/dispatcher.d.ts +20 -0
  19. package/protoc/dispatcher/dispatcher.js +535 -100
  20. package/protoc/events/events.js +54 -14
  21. package/protoc/google/protobuf/timestamp.js +1 -1
  22. package/protoc/v1/dispatcher.d.ts +169 -0
  23. package/protoc/v1/dispatcher.js +2096 -8
  24. package/protoc/v1/shared/condition.js +37 -11
  25. package/protoc/v1/shared/trigger.d.ts +89 -0
  26. package/protoc/v1/shared/trigger.js +524 -0
  27. package/protoc/v1/workflows.d.ts +34 -34
  28. package/protoc/v1/workflows.js +452 -254
  29. package/protoc/workflows/workflows.d.ts +2 -75
  30. package/protoc/workflows/workflows.js +157 -529
  31. package/util/abort-error.d.ts +10 -0
  32. package/util/abort-error.js +15 -0
  33. package/util/errors/eviction-not-supported-error.d.ts +5 -0
  34. package/util/errors/eviction-not-supported-error.js +18 -0
  35. package/util/errors/non-determinism-error.d.ts +7 -0
  36. package/util/errors/non-determinism-error.js +21 -0
  37. package/util/errors/task-run-terminated-error.d.ts +6 -0
  38. package/util/errors/task-run-terminated-error.js +15 -0
  39. package/util/hatchet-promise/hatchet-promise.d.ts +2 -1
  40. package/util/hatchet-promise/hatchet-promise.js +10 -1
  41. package/util/sleep.d.ts +3 -2
  42. package/util/sleep.js +6 -4
  43. package/v1/client/admin.d.ts +2 -2
  44. package/v1/client/client.js +1 -1
  45. package/v1/client/duration.d.ts +11 -1
  46. package/v1/client/duration.js +44 -0
  47. package/v1/client/features/runs.d.ts +16 -3
  48. package/v1/client/features/runs.js +38 -3
  49. package/v1/client/worker/context.d.ts +101 -6
  50. package/v1/client/worker/context.js +247 -21
  51. package/v1/client/worker/deprecated/index.d.ts +1 -1
  52. package/v1/client/worker/deprecated/index.js +2 -1
  53. package/v1/client/worker/deprecated/legacy-worker.d.ts +5 -0
  54. package/v1/client/worker/deprecated/legacy-worker.js +32 -23
  55. package/v1/client/worker/deprecated/pre-eviction.d.ts +12 -0
  56. package/v1/client/worker/deprecated/pre-eviction.js +37 -0
  57. package/v1/client/worker/engine-version.d.ts +5 -0
  58. package/v1/client/worker/engine-version.js +14 -0
  59. package/v1/client/worker/eviction/eviction-cache.d.ts +33 -0
  60. package/v1/client/worker/eviction/eviction-cache.js +139 -0
  61. package/v1/client/worker/eviction/eviction-manager.d.ts +42 -0
  62. package/v1/client/worker/eviction/eviction-manager.js +132 -0
  63. package/v1/client/worker/eviction/eviction-policy.d.ts +19 -0
  64. package/v1/client/worker/eviction/eviction-policy.js +8 -0
  65. package/v1/client/worker/eviction/index.d.ts +3 -0
  66. package/v1/client/worker/eviction/index.js +11 -0
  67. package/v1/client/worker/worker-internal.d.ts +23 -4
  68. package/v1/client/worker/worker-internal.js +177 -138
  69. package/v1/client/worker/worker.d.ts +1 -0
  70. package/v1/client/worker/worker.js +34 -1
  71. package/v1/conditions/sleep-condition.js +2 -1
  72. package/v1/conditions/transformer.js +2 -1
  73. package/v1/declaration.d.ts +5 -3
  74. package/v1/declaration.js +8 -0
  75. package/v1/examples/__e2e__/harness.d.ts +5 -0
  76. package/v1/examples/__e2e__/harness.js +13 -0
  77. package/v1/examples/concurrency_workflow_level/workflow.d.ts +1 -1
  78. package/v1/examples/concurrency_workflow_level/workflow.js +1 -1
  79. package/v1/examples/durable/workflow.d.ts +57 -0
  80. package/v1/examples/durable/workflow.js +162 -7
  81. package/v1/examples/durable-event/workflow.js +2 -7
  82. package/v1/examples/durable_event/workflow.d.ts +1 -0
  83. package/v1/examples/durable_event/workflow.js +4 -9
  84. package/v1/examples/durable_eviction/capacity-worker.d.ts +1 -0
  85. package/v1/examples/durable_eviction/capacity-worker.js +31 -0
  86. package/v1/examples/durable_eviction/worker.d.ts +1 -0
  87. package/v1/examples/durable_eviction/worker.js +34 -0
  88. package/v1/examples/durable_eviction/workflow.d.ts +44 -0
  89. package/v1/examples/durable_eviction/workflow.js +129 -0
  90. package/v1/examples/e2e-worker.js +42 -19
  91. package/v1/index.d.ts +5 -0
  92. package/v1/index.js +10 -0
  93. package/v1/parent-run-context-vars.d.ts +6 -0
  94. package/v1/task.d.ts +10 -2
  95. package/v1/task.js +2 -1
  96. package/version.d.ts +1 -1
  97. package/version.js +1 -1
@@ -54,7 +54,10 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
54
54
  Object.defineProperty(exports, "__esModule", { value: true });
55
55
  exports.InternalWorker = void 0;
56
56
  exports.mapRateLimitPb = mapRateLimitPb;
57
+ exports.resolveExecutionTimeout = resolveExecutionTimeout;
58
+ exports.resolveScheduleTimeout = resolveScheduleTimeout;
57
59
  const hatchet_error_1 = __importDefault(require("../../../util/errors/hatchet-error"));
60
+ const task_run_terminated_error_1 = require("../../../util/errors/task-run-terminated-error");
58
61
  const dispatcher_1 = require("../../../protoc/dispatcher");
59
62
  const hatchet_promise_1 = __importStar(require("../../../util/hatchet-promise/hatchet-promise"));
60
63
  const workflows_1 = require("../../../protoc/workflows");
@@ -65,12 +68,18 @@ const zod_to_json_schema_1 = require("zod-to-json-schema");
65
68
  const apply_namespace_1 = require("../../../util/apply-namespace");
66
69
  const sleep_1 = __importDefault(require("../../../util/sleep"));
67
70
  const abort_error_1 = require("../../../util/abort-error");
71
+ const duration_1 = require("../duration");
68
72
  const context_1 = require("./context");
69
73
  const parent_run_context_vars_1 = require("../../parent-run-context-vars");
70
74
  const health_server_1 = require("./health-server");
75
+ const eviction_manager_1 = require("./eviction/eviction-manager");
76
+ const eviction_policy_1 = require("./eviction/eviction-policy");
77
+ const engine_version_1 = require("./engine-version");
71
78
  class InternalWorker {
72
79
  constructor(client, options) {
73
80
  var _a, _b, _c, _d;
81
+ this.durable_action_set = new Set();
82
+ this.eviction_policies = new Map();
74
83
  this.workflow_registry = [];
75
84
  this.futures = {};
76
85
  this.contexts = {};
@@ -128,7 +137,12 @@ class InternalWorker {
128
137
  const newActions = workflow._durableTasks
129
138
  .filter((task) => !!task.fn)
130
139
  .reduce((acc, task) => {
131
- acc[`${(0, apply_namespace_1.applyNamespace)(workflow.name, this.client.config.namespace).toLowerCase()}:${task.name.toLowerCase()}`] = (ctx) => task.fn(ctx.input, ctx);
140
+ const actionId = `${(0, apply_namespace_1.applyNamespace)(workflow.name, this.client.config.namespace).toLowerCase()}:${task.name.toLowerCase()}`;
141
+ acc[actionId] = (ctx) => task.fn(ctx.input, ctx);
142
+ this.durable_action_set.add(actionId);
143
+ this.eviction_policies.set(actionId, task.evictionPolicy !== undefined
144
+ ? task.evictionPolicy
145
+ : eviction_policy_1.DEFAULT_DURABLE_TASK_EVICTION_POLICY);
132
146
  return acc;
133
147
  }, {});
134
148
  this.action_registry = Object.assign(Object.assign({}, this.action_registry), newActions);
@@ -154,7 +168,7 @@ class InternalWorker {
154
168
  }
155
169
  registerWorkflow(initWorkflow_1) {
156
170
  return __awaiter(this, arguments, void 0, function* (initWorkflow, durable = false) {
157
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v;
171
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w;
158
172
  // patch the namespace
159
173
  const workflow = Object.assign(Object.assign({}, initWorkflow.definition), { name: (0, apply_namespace_1.applyNamespace)(initWorkflow.definition.name, this.client.config.namespace).toLowerCase() });
160
174
  try {
@@ -177,19 +191,20 @@ class InternalWorker {
177
191
  }
178
192
  if (workflow.onFailure && typeof workflow.onFailure === 'object') {
179
193
  const onFailure = workflow.onFailure;
194
+ const scheduleTimeout = (_a = onFailure.scheduleTimeout) !== null && _a !== void 0 ? _a : (_b = workflow.taskDefaults) === null || _b === void 0 ? void 0 : _b.scheduleTimeout;
180
195
  onFailureTask = {
181
196
  readableId: 'on-failure-task',
182
197
  action: onFailureTaskName(workflow),
183
- timeout: onFailure.executionTimeout || ((_a = workflow.taskDefaults) === null || _a === void 0 ? void 0 : _a.executionTimeout) || '60s',
184
- scheduleTimeout: onFailure.scheduleTimeout || ((_b = workflow.taskDefaults) === null || _b === void 0 ? void 0 : _b.scheduleTimeout),
198
+ timeout: (0, duration_1.durationToString)(onFailure.executionTimeout || ((_c = workflow.taskDefaults) === null || _c === void 0 ? void 0 : _c.executionTimeout) || '60s'),
199
+ scheduleTimeout: scheduleTimeout ? (0, duration_1.durationToString)(scheduleTimeout) : undefined,
185
200
  inputs: '{}',
186
201
  parents: [],
187
- retries: onFailure.retries || ((_c = workflow.taskDefaults) === null || _c === void 0 ? void 0 : _c.retries) || 0,
188
- rateLimits: mapRateLimitPb(onFailure.rateLimits || ((_d = workflow.taskDefaults) === null || _d === void 0 ? void 0 : _d.rateLimits)),
189
- workerLabels: mapWorkerLabelPb(onFailure.desiredWorkerLabels || ((_e = workflow.taskDefaults) === null || _e === void 0 ? void 0 : _e.workerLabels)),
202
+ retries: onFailure.retries || ((_d = workflow.taskDefaults) === null || _d === void 0 ? void 0 : _d.retries) || 0,
203
+ rateLimits: mapRateLimitPb(onFailure.rateLimits || ((_e = workflow.taskDefaults) === null || _e === void 0 ? void 0 : _e.rateLimits)),
204
+ workerLabels: mapWorkerLabelPb(onFailure.desiredWorkerLabels || ((_f = workflow.taskDefaults) === null || _f === void 0 ? void 0 : _f.workerLabels)),
190
205
  concurrency: [],
191
- 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),
192
- 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),
206
+ backoffFactor: ((_g = onFailure.backoff) === null || _g === void 0 ? void 0 : _g.factor) || ((_j = (_h = workflow.taskDefaults) === null || _h === void 0 ? void 0 : _h.backoff) === null || _j === void 0 ? void 0 : _j.factor),
207
+ backoffMaxSeconds: ((_k = onFailure.backoff) === null || _k === void 0 ? void 0 : _k.maxSeconds) || ((_m = (_l = workflow.taskDefaults) === null || _l === void 0 ? void 0 : _l.backoff) === null || _m === void 0 ? void 0 : _m.maxSeconds),
193
208
  isDurable: false,
194
209
  slotRequests: { default: 1 },
195
210
  };
@@ -214,14 +229,14 @@ class InternalWorker {
214
229
  onSuccessTask = {
215
230
  name: 'on-success-task',
216
231
  fn: onSuccess.fn,
217
- executionTimeout: onSuccess.executionTimeout || ((_m = workflow.taskDefaults) === null || _m === void 0 ? void 0 : _m.executionTimeout) || '60s',
218
- scheduleTimeout: onSuccess.scheduleTimeout || ((_o = workflow.taskDefaults) === null || _o === void 0 ? void 0 : _o.scheduleTimeout),
232
+ executionTimeout: onSuccess.executionTimeout || ((_o = workflow.taskDefaults) === null || _o === void 0 ? void 0 : _o.executionTimeout) || '60s',
233
+ scheduleTimeout: onSuccess.scheduleTimeout || ((_p = workflow.taskDefaults) === null || _p === void 0 ? void 0 : _p.scheduleTimeout),
219
234
  parents,
220
- retries: onSuccess.retries || ((_p = workflow.taskDefaults) === null || _p === void 0 ? void 0 : _p.retries) || 0,
221
- rateLimits: onSuccess.rateLimits || ((_q = workflow.taskDefaults) === null || _q === void 0 ? void 0 : _q.rateLimits),
222
- desiredWorkerLabels: onSuccess.desiredWorkerLabels || ((_r = workflow.taskDefaults) === null || _r === void 0 ? void 0 : _r.workerLabels),
223
- concurrency: onSuccess.concurrency || ((_s = workflow.taskDefaults) === null || _s === void 0 ? void 0 : _s.concurrency),
224
- backoff: onSuccess.backoff || ((_t = workflow.taskDefaults) === null || _t === void 0 ? void 0 : _t.backoff),
235
+ retries: onSuccess.retries || ((_q = workflow.taskDefaults) === null || _q === void 0 ? void 0 : _q.retries) || 0,
236
+ rateLimits: onSuccess.rateLimits || ((_r = workflow.taskDefaults) === null || _r === void 0 ? void 0 : _r.rateLimits),
237
+ desiredWorkerLabels: onSuccess.desiredWorkerLabels || ((_s = workflow.taskDefaults) === null || _s === void 0 ? void 0 : _s.workerLabels),
238
+ concurrency: onSuccess.concurrency || ((_t = workflow.taskDefaults) === null || _t === void 0 ? void 0 : _t.concurrency),
239
+ backoff: onSuccess.backoff || ((_u = workflow.taskDefaults) === null || _u === void 0 ? void 0 : _u.backoff),
225
240
  };
226
241
  }
227
242
  if (onSuccessTask) {
@@ -285,23 +300,20 @@ class InternalWorker {
285
300
  defaultPriority: workflow.defaultPriority,
286
301
  inputJsonSchema,
287
302
  tasks: [...workflow._tasks, ...workflow._durableTasks].map((task) => {
288
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p;
303
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m;
289
304
  return ({
290
305
  readableId: task.name,
291
306
  action: `${workflow.name}:${task.name}`,
292
- timeout: task.executionTimeout ||
293
- task.timeout ||
294
- ((_a = workflow.taskDefaults) === null || _a === void 0 ? void 0 : _a.executionTimeout) ||
295
- '60s',
296
- scheduleTimeout: task.scheduleTimeout || ((_b = workflow.taskDefaults) === null || _b === void 0 ? void 0 : _b.scheduleTimeout),
307
+ timeout: resolveExecutionTimeout(task, workflow.taskDefaults),
308
+ scheduleTimeout: resolveScheduleTimeout(task, workflow.taskDefaults),
297
309
  inputs: '{}',
298
- parents: (_d = (_c = task.parents) === null || _c === void 0 ? void 0 : _c.map((p) => p.name)) !== null && _d !== void 0 ? _d : [],
310
+ parents: (_b = (_a = task.parents) === null || _a === void 0 ? void 0 : _a.map((p) => p.name)) !== null && _b !== void 0 ? _b : [],
299
311
  userData: '{}',
300
- retries: task.retries || ((_e = workflow.taskDefaults) === null || _e === void 0 ? void 0 : _e.retries) || 0,
301
- rateLimits: mapRateLimitPb(task.rateLimits || ((_f = workflow.taskDefaults) === null || _f === void 0 ? void 0 : _f.rateLimits)),
302
- workerLabels: mapWorkerLabelPb(task.desiredWorkerLabels || ((_g = workflow.taskDefaults) === null || _g === void 0 ? void 0 : _g.workerLabels)),
303
- 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),
304
- 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),
312
+ retries: task.retries || ((_c = workflow.taskDefaults) === null || _c === void 0 ? void 0 : _c.retries) || 0,
313
+ rateLimits: mapRateLimitPb(task.rateLimits || ((_d = workflow.taskDefaults) === null || _d === void 0 ? void 0 : _d.rateLimits)),
314
+ workerLabels: mapWorkerLabelPb(task.desiredWorkerLabels || ((_e = workflow.taskDefaults) === null || _e === void 0 ? void 0 : _e.workerLabels)),
315
+ backoffFactor: ((_f = task.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),
316
+ backoffMaxSeconds: ((_j = task.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),
305
317
  conditions: (0, transformer_1.taskConditionsToPb)(task, this.client.config.namespace),
306
318
  isDurable: durableTaskSet.has(task),
307
319
  slotRequests: task.slotRequests || (durableTaskSet.has(task) ? { durable: 1 } : { default: 1 }),
@@ -309,7 +321,7 @@ class InternalWorker {
309
321
  ? Array.isArray(task.concurrency)
310
322
  ? task.concurrency
311
323
  : [task.concurrency]
312
- : ((_p = workflow.taskDefaults) === null || _p === void 0 ? void 0 : _p.concurrency)
324
+ : ((_m = workflow.taskDefaults) === null || _m === void 0 ? void 0 : _m.concurrency)
313
325
  ? Array.isArray(workflow.taskDefaults.concurrency)
314
326
  ? workflow.taskDefaults.concurrency
315
327
  : [workflow.taskDefaults.concurrency]
@@ -317,11 +329,11 @@ class InternalWorker {
317
329
  });
318
330
  }),
319
331
  concurrency: concurrencySolo,
320
- defaultFilters: (_v = (_u = workflow.defaultFilters) === null || _u === void 0 ? void 0 : _u.map((f) => ({
332
+ defaultFilters: (_w = (_v = workflow.defaultFilters) === null || _v === void 0 ? void 0 : _v.map((f) => ({
321
333
  scope: f.scope,
322
334
  expression: f.expression,
323
335
  payload: f.payload ? new TextEncoder().encode(JSON.stringify(f.payload)) : undefined,
324
- }))) !== null && _v !== void 0 ? _v : [],
336
+ }))) !== null && _w !== void 0 ? _w : [],
325
337
  });
326
338
  this.registeredWorkflowPromises.push(registeredWorkflow);
327
339
  yield registeredWorkflow;
@@ -333,17 +345,83 @@ class InternalWorker {
333
345
  this.registerActions(workflow);
334
346
  });
335
347
  }
348
+ ensureEvictionManager() {
349
+ var _a, _b, _c;
350
+ if (this.evictionManager)
351
+ return this.evictionManager;
352
+ const totalDurableSlots = (_c = (_b = (_a = this.slotConfig) === null || _a === void 0 ? void 0 : _a.durable) !== null && _b !== void 0 ? _b : this.durableSlots) !== null && _c !== void 0 ? _c : 0;
353
+ this.evictionManager = new eviction_manager_1.DurableEvictionManager({
354
+ durableSlots: totalDurableSlots,
355
+ cancelLocal: (key) => {
356
+ var _a;
357
+ const err = new task_run_terminated_error_1.TaskRunTerminatedError('evicted');
358
+ const ctx = this.contexts[key];
359
+ if (ctx) {
360
+ const invocationCount = (_a = ctx.invocationCount) !== null && _a !== void 0 ? _a : 1;
361
+ this.client.durableListener.cleanupTaskState(ctx.action.taskRunExternalId, invocationCount);
362
+ if (ctx.abortController) {
363
+ ctx.abortController.abort(err);
364
+ }
365
+ }
366
+ const future = this.futures[key];
367
+ if (future) {
368
+ future.promise.catch(() => undefined);
369
+ future.cancel(hatchet_promise_1.CancellationReason.EVICTED_BY_WORKER);
370
+ }
371
+ },
372
+ requestEvictionWithAck: (key, rec) => __awaiter(this, void 0, void 0, function* () {
373
+ var _a;
374
+ const ctx = this.contexts[key];
375
+ const invocationCount = (_a = ctx === null || ctx === void 0 ? void 0 : ctx.invocationCount) !== null && _a !== void 0 ? _a : 1;
376
+ yield this.client.durableListener.sendEvictInvocation(rec.taskRunExternalId, invocationCount, rec.evictionReason);
377
+ }),
378
+ logger: this.logger,
379
+ });
380
+ this.client.durableListener.onServerEvict = (durableTaskExternalId, invocationCount) => {
381
+ var _a;
382
+ (_a = this.evictionManager) === null || _a === void 0 ? void 0 : _a.handleServerEviction(durableTaskExternalId, invocationCount);
383
+ };
384
+ this.evictionManager.start();
385
+ return this.evictionManager;
386
+ }
387
+ cleanupRun(key) {
388
+ var _a;
389
+ const ctx = this.contexts[key];
390
+ if (ctx instanceof context_1.DurableContext) {
391
+ this.client.durableListener.cleanupTaskState(ctx.action.taskRunExternalId, ctx.invocationCount);
392
+ }
393
+ (_a = this.evictionManager) === null || _a === void 0 ? void 0 : _a.unregisterRun(key);
394
+ delete this.futures[key];
395
+ delete this.contexts[key];
396
+ }
336
397
  handleStartStepRun(action) {
337
398
  return __awaiter(this, void 0, void 0, function* () {
399
+ var _a;
338
400
  const { actionId, taskRunExternalId, taskName } = action;
401
+ const actionKey = action.key;
339
402
  try {
340
- // Note: we always use a DurableContext since its a superset of the Context class
341
- const context = new context_1.DurableContext(action, this.client, this);
342
- this.contexts[taskRunExternalId] = context;
403
+ const isDurable = this.durable_action_set.has(actionId);
404
+ let context;
405
+ if (isDurable) {
406
+ const { durableListener } = this.client;
407
+ let mgr;
408
+ if ((0, engine_version_1.supportsEviction)(this.engineVersion)) {
409
+ yield durableListener.ensureStarted(this.workerId || '');
410
+ mgr = this.ensureEvictionManager();
411
+ const evictionPolicy = this.eviction_policies.get(actionId);
412
+ mgr.registerRun(actionKey, taskRunExternalId, (_a = action.durableTaskInvocationCount) !== null && _a !== void 0 ? _a : 1, evictionPolicy);
413
+ }
414
+ context = new context_1.DurableContext(action, this.client, this, durableListener, mgr, this.engineVersion);
415
+ }
416
+ else {
417
+ context = new context_1.Context(action, this.client, this);
418
+ }
419
+ this.contexts[actionKey] = context;
343
420
  const step = this.action_registry[actionId];
344
421
  if (!step) {
345
422
  this.logger.error(`Registered actions: '${Object.keys(this.action_registry).join(', ')}'`);
346
423
  this.logger.error(`Could not find step '${actionId}'`);
424
+ this.cleanupRun(actionKey);
347
425
  return;
348
426
  }
349
427
  const run = () => __awaiter(this, void 0, void 0, function* () {
@@ -367,8 +445,8 @@ class InternalWorker {
367
445
  childIndex: 0,
368
446
  desiredWorkerId: this.workerId || '',
369
447
  signal: context.abortController.signal,
448
+ durableContext: isDurable && context instanceof context_1.DurableContext ? context : undefined,
370
449
  }, () => {
371
- // Precheck: if cancellation already happened, don't execute user code.
372
450
  (0, abort_error_1.throwIfAborted)(context.abortController.signal);
373
451
  return step(context);
374
452
  });
@@ -389,13 +467,11 @@ class InternalWorker {
389
467
  return;
390
468
  }
391
469
  this.logger.info((0, logger_1.taskRunLog)(taskName, taskRunExternalId, 'completed'));
392
- // Send the action event to the dispatcher
393
470
  const event = this.getStepActionEvent(action, dispatcher_1.StepActionEventType.STEP_EVENT_TYPE_COMPLETED, false, result || null, action.retryCount);
394
471
  yield this.client.dispatcher.sendStepActionEvent(event);
395
472
  }
396
473
  catch (actionEventError) {
397
474
  this.logger.error(`Could not send completed action event: ${actionEventError.message || actionEventError}`);
398
- // send a failure event
399
475
  const failureEvent = this.getStepActionEvent(action, dispatcher_1.StepActionEventType.STEP_EVENT_TYPE_FAILED, false, actionEventError.message, action.retryCount);
400
476
  try {
401
477
  yield this.client.dispatcher.sendStepActionEvent(failureEvent);
@@ -406,9 +482,7 @@ class InternalWorker {
406
482
  this.logger.error(`Could not send action event: ${actionEventError.message || actionEventError}`);
407
483
  }
408
484
  finally {
409
- // delete the run from the futures
410
- delete this.futures[taskRunExternalId];
411
- delete this.contexts[taskRunExternalId];
485
+ this.cleanupRun(actionKey);
412
486
  }
413
487
  });
414
488
  const failure = (error) => __awaiter(this, void 0, void 0, function* () {
@@ -421,7 +495,6 @@ class InternalWorker {
421
495
  if (error.stack) {
422
496
  this.logger.error(error.stack);
423
497
  }
424
- // Send the action event to the dispatcher
425
498
  const event = this.getStepActionEvent(action, dispatcher_1.StepActionEventType.STEP_EVENT_TYPE_FAILED, shouldNotRetry, {
426
499
  message: error === null || error === void 0 ? void 0 : error.message,
427
500
  stack: error === null || error === void 0 ? void 0 : error.stack,
@@ -432,9 +505,7 @@ class InternalWorker {
432
505
  this.logger.error(`Could not send action event: ${e.message}`);
433
506
  }
434
507
  finally {
435
- // delete the run from the futures
436
- delete this.futures[taskRunExternalId];
437
- delete this.contexts[taskRunExternalId];
508
+ this.cleanupRun(actionKey);
438
509
  }
439
510
  });
440
511
  const future = new hatchet_promise_1.default((() => __awaiter(this, void 0, void 0, function* () {
@@ -457,7 +528,7 @@ class InternalWorker {
457
528
  (0, abort_error_1.throwIfAborted)(context.abortController.signal);
458
529
  yield success(result);
459
530
  }))());
460
- this.futures[taskRunExternalId] = future;
531
+ this.futures[actionKey] = future;
461
532
  // Send the action event to the dispatcher
462
533
  const event = this.getStepActionEvent(action, dispatcher_1.StepActionEventType.STEP_EVENT_TYPE_STARTED, false, undefined, action.retryCount);
463
534
  this.client.dispatcher.sendStepActionEvent(event).catch((e) => {
@@ -467,87 +538,18 @@ class InternalWorker {
467
538
  yield future.promise;
468
539
  }
469
540
  catch (e) {
470
- const message = (e === null || e === void 0 ? void 0 : e.message) || String(e);
471
- // TODO is this cased correctly...
472
- if (!message.includes('Cancelled')) {
541
+ if (!(0, task_run_terminated_error_1.isTaskRunTerminatedError)(e)) {
473
542
  this.logger.error(`Could not wait for task run ${taskRunExternalId} to finish. ` +
474
543
  `See https://docs.hatchet.run/home/cancellation for best practices on handling cancellation: `, e);
475
544
  }
476
545
  }
477
- }
478
- catch (e) {
479
- this.logger.error('Could not send action event (outer): ', e);
480
- }
481
- });
482
- }
483
- handleStartGroupKeyRun(action) {
484
- return __awaiter(this, void 0, void 0, function* () {
485
- const { actionId, getGroupKeyRunId, taskRunExternalId, taskName } = action;
486
- this.logger.error('Concurrency Key Functions have been deprecated and will be removed in a future release. Use Concurrency Expressions instead.');
487
- try {
488
- const context = new context_1.Context(action, this.client, this);
489
- const key = getGroupKeyRunId;
490
- if (!key) {
491
- this.logger.error(`No group key run id provided for action ${actionId}`);
492
- return;
493
- }
494
- this.contexts[key] = context;
495
- this.logger.debug(`Starting group key run ${key}`);
496
- const step = this.action_registry[actionId];
497
- if (!step) {
498
- this.logger.error(`Could not find step '${actionId}'`);
499
- return;
546
+ finally {
547
+ this.cleanupRun(actionKey);
500
548
  }
501
- const run = () => __awaiter(this, void 0, void 0, function* () {
502
- return step(context);
503
- });
504
- const success = (result) => {
505
- this.logger.info((0, logger_1.taskRunLog)(taskName, taskRunExternalId, 'completed'));
506
- try {
507
- // Send the action event to the dispatcher
508
- const event = this.getGroupKeyActionEvent(action, dispatcher_1.GroupKeyActionEventType.GROUP_KEY_EVENT_TYPE_COMPLETED, result);
509
- this.client.dispatcher.sendGroupKeyActionEvent(event).catch((e) => {
510
- this.logger.error(`Could not send action event: ${e.message}`);
511
- });
512
- }
513
- catch (e) {
514
- this.logger.error(`Could not send action event: ${e.message}`);
515
- }
516
- finally {
517
- // delete the run from the futures
518
- delete this.futures[key];
519
- delete this.contexts[key];
520
- }
521
- };
522
- const failure = (error) => {
523
- this.logger.error((0, logger_1.taskRunLog)(taskName, taskRunExternalId, `failed: ${error.message}`));
524
- try {
525
- // Send the action event to the dispatcher
526
- const event = this.getGroupKeyActionEvent(action, dispatcher_1.GroupKeyActionEventType.GROUP_KEY_EVENT_TYPE_FAILED, error);
527
- this.client.dispatcher.sendGroupKeyActionEvent(event).catch((e) => {
528
- this.logger.error(`Could not send action event: ${e.message}`);
529
- });
530
- }
531
- catch (e) {
532
- this.logger.error(`Could not send action event: ${e.message}`);
533
- }
534
- finally {
535
- // delete the run from the futures
536
- delete this.futures[key];
537
- delete this.contexts[key];
538
- }
539
- };
540
- const future = new hatchet_promise_1.default(run().then(success).catch(failure));
541
- this.futures[key] = future;
542
- // Send the action event to the dispatcher
543
- const event = this.getGroupKeyActionEvent(action, dispatcher_1.GroupKeyActionEventType.GROUP_KEY_EVENT_TYPE_STARTED);
544
- this.client.dispatcher.sendGroupKeyActionEvent(event).catch((e) => {
545
- this.logger.error(`Could not send action event: ${e.message}`);
546
- });
547
- yield future.promise;
548
549
  }
549
550
  catch (e) {
550
- this.logger.error(`Could not send action event: ${e.message}`);
551
+ this.cleanupRun(actionKey);
552
+ this.logger.error('Could not send action event (outer): ', e);
551
553
  }
552
554
  });
553
555
  }
@@ -584,11 +586,13 @@ class InternalWorker {
584
586
  return __awaiter(this, void 0, void 0, function* () {
585
587
  var _a, _b, _c;
586
588
  const { taskRunExternalId, taskName } = action;
589
+ const actionKey = action.key;
587
590
  try {
588
- const future = this.futures[taskRunExternalId];
589
- const context = this.contexts[taskRunExternalId];
591
+ const future = this.futures[actionKey];
592
+ const context = this.contexts[actionKey];
593
+ const cancelErr = new task_run_terminated_error_1.TaskRunTerminatedError('cancelled', 'Cancelled by worker');
590
594
  if (context && context.abortController) {
591
- context.abortController.abort('Cancelled by worker'); // TODO this reason is nonsensical
595
+ context.abortController.abort(cancelErr);
592
596
  }
593
597
  if (future) {
594
598
  const start = Date.now();
@@ -638,8 +642,7 @@ class InternalWorker {
638
642
  this.logger.error(`Cancellation: error while supervising cancellation for task run ${taskRunExternalId}: ${(e === null || e === void 0 ? void 0 : e.message) || e}`);
639
643
  }
640
644
  finally {
641
- delete this.futures[taskRunExternalId];
642
- delete this.contexts[taskRunExternalId];
645
+ this.cleanupRun(actionKey);
643
646
  }
644
647
  });
645
648
  }
@@ -654,6 +657,33 @@ class InternalWorker {
654
657
  this.killing = true;
655
658
  this.setStatus(health_server_1.workerStatus.UNHEALTHY);
656
659
  this.logger.info('Starting to exit...');
660
+ // Pause the worker on the server so it stops receiving new task assignments
661
+ // before we evict waiting durable runs, mirroring Python's pause_task_assignment().
662
+ if (this.workerId) {
663
+ try {
664
+ yield this.client.workers.pause(this.workerId);
665
+ }
666
+ catch (e) {
667
+ this.logger.error(`Could not pause worker: ${e.message}`);
668
+ }
669
+ }
670
+ if (this.evictionManager) {
671
+ try {
672
+ const evicted = yield this.evictionManager.evictAllWaiting();
673
+ if (evicted > 0) {
674
+ this.logger.info(`Evicted ${evicted} waiting durable run(s) during shutdown`);
675
+ }
676
+ }
677
+ catch (e) {
678
+ this.logger.error(`Could not evict waiting runs: ${e.message}`);
679
+ }
680
+ }
681
+ try {
682
+ yield this.client.durableListener.stop();
683
+ }
684
+ catch (e) {
685
+ this.logger.error(`Could not stop durable listener: ${e.message}`);
686
+ }
657
687
  try {
658
688
  yield ((_a = this.listener) === null || _a === void 0 ? void 0 : _a.unregister());
659
689
  }
@@ -749,20 +779,22 @@ class InternalWorker {
749
779
  }
750
780
  handleAction(action) {
751
781
  return __awaiter(this, void 0, void 0, function* () {
752
- const type = action.actionType
753
- ? (0, dispatcher_1.actionTypeFromJSON)(action.actionType)
754
- : dispatcher_1.ActionType.START_STEP_RUN;
755
- if (type === dispatcher_1.ActionType.START_STEP_RUN) {
756
- yield this.handleStartStepRun(action);
757
- }
758
- else if (type === dispatcher_1.ActionType.CANCEL_STEP_RUN) {
759
- yield this.handleCancelStepRun(action);
760
- }
761
- else if (type === dispatcher_1.ActionType.START_GET_GROUP_KEY) {
762
- yield this.handleStartGroupKeyRun(action);
763
- }
764
- else {
765
- this.logger.error(`Worker ${this.name} received unknown action type ${type}`);
782
+ const type = (0, dispatcher_1.actionTypeFromJSON)(action.actionType) || dispatcher_1.ActionType.START_STEP_RUN;
783
+ switch (type) {
784
+ case dispatcher_1.ActionType.START_STEP_RUN:
785
+ return this.handleStartStepRun(action);
786
+ case dispatcher_1.ActionType.CANCEL_STEP_RUN:
787
+ return this.handleCancelStepRun(action);
788
+ case dispatcher_1.ActionType.START_GET_GROUP_KEY:
789
+ this.logger.error(`Worker ${this.name} received unsupported action type START_GET_GROUP_KEY, please upgrade to V1...`);
790
+ return Promise.resolve();
791
+ case dispatcher_1.ActionType.UNRECOGNIZED:
792
+ this.logger.error(`Worker ${this.name} received unrecognized action type ${action.actionType}`);
793
+ return Promise.resolve();
794
+ default: {
795
+ const _ = type;
796
+ throw new Error(`Unhandled action type: ${_}`);
797
+ }
766
798
  }
767
799
  });
768
800
  }
@@ -888,3 +920,10 @@ function validateCelExpression(_expr) {
888
920
  // For now, we'll just return true to mimic the behavior.
889
921
  return true;
890
922
  }
923
+ function resolveExecutionTimeout(task, workflowDefaults) {
924
+ return (0, duration_1.durationToString)(task.executionTimeout || task.timeout || (workflowDefaults === null || workflowDefaults === void 0 ? void 0 : workflowDefaults.executionTimeout) || '60s');
925
+ }
926
+ function resolveScheduleTimeout(task, workflowDefaults) {
927
+ const value = task.scheduleTimeout || (workflowDefaults === null || workflowDefaults === void 0 ? void 0 : workflowDefaults.scheduleTimeout);
928
+ return value ? (0, duration_1.durationToString)(value) : undefined;
929
+ }
@@ -61,6 +61,7 @@ export declare class Worker {
61
61
  * @returns Promise that resolves when the worker is stopped or killed
62
62
  */
63
63
  start(): Promise<void>;
64
+ private _checkEvictionSupport;
64
65
  /**
65
66
  * Stops the worker
66
67
  * @returns Promise that resolves when the worker stops
@@ -14,10 +14,12 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
14
14
  Object.defineProperty(exports, "__esModule", { value: true });
15
15
  exports.__testing = exports.Worker = void 0;
16
16
  const sleep_1 = __importDefault(require("../../../util/sleep"));
17
+ const declaration_1 = require("../../declaration");
17
18
  const legacy_transformer_1 = require("../../../legacy/legacy-transformer");
18
19
  const worker_internal_1 = require("./worker-internal");
19
20
  const slot_utils_1 = require("./slot-utils");
20
21
  const deprecated_1 = require("./deprecated");
22
+ const engine_version_1 = require("./engine-version");
21
23
  /**
22
24
  * HatchetWorker class for workflow execution runtime
23
25
  */
@@ -97,9 +99,35 @@ class Worker {
97
99
  this._legacyWorker = yield deprecated_1.LegacyDualWorker.create(this._v1, this.name, legacyConfig);
98
100
  return this._legacyWorker.start();
99
101
  }
102
+ const engineVersion = yield (0, deprecated_1.fetchEngineVersion)(this._v1).catch(() => undefined);
103
+ this._checkEvictionSupport(engineVersion);
104
+ this._internal.engineVersion = engineVersion;
100
105
  return this._internal.start();
101
106
  });
102
107
  }
108
+ _checkEvictionSupport(engineVersion) {
109
+ if ((0, engine_version_1.supportsEviction)(engineVersion))
110
+ return;
111
+ const workflows = (this.config.workflows || []);
112
+ const tasksWithEviction = [];
113
+ for (const wf of workflows) {
114
+ if (!(wf instanceof declaration_1.BaseWorkflowDeclaration))
115
+ continue;
116
+ for (const task of wf.definition._durableTasks) {
117
+ if (task.evictionPolicy) {
118
+ tasksWithEviction.push(`${wf.definition.name}:${task.name}`);
119
+ }
120
+ }
121
+ }
122
+ if (tasksWithEviction.length === 0)
123
+ return;
124
+ const names = tasksWithEviction.join(', ');
125
+ const logger = this._v1.config.logger('Worker', this._v1.config.log_level);
126
+ (0, deprecated_1.emitDeprecationNotice)('pre-eviction-engine', `Engine ${engineVersion || 'unknown'} does not support durable eviction ` +
127
+ `(requires >= ${engine_version_1.MinEngineVersion.DURABLE_EVICTION}). ` +
128
+ `Eviction policies will be ignored for tasks: ${names}. ` +
129
+ `Please upgrade your Hatchet engine.`, new Date('2026-03-01T00:00:00Z'), logger);
130
+ }
103
131
  /**
104
132
  * Stops the worker
105
133
  * @returns Promise that resolves when the worker stops
@@ -163,9 +191,14 @@ class Worker {
163
191
  const pollInterval = 200;
164
192
  const start = Date.now();
165
193
  while (Date.now() - start < timeoutMs) {
166
- if ((_a = this._internal) === null || _a === void 0 ? void 0 : _a.workerId) {
194
+ // start() may asynchronously detect a legacy engine and set _legacyWorker
195
+ // after waitUntilReady has already entered this loop
196
+ if (this._legacyWorker) {
197
+ yield (0, sleep_1.default)(2000);
167
198
  return;
168
199
  }
200
+ if ((_a = this._internal) === null || _a === void 0 ? void 0 : _a.workerId)
201
+ return;
169
202
  yield (0, sleep_1.default)(pollInterval);
170
203
  }
171
204
  throw new Error(`Worker ${this.name} did not become ready within ${timeoutMs}ms`);
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.SleepCondition = void 0;
4
+ const duration_1 = require("../client/duration");
4
5
  const base_1 = require("./base");
5
6
  /**
6
7
  * Represents a condition that waits for a specified duration before proceeding.
@@ -27,7 +28,7 @@ class SleepCondition extends base_1.Condition {
27
28
  */
28
29
  constructor(sleepFor, readableDataKey, action) {
29
30
  super({
30
- readableDataKey: readableDataKey || `sleep-${sleepFor}`,
31
+ readableDataKey: readableDataKey || `sleep-${(0, duration_1.durationToString)(sleepFor)}`,
31
32
  action,
32
33
  orGroupId: '',
33
34
  expression: '',
@@ -6,6 +6,7 @@ const apply_namespace_1 = require("../../util/apply-namespace");
6
6
  const _1 = require(".");
7
7
  const base_1 = require("./base");
8
8
  const parent_condition_1 = require("./parent-condition");
9
+ const duration_1 = require("../client/duration");
9
10
  function taskConditionsToPb(task, namespace) {
10
11
  const waitForConditions = (0, _1.Render)(base_1.Action.QUEUE, task.waitFor);
11
12
  const cancelIfConditions = (0, _1.Render)(base_1.Action.CANCEL, task.cancelIf);
@@ -21,7 +22,7 @@ function conditionsToPb(conditions, namespace) {
21
22
  if (condition instanceof _1.SleepCondition) {
22
23
  sleepConditions.push({
23
24
  base: baseToPb(condition.base),
24
- sleepFor: condition.sleepFor,
25
+ sleepFor: (0, duration_1.durationToString)(condition.sleepFor),
25
26
  });
26
27
  }
27
28
  else if (condition instanceof _1.UserEventCondition) {