@hatchet-dev/typescript-sdk 1.31.1 → 1.33.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/README.md +46 -0
  2. package/clients/dispatcher/action-listener.d.ts +3 -8
  3. package/clients/dispatcher/action-listener.js +7 -25
  4. package/clients/dispatcher/action.d.ts +19 -0
  5. package/clients/dispatcher/action.js +35 -0
  6. package/clients/dispatcher/dispatcher-client.d.ts +2 -2
  7. package/clients/dispatcher/heartbeat/heartbeat-controller.d.ts +1 -1
  8. package/clients/listeners/durable-listener/durable-events.d.ts +56 -0
  9. package/clients/listeners/durable-listener/durable-events.js +2 -0
  10. package/clients/listeners/durable-listener/durable-listener-client.d.ts +3 -52
  11. package/clients/listeners/durable-listener/durable-listener-client.js +3 -2
  12. package/clients/listeners/durable-listener/pooled-durable-listener-client.js +2 -1
  13. package/clients/listeners/run-listener/pooled-child-listener-client.js +2 -1
  14. package/clients/rest/generated/Api.d.ts +1 -62
  15. package/clients/rest/generated/Api.js +0 -50
  16. package/clients/rest/generated/data-contracts.d.ts +0 -52
  17. package/dist/check-edge-entry.mjs +84 -0
  18. package/edge/declarations.d.ts +49 -0
  19. package/edge/declarations.js +21 -0
  20. package/edge/index.d.ts +49 -0
  21. package/edge/index.js +115 -0
  22. package/package.json +7 -2
  23. package/scripts/check-edge-entry.mjs +84 -0
  24. package/util/abort-error.d.ts +0 -10
  25. package/util/abort-error.js +0 -15
  26. package/util/abort-signal.d.ts +12 -0
  27. package/util/abort-signal.js +19 -0
  28. package/util/logger/logger.d.ts +1 -1
  29. package/v1/client/worker/context.d.ts +58 -12
  30. package/v1/client/worker/context.js +79 -51
  31. package/v1/client/worker/deprecated/pre-eviction.d.ts +5 -2
  32. package/v1/client/worker/deprecated/pre-eviction.js +5 -0
  33. package/v1/client/worker/health-server.d.ts +2 -0
  34. package/v1/client/worker/health-server.js +28 -0
  35. package/v1/client/worker/runtime.d.ts +118 -0
  36. package/v1/client/worker/runtime.js +9 -0
  37. package/v1/client/worker/worker-internal.d.ts +3 -39
  38. package/v1/client/worker/worker-internal.js +22 -393
  39. package/v1/client/worker/worker-runtime.d.ts +8 -0
  40. package/v1/client/worker/worker-runtime.js +67 -0
  41. package/v1/client/worker/workflow-proto.d.ts +76 -0
  42. package/v1/client/worker/workflow-proto.js +482 -0
  43. package/v1/parent-run-context-storage.d.ts +11 -0
  44. package/v1/parent-run-context-storage.js +28 -0
  45. package/v1/parent-run-context-vars.d.ts +15 -1
  46. package/v1/parent-run-context-vars.js +13 -4
  47. package/version.d.ts +1 -1
  48. package/version.js +1 -1
