@hatchet-dev/typescript-sdk 1.30.0 → 1.30.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.
@@ -53,8 +53,15 @@ class DurableEvictionManager {
53
53
  markActive(key) {
54
54
  this._cache.markActive(key);
55
55
  }
56
- _evictRun(key) {
57
- this._cancelLocal(key);
56
+ // The invocation count pins the teardown to the invocation that was
57
+ // actually evicted: between requesting eviction and the server's ack, the
58
+ // server may restore and re-dispatch the next invocation, which registers
59
+ // under the same action key and must not be torn down.
60
+ _evictRun(key, invocationCount) {
61
+ const current = this._cache.get(key);
62
+ if (current && current.invocationCount !== invocationCount)
63
+ return;
64
+ this._cancelLocal(key, invocationCount);
58
65
  this.unregisterRun(key);
59
66
  }
60
67
  _tickSafe() {
@@ -89,7 +96,7 @@ class DurableEvictionManager {
89
96
  this._logger.debug(`DurableEvictionManager: evicting task_run_external_id=${rec.taskRunExternalId} ` +
90
97
  `wait_kind=${rec.waitKind} resource_id=${rec.waitResourceId}`);
91
98
  yield this._requestEvictionWithAck(key, rec);
92
- this._evictRun(key);
99
+ this._evictRun(key, rec.invocationCount);
93
100
  }
94
101
  });
95
102
  }
@@ -101,7 +108,7 @@ class DurableEvictionManager {
101
108
  if (rec && rec.invocationCount !== invocationCount)
102
109
  return;
103
110
  this._logger.info(`DurableEvictionManager: server-initiated eviction for task_run_external_id=${taskRunExternalId} invocation_count=${invocationCount}`);
104
- this._evictRun(key);
111
+ this._evictRun(key, invocationCount);
105
112
  }
