@hatchet-dev/typescript-sdk 1.31.0 → 1.32.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 (46) 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 +21 -16
  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/runtime.d.ts +118 -0
  34. package/v1/client/worker/runtime.js +9 -0
  35. package/v1/client/worker/worker-internal.d.ts +3 -39
  36. package/v1/client/worker/worker-internal.js +22 -393
  37. package/v1/client/worker/worker-runtime.d.ts +8 -0
  38. package/v1/client/worker/worker-runtime.js +67 -0
  39. package/v1/client/worker/workflow-proto.d.ts +76 -0
  40. package/v1/client/worker/workflow-proto.js +482 -0
  41. package/v1/parent-run-context-storage.d.ts +11 -0
  42. package/v1/parent-run-context-storage.js +28 -0
  43. package/v1/parent-run-context-vars.d.ts +15 -1
  44. package/v1/parent-run-context-vars.js +13 -4
  45. package/version.d.ts +1 -1
  46. package/version.js +1 -1
@@ -0,0 +1,76 @@
1
+ import { CreateStepRateLimit, StickyStrategy } from '../../../protoc/workflows';
2
+ import { CreateWorkflowVersionRequest, TaskBatchConfig } from '../../../protoc/v1/workflows';
3
+ import type { DesiredWorkerLabels } from '../../../protoc/v1/shared/trigger';
4
+ import type { BaseWorkflowDeclaration, WorkflowDefinition } from '../../declaration';
5
+ import type { Concurrency, CreateWorkflowDurableTaskOpts, CreateWorkflowTaskOpts } from '../../task';
6
+ import { Duration } from '../duration';
7
+ export declare const ON_FAILURE_TASK_NAME = "on-failure-task";
8
+ export declare const ON_SUCCESS_TASK_NAME = "on-success-task";
9
+ export interface WorkflowProtoOptions {
10
+ /** The namespace prefixed onto the workflow name and its event triggers. */
11
+ namespace?: string;
12
+ /**
13
+ * When true the on-success task is left out, matching how a durable-only registration
14
+ * behaves on the worker. Defaults to false.
15
+ */
16
+ durable?: boolean;
17
+ }
18
+ /**
19
+ * Applies the namespace to a workflow definition and appends the on-success task, if the
20
+ * workflow declares one, as a regular task whose parents are the leaves of the DAG.
21
+ * Returns a new definition; the declaration is not mutated. Idempotent, so a definition
22
+ * that was already normalized comes back unchanged.
23
+ */
24
+ export declare function normalizeWorkflowDefinition(definition: WorkflowDefinition | BaseWorkflowDeclaration<any, any>, opts?: WorkflowProtoOptions): WorkflowDefinition;
25
+ export declare function mapStickyStrategyPb(sticky: WorkflowDefinition['sticky']): StickyStrategy | undefined;
26
+ /**
27
+ * Builds the registration request for a workflow. Accepts a declaration or its
28
+ * definition; the namespace is applied and the on-success task appended the same way
29
+ * `normalizeWorkflowDefinition` does, so callers may pass either the raw declaration or
30
+ * an already normalized definition.
31
+ *
32
+ * Action ids are `<workflow>:<task>`, lowercased (see `createActionId`).
33
+ */
34
+ export declare function workflowToProto(definition: WorkflowDefinition | BaseWorkflowDeclaration<any, any>, opts?: WorkflowProtoOptions): CreateWorkflowVersionRequest;
35
+ export declare function mapWorkerLabelPb(in_: CreateWorkflowTaskOpts<any, any>['desiredWorkerLabels']): Record<string, DesiredWorkerLabels>;
36
+ /** The action id of a workflow's on-failure task. */
37
+ export declare function onFailureTaskName(workflow: Pick<WorkflowDefinition, 'name'>): string;
38
+ export type LeafableTask = CreateWorkflowTaskOpts<any, any> | CreateWorkflowDurableTaskOpts<any, any>;
39
+ export declare function getLeaves(tasks: LeafableTask[]): LeafableTask[];
40
+ export declare function isLeafTask(task: LeafableTask, allTasks: LeafableTask[]): boolean;
41
+ /** Durable tasks stay on the durable pool; slotCost applies only to the default pool. */
42
+ export declare function mapSlotRequestsPb(task: {
43
+ slotRequests?: Record<string, number>;
44
+ slotCost?: number;
45
+ }, isDurable: boolean): Record<string, number>;
46
+ export declare function mapRateLimitPb(limits: CreateWorkflowTaskOpts<any, any>['rateLimits']): CreateStepRateLimit[];
47
+ /** Batch tasks are only available on non-durable tasks; durable tasks never carry `batch`. */
48
+ export declare function batchOf(task: CreateWorkflowTaskOpts<any, any> | CreateWorkflowDurableTaskOpts<any, any>): CreateWorkflowTaskOpts<any, any>['batch'];
49
+ export declare function mapConcurrencyPb(entries: Concurrency[]): {
50
+ expression: string;
51
+ maxRuns: number | undefined;
52
+ limitStrategy: import("../../task").ConcurrencyLimitStrategy | undefined;
53
+ name: string | undefined;
54
+ isTenantScoped: boolean | undefined;
55
+ maxRunsExpression: string | undefined;
56
+ }[];
57
+ export declare function taskConcurrencyArr(task: {
58
+ concurrency?: Concurrency | Concurrency[];
59
+ }, workflow: {
60
+ taskDefaults?: {
61
+ concurrency?: Concurrency | Concurrency[];
62
+ };
63
+ }): Concurrency[];
64
+ export declare function assertValidConcurrencyArr(concurrency: Concurrency[] | undefined): void;
65
+ export declare function mapBatchConfigPb(batch: CreateWorkflowTaskOpts<any, any>['batch']): TaskBatchConfig | undefined;
66
+ export declare function resolveExecutionTimeout(task: {
67
+ executionTimeout?: Duration;
68
+ timeout?: Duration;
69
+ }, workflowDefaults?: {
70
+ executionTimeout?: Duration;
71
+ }): string;
72
+ export declare function resolveScheduleTimeout(task: {
73
+ scheduleTimeout?: Duration;
74
+ }, workflowDefaults?: {
75
+ scheduleTimeout?: Duration;
76
+ }): string | undefined;
@@ -0,0 +1,482 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ var __importDefault = (this && this.__importDefault) || function (mod) {
36
+ return (mod && mod.__esModule) ? mod : { "default": mod };
37
+ };
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ exports.ON_SUCCESS_TASK_NAME = exports.ON_FAILURE_TASK_NAME = void 0;
40
+ exports.normalizeWorkflowDefinition = normalizeWorkflowDefinition;
41
+ exports.mapStickyStrategyPb = mapStickyStrategyPb;
42
+ exports.workflowToProto = workflowToProto;
43
+ exports.mapWorkerLabelPb = mapWorkerLabelPb;
44
+ exports.onFailureTaskName = onFailureTaskName;
45
+ exports.getLeaves = getLeaves;
46
+ exports.isLeafTask = isLeafTask;
47
+ exports.mapSlotRequestsPb = mapSlotRequestsPb;
48
+ exports.mapRateLimitPb = mapRateLimitPb;
49
+ exports.batchOf = batchOf;
50
+ exports.mapConcurrencyPb = mapConcurrencyPb;
51
+ exports.taskConcurrencyArr = taskConcurrencyArr;
52
+ exports.assertValidConcurrencyArr = assertValidConcurrencyArr;
53
+ exports.mapBatchConfigPb = mapBatchConfigPb;
54
+ exports.resolveExecutionTimeout = resolveExecutionTimeout;
55
+ exports.resolveScheduleTimeout = resolveScheduleTimeout;
56
+ /**
57
+ * Turns a workflow declaration into the `CreateWorkflowVersionRequest` the engine
58
+ * registers it under. Pure: nothing here talks to the engine or imports from Node, so
59
+ * a serverless runtime can register the same declarations a worker does.
60
+ * @module WorkflowProto
61
+ */
62
+ const hatchet_error_1 = __importDefault(require("../../../util/errors/hatchet-error"));
63
+ const workflows_1 = require("../../../protoc/workflows");
64
+ const workflows_2 = require("../../../protoc/v1/workflows");
65
+ const transformer_1 = require("../../conditions/transformer");
66
+ const apply_namespace_1 = require("../../../util/apply-namespace");
67
+ const action_1 = require("../../../clients/dispatcher/action");
68
+ const z = __importStar(require("zod/v4"));
69
+ const duration_1 = require("../duration");
70
+ exports.ON_FAILURE_TASK_NAME = 'on-failure-task';
71
+ exports.ON_SUCCESS_TASK_NAME = 'on-success-task';
72
+ function definitionOf(definition) {
73
+ return 'definition' in definition ? definition.definition : definition;
74
+ }
75
+ /**
76
+ * Applies the namespace to a workflow definition and appends the on-success task, if the
77
+ * workflow declares one, as a regular task whose parents are the leaves of the DAG.
78
+ * Returns a new definition; the declaration is not mutated. Idempotent, so a definition
79
+ * that was already normalized comes back unchanged.
80
+ */
81
+ function normalizeWorkflowDefinition(definition, opts = {}) {
82
+ var _a;
83
+ const source = definitionOf(definition);
84
+ // The client lowercases its namespace; doing the same here keeps this idempotent when
85
+ // a caller passes a mixed-case namespace directly.
86
+ const workflow = Object.assign(Object.assign({}, source), { name: (0, apply_namespace_1.applyNamespace)(source.name, (_a = opts.namespace) === null || _a === void 0 ? void 0 : _a.toLowerCase()).toLowerCase(), _tasks: [...source._tasks], _durableTasks: [...source._durableTasks] });
87
+ const alreadyHasOnSuccess = workflow._tasks.some((task) => task.name === exports.ON_SUCCESS_TASK_NAME);
88
+ const onSuccessTask = alreadyHasOnSuccess || opts.durable ? undefined : onSuccessTaskOf(workflow);
89
+ if (onSuccessTask) {
90
+ workflow._tasks.push(onSuccessTask);
91
+ }
92
+ return workflow;
93
+ }
94
+ function onSuccessTaskOf(workflow) {
95
+ var _a, _b, _c, _d, _e, _f, _g;
96
+ if (!workflow.onSuccess) {
97
+ return undefined;
98
+ }
99
+ const parents = getLeaves([...workflow._tasks, ...workflow._durableTasks]);
100
+ if (typeof workflow.onSuccess === 'function') {
101
+ return {
102
+ name: exports.ON_SUCCESS_TASK_NAME,
103
+ fn: workflow.onSuccess,
104
+ executionTimeout: '60s',
105
+ parents,
106
+ retries: 0,
107
+ rateLimits: [],
108
+ desiredWorkerLabels: undefined,
109
+ concurrency: [],
110
+ };
111
+ }
112
+ const onSuccess = workflow.onSuccess;
113
+ return {
114
+ name: exports.ON_SUCCESS_TASK_NAME,
115
+ fn: onSuccess.fn,
116
+ executionTimeout: onSuccess.executionTimeout || ((_a = workflow.taskDefaults) === null || _a === void 0 ? void 0 : _a.executionTimeout) || '60s',
117
+ scheduleTimeout: onSuccess.scheduleTimeout || ((_b = workflow.taskDefaults) === null || _b === void 0 ? void 0 : _b.scheduleTimeout),
118
+ parents,
119
+ retries: onSuccess.retries || ((_c = workflow.taskDefaults) === null || _c === void 0 ? void 0 : _c.retries) || 0,
120
+ rateLimits: onSuccess.rateLimits || ((_d = workflow.taskDefaults) === null || _d === void 0 ? void 0 : _d.rateLimits),
121
+ desiredWorkerLabels: onSuccess.desiredWorkerLabels || ((_e = workflow.taskDefaults) === null || _e === void 0 ? void 0 : _e.workerLabels),
122
+ concurrency: onSuccess.concurrency || ((_f = workflow.taskDefaults) === null || _f === void 0 ? void 0 : _f.concurrency),
123
+ backoff: onSuccess.backoff || ((_g = workflow.taskDefaults) === null || _g === void 0 ? void 0 : _g.backoff),
124
+ };
125
+ }
126
+ function onFailureTaskOf(workflow) {
127
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m;
128
+ if (!workflow.onFailure) {
129
+ return undefined;
130
+ }
131
+ if (typeof workflow.onFailure === 'function') {
132
+ return {
133
+ readableId: exports.ON_FAILURE_TASK_NAME,
134
+ action: onFailureTaskName(workflow),
135
+ timeout: '60s',
136
+ inputs: '{}',
137
+ parents: [],
138
+ retries: 0,
139
+ rateLimits: [],
140
+ workerLabels: {},
141
+ concurrency: [],
142
+ isDurable: false,
143
+ slotRequests: { default: 1 },
144
+ };
145
+ }
146
+ const onFailure = workflow.onFailure;
147
+ const scheduleTimeout = (_a = onFailure.scheduleTimeout) !== null && _a !== void 0 ? _a : (_b = workflow.taskDefaults) === null || _b === void 0 ? void 0 : _b.scheduleTimeout;
148
+ return {
149
+ readableId: exports.ON_FAILURE_TASK_NAME,
150
+ action: onFailureTaskName(workflow),
151
+ timeout: (0, duration_1.durationToString)(onFailure.executionTimeout || ((_c = workflow.taskDefaults) === null || _c === void 0 ? void 0 : _c.executionTimeout) || '60s'),
152
+ scheduleTimeout: scheduleTimeout ? (0, duration_1.durationToString)(scheduleTimeout) : undefined,
153
+ inputs: '{}',
154
+ parents: [],
155
+ retries: onFailure.retries || ((_d = workflow.taskDefaults) === null || _d === void 0 ? void 0 : _d.retries) || 0,
156
+ rateLimits: mapRateLimitPb(onFailure.rateLimits || ((_e = workflow.taskDefaults) === null || _e === void 0 ? void 0 : _e.rateLimits)),
157
+ workerLabels: mapWorkerLabelPb(onFailure.desiredWorkerLabels || ((_f = workflow.taskDefaults) === null || _f === void 0 ? void 0 : _f.workerLabels)),
158
+ concurrency: [],
159
+ 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),
160
+ 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),
161
+ isDurable: false,
162
+ slotRequests: mapSlotRequestsPb(onFailure, false),
163
+ };
164
+ }
165
+ function mapStickyStrategyPb(sticky) {
166
+ // `workflow.sticky` is optional. When omitted, we don't set any sticky strategy.
167
+ //
168
+ // When provided, `workflow.sticky` is a v1 (non-protobuf) config which may also include
169
+ // legacy protobuf enum values for backwards compatibility.
170
+ if (sticky == null) {
171
+ return undefined;
172
+ }
173
+ switch (sticky) {
174
+ case 'soft':
175
+ case 'SOFT':
176
+ case 0:
177
+ return workflows_1.StickyStrategy.SOFT;
178
+ case 'hard':
179
+ case 'HARD':
180
+ case 1:
181
+ return workflows_1.StickyStrategy.HARD;
182
+ default:
183
+ throw new hatchet_error_1.default(`Invalid sticky strategy: ${sticky}`);
184
+ }
185
+ }
186
+ /**
187
+ * Builds the registration request for a workflow. Accepts a declaration or its
188
+ * definition; the namespace is applied and the on-success task appended the same way
189
+ * `normalizeWorkflowDefinition` does, so callers may pass either the raw declaration or
190
+ * an already normalized definition.
191
+ *
192
+ * Action ids are `<workflow>:<task>`, lowercased (see `createActionId`).
193
+ */
194
+ function workflowToProto(definition, opts = {}) {
195
+ var _a, _b;
196
+ const workflow = normalizeWorkflowDefinition(definition, opts);
197
+ const { namespace } = opts;
198
+ const { concurrency } = workflow;
199
+ const eventTriggers = [
200
+ ...(workflow.onEvents || []).map((event) => (0, apply_namespace_1.applyNamespace)(event, namespace)),
201
+ ...(workflow.on && 'event' in workflow.on && workflow.on.event
202
+ ? Array.isArray(workflow.on.event)
203
+ ? workflow.on.event.map((event) => (0, apply_namespace_1.applyNamespace)(event, namespace))
204
+ : [(0, apply_namespace_1.applyNamespace)(workflow.on.event, namespace)]
205
+ : []),
206
+ ];
207
+ const cronTriggers = [
208
+ ...(workflow.onCrons || []),
209
+ ...(workflow.on && 'cron' in workflow.on && workflow.on.cron
210
+ ? Array.isArray(workflow.on.cron)
211
+ ? workflow.on.cron
212
+ : [workflow.on.cron]
213
+ : []),
214
+ ];
215
+ const concurrencyArr = Array.isArray(concurrency) ? concurrency : [];
216
+ const concurrencySolo = !Array.isArray(concurrency) ? concurrency : undefined;
217
+ assertValidConcurrencyArr(concurrencyArr);
218
+ assertValidConcurrencyArr(concurrencySolo ? [concurrencySolo] : undefined);
219
+ // Convert Zod schema to JSON Schema if provided
220
+ let inputJsonSchema;
221
+ if (workflow.inputValidator) {
222
+ const jsonSchema = z.toJSONSchema(workflow.inputValidator);
223
+ inputJsonSchema = new TextEncoder().encode(JSON.stringify(jsonSchema));
224
+ }
225
+ const durableTaskSet = new Set(workflow._durableTasks);
226
+ return {
227
+ name: workflow.name,
228
+ description: workflow.description || '',
229
+ version: workflow.version || '',
230
+ eventTriggers,
231
+ cronTriggers,
232
+ sticky: mapStickyStrategyPb(workflow.sticky),
233
+ concurrencyArr: mapConcurrencyPb(concurrencyArr),
234
+ onFailureTask: onFailureTaskOf(workflow),
235
+ defaultPriority: workflow.defaultPriority,
236
+ inputJsonSchema,
237
+ tasks: [...workflow._tasks, ...workflow._durableTasks].map((task) => {
238
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
239
+ return ({
240
+ readableId: task.name,
241
+ action: (0, action_1.createActionId)(workflow.name, task.name),
242
+ timeout: resolveExecutionTimeout(task, workflow.taskDefaults),
243
+ scheduleTimeout: resolveScheduleTimeout(task, workflow.taskDefaults),
244
+ inputs: '{}',
245
+ parents: (_b = (_a = task.parents) === null || _a === void 0 ? void 0 : _a.map((p) => p.name)) !== null && _b !== void 0 ? _b : [],
246
+ userData: '{}',
247
+ // Batch tasks buffer many concurrent runs into a single execution; per-item retry
248
+ // semantics don't apply, so retries is always forced to 0.
249
+ retries: batchOf(task) ? 0 : task.retries || ((_c = workflow.taskDefaults) === null || _c === void 0 ? void 0 : _c.retries) || 0,
250
+ rateLimits: mapRateLimitPb(task.rateLimits || ((_d = workflow.taskDefaults) === null || _d === void 0 ? void 0 : _d.rateLimits)),
251
+ workerLabels: mapWorkerLabelPb(task.desiredWorkerLabels || ((_e = workflow.taskDefaults) === null || _e === void 0 ? void 0 : _e.workerLabels)),
252
+ 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),
253
+ 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),
254
+ conditions: (0, transformer_1.taskConditionsToPb)(task, namespace),
255
+ isDurable: durableTaskSet.has(task),
256
+ slotRequests: mapSlotRequestsPb(task, durableTaskSet.has(task)),
257
+ batch: mapBatchConfigPb(batchOf(task)),
258
+ concurrency: (() => {
259
+ const taskConcurrency = taskConcurrencyArr(task, workflow);
260
+ assertValidConcurrencyArr(taskConcurrency);
261
+ return mapConcurrencyPb(taskConcurrency);
262
+ })(),
263
+ });
264
+ }),
265
+ concurrency: concurrencySolo ? mapConcurrencyPb([concurrencySolo])[0] : undefined,
266
+ defaultFilters: (_b = (_a = workflow.defaultFilters) === null || _a === void 0 ? void 0 : _a.map((f) => ({
267
+ scope: f.scope,
268
+ expression: f.expression,
269
+ payload: f.payload ? new TextEncoder().encode(JSON.stringify(f.payload)) : undefined,
270
+ }))) !== null && _b !== void 0 ? _b : [],
271
+ idempotency: workflow.idempotency
272
+ ? {
273
+ expression: workflow.idempotency.expression,
274
+ ttlMs: workflow.idempotency.strategy === 'status'
275
+ ? workflow.idempotency.fallbackTtlMs
276
+ : workflow.idempotency.ttlMs,
277
+ method: workflow.idempotency.strategy === 'status'
278
+ ? workflows_2.IdempotencyMethod.STATUS
279
+ : workflows_2.IdempotencyMethod.TTL,
280
+ }
281
+ : undefined,
282
+ };
283
+ }
284
+ function mapWorkerLabelPb(in_) {
285
+ if (!in_) {
286
+ return {};
287
+ }
288
+ return Object.entries(in_).reduce((acc, [key, label]) => {
289
+ if (!label) {
290
+ return Object.assign(Object.assign({}, acc), { [key]: {
291
+ strValue: undefined,
292
+ intValue: undefined,
293
+ } });
294
+ }
295
+ if (typeof label === 'string') {
296
+ return Object.assign(Object.assign({}, acc), { [key]: {
297
+ strValue: label,
298
+ intValue: undefined,
299
+ } });
300
+ }
301
+ if (typeof label === 'number') {
302
+ return Object.assign(Object.assign({}, acc), { [key]: {
303
+ strValue: undefined,
304
+ intValue: label,
305
+ } });
306
+ }
307
+ return Object.assign(Object.assign({}, acc), { [key]: {
308
+ strValue: typeof label.value === 'string' ? label.value : undefined,
309
+ intValue: typeof label.value === 'number' ? label.value : undefined,
310
+ required: label.required,
311
+ weight: label.weight,
312
+ comparator: label.comparator,
313
+ } });
314
+ }, {});
315
+ }
316
+ /** The action id of a workflow's on-failure task. */
317
+ function onFailureTaskName(workflow) {
318
+ return (0, action_1.createActionId)(workflow.name, exports.ON_FAILURE_TASK_NAME);
319
+ }
320
+ function getLeaves(tasks) {
321
+ return tasks.filter((task) => isLeafTask(task, tasks));
322
+ }
323
+ function isLeafTask(task, allTasks) {
324
+ return !allTasks.some((t) => { var _a; return (_a = t.parents) === null || _a === void 0 ? void 0 : _a.some((p) => p.name === task.name); });
325
+ }
326
+ /** Durable tasks stay on the durable pool; slotCost applies only to the default pool. */
327
+ function mapSlotRequestsPb(task, isDurable) {
328
+ if (task.slotRequests) {
329
+ return task.slotRequests;
330
+ }
331
+ if (isDurable) {
332
+ return { durable: 1 };
333
+ }
334
+ if (task.slotCost !== undefined) {
335
+ if (!Number.isInteger(task.slotCost) || task.slotCost <= 0) {
336
+ throw new Error(`slotCost must be a positive integer, got: ${task.slotCost}`);
337
+ }
338
+ return { default: task.slotCost };
339
+ }
340
+ return { default: 1 };
341
+ }
342
+ function mapRateLimitPb(limits) {
343
+ if (!limits) {
344
+ return [];
345
+ }
346
+ return limits.map((l) => {
347
+ let key = l.staticKey;
348
+ const keyExpression = l.dynamicKey;
349
+ if (l.key !== undefined) {
350
+ console.warn('key is deprecated and will be removed in a future release, please use staticKey instead');
351
+ ({ key } = l);
352
+ }
353
+ if (keyExpression !== undefined) {
354
+ if (key !== undefined) {
355
+ throw new Error('Cannot have both static key and dynamic key set');
356
+ }
357
+ key = keyExpression;
358
+ if (!validateCelExpression(keyExpression)) {
359
+ throw new Error(`Invalid CEL expression: ${keyExpression}`);
360
+ }
361
+ }
362
+ if (key === undefined) {
363
+ throw new Error(`Invalid key`);
364
+ }
365
+ let units;
366
+ let unitsExpression;
367
+ if (typeof l.units === 'number') {
368
+ ({ units } = l);
369
+ }
370
+ else {
371
+ if (!validateCelExpression(l.units)) {
372
+ throw new Error(`Invalid CEL expression: ${l.units}`);
373
+ }
374
+ unitsExpression = l.units;
375
+ }
376
+ let limitExpression;
377
+ if (l.limit !== undefined) {
378
+ if (typeof l.limit === 'number') {
379
+ limitExpression = `${l.limit}`;
380
+ }
381
+ else {
382
+ if (!validateCelExpression(l.limit)) {
383
+ throw new Error(`Invalid CEL expression: ${l.limit}`);
384
+ }
385
+ limitExpression = l.limit;
386
+ }
387
+ }
388
+ if (keyExpression !== undefined && limitExpression === undefined) {
389
+ throw new Error('CEL based keys requires limit to be set');
390
+ }
391
+ if (limitExpression === undefined) {
392
+ limitExpression = `-1`;
393
+ }
394
+ return {
395
+ key,
396
+ keyExpr: keyExpression,
397
+ units,
398
+ unitsExpr: unitsExpression,
399
+ limitValuesExpr: limitExpression,
400
+ duration: l.duration,
401
+ };
402
+ });
403
+ }
404
+ /** Batch tasks are only available on non-durable tasks; durable tasks never carry `batch`. */
405
+ function batchOf(task) {
406
+ return 'batch' in task ? task.batch : undefined;
407
+ }
408
+ // mapConcurrencyPb maps SDK concurrency entries onto the proto shape; entries keep their
409
+ // declared order, which is the chain order.
410
+ function mapConcurrencyPb(entries) {
411
+ return entries.map((c) => ({
412
+ expression: c.expression,
413
+ // a string maxRuns is a CEL expression; the static field then carries the default
414
+ // of 1, which only governs slots created before the expression existed
415
+ maxRuns: typeof c.maxRuns === 'string' ? 1 : c.maxRuns,
416
+ limitStrategy: c.limitStrategy,
417
+ name: c.name,
418
+ isTenantScoped: c.isTenantScoped,
419
+ maxRunsExpression: typeof c.maxRuns === 'string' ? c.maxRuns : undefined,
420
+ }));
421
+ }
422
+ function taskConcurrencyArr(task, workflow) {
423
+ var _a;
424
+ if (task.concurrency) {
425
+ return Array.isArray(task.concurrency) ? task.concurrency : [task.concurrency];
426
+ }
427
+ if ((_a = workflow.taskDefaults) === null || _a === void 0 ? void 0 : _a.concurrency) {
428
+ return Array.isArray(workflow.taskDefaults.concurrency)
429
+ ? workflow.taskDefaults.concurrency
430
+ : [workflow.taskDefaults.concurrency];
431
+ }
432
+ return [];
433
+ }
434
+ function assertValidConcurrencyArr(concurrency) {
435
+ concurrency === null || concurrency === void 0 ? void 0 : concurrency.forEach((c) => {
436
+ if (typeof c.maxRuns === 'string') {
437
+ if (!c.maxRuns.trim()) {
438
+ throw new Error('concurrency.maxRuns expression must be non-empty');
439
+ }
440
+ return;
441
+ }
442
+ if (c.maxRuns !== undefined && (!Number.isInteger(c.maxRuns) || c.maxRuns <= 0)) {
443
+ throw new Error(`concurrency.maxRuns must be a positive integer or a CEL expression, got: ${c.maxRuns}`);
444
+ }
445
+ });
446
+ }
447
+ function mapBatchConfigPb(batch) {
448
+ if (!batch) {
449
+ return undefined;
450
+ }
451
+ if (!Number.isInteger(batch.maxSize) || batch.maxSize <= 0) {
452
+ throw new Error(`batch.maxSize must be a positive integer, got: ${batch.maxSize}`);
453
+ }
454
+ const batchMaxIntervalMs = batch.maxInterval !== undefined ? (0, duration_1.durationToMs)(batch.maxInterval) : undefined;
455
+ if (batchMaxIntervalMs !== undefined && batchMaxIntervalMs <= 0) {
456
+ throw new Error('batch.maxInterval must be positive when provided');
457
+ }
458
+ if (batch.groupMaxRuns !== undefined &&
459
+ (!Number.isInteger(batch.groupMaxRuns) || batch.groupMaxRuns <= 0)) {
460
+ throw new Error(`batch.groupMaxRuns must be a positive integer when provided, got: ${batch.groupMaxRuns}`);
461
+ }
462
+ return {
463
+ batchMaxSize: batch.maxSize,
464
+ batchMaxIntervalMs,
465
+ batchGroupKey: batch.groupKey,
466
+ batchGroupMaxRuns: batch.groupMaxRuns,
467
+ broadcastOutput: batch.broadcastOutput,
468
+ };
469
+ }
470
+ // Helper function to validate CEL expressions
471
+ function validateCelExpression(_expr) {
472
+ // FIXME: this is a placeholder. In a real implementation, you'd need to use a CEL parser or validator.
473
+ // For now, we'll just return true to mimic the behavior.
474
+ return true;
475
+ }
476
+ function resolveExecutionTimeout(task, workflowDefaults) {
477
+ return (0, duration_1.durationToString)(task.executionTimeout || task.timeout || (workflowDefaults === null || workflowDefaults === void 0 ? void 0 : workflowDefaults.executionTimeout) || '60s');
478
+ }
479
+ function resolveScheduleTimeout(task, workflowDefaults) {
480
+ const value = task.scheduleTimeout || (workflowDefaults === null || workflowDefaults === void 0 ? void 0 : workflowDefaults.scheduleTimeout);
481
+ return value ? (0, duration_1.durationToString)(value) : undefined;
482
+ }
@@ -0,0 +1,11 @@
1
+ import { ParentRunContextStorage } from './parent-run-context-vars';
2
+ /**
3
+ * `AsyncLocalStorage` backed store for the parent run context, so a task's `run()`,
4
+ * `spawnChild` and event pushes see the task they were called from across awaits.
5
+ */
6
+ export declare function createAsyncLocalParentRunContextStorage(): ParentRunContextStorage;
7
+ /**
8
+ * Installs the `AsyncLocalStorage` store on the shared manager. Idempotent; the worker
9
+ * calls it at module load so it is in place before any task runs.
10
+ */
11
+ export declare function installAsyncLocalParentRunContext(): void;
@@ -0,0 +1,28 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createAsyncLocalParentRunContextStorage = createAsyncLocalParentRunContextStorage;
4
+ exports.installAsyncLocalParentRunContext = installAsyncLocalParentRunContext;
5
+ const async_hooks_1 = require("async_hooks");
6
+ const parent_run_context_vars_1 = require("./parent-run-context-vars");
7
+ /**
8
+ * `AsyncLocalStorage` backed store for the parent run context, so a task's `run()`,
9
+ * `spawnChild` and event pushes see the task they were called from across awaits.
10
+ */
11
+ function createAsyncLocalParentRunContextStorage() {
12
+ const storage = new async_hooks_1.AsyncLocalStorage();
13
+ return {
14
+ run: (context, fn) => storage.run(context, fn),
15
+ getStore: () => storage.getStore(),
16
+ };
17
+ }
18
+ let installed = false;
19
+ /**
20
+ * Installs the `AsyncLocalStorage` store on the shared manager. Idempotent; the worker
21
+ * calls it at module load so it is in place before any task runs.
22
+ */
23
+ function installAsyncLocalParentRunContext() {
24
+ if (installed)
25
+ return;
26
+ parent_run_context_vars_1.parentRunContextManager.useStorage(createAsyncLocalParentRunContextStorage());
27
+ installed = true;
28
+ }
@@ -18,9 +18,23 @@ export interface ParentRunContext {
18
18
  */
19
19
  durableContext?: DurableContext<unknown, unknown>;
20
20
  }