@@ -0,0 +1,118 @@
1
+ /**
2
+ * The seams between a task's `Context` / `DurableContext` and whatever runs the task.
3
+ *
4
+ * A worker process implements them over its `HatchetClient` and durable listener (see
5
+ * `worker-runtime.ts`); a serverless handler implements them over the operator's request
6
+ * and durable socket. Nothing in this module imports from Node, so the interfaces are
7
+ * usable from the edge entry point.
8
+ * @module Runtime
9
+ */
10
+ import type { Logger, LogLevel } from '../../../util/logger';
11
+ import type WorkflowRunRef from '../../../util/workflow-run-ref';
12
+ import type { WorkerLabelComparator } from '../../../protoc/v1/shared/trigger';
13
+ import type { Priority } from '../../declaration';
14
+ import type { DurableTaskEventLogEntryResult, DurableTaskEventMemoAck, DurableTaskEventRunAck, DurableTaskEventWaitForAck, MemoEvent, RunChildrenEvent, WaitForEvent } from '../../../clients/listeners/durable-listener/durable-events';
15
+ /** Labels a worker advertises for affinity-based assignment. */
16
+ export type WorkerLabels = Record<string, string | number | undefined>;
17
+ /** A worker label a spawned child run asks for. */
18
+ export type DesiredWorkerLabel = {
19
+ value: string | number;
20
+ required?: boolean;
21
+ weight?: number;
22
+ comparator?: WorkerLabelComparator;
23
+ };
24
+ /**
25
+ * Options a context passes when it spawns a child run. Mirrors the options accepted by
26
+ * `AdminClient.runWorkflow`.
27
+ */
28
+ export type SpawnRunOptions = {
29
+ parentId?: string | undefined;
30
+ parentTaskRunExternalId?: string | undefined;
31
+ /** @deprecated Use `parentTaskRunExternalId` instead. */
32
+ parentStepRunId?: string | undefined;
33
+ childIndex?: number | undefined;
34
+ childKey?: string | undefined;
35
+ /** Alias of `childKey` kept for the child run APIs on `Context`. */
36
+ key?: string | undefined;
37
+ additionalMetadata?: Record<string, string> | undefined;
38
+ desiredWorkerId?: string | undefined;
39
+ priority?: Priority;
40
+ sticky?: boolean;
41
+ returnExceptions?: boolean;
42
+ desiredWorkerLabels?: Record<string, DesiredWorkerLabel>;
43
+ _standaloneTaskName?: string | undefined;
44
+ };
45
+ export type SpawnRunRequest<Q = object> = {
46
+ workflowName: string;
47
+ input: Q;
48
+ options?: SpawnRunOptions;
49
+ };
50
+ /** Identifies every member of a batch task run when the whole batch is cancelled. */
51
+ export type CancelBatchRequest = {
52
+ workerId: string;
53
+ jobId: string;
54
+ actionId: string;
55
+ batchId: string;
56
+ memberIds: string[];
57
+ };
58
+ /**
59
+ * Everything a `Context` needs from the process running the task: a logger factory,
60
+ * the namespace, the engine-facing operations, and the worker facts `ctx.worker` reports.
61
+ *
62
+ * Implementations that cannot honour an operation (for example a serverless runtime
63
+ * without a Hatchet client) throw from it; the context does not guard against that.
64
+ */
65
+ export interface ContextRuntime {
66
+ /** The namespace applied to workflow names and event keys, if any. */
67
+ readonly namespace?: string;
68
+ /** Creates a logger for the given component, at the runtime's configured level. */
69
+ logger(name: string): Logger;
70
+ /** Cancels a single task run. */
71
+ cancelRun(taskRunExternalId: string): Promise<void>;
72
+ /** Cancels every member of a batch task run. */
73
+ cancelBatch(request: CancelBatchRequest): Promise<void>;
74
+ /** Writes a log line for a task run to the engine. */
75
+ putLog(taskRunExternalId: string, message: string, level: LogLevel | undefined, retryCount: number, extra?: Record<string, unknown>): Promise<void>;
76
+ /** Extends the execution timeout of a task run by `incrementBy` (Go duration string). */
77
+ refreshTimeout(taskRunExternalId: string, incrementBy: string): Promise<void>;
78
+ /** Releases the worker slot held by a task run. */
79
+ releaseSlot(taskRunExternalId: string): Promise<void>;
80
+ /** Streams a chunk of data from a task run. */
81
+ putStream(taskRunExternalId: string, data: string | Uint8Array, index: number): Promise<void>;
82
+ /** Spawns one run. */
83
+ runWorkflow<Q = object, P = object>(workflowName: string, input: Q, options?: SpawnRunOptions): Promise<WorkflowRunRef<P>>;
84
+ /** Spawns many runs in one call. */
85
+ runWorkflows<Q = object, P = object>(runs: SpawnRunRequest<Q>[]): Promise<WorkflowRunRef<P>[]>;
86
+ /** The id the engine assigned to the worker, once registered. */
87
+ workerId(): string | undefined;
88
+ /** Whether the worker serves the given workflow (used by sticky child runs). */
89
+ hasWorkflow(workflowName: string): boolean;
90
+ /** The worker's current labels. */
91
+ workerLabels(): WorkerLabels;
92
+ /** Replaces the worker's labels. */
93
+ upsertWorkerLabels(labels: WorkerLabels): Promise<WorkerLabels>;
94
+ }
95
+ /**
96
+ * The seam a `DurableContext` talks to for durable events: the worker implements it with
97
+ * `DurableListenerClient` over gRPC, a serverless handler with frames over a websocket.
98
+ */
99
+ export interface DurableTransport {
100
+ sendEvent(durableTaskExternalId: string, invocationCount: number, event: RunChildrenEvent): Promise<DurableTaskEventRunAck>;
101
+ sendEvent(durableTaskExternalId: string, invocationCount: number, event: WaitForEvent): Promise<DurableTaskEventWaitForAck>;
102
+ sendEvent(durableTaskExternalId: string, invocationCount: number, event: MemoEvent): Promise<DurableTaskEventMemoAck>;
103
+ waitForCallback(durableTaskExternalId: string, invocationCount: number, branchId: number, nodeId: number, opts?: {
104
+ signal?: AbortSignal;
105
+ }): Promise<DurableTaskEventLogEntryResult>;
106
+ consumeCallbackWithoutBlocking(durableTaskExternalId: string, invocationCount: number, branchId: number, nodeId: number): void;
107
+ sendMemoCompletedNotification(durableTaskExternalId: string, nodeId: number, branchId: number, invocationCount: number, memoKey: Uint8Array, memoResultPayload?: Uint8Array): Promise<void>;
108
+ cleanupTaskState(durableTaskExternalId: string, invocationCount: number): void;
109
+ sendEvictInvocation(durableTaskExternalId: string, invocationCount: number, reason?: string): Promise<void>;
110
+ }
111
+ /**
112
+ * Options for constructing a `DurableContext` on top of a `ContextRuntime`.
113
+ */
114
+ export interface DurableContextOptions {
115
+ /** The engine version the runtime is talking to; decides whether eviction is supported. */
116
+ engineVersion?: string;
117
+ }
118
+ export declare function isContextRuntime(value: unknown): value is ContextRuntime;
@@ -0,0 +1,9 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.isContextRuntime = isContextRuntime;
4
+ function isContextRuntime(value) {
5
+ if (typeof value !== 'object' || value === null)
6
+ return false;
7
+ const candidate = value;
8
+ return typeof candidate.logger === 'function' && typeof candidate.workerId === 'function';
9
+ }
@@ -1,17 +1,15 @@
1
- import { Action, ActionKey, ActionListener } from '../../../clients/dispatcher/action-listener';
1
+ import { ActionListener } from '../../../clients/dispatcher/action-listener';
2
+ import { Action, ActionKey } from '../../../clients/dispatcher/action';
2
3
  import { StepActionEvent, StepActionEventType, GroupKeyActionEvent, GroupKeyActionEventType, BatchActionEvent } from '../../../protoc/dispatcher';