106
113
  evictAllWaiting() {
107
114
  return __awaiter(this, void 0, void 0, function* () {
@@ -122,7 +129,7 @@ class DurableEvictionManager {
122
129
  // Always cancel locally even if the server ACK failed, so the
123
130
  // future settles and exitGracefully doesn't hang.
124
131
  // This will get resolved by the reassignment of the task.
125
- this._evictRun(rec.key);
132
+ this._evictRun(rec.key, rec.invocationCount);
126
133
  evicted++;
127
134
  }
128
135
  return evicted;
@@ -383,28 +383,31 @@ class InternalWorker {
383
383
  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;
384
384
  this.evictionManager = new eviction_manager_1.DurableEvictionManager({
385
385
  durableSlots: totalDurableSlots,
386
- cancelLocal: (key) => {
386
+ cancelLocal: (key, invocationCount) => {
387
387
  var _a;
388
388
  const err = new task_run_terminated_error_1.TaskRunTerminatedError('evicted');
389
389
  const ctx = this.contexts[key];
390
+ // A newer invocation may already be registered under this key (the
391
+ // server restored the run before the eviction ack arrived); it must
392
+ // not be aborted for the old invocation's eviction.
393
+ const ctxMatchesEvictedInvocation = ctx && ((_a = ctx.invocationCount) !== null && _a !== void 0 ? _a : 1) === invocationCount;
390
394
  if (ctx) {
391
- const invocationCount = (_a = ctx.invocationCount) !== null && _a !== void 0 ? _a : 1;
392
- this.client.durableListener.cleanupTaskState(ctx.action.taskRunExternalId, invocationCount);
393
- if (ctx.abortController) {
395
+ // Abort before cleanup: waiters must settle as aborted (eviction),
396
+ // not with cleanup's generic rejection, which would surface as a
397
+ // task failure.
398
+ if (ctxMatchesEvictedInvocation && ctx.abortController) {
394
399
  ctx.abortController.abort(err);
395
400
  }
401
+ this.client.durableListener.cleanupTaskState(ctx.action.taskRunExternalId, invocationCount);
396
402
  }
397
403
  const future = this.futures[key];
398
- if (future) {
404
+ if (future && ctxMatchesEvictedInvocation) {
399
405
  future.promise.catch(() => undefined);
400
406
  future.cancel(hatchet_promise_1.CancellationReason.EVICTED_BY_WORKER);
401
407
  }
402
408
  },
403
409
  requestEvictionWithAck: (key, rec) => __awaiter(this, void 0, void 0, function* () {
404
- var _a;
405
- const ctx = this.contexts[key];
406
- const invocationCount = (_a = ctx === null || ctx === void 0 ? void 0 : ctx.invocationCount) !== null && _a !== void 0 ? _a : 1;
407
- yield this.client.durableListener.sendEvictInvocation(rec.taskRunExternalId, invocationCount, rec.evictionReason);
410
+ yield this.client.durableListener.sendEvictInvocation(rec.taskRunExternalId, rec.invocationCount, rec.evictionReason);
408
411
  }),
409
412
  logger: this.logger,
410
413
  });
@@ -415,12 +418,16 @@ class InternalWorker {
415
418
  this.evictionManager.start();
416
419
  return this.evictionManager;
417
420
  }
418
- cleanupRun(key) {
421
+ cleanupRun(key, attemptContext) {
419
422
  var _a;
420
- const ctx = this.contexts[key];
423
+ const ctx = attemptContext !== null && attemptContext !== void 0 ? attemptContext : this.contexts[key];
421
424
  if (ctx instanceof context_1.DurableContext) {
422
425
  this.client.durableListener.cleanupTaskState(ctx.action.taskRunExternalId, ctx.invocationCount);
423
426
  }
427
+ // A restored invocation may have re-registered under this key while this
428
+ // attempt was shutting down; the key-owned state now belongs to it.
429
+ if (attemptContext && this.contexts[key] !== attemptContext)
430
+ return;
424
431
  (_a = this.evictionManager) === null || _a === void 0 ? void 0 : _a.unregisterRun(key);
425
432
  delete this.futures[key];
426
433
  delete this.contexts[key];
@@ -456,7 +463,7 @@ class InternalWorker {
456
463
  if (!step) {
457
464
  this.logger.error(`Registered actions: '${Object.keys(this.action_registry).join(', ')}'`);
458
465
  this.logger.error(`Could not find step '${actionId}'`);
459
- this.cleanupRun(actionKey);
466
+ this.cleanupRun(actionKey, context);
460
467
  return;
461
468
  }
462
469
  const run = () => __awaiter(this, void 0, void 0, function* () {
@@ -517,7 +524,7 @@ class InternalWorker {
517
524
  this.logger.error(`Could not send action event: ${actionEventError.message || actionEventError}`);
518
525
  }
519
526
  finally {
520
- this.cleanupRun(actionKey);
527
+ this.cleanupRun(actionKey, context);
521
528
  }
522
529
  });
523
530
  const failure = (error) => __awaiter(this, void 0, void 0, function* () {
@@ -526,6 +533,11 @@ class InternalWorker {
526
533
  if (context.cancelled) {
527
534
  return;
528
535
  }
536
+ // The run was evicted or cancelled by the worker, not failed by
537
+ // user code; the server already accounts for it.
538
+ if ((0, task_run_terminated_error_1.isTaskRunTerminatedError)(error)) {
539
+ return;
540
+ }
529
541
  this.logger.error((0, logger_1.taskRunLog)(taskName, taskRunExternalId, `failed: ${error.message}`));
530
542
  if (error.stack) {
531
543
  this.logger.error(error.stack);
@@ -540,7 +552,7 @@ class InternalWorker {
540
552
  this.logger.error(`Could not send action event: ${e.message}`);
541
553
  }
542
554
  finally {
543
- this.cleanupRun(actionKey);
555
+ this.cleanupRun(actionKey, context);
544
556
  }
545
557
  });
546
558
  const future = new hatchet_promise_1.default((() => __awaiter(this, void 0, void 0, function* () {
@@ -581,7 +593,7 @@ class InternalWorker {
581
593
  }
582
594
  }
583
595
  finally {
584
- this.cleanupRun(actionKey);
596
+ this.cleanupRun(actionKey, context);
585
597
  }
586
598
  }
587
599
  catch (e) {
package/v1/embedded.d.ts CHANGED
@@ -1,6 +1,10 @@
1
1
  import type { AxiosRequestConfig } from 'axios';
2
2
  import type { ClientConfig, HatchetClientOptions } from '../clients/hatchet-client';
3
3
  import { HatchetClient } from './client/client';
4
+ /**
5
+ * Options for the embedded engine sidecar. All fields are optional; by default the
6
+ * latest hatchet-embedded release is downloaded and started with a bundled Postgres.
7
+ */
4
8
  export interface EmbeddedOptions {
5
9
  /**
6
10
  * hatchet-embedded release tag to download (defaults to HATCHET_CLIENT_EMBEDDED_VERSION or
@@ -20,7 +24,9 @@ export interface EmbeddedOptions {
20
24
  databaseUrl?: string;
21
25
  /** store the bundled Postgres runtime and data under this directory */
22
26
  postgresDataDir?: string;
27
+ /** bind the engine's gRPC server to this port */
23
28
  grpcPort?: number;
29
+ /** bind the REST API server to this port */
24
30
  apiPort?: number;
25
31
  /** set to false to start only the engine + gRPC, no REST API */
26
32
  startApi?: boolean;
@@ -28,14 +34,25 @@ export interface EmbeddedOptions {
28
34
  runMigrations?: boolean;
29
35
  /** use RabbitMQ instead of the Postgres message queue */
30
36
  rabbitmqUrl?: string;
37
+ /** log level for the engine's output */
31
38
  logLevel?: string;
39
+ /** how long to wait for the engine to become ready, in milliseconds (default 300000) */
32
40
  readyTimeoutMs?: number;
33
41
  }
42
+ /**
43
+ * A running embedded engine sidecar and its connection details, as returned by
44
+ * `startEmbeddedSidecar`.
45
+ */
34
46
  export interface EmbeddedSidecar {
47
+ /** API token for the sidecar's default tenant */
35
48
  token: string;
49
+ /** ID of the sidecar's default tenant */
36
50
  tenantId: string;
51
+ /** host:port of the engine's gRPC server */
37
52
  grpcAddress: string;
53
+ /** base URL of the REST API (empty when `startApi` is false) */
38
54
  apiUrl: string;
55
+ /** gracefully stops the sidecar and resolves once it has fully exited */
39
56
  stop: () => Promise<void>;
40
57
  }
41
58
  /**
@@ -46,12 +63,25 @@ export interface EmbeddedSidecar {
46
63
  * your program has returned.
47
64
  */
48
65
  export declare function stopEmbeddedSidecar(): Promise<void>;
66
+ /**
67
+ * Downloads (and caches) the hatchet-embedded sidecar binary, spawns it, and waits
68
+ * until the embedded engine is ready. The sidecar shuts down when this process exits.
69
+ * Use {@link HatchetEmbeddedClient.init} unless you need the raw connection details.
70
+ */
49
71
  export declare function startEmbeddedSidecar(opts?: EmbeddedOptions): Promise<EmbeddedSidecar>;
72
+ /**
73
+ * Entry point for embedded mode. `init()` starts a full local Hatchet engine and
74
+ * returns a regular Hatchet client connected to it. No API token or Docker is needed.
75
+ * See the [embedded mode guide](https://docs.hatchet.run/v1/embedded).
76
+ */
50
77
  export declare class HatchetEmbeddedClient {
51
78
  /**
52
79
  * Runs a full Hatchet engine locally via the hatchet-embedded sidecar (downloaded
53
- * on first use) and returns a client wired to it. By default the sidecar starts a
54
- * bundled Postgres; pass `databaseUrl` to point it at your own instead.
80
+ * on first use) and returns a client wired to it. No API token or Docker is needed,
81
+ * which makes this a good fit for local development and CI. By default the sidecar
82
+ * starts a bundled Postgres; pass `databaseUrl` to point it at your own instead.
83
+ *
84
+ * See the [embedded mode guide](https://docs.hatchet.run/v1/embedded).
55
85
  * @param embeddedOpts - Options for the embedded engine (version, ports, database, ...).
56
86
  * @param config - Optional configuration overrides for the client.
57
87
  * @param options - Optional client options.
@@ -60,6 +90,10 @@ export declare class HatchetEmbeddedClient {
60
90
  */
61
91
  static init<T extends Record<string, any> = {}, U extends Record<string, any> = {}>(embeddedOpts?: EmbeddedOptions, config?: Omit<Partial<ClientConfig>, 'middleware'>, options?: HatchetClientOptions, axiosConfig?: AxiosRequestConfig): Promise<EmbeddedClient<T, U>>;
62
92
  }
93
+ /**
94
+ * A `HatchetClient` connected to an embedded engine, extended with `stopEmbedded`.
95
+ * Returned by {@link HatchetEmbeddedClient.init}.
96
+ */
63
97
  export type EmbeddedClient<T extends Record<string, any> = {}, U extends Record<string, any> = {}> = HatchetClient<T, U> & {
64
98
  /**
65
99
  * Gracefully stops the embedded engine sidecar and resolves once it has
package/v1/embedded.js CHANGED
@@ -52,6 +52,25 @@ Object.defineProperty(exports, "__esModule", { value: true });
52
52
  exports.HatchetEmbeddedClient = void 0;
53
53
  exports.stopEmbeddedSidecar = stopEmbeddedSidecar;
54
54
  exports.startEmbeddedSidecar = startEmbeddedSidecar;
55
+ /**
56
+ * Embedded mode runs a full local Hatchet engine as a sidecar process managed by your
57
+ * application. It needs no API token, no Docker, and no external services (by default
58
+ * the sidecar starts a bundled Postgres), which makes it a good fit for local
59
+ * development and CI.
60
+ *
61
+ * ```typescript
62
+ * import { HatchetEmbeddedClient } from './embedded.js';
63
+ *
64
+ * const hatchet = await HatchetEmbeddedClient.init();
65
+ * // ... register workers and run tasks as usual ...
66
+ * await hatchet.stopEmbedded();
67
+ * ```
68
+ *
69
+ * See the [embedded mode guide](https://docs.hatchet.run/v1/embedded) for setup and
70
+ * configuration details.
71
+ *
72
+ * @module Embedded
73
+ */
55
74
  const child_process_1 = require("child_process");
56
75
  const client_1 = require("./client/client");
57
76
  const crypto_1 = require("crypto");
@@ -211,12 +230,7 @@ function waitForHandshake(child, handshakePath, timeoutMs) {
211
230
  throw new Error(`hatchet embedded sidecar did not become ready within ${timeoutMs}ms`);
212
231
  });
213
232
  }
214
- /**
215
- * Downloads (and caches) the hatchet-embedded sidecar binary, spawns it, and
216
- * waits until the embedded engine is ready. The sidecar shuts down when this
217
- * process exits. Use `HatchetEmbedded()` unless you need the raw
218
- * connection details.
219
- */
233
+ // sidecars started in this process that have not been stopped yet
220
234
  const activeSidecars = new Set();
221
235
  /**
222
236
  * Gracefully stops every sidecar started in this process by
@@ -232,6 +246,11 @@ function stopEmbeddedSidecar() {
232
246
  }
233
247
  });
234
248
  }
249
+ /**
250
+ * Downloads (and caches) the hatchet-embedded sidecar binary, spawns it, and waits
251
+ * until the embedded engine is ready. The sidecar shuts down when this process exits.
252
+ * Use {@link HatchetEmbeddedClient.init} unless you need the raw connection details.
253
+ */
235
254
  function startEmbeddedSidecar() {
236
255
  return __awaiter(this, arguments, void 0, function* (opts = {}) {
237
256
  var _a, _b;
@@ -321,11 +340,19 @@ function startEmbeddedSidecar() {
321
340
  return sidecar;
322
341
  });
323
342
  }
343
+ /**
344
+ * Entry point for embedded mode. `init()` starts a full local Hatchet engine and
345
+ * returns a regular Hatchet client connected to it. No API token or Docker is needed.
346
+ * See the [embedded mode guide](https://docs.hatchet.run/v1/embedded).
347
+ */
324
348
  class HatchetEmbeddedClient {
325
349
  /**
326
350
  * Runs a full Hatchet engine locally via the hatchet-embedded sidecar (downloaded
327
- * on first use) and returns a client wired to it. By default the sidecar starts a
328
- * bundled Postgres; pass `databaseUrl` to point it at your own instead.
351
+ * on first use) and returns a client wired to it. No API token or Docker is needed,
352
+ * which makes this a good fit for local development and CI. By default the sidecar
353
+ * starts a bundled Postgres; pass `databaseUrl` to point it at your own instead.
354
+ *
355
+ * See the [embedded mode guide](https://docs.hatchet.run/v1/embedded).
329
356
  * @param embeddedOpts - Options for the embedded engine (version, ports, database, ...).
330
357
  * @param config - Optional configuration overrides for the client.
331
358
  * @param options - Optional client options.
@@ -0,0 +1,42 @@
1
+ export type LeafInput = {
2
+ mid: number;
3
+ branch: number;
4
+ delayMs: number;
5
+ generation: number;
6
+ };
7
+ export type LeafOutput = {
8
+ mid: number;
9
+ branch: number;
10
+ generation: number;
11
+ };
12
+ export type MidInput = {
13
+ mid: number;
14
+ branches: number;
15
+ childDelayMs: number;
16
+ delayStepMs: number;
17
+ };
18
+ export type MidOutput = {
19
+ mid: number;
20
+ completedBranches: number[];
21
+ invocationCount: number;
22
+ };
23
+ export type RootInput = {
24
+ durables?: number;
25
+ branches?: number;
26
+ childDelayMs?: number;
27
+ delayStepMs?: number;
28
+ };
29
+ export type RootOutput = {
30
+ rootInvocationCount: number;
31
+ midInvocationCounts: number[];
32
+ completedMids: number[];
33
+ };
34
+ export declare const ROOT_DEFAULTS: {
35
+ durables: number;
36
+ branches: number;
37
+ childDelayMs: number;
38
+ delayStepMs: number;
39
+ };
40
+ export declare const callbackOrderingLeaf: import("../..").TaskWorkflowDeclaration<LeafInput, LeafOutput, {}, {}, {}, {}>;
41
+ export declare const callbackOrderingMid: import("../..").TaskWorkflowDeclaration<MidInput, MidOutput, {}, {}, {}, {}>;
42
+ export declare const callbackOrderingRoot: import("../..").TaskWorkflowDeclaration<RootInput, RootOutput, {}, {}, {}, {}>;
@@ -0,0 +1,95 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ var __importDefault = (this && this.__importDefault) || function (mod) {
12
+ return (mod && mod.__esModule) ? mod : { "default": mod };
13
+ };
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.callbackOrderingRoot = exports.callbackOrderingMid = exports.callbackOrderingLeaf = exports.ROOT_DEFAULTS = void 0;
16
+ const sleep_1 = __importDefault(require("../../../util/sleep"));
17
+ const hatchet_client_1 = require("../hatchet-client");
18
+ const WORKFLOW_PREFIX = 'durable-callback-ordering';
19
+ const EVICTION_POLICY = {
20
+ ttl: 250,
21
+ allowCapacityEviction: true,
22
+ priority: 0,
23
+ };
24
+ exports.ROOT_DEFAULTS = {
25
+ durables: 4,
26
+ branches: 8,
27
+ childDelayMs: 1500,
28
+ delayStepMs: 3,
29
+ };
30
+ exports.callbackOrderingLeaf = hatchet_client_1.hatchet.task({
31
+ name: `${WORKFLOW_PREFIX}-leaf`,
32
+ executionTimeout: '1m',
33
+ fn: (input) => __awaiter(void 0, void 0, void 0, function* () {
34
+ yield (0, sleep_1.default)(input.delayMs);
35
+ return { mid: input.mid, branch: input.branch, generation: input.generation };
36
+ }),
37
+ });
38
+ exports.callbackOrderingMid = hatchet_client_1.hatchet.durableTask({
39
+ name: `${WORKFLOW_PREFIX}-mid`,
40
+ executionTimeout: '5m',
41
+ retries: 0,
42
+ evictionPolicy: EVICTION_POLICY,
43
+ fn: (input, ctx) => __awaiter(void 0, void 0, void 0, function* () {
44
+ // Staggered first-generation children complete out of spawn order, so each
45
+ // branch's second-generation spawn is emitted in completion order. Replays
46
+ // after eviction must re-deliver those completions in the recorded order or
47
+ // the re-emitted spawn sequence diverges from the event log.
48
+ const branch = (branchIndex) => __awaiter(void 0, void 0, void 0, function* () {
49
+ const firstDelayMs = input.childDelayMs + (input.branches - branchIndex - 1) * input.delayStepMs;
50
+ const first = yield exports.callbackOrderingLeaf.run({
51
+ mid: input.mid,
52
+ branch: branchIndex,
53
+ delayMs: firstDelayMs,
54
+ generation: 1,
55
+ });
56
+ const second = yield exports.callbackOrderingLeaf.run({
57
+ mid: input.mid,
58
+ branch: first.branch,
59
+ delayMs: input.childDelayMs,
60
+ generation: 2,
61
+ });
62
+ return second.branch;
63
+ });
64
+ const completed = yield Promise.all(Array.from({ length: input.branches }, (_, branchIndex) => branch(branchIndex)));
65
+ return {
66
+ mid: input.mid,
67
+ completedBranches: completed,
68
+ invocationCount: ctx.invocationCount,
69
+ };
70
+ }),
71
+ });
72
+ exports.callbackOrderingRoot = hatchet_client_1.hatchet.durableTask({
73
+ name: `${WORKFLOW_PREFIX}-root`,
74
+ executionTimeout: '10m',
75
+ retries: 0,
76
+ evictionPolicy: EVICTION_POLICY,
77
+ fn: (input, ctx) => __awaiter(void 0, void 0, void 0, function* () {
78
+ var _a;
79
+ const durables = (_a = input.durables) !== null && _a !== void 0 ? _a : exports.ROOT_DEFAULTS.durables;
80
+ const results = yield Promise.all(Array.from({ length: durables }, (_, midIndex) => {
81
+ var _a, _b, _c;
82
+ return exports.callbackOrderingMid.run({
83
+ mid: midIndex,
84
+ branches: (_a = input.branches) !== null && _a !== void 0 ? _a : exports.ROOT_DEFAULTS.branches,
85
+ childDelayMs: (_b = input.childDelayMs) !== null && _b !== void 0 ? _b : exports.ROOT_DEFAULTS.childDelayMs,
86
+ delayStepMs: (_c = input.delayStepMs) !== null && _c !== void 0 ? _c : exports.ROOT_DEFAULTS.delayStepMs,
87
+ });
88
+ }));
89
+ return {
90
+ rootInvocationCount: ctx.invocationCount,
91
+ midInvocationCounts: results.map((item) => item.invocationCount),
92
+ completedMids: results.map((item) => item.mid),
93
+ };
94
+ }),
95
+ });
@@ -13,6 +13,11 @@ export declare const evictableSleepForGracefulTermination: import("../..").TaskW
13
13
  export declare const evictableWaitForEvent: import("../..").TaskWorkflowDeclaration<import("../..").JsonObject, {
14
14
  status: string;
15
15
  }, {}, {}, {}, {}>;
16
+ export declare const MEMO_EVENT_KEY = "durable-eviction:memo-event";
17
+ export declare const evictableMemoThenWaitForEvent: import("../..").TaskWorkflowDeclaration<import("../..").JsonObject, {
18
+ status: string;
19
+ memoizedNow: string;
20
+ }, {}, {}, {}, {}>;
16
21
  export declare const evictableChildSpawn: import("../..").TaskWorkflowDeclaration<import("../..").JsonObject, {
17
22
  child: {
18
23
  child_status: string;
@@ -12,7 +12,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
12
12
  return (mod && mod.__esModule) ? mod : { "default": mod };
13
13
  };
14
14
  Object.defineProperty(exports, "__esModule", { value: true });
15
- exports.nonEvictableSleep = exports.capacityEvictableSleep = exports.CAPACITY_SLEEP_SECONDS = exports.evictableChildBulkSpawn = exports.bulkChildTask = exports.multipleEviction = exports.evictableChildSpawn = exports.evictableWaitForEvent = exports.evictableSleepForGracefulTermination = exports.evictableSleep = exports.childTask = exports.EVENT_KEY = exports.LONG_SLEEP_SECONDS = exports.EVICTION_TTL_SECONDS = void 0;
15
+ exports.nonEvictableSleep = exports.capacityEvictableSleep = exports.CAPACITY_SLEEP_SECONDS = exports.evictableChildBulkSpawn = exports.bulkChildTask = exports.multipleEviction = exports.evictableChildSpawn = exports.evictableMemoThenWaitForEvent = exports.MEMO_EVENT_KEY = exports.evictableWaitForEvent = exports.evictableSleepForGracefulTermination = exports.evictableSleep = exports.childTask = exports.EVENT_KEY = exports.LONG_SLEEP_SECONDS = exports.EVICTION_TTL_SECONDS = void 0;
16
16
  const sleep_1 = __importDefault(require("../../../util/sleep"));
17
17
  const hatchet_client_1 = require("../hatchet-client");
18
18
  exports.EVICTION_TTL_SECONDS = 5;
@@ -66,6 +66,20 @@ exports.evictableWaitForEvent = hatchet_client_1.hatchet.durableTask({
66
66
  return { status: 'completed' };
67
67
  }),
68
68
  });
69
+ exports.MEMO_EVENT_KEY = 'durable-eviction:memo-event';
70
+ exports.evictableMemoThenWaitForEvent = hatchet_client_1.hatchet.durableTask({
71
+ name: 'evictable-memo-then-wait-for-event',
72
+ executionTimeout: '5m',
73
+ evictionPolicy: EVICTION_POLICY,
74
+ fn: (_input, ctx) => __awaiter(void 0, void 0, void 0, function* () {
75
+ // now() records a memo node. On replay after eviction, the server
76
+ // re-delivers that node's completion, which never has a callback waiter
77
+ // and must not block the event completion queued behind it.
78
+ const memoizedNow = yield ctx.now();
79
+ yield ctx.waitForEvent(exports.MEMO_EVENT_KEY, 'true');
80
+ return { status: 'completed', memoizedNow: memoizedNow.toISOString() };
81
+ }),
82
+ });
69
83
  exports.evictableChildSpawn = hatchet_client_1.hatchet.durableTask({
70
84
  name: 'evictable-child-spawn',
71
85
  executionTimeout: '5m',
@@ -30,24 +30,25 @@ const workflow_9 = require("./dag/workflow");
30
30
  const workflow_10 = require("./durable/workflow");
31
31
  const workflow_11 = require("./durable_event/workflow");
32
32
  const workflow_12 = require("./durable_eviction/workflow");
33
- const workflow_13 = require("./durable_sleep/workflow");
34
- const workflow_14 = require("./logger/workflow");
35
- const workflow_15 = require("./non_retryable/workflow");
36
- const workflow_16 = require("./on_failure/workflow");
37
- const workflow_17 = require("./idempotency/workflow");
38
- const workflow_18 = require("./on_event/workflow");
39
- const workflow_19 = require("./return_exceptions/workflow");
40
- const workflow_20 = require("./run_details/workflow");
33
+ const workflow_13 = require("./durable_callback_ordering/workflow");
34
+ const workflow_14 = require("./durable_sleep/workflow");
35
+ const workflow_15 = require("./logger/workflow");
36
+ const workflow_16 = require("./non_retryable/workflow");
37
+ const workflow_17 = require("./on_failure/workflow");
38
+ const workflow_18 = require("./idempotency/workflow");
39
+ const workflow_19 = require("./on_event/workflow");
40
+ const workflow_20 = require("./return_exceptions/workflow");
41
+ const workflow_21 = require("./run_details/workflow");
41
42
  const e2e_workflows_1 = require("./simple/e2e-workflows");
42
- const workflow_21 = require("./batch_assign/workflow");
43
- const workflow_22 = require("./streaming/workflow");
44
- const workflow_23 = require("./subscribe_to_stream/workflow");
45
- const workflow_24 = require("./timeout/workflow");
46
- const workflow_25 = require("./webhooks/workflow");
47
- const workflow_26 = require("./child_index/workflow");
48
- const workflow_27 = require("./support_agent/workflow");
49
- const workflow_28 = require("./welcome_email/workflow");
50
- const workflow_29 = require("./pdf_pipeline/workflow");
43
+ const workflow_22 = require("./batch_assign/workflow");
44
+ const workflow_23 = require("./streaming/workflow");
45
+ const workflow_24 = require("./subscribe_to_stream/workflow");
46
+ const workflow_25 = require("./timeout/workflow");
47
+ const workflow_26 = require("./webhooks/workflow");
48
+ const workflow_27 = require("./child_index/workflow");
49
+ const workflow_28 = require("./support_agent/workflow");
50
+ const workflow_29 = require("./welcome_email/workflow");
51
+ const workflow_30 = require("./pdf_pipeline/workflow");
51
52
  const workflows = [
52
53
  workflow_1.bulkChild,
53
54
  workflow_1.bulkParentWorkflow,
@@ -81,54 +82,58 @@ const workflows = [
81
82
  workflow_10.errorRaisingDurableParent,
82
83
  workflow_11.durableEvent,
83
84
  workflow_11.durableEventWithFilter,
84
- workflow_13.durableSleep,
85
+ workflow_13.callbackOrderingLeaf,
86
+ workflow_13.callbackOrderingMid,
87
+ workflow_13.callbackOrderingRoot,
88
+ workflow_14.durableSleep,
85
89
  workflow_12.evictableSleep,
86
90
  workflow_12.evictableWaitForEvent,
91
+ workflow_12.evictableMemoThenWaitForEvent,
87
92
  workflow_12.evictableChildSpawn,
88
93
  workflow_12.multipleEviction,
89
94
  workflow_12.nonEvictableSleep,
90
95
  workflow_12.childTask,
91
96
  workflow_12.bulkChildTask,
92
97
  workflow_12.evictableChildBulkSpawn,
93
- (0, workflow_14.createLoggingWorkflow)(hatchet_client_1.hatchet),
94
- workflow_15.nonRetryableWorkflow,
95
- workflow_16.failureWorkflow,
96
- workflow_17.idempotentTask,
97
- workflow_17.idempotentTaskShortWindow,
98
- workflow_18.lower,
99
- workflow_19.returnExceptionsTask,
100
- workflow_20.runDetailTestWorkflow,
98
+ (0, workflow_15.createLoggingWorkflow)(hatchet_client_1.hatchet),
99
+ workflow_16.nonRetryableWorkflow,
100
+ workflow_17.failureWorkflow,
101
+ workflow_18.idempotentTask,
102
+ workflow_18.idempotentTaskShortWindow,
103
+ workflow_19.lower,
104
+ workflow_20.returnExceptionsTask,
105
+ workflow_21.runDetailTestWorkflow,
101
106
  e2e_workflows_1.helloWorld,
102
107
  e2e_workflows_1.helloWorldDurable,
103
- workflow_22.streamingTask,
104
- workflow_23.dagStream,
105
- workflow_23.longStream,
106
- workflow_24.timeoutTask,
107
- workflow_24.refreshTimeoutTask,
108
- workflow_25.webhookWorkflow,
109
- workflow_26.childIndexChild,
110
- workflow_26.childIndexParent,
111
- workflow_26.scenarioTask,
112
- workflow_26.orchestratorTask,
113
- workflow_27.supportAgent,
114
- workflow_27.triageTicket,
115
- workflow_27.generateReply,
116
- workflow_27.escalateTicket,
117
- workflow_28.welcomeEmail,
118
- workflow_29.pdfPipeline,
119
- workflow_21.batchSimple,
120
- workflow_21.batchKeyed,
121
- workflow_21.batchKeyedFailable,
122
- workflow_21.batchKeyedInterval,
123
- workflow_21.batchLarge,
124
- workflow_21.batchSingle,
125
- workflow_21.batchOrdered,
126
- workflow_21.batchBroadcast,
127
- workflow_21.batchCancel,
128
- workflow_21.child,
129
- workflow_21.childBatch,
130
- workflow_21.batchChildSpawn,
131
- workflow_21.batchChildBatchSpawn,
108
+ workflow_23.streamingTask,
109
+ workflow_24.dagStream,
110
+ workflow_24.longStream,
111
+ workflow_25.timeoutTask,
112
+ workflow_25.refreshTimeoutTask,
113
+ workflow_26.webhookWorkflow,
114
+ workflow_27.childIndexChild,
115
+ workflow_27.childIndexParent,
116
+ workflow_27.scenarioTask,
117
+ workflow_27.orchestratorTask,
118
+ workflow_28.supportAgent,
119
+ workflow_28.triageTicket,
120
+ workflow_28.generateReply,
121
+ workflow_28.escalateTicket,
122
+ workflow_29.welcomeEmail,
123
+ workflow_30.pdfPipeline,
124
+ workflow_22.batchSimple,
125
+ workflow_22.batchKeyed,
126
+ workflow_22.batchKeyedFailable,
127
+ workflow_22.batchKeyedInterval,
128
+ workflow_22.batchLarge,
129
+ workflow_22.batchSingle,
130
+ workflow_22.batchOrdered,
131
+ workflow_22.batchBroadcast,
132
+ workflow_22.batchCancel,
133
+ workflow_22.child,
134
+ workflow_22.childBatch,
135
+ workflow_22.batchChildSpawn,
136
+ workflow_22.batchChildBatchSpawn,
132
137
  ];
133
138
  function main() {
134
139
  return __awaiter(this, void 0, void 0, function* () {
package/version.d.ts CHANGED
@@ -1 +1 @@
1
- export declare const HATCHET_VERSION = "1.30.0";
1
+ export declare const HATCHET_VERSION = "1.30.1";
package/version.js CHANGED
@@ -1,4 +1,4 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.HATCHET_VERSION = void 0;
4
- exports.HATCHET_VERSION = '1.30.0';
4
+ exports.HATCHET_VERSION = '1.30.1';