@hatchet-dev/typescript-sdk 1.29.3 → 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.
- package/clients/dispatcher/action-listener.d.ts +1 -1
- package/clients/dispatcher/action-listener.js +6 -0
- package/clients/listeners/durable-listener/durable-listener-client.d.ts +6 -1
- package/clients/listeners/durable-listener/durable-listener-client.js +124 -37
- package/clients/rest/generated/Api.d.ts +6 -1
- package/clients/rest/generated/Api.js +1 -1
- package/clients/rest/generated/data-contracts.d.ts +9 -1
- package/clients/rest/generated/data-contracts.js +1 -0
- package/legacy/workflow.d.ts +4 -4
- package/legacy/workflow.js +1 -1
- package/package.json +1 -1
- package/protoc/v1/workflows.d.ts +17 -0
- package/protoc/v1/workflows.js +74 -2
- package/v1/client/worker/context.d.ts +2 -0
- package/v1/client/worker/context.js +26 -8
- package/v1/client/worker/eviction/eviction-manager.d.ts +1 -1
- package/v1/client/worker/eviction/eviction-manager.js +12 -5
- package/v1/client/worker/worker-internal.d.ts +2 -1
- package/v1/client/worker/worker-internal.js +52 -25
- package/v1/embedded.d.ts +36 -2
- package/v1/embedded.js +35 -8
- package/v1/examples/concurrency_cancel_queued_except_newest/workflow.d.ts +9 -0
- package/v1/examples/concurrency_cancel_queued_except_newest/workflow.js +47 -0
- package/v1/examples/concurrency_cancel_queued_except_oldest/workflow.d.ts +9 -0
- package/v1/examples/concurrency_cancel_queued_except_oldest/workflow.js +47 -0
- package/v1/examples/durable_callback_ordering/workflow.d.ts +42 -0
- package/v1/examples/durable_callback_ordering/workflow.js +95 -0
- package/v1/examples/durable_eviction/workflow.d.ts +5 -0
- package/v1/examples/durable_eviction/workflow.js +15 -1
- package/v1/examples/e2e-worker.js +99 -90
- package/version.d.ts +1 -1
- package/version.js +1 -1
|
@@ -7,7 +7,7 @@ declare enum ListenStrategy {
|
|
|
7
7
|
LISTEN_STRATEGY_V1 = 1,
|
|
8
8
|
LISTEN_STRATEGY_V2 = 2
|
|
9
9
|
}
|
|
10
|
-
export type ActionKey = `${string}/${number}`;
|
|
10
|
+
export type ActionKey = `${string}/${number}` | `${string}/${number}/${number}`;
|
|
11
11
|
export type Action = AssignedAction & {
|
|
12
12
|
readonly key: ActionKey;
|
|
13
13
|
};
|
|
@@ -90,6 +90,12 @@ function createAction(assignedAction) {
|
|
|
90
90
|
const action = assignedAction;
|
|
91
91
|
Object.defineProperty(action, 'key', {
|
|
92
92
|
get() {
|
|
93
|
+
// Durable task invocations each get a distinct key so a restored
|
|
94
|
+
// invocation never collides with the one it replaces in contexts,
|
|
95
|
+
// futures, or eviction state.
|
|
96
|
+
if (this.durableTaskInvocationCount !== undefined) {
|
|
97
|
+
return `${this.taskRunExternalId}/${this.retryCount}/${this.durableTaskInvocationCount}`;
|
|
98
|
+
}
|
|
93
99
|
return `${this.taskRunExternalId}/${this.retryCount}`;
|
|
94
100
|
},
|
|
95
101
|
enumerable: true,
|
|
@@ -65,7 +65,7 @@ export declare class DurableListenerClient {
|
|
|
65
65
|
private _requestNotify;
|
|
66
66
|
private _pendingEventAcks;
|
|
67
67
|
private _pendingCallbacks;
|
|
68
|
-
private
|
|
68
|
+
private _orderedCompletions;
|
|
69
69
|
private _pendingEvictionAcks;
|
|
70
70
|
private _receiveAbort;
|
|
71
71
|
private _statusInterval;
|
|
@@ -90,6 +90,11 @@ export declare class DurableListenerClient {
|
|
|
90
90
|
sendEvent(durableTaskExternalId: string, invocationCount: number, event: RunChildrenEvent): Promise<DurableTaskEventRunAck>;
|
|
91
91
|
sendEvent(durableTaskExternalId: string, invocationCount: number, event: WaitForEvent): Promise<DurableTaskEventWaitForAck>;
|
|
92
92
|
sendEvent(durableTaskExternalId: string, invocationCount: number, event: MemoEvent): Promise<DurableTaskEventMemoAck>;
|
|
93
|
+
private _drainsInProgress;
|
|
94
|
+
private _drainRerunRequests;
|
|
95
|
+
private _drainOrderedCompletions;
|
|
96
|
+
private _drainLoop;
|
|
97
|
+
consumeCallbackWithoutBlocking(durableTaskExternalId: string, invocationCount: number, branchId: number, nodeId: number): void;
|
|
93
98
|
waitForCallback(durableTaskExternalId: string, invocationCount: number, branchId: number, nodeId: number, opts?: {
|
|
94
99
|
signal?: AbortSignal;
|
|
95
100
|
}): Promise<DurableTaskEventLogEntryResult>;
|
|
@@ -38,6 +38,7 @@ const abort_controller_x_1 = require("abort-controller-x");
|
|
|
38
38
|
const hatchet_error_1 = require("../../../util/errors/hatchet-error");
|
|
39
39
|
const dispatcher_1 = require("../../../protoc/v1/dispatcher");
|
|
40
40
|
const non_determinism_error_1 = require("../../../util/errors/non-determinism-error");
|
|
41
|
+
const task_run_terminated_error_1 = require("../../../util/errors/task-run-terminated-error");
|
|
41
42
|
const abort_error_1 = require("../../../util/abort-error");
|
|
42
43
|
const sleep_1 = __importDefault(require("../../../util/sleep"));
|
|
43
44
|
const listener_severity_1 = require("../../dispatcher/listener-severity");
|
|
@@ -110,6 +111,9 @@ function eventLogEntryResultFromProto(proto) {
|
|
|
110
111
|
function ackKey(taskExtId, invocationCount) {
|
|
111
112
|
return `${taskExtId}:${invocationCount}`;
|
|
112
113
|
}
|
|
114
|
+
function completionOrderKey(taskExtId, invocationCount) {
|
|
115
|
+
return `${taskExtId}:${invocationCount}`;
|
|
116
|
+
}
|
|
113
117
|
function callbackKey(taskExtId, invocationCount, branchId, nodeId) {
|
|
114
118
|
return `${taskExtId}:${invocationCount}:${branchId}:${nodeId}`;
|
|
115
119
|
}
|
|
@@ -131,13 +135,15 @@ class DurableListenerClient {
|
|
|
131
135
|
this._requestQueue = [];
|
|
132
136
|
this._pendingEventAcks = new Map();
|
|
133
137
|
this._pendingCallbacks = new Map();
|
|
134
|
-
// Completions
|
|
135
|
-
//
|
|
136
|
-
//
|
|
137
|
-
//
|
|
138
|
-
this.
|
|
138
|
+
// Completions held in server delivery order until their waiters can
|
|
139
|
+
// consume them without overtaking an earlier-delivered completion.
|
|
140
|
+
// The TTL only garbage-collects queues for invocations that died
|
|
141
|
+
// without consuming everything; the server stall-evicts long before.
|
|
142
|
+
this._orderedCompletions = new TTLMap(300000);
|
|
139
143
|
this._pendingEvictionAcks = new Map();
|
|
140
144
|
this._consecutiveFailures = 0;
|
|
145
|
+
this._drainsInProgress = new Set();
|
|
146
|
+
this._drainRerunRequests = new Set();
|
|
141
147
|
this.config = config;
|
|
142
148
|
this.client = factory.create(dispatcher_1.V1DispatcherDefinition, channel);
|
|
143
149
|
this.logger = config.logger(`DurableListener`, config.log_level);
|
|
@@ -185,7 +191,7 @@ class DurableListenerClient {
|
|
|
185
191
|
this._receiveAbort.abort();
|
|
186
192
|
}
|
|
187
193
|
this._failPendingAcks(new Error('DurableListener stopped'));
|
|
188
|
-
this.
|
|
194
|
+
this._orderedCompletions.destroy();
|
|
189
195
|
});
|
|
190
196
|
}
|
|
191
197
|
_connect() {
|
|
@@ -332,10 +338,10 @@ class DurableListenerClient {
|
|
|
332
338
|
d.reject(exc);
|
|
333
339
|
}
|
|
334
340
|
this._pendingCallbacks.clear();
|
|
335
|
-
this.
|
|
341
|
+
this._orderedCompletions.clear();
|
|
336
342
|
}
|
|
337
343
|
_handleResponse(response) {
|
|
338
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _0, _1, _2;
|
|
344
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _0, _1, _2, _3, _4, _5;
|
|
339
345
|
if (response.registerWorker) {
|
|
340
346
|
// registration acknowledged
|
|
341
347
|
}
|
|
@@ -396,14 +402,28 @@ class DurableListenerClient {
|
|
|
396
402
|
const { ref } = completed;
|
|
397
403
|
const key = callbackKey((_o = ref === null || ref === void 0 ? void 0 : ref.durableTaskExternalId) !== null && _o !== void 0 ? _o : '', (_p = ref === null || ref === void 0 ? void 0 : ref.invocationCount) !== null && _p !== void 0 ? _p : 0, (_q = ref === null || ref === void 0 ? void 0 : ref.branchId) !== null && _q !== void 0 ? _q : 0, (_r = ref === null || ref === void 0 ? void 0 : ref.nodeId) !== null && _r !== void 0 ? _r : 0);
|
|
398
404
|
const result = eventLogEntryResultFromProto(completed);
|
|
399
|
-
const
|
|
400
|
-
|
|
401
|
-
pending
|
|
402
|
-
|
|
403
|
-
}
|
|
404
|
-
|
|
405
|
-
|
|
405
|
+
const orderKey = completionOrderKey((_s = ref === null || ref === void 0 ? void 0 : ref.durableTaskExternalId) !== null && _s !== void 0 ? _s : '', (_t = ref === null || ref === void 0 ? void 0 : ref.invocationCount) !== null && _t !== void 0 ? _t : 0);
|
|
406
|
+
const queue = (_u = this._orderedCompletions.get(orderKey)) !== null && _u !== void 0 ? _u : {
|
|
407
|
+
pending: [],
|
|
408
|
+
delivered: new Set(),
|
|
409
|
+
};
|
|
410
|
+
if (!queue.delivered.has(key)) {
|
|
411
|
+
queue.delivered.add(key);
|
|
412
|
+
queue.pending.push({ key, result });
|
|
413
|
+
}
|
|
414
|
+
else if (queue.pending.every((entry) => entry.key !== key)) {
|
|
415
|
+
// Re-delivery of a completion that already drained (reconnect,
|
|
416
|
+
// worker-status re-send, or a repeated wait on a node deduped by
|
|
417
|
+
// child key). Its satisfied order was released before anything
|
|
418
|
+
// still queued, so handing it straight to a waiter keeps order.
|
|
419
|
+
const redelivered = this._pendingCallbacks.get(key);
|
|
420
|
+
if (redelivered) {
|
|
421
|
+
this._pendingCallbacks.delete(key);
|
|
422
|
+
redelivered.resolve(result);
|
|
423
|
+
}
|
|
406
424
|
}
|
|
425
|
+
this._orderedCompletions.set(orderKey, queue);
|
|
426
|
+
this._drainOrderedCompletions(orderKey);
|
|
407
427
|
}
|
|
408
428
|
else if (response.evictionAck) {
|
|
409
429
|
const ack = response.evictionAck;
|
|
@@ -418,34 +438,36 @@ class DurableListenerClient {
|
|
|
418
438
|
const evict = response.serverEvict;
|
|
419
439
|
this.logger.info(`received server eviction notification for task ${evict.durableTaskExternalId} ` +
|
|
420
440
|
`invocation ${evict.invocationCount}: ${evict.reason}`);
|
|
421
|
-
|
|
441
|
+
// onServerEvict aborts the run first so waiters settle as aborted
|
|
442
|
+
// (eviction) rather than with cleanup's generic rejection.
|
|
422
443
|
if (this.onServerEvict) {
|
|
423
444
|
this.onServerEvict(evict.durableTaskExternalId, evict.invocationCount);
|
|
424
445
|
}
|
|
446
|
+
this.cleanupTaskState(evict.durableTaskExternalId, evict.invocationCount);
|
|
425
447
|
}
|
|
426
448
|
else if (response.error) {
|
|
427
449
|
const { error } = response;
|
|
428
450
|
const { ref } = error;
|
|
429
451
|
let exc;
|
|
430
452
|
if (error.errorType === dispatcher_1.DurableTaskErrorType.DURABLE_TASK_ERROR_TYPE_NONDETERMINISM) {
|
|
431
|
-
exc = new non_determinism_error_1.NonDeterminismError((
|
|
453
|
+
exc = new non_determinism_error_1.NonDeterminismError((_v = ref === null || ref === void 0 ? void 0 : ref.durableTaskExternalId) !== null && _v !== void 0 ? _v : '', (_w = ref === null || ref === void 0 ? void 0 : ref.invocationCount) !== null && _w !== void 0 ? _w : 0, (_x = ref === null || ref === void 0 ? void 0 : ref.nodeId) !== null && _x !== void 0 ? _x : 0, error.errorMessage);
|
|
432
454
|
}
|
|
433
455
|
else {
|
|
434
456
|
exc = new Error(`Unspecified durable task error: ${error.errorMessage} (type: ${error.errorType})`);
|
|
435
457
|
}
|
|
436
|
-
const eAckKey = ackKey((
|
|
458
|
+
const eAckKey = ackKey((_y = ref === null || ref === void 0 ? void 0 : ref.durableTaskExternalId) !== null && _y !== void 0 ? _y : '', (_z = ref === null || ref === void 0 ? void 0 : ref.invocationCount) !== null && _z !== void 0 ? _z : 0);
|
|
437
459
|
const pendingAck = this._pendingEventAcks.get(eAckKey);
|
|
438
460
|
if (pendingAck) {
|
|
439
461
|
pendingAck.reject(exc);
|
|
440
462
|
this._pendingEventAcks.delete(eAckKey);
|
|
441
463
|
}
|
|
442
|
-
const eCbKey = callbackKey((
|
|
464
|
+
const eCbKey = callbackKey((_0 = ref === null || ref === void 0 ? void 0 : ref.durableTaskExternalId) !== null && _0 !== void 0 ? _0 : '', (_1 = ref === null || ref === void 0 ? void 0 : ref.invocationCount) !== null && _1 !== void 0 ? _1 : 0, (_2 = ref === null || ref === void 0 ? void 0 : ref.branchId) !== null && _2 !== void 0 ? _2 : 0, (_3 = ref === null || ref === void 0 ? void 0 : ref.nodeId) !== null && _3 !== void 0 ? _3 : 0);
|
|
443
465
|
const pendingCb = this._pendingCallbacks.get(eCbKey);
|
|
444
466
|
if (pendingCb) {
|
|
445
467
|
pendingCb.reject(exc);
|
|
446
468
|
this._pendingCallbacks.delete(eCbKey);
|
|
447
469
|
}
|
|
448
|
-
const eEvKey = evictionKey((
|
|
470
|
+
const eEvKey = evictionKey((_4 = ref === null || ref === void 0 ? void 0 : ref.durableTaskExternalId) !== null && _4 !== void 0 ? _4 : '', (_5 = ref === null || ref === void 0 ? void 0 : ref.invocationCount) !== null && _5 !== void 0 ? _5 : 0);
|
|
449
471
|
const pendingEv = this._pendingEvictionAcks.get(eEvKey);
|
|
450
472
|
if (pendingEv) {
|
|
451
473
|
pendingEv.reject(exc);
|
|
@@ -498,26 +520,87 @@ class DurableListenerClient {
|
|
|
498
520
|
return d.promise;
|
|
499
521
|
});
|
|
500
522
|
}
|
|
523
|
+
// Hands queued completions to their waiters strictly in delivery order.
|
|
524
|
+
// Stops at the first completion whose waiter has not registered yet:
|
|
525
|
+
// releasing a later completion first would resume its coroutine ahead of
|
|
526
|
+
// the recorded order and diverge the re-emitted event sequence.
|
|
527
|
+
_drainOrderedCompletions(orderKey) {
|
|
528
|
+
if (this._drainsInProgress.has(orderKey)) {
|
|
529
|
+
this._drainRerunRequests.add(orderKey);
|
|
530
|
+
return;
|
|
531
|
+
}
|
|
532
|
+
this._drainsInProgress.add(orderKey);
|
|
533
|
+
void this._drainLoop(orderKey).finally(() => {
|
|
534
|
+
this._drainsInProgress.delete(orderKey);
|
|
535
|
+
if (this._drainRerunRequests.delete(orderKey)) {
|
|
536
|
+
this._drainOrderedCompletions(orderKey);
|
|
537
|
+
}
|
|
538
|
+
});
|
|
539
|
+
}
|
|
540
|
+
_drainLoop(orderKey) {
|
|
541
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
542
|
+
let released = false;
|
|
543
|
+
while (true) {
|
|
544
|
+
if (released) {
|
|
545
|
+
// A macrotask boundary between releases lets the just-resumed waiter
|
|
546
|
+
// run to its next suspension point (emitting its next durable event)
|
|
547
|
+
// before the following completion is released. Resolving several
|
|
548
|
+
// deferreds back-to-back would instead resume them in the order their
|
|
549
|
+
// continuations happened to be attached, not in release order.
|
|
550
|
+
yield new Promise((resolve) => {
|
|
551
|
+
setImmediate(resolve);
|
|
552
|
+
});
|
|
553
|
+
}
|
|
554
|
+
const queue = this._orderedCompletions.get(orderKey);
|
|
555
|
+
if (!queue || queue.pending.length === 0)
|
|
556
|
+
return;
|
|
557
|
+
const [head] = queue.pending;
|
|
558
|
+
const waiter = this._pendingCallbacks.get(head.key);
|
|
559
|
+
if (!waiter)
|
|
560
|
+
return;
|
|
561
|
+
queue.pending.shift();
|
|
562
|
+
this._pendingCallbacks.delete(head.key);
|
|
563
|
+
waiter.resolve(head.result);
|
|
564
|
+
released = true;
|
|
565
|
+
}
|
|
566
|
+
});
|
|
567
|
+
}
|
|
568
|
+
// A memo's value arrives in its ack and its completion is never awaited,
|
|
569
|
+
// but the server still delivers that completion on replay, in recorded
|
|
570
|
+
// order. Register a waiter that nothing blocks on so the drain can hand the
|
|
571
|
+
// completion through instead of stalling the queue at it forever.
|
|
572
|
+
consumeCallbackWithoutBlocking(durableTaskExternalId, invocationCount, branchId, nodeId) {
|
|
573
|
+
const key = callbackKey(durableTaskExternalId, invocationCount, branchId, nodeId);
|
|
574
|
+
if (this._pendingCallbacks.has(key))
|
|
575
|
+
return;
|
|
576
|
+
const d = deferred();
|
|
577
|
+
d.promise.catch(() => { });
|
|
578
|
+
this._pendingCallbacks.set(key, d);
|
|
579
|
+
this._drainOrderedCompletions(completionOrderKey(durableTaskExternalId, invocationCount));
|
|
580
|
+
}
|
|
501
581
|
waitForCallback(durableTaskExternalId, invocationCount, branchId, nodeId, opts) {
|
|
502
582
|
return __awaiter(this, void 0, void 0, function* () {
|
|
503
|
-
const
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
this._bufferedCompletions.delete(key);
|
|
507
|
-
return early;
|
|
583
|
+
const signal = opts === null || opts === void 0 ? void 0 : opts.signal;
|
|
584
|
+
if (signal === null || signal === void 0 ? void 0 : signal.aborted) {
|
|
585
|
+
return Promise.reject((0, abort_error_1.createAbortError)('Operation cancelled by AbortSignal'));
|
|
508
586
|
}
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
587
|
+
const key = callbackKey(durableTaskExternalId, invocationCount, branchId, nodeId);
|
|
588
|
+
let d = this._pendingCallbacks.get(key);
|
|
589
|
+
if (!d) {
|
|
590
|
+
d = deferred();
|
|
591
|
+
// A deferred can outlive its waiters (eviction aborts the caller while
|
|
592
|
+
// the entry stays registered); a later cleanupTaskState rejection must
|
|
593
|
+
// not crash the process as an unhandled rejection.
|
|
594
|
+
d.promise.catch(() => { });
|
|
595
|
+
this._pendingCallbacks.set(key, d);
|
|
596
|
+
this._drainOrderedCompletions(completionOrderKey(durableTaskExternalId, invocationCount));
|
|
597
|
+
if (this._pendingCallbacks.has(key)) {
|
|
598
|
+
this._pollWorkerStatus();
|
|
599
|
+
}
|
|
512
600
|
}
|
|
513
|
-
const d = this._pendingCallbacks.get(key);
|
|
514
|
-
const signal = opts === null || opts === void 0 ? void 0 : opts.signal;
|
|
515
601
|
if (!signal) {
|
|
516
602
|
return d.promise;
|
|
517
603
|
}
|
|
518
|
-
if (signal.aborted) {
|
|
519
|
-
return Promise.reject((0, abort_error_1.createAbortError)('Operation cancelled by AbortSignal'));
|
|
520
|
-
}
|
|
521
604
|
return new Promise((resolve, reject) => {
|
|
522
605
|
let settled = false;
|
|
523
606
|
const onAbort = () => {
|
|
@@ -544,24 +627,28 @@ class DurableListenerClient {
|
|
|
544
627
|
});
|
|
545
628
|
}
|
|
546
629
|
cleanupTaskState(durableTaskExternalId, invocationCount) {
|
|
630
|
+
// Rejecting with TaskRunTerminatedError marks any coroutine still blocked
|
|
631
|
+
// on this invocation's state as evicted rather than failed, matching
|
|
632
|
+
// Python's future cancellation on cleanup.
|
|
633
|
+
const evicted = () => new task_run_terminated_error_1.TaskRunTerminatedError('evicted', 'task state cleaned up');
|
|
547
634
|
for (const [k, d] of this._pendingCallbacks) {
|
|
548
635
|
const parts = k.split(':');
|
|
549
636
|
if (parts[0] === durableTaskExternalId && parseInt(parts[1], 10) <= invocationCount) {
|
|
550
|
-
d.reject(
|
|
637
|
+
d.reject(evicted());
|
|
551
638
|
this._pendingCallbacks.delete(k);
|
|
552
639
|
}
|
|
553
640
|
}
|
|
554
641
|
for (const [k, d] of this._pendingEventAcks) {
|
|
555
642
|
const parts = k.split(':');
|
|
556
643
|
if (parts[0] === durableTaskExternalId && parseInt(parts[1], 10) <= invocationCount) {
|
|
557
|
-
d.reject(
|
|
644
|
+
d.reject(evicted());
|
|
558
645
|
this._pendingEventAcks.delete(k);
|
|
559
646
|
}
|
|
560
647
|
}
|
|
561
|
-
for (const k of this.
|
|
648
|
+
for (const k of this._orderedCompletions.keys()) {
|
|
562
649
|
const parts = k.split(':');
|
|
563
650
|
if (parts[0] === durableTaskExternalId && parseInt(parts[1], 10) <= invocationCount) {
|
|
564
|
-
this.
|
|
651
|
+
this._orderedCompletions.delete(k);
|
|
565
652
|
}
|
|
566
653
|
}
|
|
567
654
|
}
|
|
@@ -350,7 +350,10 @@ export declare class Api<SecurityDataType = unknown> extends HttpClient<Security
|
|
|
350
350
|
* @request GET:/api/v1/stable/workflow-runs/{v1-workflow-run}
|
|
351
351
|
* @secure
|
|
352
352
|
*/
|
|
353
|
-
v1WorkflowRunGet: (v1WorkflowRun: string,
|
|
353
|
+
v1WorkflowRunGet: (v1WorkflowRun: string, query?: {
|
|
354
|
+
/** Whether to include the DAG orchestrator's task events, which are hidden by default */
|
|
355
|
+
includeOrchestratorEvents?: boolean;
|
|
356
|
+
}, params?: RequestParams) => Promise<import("axios").AxiosResponse<V1WorkflowRunDetails, any, {}>>;
|
|
354
357
|
/**
|
|
355
358
|
* @description Get the status of a workflow run.
|
|
356
359
|
*
|
|
@@ -2024,6 +2027,8 @@ export declare class Api<SecurityDataType = unknown> extends HttpClient<Security
|
|
|
2024
2027
|
statuses?: WorkerStatus[];
|
|
2025
2028
|
/** Filter by worker labels */
|
|
2026
2029
|
labels?: string[];
|
|
2030
|
+
/** Whether to include engine-managed operator workers, which are hidden by default */
|
|
2031
|
+
includeOperators?: boolean;
|
|
2027
2032
|
}, params?: RequestParams) => Promise<import("axios").AxiosResponse<WorkerList, any, {}>>;
|
|
2028
2033
|
/**
|
|
2029
2034
|
* @description Update a worker
|
|
@@ -175,7 +175,7 @@ class Api extends http_client_1.HttpClient {
|
|
|
175
175
|
* @request GET:/api/v1/stable/workflow-runs/{v1-workflow-run}
|
|
176
176
|
* @secure
|
|
177
177
|
*/
|
|
178
|
-
this.v1WorkflowRunGet = (v1WorkflowRun, params = {}) => this.request(Object.assign({ path: `/api/v1/stable/workflow-runs/${v1WorkflowRun}`, method: 'GET', secure: true, format: 'json' }, params));
|
|
178
|
+
this.v1WorkflowRunGet = (v1WorkflowRun, query, params = {}) => this.request(Object.assign({ path: `/api/v1/stable/workflow-runs/${v1WorkflowRun}`, method: 'GET', query: query, secure: true, format: 'json' }, params));
|
|
179
179
|
/**
|
|
180
180
|
* @description Get the status of a workflow run.
|
|
181
181
|
*
|
|
@@ -25,7 +25,8 @@ export declare enum PullRequestState {
|
|
|
25
25
|
export declare enum FeatureFlagId {
|
|
26
26
|
TenantLogWorkflowFilterEnabled = "tenant-log-workflow-filter-enabled",
|
|
27
27
|
TraceMinimapEnabled = "trace-minimap-enabled",
|
|
28
|
-
OrganizationSsoEnabled = "organization-sso-enabled"
|
|
28
|
+
OrganizationSsoEnabled = "organization-sso-enabled",
|
|
29
|
+
OperatorDetailsEnabled = "operator-details-enabled"
|
|
29
30
|
}
|
|
30
31
|
export declare enum WebhookWorkerRequestMethod {
|
|
31
32
|
GET = "GET",
|
|
@@ -2328,6 +2329,13 @@ export interface Worker {
|
|
|
2328
2329
|
*/
|
|
2329
2330
|
webhookId?: string;
|
|
2330
2331
|
runtimeInfo?: WorkerRuntimeInfo;
|
|
2332
|
+
/**
|
|
2333
|
+
* The id of the operator that owns this worker, if it is an engine-managed operator worker.
|
|
2334
|
+
* @format uuid
|
|
2335
|
+
*/
|
|
2336
|
+
operatorId?: string;
|
|
2337
|
+
/** The number of durable task runs owned by this operator that are currently evicted while waiting on durable events. Evicted runs hold no slots. Only set for operator workers. */
|
|
2338
|
+
evictedDurableTaskCount?: number;
|
|
2331
2339
|
}
|
|
2332
2340
|
export interface WorkerList {
|
|
2333
2341
|
pagination?: PaginationResponse;
|
|
@@ -46,6 +46,7 @@ var FeatureFlagId;
|
|
|
46
46
|
FeatureFlagId["TenantLogWorkflowFilterEnabled"] = "tenant-log-workflow-filter-enabled";
|
|
47
47
|
FeatureFlagId["TraceMinimapEnabled"] = "trace-minimap-enabled";
|
|
48
48
|
FeatureFlagId["OrganizationSsoEnabled"] = "organization-sso-enabled";
|
|
49
|
+
FeatureFlagId["OperatorDetailsEnabled"] = "operator-details-enabled";
|
|
49
50
|
})(FeatureFlagId || (exports.FeatureFlagId = FeatureFlagId = {}));
|
|
50
51
|
var WebhookWorkerRequestMethod;
|
|
51
52
|
(function (WebhookWorkerRequestMethod) {
|
package/legacy/workflow.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as z from 'zod/v4';
|
|
2
2
|
import { CreateStep } from '../step';
|
|
3
|
-
import { ConcurrencyLimitStrategy as PbConcurrencyLimitStrategy, StickyStrategy as PbStickyStrategy } from '../protoc/workflows';
|
|
3
|
+
import { ConcurrencyLimitStrategy as PbConcurrencyLimitStrategy, StickyStrategy as PbStickyStrategy } from '../protoc/v1/workflows';
|
|
4
4
|
declare const StepsSchema: z.ZodArray<z.ZodObject<{
|
|
5
5
|
name: z.ZodString;
|
|
6
6
|
parents: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
@@ -12,7 +12,7 @@ declare const StepsSchema: z.ZodArray<z.ZodObject<{
|
|
|
12
12
|
dynamicKey: z.ZodOptional<z.ZodString>;
|
|
13
13
|
units: z.ZodUnion<readonly [z.ZodNumber, z.ZodString]>;
|
|
14
14
|
limit: z.ZodOptional<z.ZodUnion<readonly [z.ZodNumber, z.ZodString]>>;
|
|
15
|
-
duration: z.ZodOptional<z.ZodEnum<typeof import("
|
|
15
|
+
duration: z.ZodOptional<z.ZodEnum<typeof import("..").RateLimitDuration>>;
|
|
16
16
|
}, z.core.$strip>>>;
|
|
17
17
|
worker_labels: z.ZodOptional<z.ZodRecord<z.ZodAny, z.ZodLazy<z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodObject<{
|
|
18
18
|
value: z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>;
|
|
@@ -63,7 +63,7 @@ export declare const CreateWorkflowSchema: z.ZodObject<{
|
|
|
63
63
|
dynamicKey: z.ZodOptional<z.ZodString>;
|
|
64
64
|
units: z.ZodUnion<readonly [z.ZodNumber, z.ZodString]>;
|
|
65
65
|
limit: z.ZodOptional<z.ZodUnion<readonly [z.ZodNumber, z.ZodString]>>;
|
|
66
|
-
duration: z.ZodOptional<z.ZodEnum<typeof import("
|
|
66
|
+
duration: z.ZodOptional<z.ZodEnum<typeof import("..").RateLimitDuration>>;
|
|
67
67
|
}, z.core.$strip>>>;
|
|
68
68
|
worker_labels: z.ZodOptional<z.ZodRecord<z.ZodAny, z.ZodLazy<z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodObject<{
|
|
69
69
|
value: z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>;
|
|
@@ -87,7 +87,7 @@ export declare const CreateWorkflowSchema: z.ZodObject<{
|
|
|
87
87
|
dynamicKey: z.ZodOptional<z.ZodString>;
|
|
88
88
|
units: z.ZodUnion<readonly [z.ZodNumber, z.ZodString]>;
|
|
89
89
|
limit: z.ZodOptional<z.ZodUnion<readonly [z.ZodNumber, z.ZodString]>>;
|
|
90
|
-
duration: z.ZodOptional<z.ZodEnum<typeof import("
|
|
90
|
+
duration: z.ZodOptional<z.ZodEnum<typeof import("..").RateLimitDuration>>;
|
|
91
91
|
}, z.core.$strip>>>;
|
|
92
92
|
worker_labels: z.ZodOptional<z.ZodRecord<z.ZodAny, z.ZodLazy<z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodObject<{
|
|
93
93
|
value: z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>;
|
package/legacy/workflow.js
CHANGED
|
@@ -36,7 +36,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
36
36
|
exports.CreateWorkflowSchema = exports.StickyStrategy = exports.HatchetTimeoutSchema = exports.WorkflowConcurrency = exports.ConcurrencyLimitStrategy = void 0;
|
|
37
37
|
const z = __importStar(require("zod/v4"));
|
|
38
38
|
const step_1 = require("../step");
|
|
39
|
-
const workflows_1 = require("../protoc/workflows");
|
|
39
|
+
const workflows_1 = require("../protoc/v1/workflows");
|
|
40
40
|
const CronConfigSchema = z.object({
|
|
41
41
|
cron: z.string(),
|
|
42
42
|
event: z.undefined().optional(),
|
package/package.json
CHANGED
package/protoc/v1/workflows.d.ts
CHANGED
|
@@ -48,6 +48,8 @@ export declare enum ConcurrencyLimitStrategy {
|
|
|
48
48
|
QUEUE_NEWEST = 2,
|
|
49
49
|
GROUP_ROUND_ROBIN = 3,
|
|
50
50
|
CANCEL_NEWEST = 4,
|
|
51
|
+
CANCEL_QUEUED_EXCEPT_NEWEST = 5,
|
|
52
|
+
CANCEL_QUEUED_EXCEPT_OLDEST = 6,
|
|
51
53
|
UNRECOGNIZED = -1
|
|
52
54
|
}
|
|
53
55
|
export declare function concurrencyLimitStrategyFromJSON(object: any): ConcurrencyLimitStrategy;
|
|
@@ -168,6 +170,15 @@ export interface DefaultFilter {
|
|
|
168
170
|
/** (optional) the payload for the filter, if any. A JSON object as a string. */
|
|
169
171
|
payload?: Uint8Array | undefined;
|
|
170
172
|
}
|
|
173
|
+
/**
|
|
174
|
+
* Concurrency declares one entry in a concurrency chain. Entries are processed in array
|
|
175
|
+
* order, so a tenant-scoped entry may come before or after a workflow-scoped one.
|
|
176
|
+
*
|
|
177
|
+
* A tenant-scoped entry (is_tenant_scoped = true) defines (or updates in place) a strategy
|
|
178
|
+
* shared across workflows, keyed by name: every task declaring the same name consumes the
|
|
179
|
+
* same concurrency limit. Registrations whose chains order the same tenant-scoped
|
|
180
|
+
* strategies inconsistently are rejected, since inconsistent orders can deadlock.
|
|
181
|
+
*/
|
|
171
182
|
export interface Concurrency {
|
|
172
183
|
/** (required) the expression to use for concurrency */
|
|
173
184
|
expression: string;
|
|
@@ -175,6 +186,12 @@ export interface Concurrency {
|
|
|
175
186
|
maxRuns?: number | undefined;
|
|
176
187
|
/** (optional) the strategy to use when the concurrency limit is reached, default CANCEL_IN_PROGRESS */
|
|
177
188
|
limitStrategy?: ConcurrencyLimitStrategy | undefined;
|
|
189
|
+
/** (required when is_tenant_scoped) the strategy name; unique per tenant for tenant-scoped strategies */
|
|
190
|
+
name?: string | undefined;
|
|
191
|
+
/** (optional) when true, the entry is a tenant-scoped strategy shared across workflows, default false */
|
|
192
|
+
isTenantScoped?: boolean | undefined;
|
|
193
|
+
/** (optional) CEL expression over task input returning the max runs for that task's concurrency group; the group's effective limit is the value from its most recently created task. Overrides max_runs per group; a non-integer or negative result fails the task, and 0 holds the group until a newer task raises the limit */
|
|
194
|
+
maxRunsExpression?: string | undefined;
|
|
178
195
|
}
|
|
179
196
|
export interface TaskBatchConfig {
|
|
180
197
|
/** (required) maximum items per batch */
|
package/protoc/v1/workflows.js
CHANGED
|
@@ -209,6 +209,8 @@ var ConcurrencyLimitStrategy;
|
|
|
209
209
|
ConcurrencyLimitStrategy[ConcurrencyLimitStrategy["QUEUE_NEWEST"] = 2] = "QUEUE_NEWEST";
|
|
210
210
|
ConcurrencyLimitStrategy[ConcurrencyLimitStrategy["GROUP_ROUND_ROBIN"] = 3] = "GROUP_ROUND_ROBIN";
|
|
211
211
|
ConcurrencyLimitStrategy[ConcurrencyLimitStrategy["CANCEL_NEWEST"] = 4] = "CANCEL_NEWEST";
|
|
212
|
+
ConcurrencyLimitStrategy[ConcurrencyLimitStrategy["CANCEL_QUEUED_EXCEPT_NEWEST"] = 5] = "CANCEL_QUEUED_EXCEPT_NEWEST";
|
|
213
|
+
ConcurrencyLimitStrategy[ConcurrencyLimitStrategy["CANCEL_QUEUED_EXCEPT_OLDEST"] = 6] = "CANCEL_QUEUED_EXCEPT_OLDEST";
|
|
212
214
|
ConcurrencyLimitStrategy[ConcurrencyLimitStrategy["UNRECOGNIZED"] = -1] = "UNRECOGNIZED";
|
|
213
215
|
})(ConcurrencyLimitStrategy || (exports.ConcurrencyLimitStrategy = ConcurrencyLimitStrategy = {}));
|
|
214
216
|
function concurrencyLimitStrategyFromJSON(object) {
|
|
@@ -228,6 +230,12 @@ function concurrencyLimitStrategyFromJSON(object) {
|
|
|
228
230
|
case 4:
|
|
229
231
|
case 'CANCEL_NEWEST':
|
|
230
232
|
return ConcurrencyLimitStrategy.CANCEL_NEWEST;
|
|
233
|
+
case 5:
|
|
234
|
+
case 'CANCEL_QUEUED_EXCEPT_NEWEST':
|
|
235
|
+
return ConcurrencyLimitStrategy.CANCEL_QUEUED_EXCEPT_NEWEST;
|
|
236
|
+
case 6:
|
|
237
|
+
case 'CANCEL_QUEUED_EXCEPT_OLDEST':
|
|
238
|
+
return ConcurrencyLimitStrategy.CANCEL_QUEUED_EXCEPT_OLDEST;
|
|
231
239
|
case -1:
|
|
232
240
|
case 'UNRECOGNIZED':
|
|
233
241
|
default:
|
|
@@ -246,6 +254,10 @@ function concurrencyLimitStrategyToJSON(object) {
|
|
|
246
254
|
return 'GROUP_ROUND_ROBIN';
|
|
247
255
|
case ConcurrencyLimitStrategy.CANCEL_NEWEST:
|
|
248
256
|
return 'CANCEL_NEWEST';
|
|
257
|
+
case ConcurrencyLimitStrategy.CANCEL_QUEUED_EXCEPT_NEWEST:
|
|
258
|
+
return 'CANCEL_QUEUED_EXCEPT_NEWEST';
|
|
259
|
+
case ConcurrencyLimitStrategy.CANCEL_QUEUED_EXCEPT_OLDEST:
|
|
260
|
+
return 'CANCEL_QUEUED_EXCEPT_OLDEST';
|
|
249
261
|
case ConcurrencyLimitStrategy.UNRECOGNIZED:
|
|
250
262
|
default:
|
|
251
263
|
return 'UNRECOGNIZED';
|
|
@@ -1782,7 +1794,14 @@ exports.DefaultFilter = {
|
|
|
1782
1794
|
},
|
|
1783
1795
|
};
|
|
1784
1796
|
function createBaseConcurrency() {
|
|
1785
|
-
return {
|
|
1797
|
+
return {
|
|
1798
|
+
expression: '',
|
|
1799
|
+
maxRuns: undefined,
|
|
1800
|
+
limitStrategy: undefined,
|
|
1801
|
+
name: undefined,
|
|
1802
|
+
isTenantScoped: undefined,
|
|
1803
|
+
maxRunsExpression: undefined,
|
|
1804
|
+
};
|
|
1786
1805
|
}
|
|
1787
1806
|
exports.Concurrency = {
|
|
1788
1807
|
encode(message, writer = new wire_1.BinaryWriter()) {
|
|
@@ -1795,6 +1814,15 @@ exports.Concurrency = {
|
|
|
1795
1814
|
if (message.limitStrategy !== undefined) {
|
|
1796
1815
|
writer.uint32(24).int32(message.limitStrategy);
|
|
1797
1816
|
}
|
|
1817
|
+
if (message.name !== undefined) {
|
|
1818
|
+
writer.uint32(34).string(message.name);
|
|
1819
|
+
}
|
|
1820
|
+
if (message.isTenantScoped !== undefined) {
|
|
1821
|
+
writer.uint32(40).bool(message.isTenantScoped);
|
|
1822
|
+
}
|
|
1823
|
+
if (message.maxRunsExpression !== undefined) {
|
|
1824
|
+
writer.uint32(50).string(message.maxRunsExpression);
|
|
1825
|
+
}
|
|
1798
1826
|
return writer;
|
|
1799
1827
|
},
|
|
1800
1828
|
decode(input, length) {
|
|
@@ -1825,6 +1853,27 @@ exports.Concurrency = {
|
|
|
1825
1853
|
message.limitStrategy = reader.int32();
|
|
1826
1854
|
continue;
|
|
1827
1855
|
}
|
|
1856
|
+
case 4: {
|
|
1857
|
+
if (tag !== 34) {
|
|
1858
|
+
break;
|
|
1859
|
+
}
|
|
1860
|
+
message.name = reader.string();
|
|
1861
|
+
continue;
|
|
1862
|
+
}
|
|
1863
|
+
case 5: {
|
|
1864
|
+
if (tag !== 40) {
|
|
1865
|
+
break;
|
|
1866
|
+
}
|
|
1867
|
+
message.isTenantScoped = reader.bool();
|
|
1868
|
+
continue;
|
|
1869
|
+
}
|
|
1870
|
+
case 6: {
|
|
1871
|
+
if (tag !== 50) {
|
|
1872
|
+
break;
|
|
1873
|
+
}
|
|
1874
|
+
message.maxRunsExpression = reader.string();
|
|
1875
|
+
continue;
|
|
1876
|
+
}
|
|
1828
1877
|
}
|
|
1829
1878
|
if ((tag & 7) === 4 || tag === 0) {
|
|
1830
1879
|
break;
|
|
@@ -1846,6 +1895,17 @@ exports.Concurrency = {
|
|
|
1846
1895
|
: isSet(object.limit_strategy)
|
|
1847
1896
|
? concurrencyLimitStrategyFromJSON(object.limit_strategy)
|
|
1848
1897
|
: undefined,
|
|
1898
|
+
name: isSet(object.name) ? globalThis.String(object.name) : undefined,
|
|
1899
|
+
isTenantScoped: isSet(object.isTenantScoped)
|
|
1900
|
+
? globalThis.Boolean(object.isTenantScoped)
|
|
1901
|
+
: isSet(object.is_tenant_scoped)
|
|
1902
|
+
? globalThis.Boolean(object.is_tenant_scoped)
|
|
1903
|
+
: undefined,
|
|
1904
|
+
maxRunsExpression: isSet(object.maxRunsExpression)
|
|
1905
|
+
? globalThis.String(object.maxRunsExpression)
|
|
1906
|
+
: isSet(object.max_runs_expression)
|
|
1907
|
+
? globalThis.String(object.max_runs_expression)
|
|
1908
|
+
: undefined,
|
|
1849
1909
|
};
|
|
1850
1910
|
},
|
|
1851
1911
|
toJSON(message) {
|
|
@@ -1859,17 +1919,29 @@ exports.Concurrency = {
|
|
|
1859
1919
|
if (message.limitStrategy !== undefined) {
|
|
1860
1920
|
obj.limitStrategy = concurrencyLimitStrategyToJSON(message.limitStrategy);
|
|
1861
1921
|
}
|
|
1922
|
+
if (message.name !== undefined) {
|
|
1923
|
+
obj.name = message.name;
|
|
1924
|
+
}
|
|
1925
|
+
if (message.isTenantScoped !== undefined) {
|
|
1926
|
+
obj.isTenantScoped = message.isTenantScoped;
|
|
1927
|
+
}
|
|
1928
|
+
if (message.maxRunsExpression !== undefined) {
|
|
1929
|
+
obj.maxRunsExpression = message.maxRunsExpression;
|
|
1930
|
+
}
|
|
1862
1931
|
return obj;
|
|
1863
1932
|
},
|
|
1864
1933
|
create(base) {
|
|
1865
1934
|
return exports.Concurrency.fromPartial(base !== null && base !== void 0 ? base : {});
|
|
1866
1935
|
},
|
|
1867
1936
|
fromPartial(object) {
|
|
1868
|
-
var _a, _b, _c;
|
|
1937
|
+
var _a, _b, _c, _d, _e, _f;
|
|
1869
1938
|
const message = createBaseConcurrency();
|
|
1870
1939
|
message.expression = (_a = object.expression) !== null && _a !== void 0 ? _a : '';
|
|
1871
1940
|
message.maxRuns = (_b = object.maxRuns) !== null && _b !== void 0 ? _b : undefined;
|
|
1872
1941
|
message.limitStrategy = (_c = object.limitStrategy) !== null && _c !== void 0 ? _c : undefined;
|
|
1942
|
+
message.name = (_d = object.name) !== null && _d !== void 0 ? _d : undefined;
|
|
1943
|
+
message.isTenantScoped = (_e = object.isTenantScoped) !== null && _e !== void 0 ? _e : undefined;
|
|
1944
|
+
message.maxRunsExpression = (_f = object.maxRunsExpression) !== null && _f !== void 0 ? _f : undefined;
|
|
1873
1945
|
return message;
|
|
1874
1946
|
},
|
|
1875
1947
|
};
|
|
@@ -381,6 +381,8 @@ export declare class DurableContext<T, K = {}> extends Context<T, K> {
|
|
|
381
381
|
private _evictionManager;
|
|
382
382
|
private _engineVersion;
|
|
383
383
|
private _waitKey;
|
|
384
|
+
private _sendEventLock;
|
|
385
|
+
private _serializeSendEvent;
|
|
384
386
|
constructor(action: Action, v1: HatchetClient, worker: InternalWorker, durableListener: DurableListenerClient, evictionManager?: DurableEvictionManager, engineVersion?: string);
|
|
385
387
|
get supportsEviction(): boolean;
|
|
386
388
|
get durableListener(): DurableListenerClient;
|