3
4
  import HatchetPromise from '../../../util/hatchet-promise/hatchet-promise';
4
- import { CreateStepRateLimit } from '../../../protoc/workflows';
5
5
  import { Logger } from '../../../util/logger';
6
6
  import { BaseWorkflowDeclaration, WorkflowDefinition, HatchetClient } from '../..';
7
- import { TaskBatchConfig } from '../../../protoc/v1/workflows';
8
- import { Concurrency, CreateWorkflowTaskOpts } from '../../task';
9
7
  import { WorkerLabels } from '../../../clients/dispatcher/dispatcher-client';
10
- import { Duration } from '../duration';
11
8
  import { Context } from './context';
12
9
  import { SlotConfig } from '../../slot-types';
13
10
  import { DurableEvictionManager } from './eviction/eviction-manager';
14
11
  import { EvictionPolicy } from './eviction/eviction-policy';
12
+ export { assertValidConcurrencyArr, mapBatchConfigPb, mapConcurrencyPb, mapRateLimitPb, mapSlotRequestsPb, resolveExecutionTimeout, resolveScheduleTimeout, taskConcurrencyArr, } from './workflow-proto';
15
13
  export type ActionRegistry = Record<Action['actionId'], Function>;
16
14
  export interface WorkerOpts {
17
15
  name: string;
@@ -107,37 +105,3 @@ export declare class InternalWorker {
107
105
  handleAction(action: Action): Promise<void | Error>;
108
106
  upsertLabels(labels: WorkerLabels): Promise<WorkerLabels>;
109
107
  }
110
- /** Durable tasks stay on the durable pool; slotCost applies only to the default pool. */
111
- export declare function mapSlotRequestsPb(task: {
112
- slotRequests?: Record<string, number>;
113
- slotCost?: number;
114
- }, isDurable: boolean): Record<string, number>;
115
- export declare function mapRateLimitPb(limits: CreateWorkflowTaskOpts<any, any>['rateLimits']): CreateStepRateLimit[];
116
- export declare function mapConcurrencyPb(entries: Concurrency[]): {
117
- expression: string;
118
- maxRuns: number | undefined;
119
- limitStrategy: import("../..").ConcurrencyLimitStrategy | undefined;
120
- name: string | undefined;
121
- isTenantScoped: boolean | undefined;
122
- maxRunsExpression: string | undefined;
123
- }[];
124
- export declare function taskConcurrencyArr(task: {
125
- concurrency?: Concurrency | Concurrency[];
126
- }, workflow: {
127
- taskDefaults?: {
128
- concurrency?: Concurrency | Concurrency[];
129
- };
130
- }): Concurrency[];
131
- export declare function assertValidConcurrencyArr(concurrency: Concurrency[] | undefined): void;
132
- export declare function mapBatchConfigPb(batch: CreateWorkflowTaskOpts<any, any>['batch']): TaskBatchConfig | undefined;
133
- export declare function resolveExecutionTimeout(task: {
134
- executionTimeout?: Duration;
135
- timeout?: Duration;
136
- }, workflowDefaults?: {
137
- executionTimeout?: Duration;
138
- }): string;
139
- export declare function resolveScheduleTimeout(task: {
140
- scheduleTimeout?: Duration;
141
- }, workflowDefaults?: {
142
- scheduleTimeout?: Duration;
143
- }): string | undefined;
@@ -52,35 +52,37 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
52
52
  return (mod && mod.__esModule) ? mod : { "default": mod };
53
53
  };
54
54
  Object.defineProperty(exports, "__esModule", { value: true });
55
- exports.InternalWorker = void 0;
56
- exports.mapSlotRequestsPb = mapSlotRequestsPb;
57
- exports.mapRateLimitPb = mapRateLimitPb;
58
- exports.mapConcurrencyPb = mapConcurrencyPb;
59
- exports.taskConcurrencyArr = taskConcurrencyArr;
60
- exports.assertValidConcurrencyArr = assertValidConcurrencyArr;
61
- exports.mapBatchConfigPb = mapBatchConfigPb;
62
- exports.resolveExecutionTimeout = resolveExecutionTimeout;
63
- exports.resolveScheduleTimeout = resolveScheduleTimeout;
55
+ exports.InternalWorker = exports.taskConcurrencyArr = exports.resolveScheduleTimeout = exports.resolveExecutionTimeout = exports.mapSlotRequestsPb = exports.mapRateLimitPb = exports.mapConcurrencyPb = exports.mapBatchConfigPb = exports.assertValidConcurrencyArr = void 0;
64
56
  const hatchet_error_1 = __importDefault(require("../../../util/errors/hatchet-error"));