21
+ /**
22
+ * Where the manager keeps the context of the task currently executing. Node workers
23
+ * install an `AsyncLocalStorage` backed store (see `parent-run-context-storage.ts`);
24
+ * runtimes without async context tracking keep the default store, which never has a
25
+ * context.
26
+ */
27
+ export interface ParentRunContextStorage {
28
+ run<T>(context: ParentRunContext, fn: () => T): T;
29
+ getStore(): ParentRunContext | undefined;
30
+ }
21
31
  export declare class ParentRunContextManager {
22
32
  private storage;
23
- constructor();
33
+ constructor(storage?: ParentRunContextStorage);
34
+ /**
35
+ * Replaces the store used for subsequent `runWithContext` and `getContext` calls.
36
+ */
37
+ useStorage(storage: ParentRunContextStorage): void;
24
38
  runWithContext<T>(opts: ParentRunContext, fn: () => T): T;
25
39
  incrementChildIndex(n: number): void;
26
40
  getContext(): ParentRunContext | undefined;
@@ -1,10 +1,19 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.parentRunContextManager = exports.ParentRunContextManager = void 0;
4
- const async_hooks_1 = require("async_hooks");
4
+ const noContextStorage = {
5
+ run: (_context, fn) => fn(),
6
+ getStore: () => undefined,
7
+ };
5
8
  class ParentRunContextManager {
6
- constructor() {
7
- this.storage = new async_hooks_1.AsyncLocalStorage();
9
+ constructor(storage = noContextStorage) {
10
+ this.storage = storage;
11
+ }
12
+ /**
13
+ * Replaces the store used for subsequent `runWithContext` and `getContext` calls.
14
+ */
15
+ useStorage(storage) {
16
+ this.storage = storage;
8
17
  }
9
18
  runWithContext(opts, fn) {
10
19
  return this.storage.run(Object.assign({}, opts), fn);
@@ -13,7 +22,7 @@ class ParentRunContextManager {
13
22
  var _a;
14
23
  const parentRunContext = this.getContext();
15
24
  if (parentRunContext) {
16
- // Mutate in place do NOT use enterWith here.
25
+ // Mutate in place, do NOT use enterWith here.
17
26
  // storage.run() gives every async descendant the same object reference,
18
27
  // so direct mutation is visible across all await boundaries within the
19
28
  // same task execution. enterWith would replace the object, and the new
package/version.d.ts CHANGED
@@ -1 +1 @@
1
- export declare const HATCHET_VERSION = "1.31.0";
1
+ export declare const HATCHET_VERSION = "1.32.0";
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.31.0';
4
+ exports.HATCHET_VERSION = '1.32.0';