65
57
  const task_run_terminated_error_1 = require("../../../util/errors/task-run-terminated-error");
58
+ const action_1 = require("../../../clients/dispatcher/action");
66
59
  const dispatcher_1 = require("../../../protoc/dispatcher");
67
60
  const hatchet_promise_1 = __importStar(require("../../../util/hatchet-promise/hatchet-promise"));
68
- const workflows_1 = require("../../../protoc/workflows");
69
61
  const logger_1 = require("../../../util/logger");
70
- const workflows_2 = require("../../../protoc/v1/workflows");
71
62
  const task_1 = require("../../task");
72
- const transformer_1 = require("../../conditions/transformer");
73
- const z = __importStar(require("zod/v4"));
74
63
  const apply_namespace_1 = require("../../../util/apply-namespace");
75
64
  const sleep_1 = __importDefault(require("../../../util/sleep"));
76
65
  const abort_error_1 = require("../../../util/abort-error");
77
- const duration_1 = require("../duration");
78
66
  const context_1 = require("./context");
79
67
  const parent_run_context_vars_1 = require("../../parent-run-context-vars");
68
+ const parent_run_context_storage_1 = require("../../parent-run-context-storage");
69
+ const workflow_proto_1 = require("./workflow-proto");
80
70
  const health_server_1 = require("./health-server");
81
71
  const eviction_manager_1 = require("./eviction/eviction-manager");
82
72
  const eviction_policy_1 = require("./eviction/eviction-policy");
83
73
  const engine_version_1 = require("./engine-version");
74
+ var workflow_proto_2 = require("./workflow-proto");
75
+ Object.defineProperty(exports, "assertValidConcurrencyArr", { enumerable: true, get: function () { return workflow_proto_2.assertValidConcurrencyArr; } });
76
+ Object.defineProperty(exports, "mapBatchConfigPb", { enumerable: true, get: function () { return workflow_proto_2.mapBatchConfigPb; } });
77
+ Object.defineProperty(exports, "mapConcurrencyPb", { enumerable: true, get: function () { return workflow_proto_2.mapConcurrencyPb; } });
78
+ Object.defineProperty(exports, "mapRateLimitPb", { enumerable: true, get: function () { return workflow_proto_2.mapRateLimitPb; } });
79
+ Object.defineProperty(exports, "mapSlotRequestsPb", { enumerable: true, get: function () { return workflow_proto_2.mapSlotRequestsPb; } });
80
+ Object.defineProperty(exports, "resolveExecutionTimeout", { enumerable: true, get: function () { return workflow_proto_2.resolveExecutionTimeout; } });
81
+ Object.defineProperty(exports, "resolveScheduleTimeout", { enumerable: true, get: function () { return workflow_proto_2.resolveScheduleTimeout; } });
82
+ Object.defineProperty(exports, "taskConcurrencyArr", { enumerable: true, get: function () { return workflow_proto_2.taskConcurrencyArr; } });
83
+ // Tasks read the parent run context across awaits; the worker is the only place that
84
+ // enters it, so it installs the AsyncLocalStorage store before any task can run.
85
+ (0, parent_run_context_storage_1.installAsyncLocalParentRunContext)();
84
86
  class InternalWorker {
85
87
  constructor(client, options) {
86
88
  var _a, _b, _c, _d;
@@ -143,7 +145,7 @@ class InternalWorker {
143
145
  const newActions = workflow._durableTasks
144
146
  .filter((task) => !!task.fn)
145
147
  .reduce((acc, task) => {
146
- const actionId = `${(0, apply_namespace_1.applyNamespace)(workflow.name, this.client.config.namespace).toLowerCase()}:${task.name.toLowerCase()}`;
148
+ const actionId = (0, action_1.createActionId)((0, apply_namespace_1.applyNamespace)(workflow.name, this.client.config.namespace), task.name);
147
149
  acc[actionId] = (ctx) => task.fn(ctx.input, ctx);
148
150
  this.durable_action_set.add(actionId);
149
151
  this.eviction_policies.set(actionId, task.evictionPolicy !== undefined
@@ -157,7 +159,7 @@ class InternalWorker {
157
159
  const newActions = workflow._tasks
158
160
  .filter((task) => !!task.fn)
159
161
  .reduce((acc, task) => {
160
- const actionId = `${workflow.name}:${task.name.toLowerCase()}`;
162
+ const actionId = (0, action_1.createActionId)(workflow.name, task.name);
161
163
  if (task.batch) {
162
164
  acc[actionId] = (ctx) => task.fn(decodeBatchItems(ctx.action.actionPayload), ctx);
163
165
  }
@@ -173,192 +175,17 @@ class InternalWorker {
173
175
  : undefined;
174
176
  const onFailureAction = onFailureFn
175
177
  ? {
176
- [onFailureTaskName(workflow)]: (ctx) => onFailureFn(ctx.input, ctx),
178
+ [(0, workflow_proto_1.onFailureTaskName)(workflow)]: (ctx) => onFailureFn(ctx.input, ctx),
177
179
  }
178
180
  : {};
179
181
  this.action_registry = Object.assign(Object.assign(Object.assign({}, this.action_registry), newActions), onFailureAction);
180
182
  }
181
183
  registerWorkflow(initWorkflow_1) {
182
184
  return __awaiter(this, arguments, void 0, function* (initWorkflow, durable = false) {
183
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w;
184
- // patch the namespace
185
- const workflow = Object.assign(Object.assign({}, initWorkflow.definition), { name: (0, apply_namespace_1.applyNamespace)(initWorkflow.definition.name, this.client.config.namespace).toLowerCase() });
185
+ const { namespace } = this.client.config;
186
+ const workflow = (0, workflow_proto_1.normalizeWorkflowDefinition)(initWorkflow, { namespace, durable });
186
187
  try {
187
- const { concurrency } = workflow;
188
- let onFailureTask;
189
- if (workflow.onFailure && typeof workflow.onFailure === 'function') {
190
- onFailureTask = {
191
- readableId: 'on-failure-task',
192
- action: onFailureTaskName(workflow),
193
- timeout: '60s',
194
- inputs: '{}',
195
- parents: [],
196
- retries: 0,
197
- rateLimits: [],
198
- workerLabels: {},
199
- concurrency: [],
200
- isDurable: false,
201
- slotRequests: { default: 1 },
202
- };
203
- }
204
- if (workflow.onFailure && typeof workflow.onFailure === 'object') {
205
- const onFailure = workflow.onFailure;
206
- const scheduleTimeout = (_a = onFailure.scheduleTimeout) !== null && _a !== void 0 ? _a : (_b = workflow.taskDefaults) === null || _b === void 0 ? void 0 : _b.scheduleTimeout;
207
- onFailureTask = {
208
- readableId: 'on-failure-task',
209
- action: onFailureTaskName(workflow),
210
- timeout: (0, duration_1.durationToString)(onFailure.executionTimeout || ((_c = workflow.taskDefaults) === null || _c === void 0 ? void 0 : _c.executionTimeout) || '60s'),
211
- scheduleTimeout: scheduleTimeout ? (0, duration_1.durationToString)(scheduleTimeout) : undefined,
212
- inputs: '{}',
213
- parents: [],
214
- retries: onFailure.retries || ((_d = workflow.taskDefaults) === null || _d === void 0 ? void 0 : _d.retries) || 0,
215
- rateLimits: mapRateLimitPb(onFailure.rateLimits || ((_e = workflow.taskDefaults) === null || _e === void 0 ? void 0 : _e.rateLimits)),
216
- workerLabels: mapWorkerLabelPb(onFailure.desiredWorkerLabels || ((_f = workflow.taskDefaults) === null || _f === void 0 ? void 0 : _f.workerLabels)),
217
- concurrency: [],
218
- 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),
219
- 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),
220
- isDurable: false,
221
- slotRequests: mapSlotRequestsPb(onFailure, false),
222
- };
223
- }
224
- let onSuccessTask;
225
- if (!durable && workflow.onSuccess && typeof workflow.onSuccess === 'function') {
226
- const parents = getLeaves([...workflow._tasks, ...workflow._durableTasks]);
227
- onSuccessTask = {
228
- name: 'on-success-task',
229
- fn: workflow.onSuccess,
230
- executionTimeout: '60s',
231
- parents,
232
- retries: 0,
233
- rateLimits: [],
234
- desiredWorkerLabels: undefined,
235
- concurrency: [],
236
- };
237
- }
238
- if (!durable && workflow.onSuccess && typeof workflow.onSuccess === 'object') {
239
- const onSuccess = workflow.onSuccess;
240
- const parents = getLeaves([...workflow._tasks, ...workflow._durableTasks]);
241
- onSuccessTask = {
242
- name: 'on-success-task',
243
- fn: onSuccess.fn,
244
- executionTimeout: onSuccess.executionTimeout || ((_o = workflow.taskDefaults) === null || _o === void 0 ? void 0 : _o.executionTimeout) || '60s',
245
- scheduleTimeout: onSuccess.scheduleTimeout || ((_p = workflow.taskDefaults) === null || _p === void 0 ? void 0 : _p.scheduleTimeout),
246
- parents,
247
- retries: onSuccess.retries || ((_q = workflow.taskDefaults) === null || _q === void 0 ? void 0 : _q.retries) || 0,
248
- rateLimits: onSuccess.rateLimits || ((_r = workflow.taskDefaults) === null || _r === void 0 ? void 0 : _r.rateLimits),
249
- desiredWorkerLabels: onSuccess.desiredWorkerLabels || ((_s = workflow.taskDefaults) === null || _s === void 0 ? void 0 : _s.workerLabels),
250
- concurrency: onSuccess.concurrency || ((_t = workflow.taskDefaults) === null || _t === void 0 ? void 0 : _t.concurrency),
251
- backoff: onSuccess.backoff || ((_u = workflow.taskDefaults) === null || _u === void 0 ? void 0 : _u.backoff),
252
- };
253
- }
254
- if (onSuccessTask) {
255
- workflow._tasks.push(onSuccessTask);
256
- }
257
- const eventTriggers = [
258
- ...(workflow.onEvents || []).map((event) => (0, apply_namespace_1.applyNamespace)(event, this.client.config.namespace)),
259
- ...(workflow.on && 'event' in workflow.on && workflow.on.event
260
- ? Array.isArray(workflow.on.event)
261
- ? workflow.on.event.map((event) => (0, apply_namespace_1.applyNamespace)(event, this.client.config.namespace))
262
- : [(0, apply_namespace_1.applyNamespace)(workflow.on.event, this.client.config.namespace)]
263
- : []),
264
- ];
265
- const cronTriggers = [
266
- ...(workflow.onCrons || []),
267
- ...(workflow.on && 'cron' in workflow.on && workflow.on.cron
268
- ? Array.isArray(workflow.on.cron)
269
- ? workflow.on.cron
270
- : [workflow.on.cron]
271
- : []),
272
- ];
273
- const concurrencyArr = Array.isArray(concurrency) ? concurrency : [];
274
- const concurrencySolo = !Array.isArray(concurrency) ? concurrency : undefined;
275
- assertValidConcurrencyArr(concurrencyArr);
276
- assertValidConcurrencyArr(concurrencySolo ? [concurrencySolo] : undefined);
277
- // Convert Zod schema to JSON Schema if provided
278
- let inputJsonSchema;
279
- if (workflow.inputValidator) {
280
- const jsonSchema = z.toJSONSchema(workflow.inputValidator);
281
- inputJsonSchema = new TextEncoder().encode(JSON.stringify(jsonSchema));
282
- }
283
- const durableTaskSet = new Set(workflow._durableTasks);
284
- let stickyStrategy;
285
- // `workflow.sticky` is optional. When omitted, we don't set any sticky strategy.
286
- //
287
- // When provided, `workflow.sticky` is a v1 (non-protobuf) config which may also include
288
- // legacy protobuf enum values for backwards compatibility.
289
- if (workflow.sticky != null) {
290
- switch (workflow.sticky) {
291
- case 'soft':
292
- case 'SOFT':
293
- case 0:
294
- stickyStrategy = workflows_1.StickyStrategy.SOFT;
295
- break;
296
- case 'hard':
297
- case 'HARD':
298
- case 1:
299
- stickyStrategy = workflows_1.StickyStrategy.HARD;
300
- break;
301
- default:
302
- throw new hatchet_error_1.default(`Invalid sticky strategy: ${workflow.sticky}`);
303
- }
304
- }
305
- const registeredWorkflow = this.client.admin.putWorkflow({
306
- name: workflow.name,
307
- description: workflow.description || '',
308
- version: workflow.version || '',
309
- eventTriggers,
310
- cronTriggers,
311
- sticky: stickyStrategy,
312
- concurrencyArr: mapConcurrencyPb(concurrencyArr),
313
- onFailureTask,
314
- defaultPriority: workflow.defaultPriority,
315
- inputJsonSchema,
316
- tasks: [...workflow._tasks, ...workflow._durableTasks].map((task) => {
317
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
318
- return ({
319
- readableId: task.name,
320
- action: `${workflow.name}:${task.name}`,
321
- timeout: resolveExecutionTimeout(task, workflow.taskDefaults),
322
- scheduleTimeout: resolveScheduleTimeout(task, workflow.taskDefaults),
323
- inputs: '{}',
324
- parents: (_b = (_a = task.parents) === null || _a === void 0 ? void 0 : _a.map((p) => p.name)) !== null && _b !== void 0 ? _b : [],
325
- userData: '{}',
326
- // Batch tasks buffer many concurrent runs into a single execution; per-item retry
327
- // semantics don't apply, so retries is always forced to 0.
328
- retries: batchOf(task) ? 0 : task.retries || ((_c = workflow.taskDefaults) === null || _c === void 0 ? void 0 : _c.retries) || 0,
329
- rateLimits: mapRateLimitPb(task.rateLimits || ((_d = workflow.taskDefaults) === null || _d === void 0 ? void 0 : _d.rateLimits)),
330
- workerLabels: mapWorkerLabelPb(task.desiredWorkerLabels || ((_e = workflow.taskDefaults) === null || _e === void 0 ? void 0 : _e.workerLabels)),
331
- 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),
332
- 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),
333
- conditions: (0, transformer_1.taskConditionsToPb)(task, this.client.config.namespace),
334
- isDurable: durableTaskSet.has(task),
335
- slotRequests: mapSlotRequestsPb(task, durableTaskSet.has(task)),
336
- batch: mapBatchConfigPb(batchOf(task)),
337
- concurrency: (() => {
338
- const taskConcurrency = taskConcurrencyArr(task, workflow);
339
- assertValidConcurrencyArr(taskConcurrency);
340
- return mapConcurrencyPb(taskConcurrency);
341
- })(),
342
- });
343
- }),
344
- concurrency: concurrencySolo ? mapConcurrencyPb([concurrencySolo])[0] : undefined,
345
- defaultFilters: (_w = (_v = workflow.defaultFilters) === null || _v === void 0 ? void 0 : _v.map((f) => ({
346
- scope: f.scope,
347
- expression: f.expression,
348
- payload: f.payload ? new TextEncoder().encode(JSON.stringify(f.payload)) : undefined,
349
- }))) !== null && _w !== void 0 ? _w : [],
350
- idempotency: workflow.idempotency
351
- ? {
352
- expression: workflow.idempotency.expression,
353
- ttlMs: workflow.idempotency.strategy === 'status'
354
- ? workflow.idempotency.fallbackTtlMs
355
- : workflow.idempotency.ttlMs,
356
- method: workflow.idempotency.strategy === 'status'
357
- ? workflows_2.IdempotencyMethod.STATUS
358
- : workflows_2.IdempotencyMethod.TTL,
359
- }
360
- : undefined,
361
- });
188
+ const registeredWorkflow = this.client.admin.putWorkflow((0, workflow_proto_1.workflowToProto)(workflow, { namespace, durable }));
362
189
  this.registeredWorkflowPromises.push(registeredWorkflow);
363
190
  yield registeredWorkflow;
364
191
  this.workflow_registry.push(workflow);
@@ -960,125 +787,6 @@ class InternalWorker {
960
787
  }
961
788
  }
962
789
  exports.InternalWorker = InternalWorker;
963
- function mapWorkerLabelPb(in_) {
964
- if (!in_) {
965
- return {};
966
- }
967
- return Object.entries(in_).reduce((acc, [key, label]) => {
968
- if (!label) {
969
- return Object.assign(Object.assign({}, acc), { [key]: {
970
- strValue: undefined,
971
- intValue: undefined,
972
- } });
973
- }
974
- if (typeof label === 'string') {
975
- return Object.assign(Object.assign({}, acc), { [key]: {
976
- strValue: label,
977
- intValue: undefined,
978
- } });
979
- }
980
- if (typeof label === 'number') {
981
- return Object.assign(Object.assign({}, acc), { [key]: {
982
- strValue: undefined,
983
- intValue: label,
984
- } });
985
- }
986
- return Object.assign(Object.assign({}, acc), { [key]: {
987
- strValue: typeof label.value === 'string' ? label.value : undefined,
988
- intValue: typeof label.value === 'number' ? label.value : undefined,
989
- required: label.required,
990
- weight: label.weight,
991
- comparator: label.comparator,
992
- } });
993
- }, {});
994
- }
995
- function onFailureTaskName(workflow) {
996
- return `${workflow.name}:on-failure-task`;
997
- }
998
- function getLeaves(tasks) {
999
- return tasks.filter((task) => isLeafTask(task, tasks));
1000
- }
1001
- function isLeafTask(task, allTasks) {
1002
- return !allTasks.some((t) => { var _a; return (_a = t.parents) === null || _a === void 0 ? void 0 : _a.some((p) => p.name === task.name); });
1003
- }
1004
- /** Durable tasks stay on the durable pool; slotCost applies only to the default pool. */
1005
- function mapSlotRequestsPb(task, isDurable) {
1006
- if (task.slotRequests) {
1007
- return task.slotRequests;
1008
- }
1009
- if (isDurable) {
1010
- return { durable: 1 };
1011
- }
1012
- if (task.slotCost !== undefined) {
1013
- if (!Number.isInteger(task.slotCost) || task.slotCost <= 0) {
1014
- throw new Error(`slotCost must be a positive integer, got: ${task.slotCost}`);
1015
- }
1016
- return { default: task.slotCost };
1017
- }
1018
- return { default: 1 };
1019
- }
1020
- function mapRateLimitPb(limits) {
1021
- if (!limits) {
1022
- return [];
1023
- }
1024
- return limits.map((l) => {
1025
- let key = l.staticKey;
1026
- const keyExpression = l.dynamicKey;
1027
- if (l.key !== undefined) {
1028
- console.warn('key is deprecated and will be removed in a future release, please use staticKey instead');
1029
- ({ key } = l);
1030
- }
1031
- if (keyExpression !== undefined) {
1032
- if (key !== undefined) {
1033
- throw new Error('Cannot have both static key and dynamic key set');
1034
- }
1035
- key = keyExpression;
1036
- if (!validateCelExpression(keyExpression)) {
1037
- throw new Error(`Invalid CEL expression: ${keyExpression}`);
1038
- }
1039
- }
1040
- if (key === undefined) {
1041
- throw new Error(`Invalid key`);
1042
- }
1043
- let units;
1044
- let unitsExpression;
1045
- if (typeof l.units === 'number') {
1046
- ({ units } = l);
1047
- }
1048
- else {
1049
- if (!validateCelExpression(l.units)) {
1050
- throw new Error(`Invalid CEL expression: ${l.units}`);
1051
- }
1052
- unitsExpression = l.units;
1053
- }
1054
- let limitExpression;
1055
- if (l.limit !== undefined) {
1056
- if (typeof l.limit === 'number') {
1057
- limitExpression = `${l.limit}`;
1058
- }
1059
- else {
1060
- if (!validateCelExpression(l.limit)) {
1061
- throw new Error(`Invalid CEL expression: ${l.limit}`);
1062
- }
1063
- limitExpression = l.limit;
1064
- }
1065
- }
1066
- if (keyExpression !== undefined && limitExpression === undefined) {
1067
- throw new Error('CEL based keys requires limit to be set');
1068
- }
1069
- if (limitExpression === undefined) {
1070
- limitExpression = `-1`;
1071
- }
1072
- return {
1073
- key,
1074
- keyExpr: keyExpression,
1075
- units,
1076
- unitsExpr: unitsExpression,
1077
- limitValuesExpr: limitExpression,
1078
- duration: l.duration,
1079
- };
1080
- });
1081
- }
1082
790
  /**
1083
791
  * Decodes the buffered items of a batch task's START_BATCH action into a Record keyed by
1084
792
  * each buffered item's task-run external id, mapping to that item's input. The wire shape
@@ -1105,82 +813,3 @@ function parseBatchPayload(actionPayload) {
1105
813
  return {};
1106
814
  }
1107
815
  }
1108
- /** Batch tasks are only available on non-durable tasks; durable tasks never carry `batch`. */
1109
- function batchOf(task) {
1110
- return 'batch' in task ? task.batch : undefined;
1111
- }
1112
- // mapConcurrencyPb maps SDK concurrency entries onto the proto shape; entries keep their
1113
- // declared order, which is the chain order.
1114
- function mapConcurrencyPb(entries) {
1115
- return entries.map((c) => ({
1116
- expression: c.expression,
1117
- // a string maxRuns is a CEL expression; the static field then carries the default
1118
- // of 1, which only governs slots created before the expression existed
1119
- maxRuns: typeof c.maxRuns === 'string' ? 1 : c.maxRuns,
1120
- limitStrategy: c.limitStrategy,
1121
- name: c.name,
1122
- isTenantScoped: c.isTenantScoped,
1123
- maxRunsExpression: typeof c.maxRuns === 'string' ? c.maxRuns : undefined,
1124
- }));
1125
- }
1126
- function taskConcurrencyArr(task, workflow) {
1127
- var _a;
1128
- if (task.concurrency) {
1129
- return Array.isArray(task.concurrency) ? task.concurrency : [task.concurrency];
1130
- }
1131
- if ((_a = workflow.taskDefaults) === null || _a === void 0 ? void 0 : _a.concurrency) {
1132
- return Array.isArray(workflow.taskDefaults.concurrency)
1133
- ? workflow.taskDefaults.concurrency
1134
- : [workflow.taskDefaults.concurrency];
1135
- }
1136
- return [];
1137
- }
1138
- function assertValidConcurrencyArr(concurrency) {
1139
- concurrency === null || concurrency === void 0 ? void 0 : concurrency.forEach((c) => {
1140
- if (typeof c.maxRuns === 'string') {
1141
- if (!c.maxRuns.trim()) {
1142
- throw new Error('concurrency.maxRuns expression must be non-empty');
1143
- }
1144
- return;
1145
- }
1146
- if (c.maxRuns !== undefined && (!Number.isInteger(c.maxRuns) || c.maxRuns <= 0)) {
1147
- throw new Error(`concurrency.maxRuns must be a positive integer or a CEL expression, got: ${c.maxRuns}`);
1148
- }
1149
- });
1150
- }
1151
- function mapBatchConfigPb(batch) {
1152
- if (!batch) {
1153
- return undefined;
1154
- }
1155
- if (!Number.isInteger(batch.maxSize) || batch.maxSize <= 0) {
1156
- throw new Error(`batch.maxSize must be a positive integer, got: ${batch.maxSize}`);
1157
- }
1158
- const batchMaxIntervalMs = batch.maxInterval !== undefined ? (0, duration_1.durationToMs)(batch.maxInterval) : undefined;
1159
- if (batchMaxIntervalMs !== undefined && batchMaxIntervalMs <= 0) {
1160
- throw new Error('batch.maxInterval must be positive when provided');
1161
- }
1162
- if (batch.groupMaxRuns !== undefined &&
1163
- (!Number.isInteger(batch.groupMaxRuns) || batch.groupMaxRuns <= 0)) {
1164
- throw new Error(`batch.groupMaxRuns must be a positive integer when provided, got: ${batch.groupMaxRuns}`);
1165
- }
1166
- return {
1167
- batchMaxSize: batch.maxSize,
1168
- batchMaxIntervalMs,
1169
- batchGroupKey: batch.groupKey,
1170
- batchGroupMaxRuns: batch.groupMaxRuns,
1171
- broadcastOutput: batch.broadcastOutput,
1172
- };
1173
- }
1174
- // Helper function to validate CEL expressions
1175
- function validateCelExpression(_expr) {
1176
- // FIXME: this is a placeholder. In a real implementation, you'd need to use a CEL parser or validator.
1177
- // For now, we'll just return true to mimic the behavior.
1178
- return true;
1179
- }
1180
- function resolveExecutionTimeout(task, workflowDefaults) {
1181
- return (0, duration_1.durationToString)(task.executionTimeout || task.timeout || (workflowDefaults === null || workflowDefaults === void 0 ? void 0 : workflowDefaults.executionTimeout) || '60s');
1182
- }
1183
- function resolveScheduleTimeout(task, workflowDefaults) {
1184
- const value = task.scheduleTimeout || (workflowDefaults === null || workflowDefaults === void 0 ? void 0 : workflowDefaults.scheduleTimeout);
1185
- return value ? (0, duration_1.durationToString)(value) : undefined;
1186
- }