@trigger.dev/sdk 3.0.0-beta.26 → 3.0.0-beta.28

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/dist/v3/index.mjs CHANGED
@@ -1,7 +1,7 @@
1
- import { SpanKind, trace, context, SpanStatusCode } from '@opentelemetry/api';
2
- import { SEMATTRS_MESSAGING_OPERATION, SEMATTRS_MESSAGING_DESTINATION, SEMATTRS_MESSAGING_SYSTEM, SEMATTRS_HTTP_STATUS_CODE, SEMATTRS_HTTP_METHOD, SEMATTRS_HTTP_URL, SEMATTRS_HTTP_HOST, SEMATTRS_HTTP_SCHEME, SEMATTRS_HTTP_RESPONSE_CONTENT_LENGTH } from '@opentelemetry/semantic-conventions';
3
- import { TriggerTracer, runtime, SemanticInternalAttributes, accessoryAttributes, apiClientManager, taskCatalog, stringifyIO, taskContext, logger, defaultRetryOptions, conditionallyImportPacket, parsePacket, createErrorTaskError, calculateNextRetryDelay, defaultFetchRetryOptions, calculateResetAt, eventFilterMatches, flattenAttributes } from '@trigger.dev/core/v3';
1
+ import { TriggerTracer, SemanticInternalAttributes, runtime, accessoryAttributes, apiClientManager, taskCatalog, defaultRetryOptions, calculateNextRetryDelay, defaultFetchRetryOptions, calculateResetAt, eventFilterMatches, flattenAttributes, stringifyIO, taskContext, logger, conditionallyImportPacket, parsePacket, createErrorTaskError } from '@trigger.dev/core/v3';
4
2
  export { APIError, AuthenticationError, BadRequestError, ConflictError, InternalServerError, NotFoundError, PermissionDeniedError, RateLimitError, UnprocessableEntityError, logger } from '@trigger.dev/core/v3';
3
+ import { trace, context, SpanStatusCode, SpanKind } from '@opentelemetry/api';
4
+ import { SEMATTRS_HTTP_STATUS_CODE, SEMATTRS_HTTP_METHOD, SEMATTRS_HTTP_URL, SEMATTRS_HTTP_HOST, SEMATTRS_HTTP_SCHEME, SEMATTRS_HTTP_RESPONSE_CONTENT_LENGTH, SEMATTRS_MESSAGING_OPERATION, SEMATTRS_MESSAGING_DESTINATION, SEMATTRS_MESSAGING_SYSTEM } from '@opentelemetry/semantic-conventions';
5
5
  import { AsyncLocalStorage } from 'node:async_hooks';
6
6
 
7
7
  var __defProp = Object.defineProperty;
@@ -17,921 +17,932 @@ var __publicField = (obj, key, value) => {
17
17
  };
18
18
 
19
19
  // package.json
20
- var version = "3.0.0-beta.26";
20
+ var version = "3.0.0-beta.28";
21
+
22
+ // src/v3/tracer.ts
21
23
  var tracer = new TriggerTracer({
22
24
  name: "@trigger.dev/sdk",
23
25
  version
24
26
  });
25
27
 
26
- // src/v3/shared.ts
27
- function queue(options) {
28
- return options;
29
- }
30
- __name(queue, "queue");
31
- function createTask(params) {
32
- const task3 = {
33
- id: params.id,
34
- trigger: async (payload, options) => {
35
- const apiClient = apiClientManager.client;
36
- if (!apiClient) {
37
- throw apiClientMissingError();
28
+ // src/v3/cache.ts
29
+ var _InMemoryCache = class _InMemoryCache {
30
+ constructor() {
31
+ __publicField(this, "_cache", /* @__PURE__ */ new Map());
32
+ }
33
+ get(key) {
34
+ return this._cache.get(key);
35
+ }
36
+ set(key, value) {
37
+ this._cache.set(key, value);
38
+ return void 0;
39
+ }
40
+ delete(key) {
41
+ this._cache.delete(key);
42
+ return void 0;
43
+ }
44
+ };
45
+ __name(_InMemoryCache, "InMemoryCache");
46
+ var InMemoryCache = _InMemoryCache;
47
+ function createCache(store) {
48
+ return /* @__PURE__ */ __name(function cache(cacheKey, fn) {
49
+ return tracer.startActiveSpan("cache", async (span) => {
50
+ span.setAttribute("cache.key", cacheKey);
51
+ span.setAttribute(SemanticInternalAttributes.STYLE_ICON, "device-sd-card");
52
+ const cacheEntry = await store.get(cacheKey);
53
+ if (cacheEntry) {
54
+ span.updateName(`cache.hit ${cacheKey}`);
55
+ return cacheEntry.value;
38
56
  }
39
- const taskMetadata = taskCatalog.getTaskMetadata(params.id);
40
- const payloadPacket = await stringifyIO(payload);
41
- const handle = await tracer.startActiveSpan(taskMetadata ? "Trigger" : `${params.id} trigger()`, async (span) => {
42
- const response = await apiClient.triggerTask(params.id, {
43
- payload: payloadPacket.data,
44
- options: {
45
- queue: params.queue,
46
- concurrencyKey: options?.concurrencyKey,
47
- test: taskContext.ctx?.run.isTest,
48
- payloadType: payloadPacket.dataType,
49
- idempotencyKey: options?.idempotencyKey
50
- }
51
- }, {
52
- spanParentAsLink: true
53
- });
54
- span.setAttribute("messaging.message.id", response.id);
55
- return response;
57
+ span.updateName(`cache.miss ${cacheKey}`);
58
+ const value = await tracer.startActiveSpan("cache.getFreshValue", async (span2) => {
59
+ return await fn();
56
60
  }, {
57
- kind: SpanKind.PRODUCER,
58
61
  attributes: {
59
- [SEMATTRS_MESSAGING_OPERATION]: "publish",
60
- [SemanticInternalAttributes.STYLE_ICON]: "trigger",
61
- ["messaging.client_id"]: taskContext.worker?.id,
62
- [SEMATTRS_MESSAGING_DESTINATION]: params.queue?.name ?? params.id,
63
- [SEMATTRS_MESSAGING_SYSTEM]: "trigger.dev",
64
- ...taskMetadata ? accessoryAttributes({
65
- items: [
66
- {
67
- text: `${taskMetadata.exportName}.trigger()`,
68
- variant: "normal"
69
- }
70
- ],
71
- style: "codepath"
72
- }) : {}
62
+ "cache.key": cacheKey,
63
+ [SemanticInternalAttributes.STYLE_ICON]: "device-sd-card"
73
64
  }
74
65
  });
75
- return handle;
76
- },
77
- batchTrigger: async (items) => {
78
- const apiClient = apiClientManager.client;
79
- if (!apiClient) {
80
- throw apiClientMissingError();
81
- }
82
- const taskMetadata = taskCatalog.getTaskMetadata(params.id);
83
- const response = await tracer.startActiveSpan(taskMetadata ? "Batch trigger" : `${params.id} batchTrigger()`, async (span) => {
84
- const response2 = await apiClient.batchTriggerTask(params.id, {
85
- items: await Promise.all(items.map(async (item) => {
86
- const payloadPacket = await stringifyIO(item.payload);
87
- return {
88
- payload: payloadPacket.data,
89
- options: {
90
- queue: item.options?.queue ?? params.queue,
91
- concurrencyKey: item.options?.concurrencyKey,
92
- test: taskContext.ctx?.run.isTest,
93
- payloadType: payloadPacket.dataType,
94
- idempotencyKey: item.options?.idempotencyKey
95
- }
96
- };
97
- }))
98
- }, {
99
- spanParentAsLink: true
66
+ await tracer.startActiveSpan("cache.set", async (span2) => {
67
+ await store.set(cacheKey, {
68
+ value,
69
+ metadata: {
70
+ createdTime: Date.now()
71
+ }
100
72
  });
101
- span.setAttribute("messaging.message.id", response2.batchId);
102
- return response2;
103
73
  }, {
104
- kind: SpanKind.PRODUCER,
105
74
  attributes: {
106
- [SEMATTRS_MESSAGING_OPERATION]: "publish",
107
- ["messaging.batch.message_count"]: items.length,
108
- ["messaging.client_id"]: taskContext.worker?.id,
109
- [SEMATTRS_MESSAGING_DESTINATION]: params.queue?.name ?? params.id,
110
- [SEMATTRS_MESSAGING_SYSTEM]: "trigger.dev",
111
- [SemanticInternalAttributes.STYLE_ICON]: "trigger",
112
- ...taskMetadata ? accessoryAttributes({
75
+ "cache.key": cacheKey,
76
+ [SemanticInternalAttributes.STYLE_ICON]: "device-sd-card"
77
+ }
78
+ });
79
+ return value;
80
+ });
81
+ }, "cache");
82
+ }
83
+ __name(createCache, "createCache");
84
+ function onThrow(fn, options) {
85
+ const opts = {
86
+ ...defaultRetryOptions,
87
+ ...options
88
+ };
89
+ return tracer.startActiveSpan(`retry.onThrow()`, async (span) => {
90
+ let attempt = 1;
91
+ while (attempt <= opts.maxAttempts) {
92
+ const innerSpan = tracer.startSpan("retry.fn()", {
93
+ attributes: {
94
+ [SemanticInternalAttributes.STYLE_ICON]: "function",
95
+ ...accessoryAttributes({
113
96
  items: [
114
97
  {
115
- text: `${taskMetadata.exportName}.batchTrigger()`,
98
+ text: `${attempt}/${opts.maxAttempts}`,
116
99
  variant: "normal"
117
100
  }
118
101
  ],
119
102
  style: "codepath"
120
- }) : {}
103
+ })
121
104
  }
122
105
  });
123
- return response;
124
- },
125
- triggerAndWait: async (payload, options) => {
126
- const ctx = taskContext.ctx;
127
- if (!ctx) {
128
- throw new Error("triggerAndWait can only be used from inside a task.run()");
129
- }
130
- const apiClient = apiClientManager.client;
131
- if (!apiClient) {
132
- throw apiClientMissingError();
133
- }
134
- const taskMetadata = taskCatalog.getTaskMetadata(params.id);
135
- const payloadPacket = await stringifyIO(payload);
136
- return await tracer.startActiveSpan(taskMetadata ? "Trigger" : `${params.id} triggerAndWait()`, async (span) => {
137
- const response = await apiClient.triggerTask(params.id, {
138
- payload: payloadPacket.data,
139
- options: {
140
- dependentAttempt: ctx.attempt.id,
141
- lockToVersion: taskContext.worker?.version,
142
- queue: params.queue,
143
- concurrencyKey: options?.concurrencyKey,
144
- test: taskContext.ctx?.run.isTest,
145
- payloadType: payloadPacket.dataType,
146
- idempotencyKey: options?.idempotencyKey
147
- }
106
+ const contextWithSpanSet = trace.setSpan(context.active(), innerSpan);
107
+ try {
108
+ const result = await context.with(contextWithSpanSet, async () => {
109
+ return fn({
110
+ attempt,
111
+ maxAttempts: opts.maxAttempts
112
+ });
148
113
  });
149
- span.setAttribute("messaging.message.id", response.id);
150
- if (options?.idempotencyKey) {
151
- const result2 = await apiClient.getRunResult(response.id);
152
- if (result2) {
153
- logger.log(`Result reused from previous task run with idempotency key '${options.idempotencyKey}'.`, {
154
- runId: response.id,
155
- idempotencyKey: options.idempotencyKey
156
- });
157
- return await handleTaskRunExecutionResult(result2);
158
- }
114
+ innerSpan.end();
115
+ return result;
116
+ } catch (e) {
117
+ if (e instanceof Error || typeof e === "string") {
118
+ innerSpan.recordException(e);
119
+ } else {
120
+ innerSpan.recordException(String(e));
159
121
  }
160
- const result = await runtime.waitForTask({
161
- id: response.id,
162
- ctx
122
+ innerSpan.setStatus({
123
+ code: SpanStatusCode.ERROR
163
124
  });
164
- return await handleTaskRunExecutionResult(result);
165
- }, {
166
- kind: SpanKind.PRODUCER,
167
- attributes: {
168
- [SemanticInternalAttributes.STYLE_ICON]: "trigger",
169
- [SEMATTRS_MESSAGING_OPERATION]: "publish",
170
- ["messaging.client_id"]: taskContext.worker?.id,
171
- [SEMATTRS_MESSAGING_DESTINATION]: params.queue?.name ?? params.id,
172
- [SEMATTRS_MESSAGING_SYSTEM]: "trigger.dev",
173
- ...taskMetadata ? accessoryAttributes({
174
- items: [
175
- {
176
- text: `${taskMetadata.exportName}.triggerAndWait()`,
177
- variant: "normal"
178
- }
179
- ],
180
- style: "codepath"
181
- }) : {}
125
+ const nextRetryDelay = calculateNextRetryDelay(opts, attempt);
126
+ if (!nextRetryDelay) {
127
+ innerSpan.end();
128
+ throw e;
182
129
  }
183
- });
184
- },
185
- batchTriggerAndWait: async (items) => {
186
- const ctx = taskContext.ctx;
187
- if (!ctx) {
188
- throw new Error("batchTriggerAndWait can only be used from inside a task.run()");
189
- }
190
- const apiClient = apiClientManager.client;
191
- if (!apiClient) {
192
- throw apiClientMissingError();
130
+ innerSpan.setAttribute(SemanticInternalAttributes.RETRY_AT, new Date(Date.now() + nextRetryDelay).toISOString());
131
+ innerSpan.setAttribute(SemanticInternalAttributes.RETRY_COUNT, attempt);
132
+ innerSpan.setAttribute(SemanticInternalAttributes.RETRY_DELAY, `${nextRetryDelay}ms`);
133
+ innerSpan.end();
134
+ await runtime.waitForDuration(nextRetryDelay);
135
+ } finally {
136
+ attempt++;
193
137
  }
194
- const taskMetadata = taskCatalog.getTaskMetadata(params.id);
195
- return await tracer.startActiveSpan(taskMetadata ? "Batch trigger" : `${params.id} batchTriggerAndWait()`, async (span) => {
196
- const response = await apiClient.batchTriggerTask(params.id, {
197
- items: await Promise.all(items.map(async (item) => {
198
- const payloadPacket = await stringifyIO(item.payload);
199
- return {
200
- payload: payloadPacket.data,
201
- options: {
202
- lockToVersion: taskContext.worker?.version,
203
- queue: item.options?.queue ?? params.queue,
204
- concurrencyKey: item.options?.concurrencyKey,
205
- test: taskContext.ctx?.run.isTest,
206
- payloadType: payloadPacket.dataType,
207
- idempotencyKey: item.options?.idempotencyKey
208
- }
209
- };
210
- })),
211
- dependentAttempt: ctx.attempt.id
212
- });
213
- span.setAttribute("messaging.message.id", response.batchId);
214
- const getBatchResults = /* @__PURE__ */ __name(async () => {
215
- const hasIdempotencyKey = items.some((item) => item.options?.idempotencyKey);
216
- if (hasIdempotencyKey) {
217
- const results = await apiClient.getBatchResults(response.batchId);
218
- if (results) {
219
- return results;
220
- }
138
+ }
139
+ throw new Error("Max attempts reached");
140
+ }, {
141
+ attributes: {
142
+ [SemanticInternalAttributes.STYLE_ICON]: "arrow-capsule"
143
+ }
144
+ });
145
+ }
146
+ __name(onThrow, "onThrow");
147
+ var normalizeUrlFromInput = /* @__PURE__ */ __name((input) => {
148
+ if (typeof input === "string") {
149
+ return new URL(input);
150
+ }
151
+ if (input instanceof URL) {
152
+ return input;
153
+ }
154
+ return new URL(input.url);
155
+ }, "normalizeUrlFromInput");
156
+ var normalizeHttpMethod = /* @__PURE__ */ __name((input, init) => {
157
+ if (typeof input === "string" || input instanceof URL) {
158
+ return (init?.method || "GET").toUpperCase();
159
+ }
160
+ return (input.method ?? init?.method ?? "GET").toUpperCase();
161
+ }, "normalizeHttpMethod");
162
+ var fetchHttpHandlerStorage = new AsyncLocalStorage();
163
+ var fetchWithInterceptors = /* @__PURE__ */ __name(async (input, init) => {
164
+ const handlers = fetchHttpHandlerStorage.getStore();
165
+ if (handlers) {
166
+ try {
167
+ const { getResponse } = await import('msw');
168
+ const request = new Request(input, init);
169
+ const response = await getResponse(handlers, request);
170
+ if (response) {
171
+ return response;
172
+ }
173
+ } catch (e) {
174
+ return fetch(input, init);
175
+ }
176
+ }
177
+ return fetch(input, init);
178
+ }, "fetchWithInterceptors");
179
+ var _a;
180
+ var FetchErrorWithSpan = (_a = class extends Error {
181
+ constructor(originalError, span) {
182
+ super("Fetch error");
183
+ this.originalError = originalError;
184
+ this.span = span;
185
+ }
186
+ }, __name(_a, "FetchErrorWithSpan"), _a);
187
+ var MAX_ATTEMPTS = 10;
188
+ async function retryFetch(input, init) {
189
+ return tracer.startActiveSpan("retry.fetch()", async (span) => {
190
+ let attempt = 1;
191
+ while (true) {
192
+ try {
193
+ const abortController = new AbortController();
194
+ const timeoutId = init?.timeoutInMs ? setTimeout(() => {
195
+ abortController.abort();
196
+ }, init?.timeoutInMs) : void 0;
197
+ init?.signal?.addEventListener("abort", () => {
198
+ abortController.abort();
199
+ });
200
+ const [response, span2] = await doFetchRequest(input, {
201
+ ...init ?? {},
202
+ signal: abortController.signal
203
+ }, attempt);
204
+ if (timeoutId) {
205
+ clearTimeout(timeoutId);
206
+ }
207
+ if (response.ok) {
208
+ span2.setAttributes(createFetchResponseAttributes(response));
209
+ span2.end();
210
+ return response;
211
+ }
212
+ const nextRetry = await calculateRetryDelayForResponse(resolveDefaults(init?.retry, "byStatus", defaultFetchRetryOptions.byStatus), response, attempt);
213
+ if (!nextRetry) {
214
+ span2.setAttributes(createFetchResponseAttributes(response));
215
+ span2.end();
216
+ return response;
217
+ }
218
+ if (attempt >= MAX_ATTEMPTS) {
219
+ span2.setAttributes(createFetchResponseAttributes(response));
220
+ span2.end();
221
+ return response;
222
+ }
223
+ if (nextRetry.type === "delay") {
224
+ span2.setAttribute(SemanticInternalAttributes.RETRY_AT, new Date(Date.now() + nextRetry.value).toISOString());
225
+ span2.setAttribute(SemanticInternalAttributes.RETRY_COUNT, attempt);
226
+ span2.setAttribute(SemanticInternalAttributes.RETRY_DELAY, `${nextRetry.value}ms`);
227
+ span2.end();
228
+ await runtime.waitForDuration(nextRetry.value);
229
+ } else {
230
+ const now = Date.now();
231
+ const nextRetryDate = new Date(nextRetry.value);
232
+ const isInFuture = nextRetryDate.getTime() > now;
233
+ span2.setAttribute(SemanticInternalAttributes.RETRY_AT, new Date(nextRetry.value).toISOString());
234
+ span2.setAttribute(SemanticInternalAttributes.RETRY_COUNT, attempt);
235
+ if (isInFuture) {
236
+ span2.setAttribute(SemanticInternalAttributes.RETRY_DELAY, `${nextRetry.value - now}ms`);
221
237
  }
222
- return {
223
- id: response.batchId,
224
- items: []
225
- };
226
- }, "getBatchResults");
227
- const existingResults = await getBatchResults();
228
- const incompleteRuns = response.runs.filter((runId) => !existingResults.items.some((item) => item.id === runId));
229
- if (incompleteRuns.length === 0) {
230
- logger.log(`Results reused from previous task runs because of the provided idempotency keys.`);
231
- const runs3 = await handleBatchTaskRunExecutionResult(existingResults.items);
232
- return {
233
- id: existingResults.id,
234
- runs: runs3
235
- };
238
+ span2.end();
239
+ await runtime.waitUntil(new Date(nextRetry.value));
236
240
  }
237
- const result = await runtime.waitForBatch({
238
- id: response.batchId,
239
- runs: incompleteRuns,
240
- ctx
241
- });
242
- const combinedItems = [];
243
- for (const runId of response.runs) {
244
- const existingItem = existingResults.items.find((item) => item.id === runId);
245
- if (existingItem) {
246
- combinedItems.push(existingItem);
247
- } else {
248
- const newItem = result.items.find((item) => item.id === runId);
249
- if (newItem) {
250
- combinedItems.push(newItem);
241
+ } catch (e) {
242
+ if (e instanceof FetchErrorWithSpan && e.originalError instanceof Error) {
243
+ if (e.originalError.name === "AbortError") {
244
+ const nextRetryDelay = calculateNextRetryDelay(resolveDefaults(init?.retry, "timeout", defaultFetchRetryOptions.timeout), attempt);
245
+ if (!nextRetryDelay) {
246
+ e.span.end();
247
+ throw e;
248
+ }
249
+ if (attempt >= MAX_ATTEMPTS) {
250
+ e.span.end();
251
+ throw e;
251
252
  }
253
+ e.span.setAttribute(SemanticInternalAttributes.RETRY_AT, new Date(Date.now() + nextRetryDelay).toISOString());
254
+ e.span.setAttribute(SemanticInternalAttributes.RETRY_COUNT, attempt);
255
+ e.span.setAttribute(SemanticInternalAttributes.RETRY_DELAY, `${nextRetryDelay}ms`);
256
+ e.span.end();
257
+ await runtime.waitForDuration(nextRetryDelay);
258
+ continue;
259
+ } else if (e.originalError.name === "TypeError" && "cause" in e.originalError && e.originalError.cause instanceof Error) {
260
+ const nextRetryDelay = calculateNextRetryDelay(resolveDefaults(init?.retry, "connectionError", defaultFetchRetryOptions.connectionError), attempt);
261
+ if (!nextRetryDelay) {
262
+ e.span.end();
263
+ throw e;
264
+ }
265
+ if (attempt >= MAX_ATTEMPTS) {
266
+ e.span.end();
267
+ throw e;
268
+ }
269
+ e.span.setAttribute(SemanticInternalAttributes.RETRY_AT, new Date(Date.now() + nextRetryDelay).toISOString());
270
+ e.span.setAttribute(SemanticInternalAttributes.RETRY_COUNT, attempt);
271
+ e.span.setAttribute(SemanticInternalAttributes.RETRY_DELAY, `${nextRetryDelay}ms`);
272
+ e.span.end();
273
+ await runtime.waitForDuration(nextRetryDelay);
274
+ continue;
252
275
  }
253
276
  }
254
- const runs2 = await handleBatchTaskRunExecutionResult(combinedItems);
255
- return {
256
- id: result.id,
257
- runs: runs2
258
- };
259
- }, {
260
- kind: SpanKind.PRODUCER,
261
- attributes: {
262
- [SEMATTRS_MESSAGING_OPERATION]: "publish",
263
- ["messaging.batch.message_count"]: items.length,
264
- ["messaging.client_id"]: taskContext.worker?.id,
265
- [SEMATTRS_MESSAGING_DESTINATION]: params.queue?.name ?? params.id,
266
- [SEMATTRS_MESSAGING_SYSTEM]: "trigger.dev",
267
- [SemanticInternalAttributes.STYLE_ICON]: "trigger",
268
- ...taskMetadata ? accessoryAttributes({
269
- items: [
270
- {
271
- text: `${taskMetadata.exportName}.batchTriggerAndWait()`,
272
- variant: "normal"
273
- }
274
- ],
275
- style: "codepath"
276
- }) : {}
277
+ if (e instanceof FetchErrorWithSpan) {
278
+ e.span.end();
277
279
  }
278
- });
280
+ throw e;
281
+ } finally {
282
+ attempt++;
283
+ }
279
284
  }
280
- };
281
- taskCatalog.registerTaskMetadata({
282
- id: params.id,
283
- packageVersion: version,
284
- queue: params.queue,
285
- retry: params.retry ? {
286
- ...defaultRetryOptions,
287
- ...params.retry
288
- } : void 0,
289
- machine: params.machine,
290
- fns: {
291
- run: params.run,
292
- init: params.init,
293
- cleanup: params.cleanup,
294
- middleware: params.middleware,
295
- handleError: params.handleError,
296
- onSuccess: params.onSuccess,
297
- onFailure: params.onFailure,
298
- onStart: params.onStart
285
+ }, {
286
+ attributes: {
287
+ [SemanticInternalAttributes.STYLE_ICON]: "arrow-capsule",
288
+ ...createFetchAttributes(input, init),
289
+ ...createFetchRetryOptionsAttributes(init?.retry)
299
290
  }
300
291
  });
301
- return task3;
302
292
  }
303
- __name(createTask, "createTask");
304
- async function handleBatchTaskRunExecutionResult(items) {
305
- const someObjectStoreOutputs = items.some((item) => item.ok && item.outputType === "application/store");
306
- if (!someObjectStoreOutputs) {
307
- const results = await Promise.all(items.map(async (item) => {
308
- return await handleTaskRunExecutionResult(item);
309
- }));
310
- return results;
311
- }
312
- return await tracer.startActiveSpan("store.downloadPayloads", async (span) => {
313
- const results = await Promise.all(items.map(async (item) => {
314
- return await handleTaskRunExecutionResult(item);
315
- }));
316
- return results;
317
- }, {
318
- kind: SpanKind.INTERNAL,
319
- [SemanticInternalAttributes.STYLE_ICON]: "cloud-download"
293
+ __name(retryFetch, "retryFetch");
294
+ var doFetchRequest = /* @__PURE__ */ __name(async (input, init, attemptCount = 0) => {
295
+ const httpMethod = normalizeHttpMethod(input, init);
296
+ const span = tracer.startSpan(`HTTP ${httpMethod}`, {
297
+ attributes: {
298
+ [SemanticInternalAttributes.STYLE_ICON]: "world",
299
+ ...attemptCount > 1 ? {
300
+ ["http.request.resend_count"]: attemptCount - 1
301
+ } : {},
302
+ ...createFetchAttributes(input, init)
303
+ }
320
304
  });
321
- }
322
- __name(handleBatchTaskRunExecutionResult, "handleBatchTaskRunExecutionResult");
323
- async function handleTaskRunExecutionResult(execution) {
324
- if (execution.ok) {
325
- const outputPacket = {
326
- data: execution.output,
327
- dataType: execution.outputType
328
- };
329
- const importedPacket = await conditionallyImportPacket(outputPacket, tracer);
330
- return {
331
- ok: true,
332
- id: execution.id,
333
- output: await parsePacket(importedPacket)
334
- };
335
- } else {
336
- return {
337
- ok: false,
338
- id: execution.id,
339
- error: createErrorTaskError(execution.error)
340
- };
341
- }
342
- }
343
- __name(handleTaskRunExecutionResult, "handleTaskRunExecutionResult");
344
- function apiClientMissingError() {
345
- const hasBaseUrl = !!apiClientManager.baseURL;
346
- const hasAccessToken = !!apiClientManager.accessToken;
347
- if (!hasBaseUrl && !hasAccessToken) {
348
- return `You need to set the TRIGGER_API_URL and TRIGGER_SECRET_KEY environment variables.`;
349
- } else if (!hasBaseUrl) {
350
- return `You need to set the TRIGGER_API_URL environment variable.`;
351
- } else if (!hasAccessToken) {
352
- return `You need to set the TRIGGER_SECRET_KEY environment variable.`;
353
- }
354
- return `Unknown error`;
355
- }
356
- __name(apiClientMissingError, "apiClientMissingError");
357
-
358
- // src/v3/tasks.ts
359
- function task(options) {
360
- return createTask(options);
361
- }
362
- __name(task, "task");
363
- var wait = {
364
- for: async (options) => {
365
- return tracer.startActiveSpan(`wait.for()`, async (span) => {
366
- const durationInMs = calculateDurationInMs(options);
367
- await runtime.waitForDuration(durationInMs);
368
- }, {
369
- attributes: {
370
- [SemanticInternalAttributes.STYLE_ICON]: "wait",
371
- ...accessoryAttributes({
372
- items: [
373
- {
374
- text: nameForWaitOptions(options),
375
- variant: "normal"
376
- }
377
- ],
378
- style: "codepath"
379
- })
305
+ try {
306
+ const response = await fetchWithInterceptors(input, {
307
+ ...init,
308
+ headers: {
309
+ ...init?.headers,
310
+ "x-retry-count": attemptCount.toString()
380
311
  }
381
312
  });
382
- },
383
- until: async (options) => {
384
- return tracer.startActiveSpan(`wait.until()`, async (span) => {
385
- const start = Date.now();
386
- if (options.throwIfInThePast && options.date < /* @__PURE__ */ new Date()) {
387
- throw new Error("Date is in the past");
388
- }
389
- const durationInMs = options.date.getTime() - start;
390
- await runtime.waitForDuration(durationInMs);
391
- }, {
392
- attributes: {
393
- [SemanticInternalAttributes.STYLE_ICON]: "wait",
394
- ...accessoryAttributes({
395
- items: [
396
- {
397
- text: options.date.toISOString(),
398
- variant: "normal"
399
- }
400
- ],
401
- style: "codepath"
402
- })
403
- }
313
+ span.setAttributes(createFetchResponseAttributes(response));
314
+ if (!response.ok) {
315
+ span.recordException(`${response.status}: ${response.statusText}`);
316
+ span.setStatus({
317
+ code: SpanStatusCode.ERROR,
318
+ message: `${response.status}: ${response.statusText}`
319
+ });
320
+ }
321
+ return [
322
+ response,
323
+ span
324
+ ];
325
+ } catch (e) {
326
+ if (typeof e === "string" || e instanceof Error) {
327
+ span.recordException(e);
328
+ }
329
+ span.setStatus({
330
+ code: SpanStatusCode.ERROR
404
331
  });
332
+ span.setAttribute(SEMATTRS_HTTP_STATUS_CODE, 0);
333
+ span.setAttribute("http.status_text", "This operation was aborted.");
334
+ throw new FetchErrorWithSpan(e, span);
405
335
  }
406
- };
407
- function nameForWaitOptions(options) {
408
- if ("seconds" in options) {
409
- return options.seconds === 1 ? `1 second` : `${options.seconds} seconds`;
410
- }
411
- if ("minutes" in options) {
412
- return options.minutes === 1 ? `1 minute` : `${options.minutes} minutes`;
413
- }
414
- if ("hours" in options) {
415
- return options.hours === 1 ? `1 hour` : `${options.hours} hours`;
416
- }
417
- if ("days" in options) {
418
- return options.days === 1 ? `1 day` : `${options.days} days`;
419
- }
420
- if ("weeks" in options) {
421
- return options.weeks === 1 ? `1 week` : `${options.weeks} weeks`;
422
- }
423
- if ("months" in options) {
424
- return options.months === 1 ? `1 month` : `${options.months} months`;
425
- }
426
- if ("years" in options) {
427
- return options.years === 1 ? `1 year` : `${options.years} years`;
336
+ }, "doFetchRequest");
337
+ var calculateRetryDelayForResponse = /* @__PURE__ */ __name(async (retry2, response, attemptCount) => {
338
+ if (!retry2) {
339
+ return;
428
340
  }
429
- return "NaN";
430
- }
431
- __name(nameForWaitOptions, "nameForWaitOptions");
432
- function calculateDurationInMs(options) {
433
- if ("seconds" in options) {
434
- return options.seconds * 1e3;
341
+ const strategy = await getRetryStrategyForResponse(response, retry2);
342
+ if (!strategy) {
343
+ return;
435
344
  }
436
- if ("minutes" in options) {
437
- return options.minutes * 1e3 * 60;
345
+ switch (strategy.strategy) {
346
+ case "backoff": {
347
+ const value = calculateNextRetryDelay({
348
+ ...defaultRetryOptions,
349
+ ...strategy
350
+ }, attemptCount);
351
+ if (value) {
352
+ return {
353
+ type: "delay",
354
+ value
355
+ };
356
+ }
357
+ break;
358
+ }
359
+ case "headers": {
360
+ const resetAt = response.headers.get(strategy.resetHeader);
361
+ if (typeof resetAt === "string") {
362
+ const resetTimestamp = calculateResetAt(resetAt, strategy.resetFormat ?? "unix_timestamp_in_ms");
363
+ if (resetTimestamp) {
364
+ return {
365
+ type: "timestamp",
366
+ value: resetTimestamp
367
+ };
368
+ }
369
+ }
370
+ break;
371
+ }
438
372
  }
439
- if ("hours" in options) {
440
- return options.hours * 1e3 * 60 * 60;
373
+ }, "calculateRetryDelayForResponse");
374
+ var getRetryStrategyForResponse = /* @__PURE__ */ __name(async (response, retry2) => {
375
+ const statusCodes = Object.keys(retry2);
376
+ const clonedResponse = response.clone();
377
+ for (let i = 0; i < statusCodes.length; i++) {
378
+ const statusRange = statusCodes[i];
379
+ const strategy = retry2[statusRange];
380
+ if (isStatusCodeInRange(response.status, statusRange)) {
381
+ if (strategy.bodyFilter) {
382
+ const body = safeJsonParse(await clonedResponse.text());
383
+ if (!body) {
384
+ continue;
385
+ }
386
+ if (eventFilterMatches(body, strategy.bodyFilter)) {
387
+ return strategy;
388
+ } else {
389
+ continue;
390
+ }
391
+ }
392
+ return strategy;
393
+ }
441
394
  }
442
- if ("days" in options) {
443
- return options.days * 1e3 * 60 * 60 * 24;
395
+ }, "getRetryStrategyForResponse");
396
+ var isStatusCodeInRange = /* @__PURE__ */ __name((statusCode, statusRange) => {
397
+ if (statusRange === "all") {
398
+ return true;
444
399
  }
445
- if ("weeks" in options) {
446
- return options.weeks * 1e3 * 60 * 60 * 24 * 7;
400
+ if (statusRange.includes(",")) {
401
+ const statusCodes = statusRange.split(",").map((s) => s.trim());
402
+ return statusCodes.some((s) => isStatusCodeInRange(statusCode, s));
447
403
  }
448
- if ("months" in options) {
449
- return options.months * 1e3 * 60 * 60 * 24 * 30;
404
+ const [start, end] = statusRange.split("-");
405
+ if (end) {
406
+ return statusCode >= parseInt(start, 10) && statusCode <= parseInt(end, 10);
450
407
  }
451
- if ("years" in options) {
452
- return options.years * 1e3 * 60 * 60 * 24 * 365;
408
+ if (start.endsWith("xx")) {
409
+ const prefix = start.slice(0, -2);
410
+ const statusCodePrefix = Math.floor(statusCode / 100).toString();
411
+ return statusCodePrefix === prefix;
453
412
  }
454
- throw new Error("Invalid options");
455
- }
456
- __name(calculateDurationInMs, "calculateDurationInMs");
457
- var _InMemoryCache = class _InMemoryCache {
458
- constructor() {
459
- __publicField(this, "_cache", /* @__PURE__ */ new Map());
413
+ const statusCodeString = statusCode.toString();
414
+ const rangePrefix = start.slice(0, -1);
415
+ if (start.endsWith("x") && statusCodeString.startsWith(rangePrefix)) {
416
+ return true;
460
417
  }
461
- get(key) {
462
- return this._cache.get(key);
418
+ return statusCode === parseInt(start, 10);
419
+ }, "isStatusCodeInRange");
420
+ var createAttributesFromHeaders = /* @__PURE__ */ __name((headers) => {
421
+ const attributes = {};
422
+ const normalizedHeaderKey = /* @__PURE__ */ __name((key) => {
423
+ return key.toLowerCase();
424
+ }, "normalizedHeaderKey");
425
+ headers.forEach((value, key) => {
426
+ attributes[`http.response.header.${normalizedHeaderKey(key)}`] = value;
427
+ });
428
+ return attributes;
429
+ }, "createAttributesFromHeaders");
430
+ var safeJsonParse = /* @__PURE__ */ __name((json) => {
431
+ try {
432
+ return JSON.parse(json);
433
+ } catch (e) {
434
+ return null;
463
435
  }
464
- set(key, value) {
465
- this._cache.set(key, value);
466
- return void 0;
436
+ }, "safeJsonParse");
437
+ var interceptFetch = /* @__PURE__ */ __name((...handlers) => {
438
+ return {
439
+ run: async (fn) => {
440
+ const current = fetchHttpHandlerStorage.getStore();
441
+ if (current) {
442
+ current.push(...handlers);
443
+ return fn();
444
+ } else {
445
+ return fetchHttpHandlerStorage.run(handlers, fn);
446
+ }
447
+ }
448
+ };
449
+ }, "interceptFetch");
450
+ var resolveDefaults = /* @__PURE__ */ __name((obj, key, defaults) => {
451
+ if (!obj) {
452
+ return defaults;
467
453
  }
468
- delete(key) {
469
- this._cache.delete(key);
470
- return void 0;
454
+ if (obj[key] === void 0 || obj[key] === null) {
455
+ return defaults;
471
456
  }
472
- };
473
- __name(_InMemoryCache, "InMemoryCache");
474
- var InMemoryCache = _InMemoryCache;
475
- function createCache(store) {
476
- return /* @__PURE__ */ __name(function cache(cacheKey, fn) {
477
- return tracer.startActiveSpan("cache", async (span) => {
478
- span.setAttribute("cache.key", cacheKey);
479
- span.setAttribute(SemanticInternalAttributes.STYLE_ICON, "device-sd-card");
480
- const cacheEntry = await store.get(cacheKey);
481
- if (cacheEntry) {
482
- span.updateName(`cache.hit ${cacheKey}`);
483
- return cacheEntry.value;
484
- }
485
- span.updateName(`cache.miss ${cacheKey}`);
486
- const value = await tracer.startActiveSpan("cache.getFreshValue", async (span2) => {
487
- return await fn();
488
- }, {
489
- attributes: {
490
- "cache.key": cacheKey,
491
- [SemanticInternalAttributes.STYLE_ICON]: "device-sd-card"
457
+ return obj[key];
458
+ }, "resolveDefaults");
459
+ var createFetchAttributes = /* @__PURE__ */ __name((input, init) => {
460
+ const url = normalizeUrlFromInput(input);
461
+ const httpMethod = normalizeHttpMethod(input, init);
462
+ return {
463
+ [SEMATTRS_HTTP_METHOD]: httpMethod,
464
+ [SEMATTRS_HTTP_URL]: url.href,
465
+ [SEMATTRS_HTTP_HOST]: url.hostname,
466
+ ["server.host"]: url.hostname,
467
+ ["server.port"]: url.port,
468
+ [SEMATTRS_HTTP_SCHEME]: url.protocol.replace(":", ""),
469
+ ...accessoryAttributes({
470
+ items: [
471
+ {
472
+ text: url.hostname,
473
+ variant: "normal"
492
474
  }
493
- });
494
- await tracer.startActiveSpan("cache.set", async (span2) => {
495
- await store.set(cacheKey, {
496
- value,
497
- metadata: {
498
- createdTime: Date.now()
475
+ ],
476
+ style: "codepath"
477
+ })
478
+ };
479
+ }, "createFetchAttributes");
480
+ var createFetchResponseAttributes = /* @__PURE__ */ __name((response) => {
481
+ return {
482
+ [SEMATTRS_HTTP_STATUS_CODE]: response.status,
483
+ "http.status_text": response.statusText,
484
+ [SEMATTRS_HTTP_RESPONSE_CONTENT_LENGTH]: response.headers.get("content-length") || "0",
485
+ ...createAttributesFromHeaders(response.headers)
486
+ };
487
+ }, "createFetchResponseAttributes");
488
+ var createFetchRetryOptionsAttributes = /* @__PURE__ */ __name((retry2) => {
489
+ const byStatus = resolveDefaults(retry2, "byStatus", defaultFetchRetryOptions.byStatus);
490
+ const connectionError = resolveDefaults(retry2, "connectionError", defaultFetchRetryOptions.connectionError);
491
+ const timeout = resolveDefaults(retry2, "timeout", defaultFetchRetryOptions.timeout);
492
+ return {
493
+ ...flattenAttributes(byStatus, "retry.byStatus"),
494
+ ...flattenAttributes(connectionError, "retry.connectionError"),
495
+ ...flattenAttributes(timeout, "retry.timeout")
496
+ };
497
+ }, "createFetchRetryOptionsAttributes");
498
+ var retry = {
499
+ onThrow,
500
+ fetch: retryFetch,
501
+ interceptFetch
502
+ };
503
+ function queue(options) {
504
+ return options;
505
+ }
506
+ __name(queue, "queue");
507
+ function createTask(params) {
508
+ const task3 = {
509
+ id: params.id,
510
+ trigger: async (payload, options) => {
511
+ const apiClient = apiClientManager.client;
512
+ if (!apiClient) {
513
+ throw apiClientMissingError();
514
+ }
515
+ const taskMetadata = taskCatalog.getTaskMetadata(params.id);
516
+ const payloadPacket = await stringifyIO(payload);
517
+ const handle = await tracer.startActiveSpan(taskMetadata ? "Trigger" : `${params.id} trigger()`, async (span) => {
518
+ const response = await apiClient.triggerTask(params.id, {
519
+ payload: payloadPacket.data,
520
+ options: {
521
+ queue: params.queue,
522
+ concurrencyKey: options?.concurrencyKey,
523
+ test: taskContext.ctx?.run.isTest,
524
+ payloadType: payloadPacket.dataType,
525
+ idempotencyKey: options?.idempotencyKey
499
526
  }
527
+ }, {
528
+ spanParentAsLink: true
500
529
  });
530
+ span.setAttribute("messaging.message.id", response.id);
531
+ return response;
501
532
  }, {
533
+ kind: SpanKind.PRODUCER,
502
534
  attributes: {
503
- "cache.key": cacheKey,
504
- [SemanticInternalAttributes.STYLE_ICON]: "device-sd-card"
535
+ [SEMATTRS_MESSAGING_OPERATION]: "publish",
536
+ [SemanticInternalAttributes.STYLE_ICON]: "trigger",
537
+ ["messaging.client_id"]: taskContext.worker?.id,
538
+ [SEMATTRS_MESSAGING_DESTINATION]: params.queue?.name ?? params.id,
539
+ [SEMATTRS_MESSAGING_SYSTEM]: "trigger.dev",
540
+ ...taskMetadata ? accessoryAttributes({
541
+ items: [
542
+ {
543
+ text: `${taskMetadata.exportName}.trigger()`,
544
+ variant: "normal"
545
+ }
546
+ ],
547
+ style: "codepath"
548
+ }) : {}
505
549
  }
506
550
  });
507
- return value;
508
- });
509
- }, "cache");
510
- }
511
- __name(createCache, "createCache");
512
- function onThrow(fn, options) {
513
- const opts = {
514
- ...defaultRetryOptions,
515
- ...options
516
- };
517
- return tracer.startActiveSpan(`retry.onThrow()`, async (span) => {
518
- let attempt = 1;
519
- while (attempt <= opts.maxAttempts) {
520
- const innerSpan = tracer.startSpan("retry.fn()", {
551
+ return handle;
552
+ },
553
+ batchTrigger: async (items) => {
554
+ const apiClient = apiClientManager.client;
555
+ if (!apiClient) {
556
+ throw apiClientMissingError();
557
+ }
558
+ const taskMetadata = taskCatalog.getTaskMetadata(params.id);
559
+ const response = await tracer.startActiveSpan(taskMetadata ? "Batch trigger" : `${params.id} batchTrigger()`, async (span) => {
560
+ const response2 = await apiClient.batchTriggerTask(params.id, {
561
+ items: await Promise.all(items.map(async (item) => {
562
+ const payloadPacket = await stringifyIO(item.payload);
563
+ return {
564
+ payload: payloadPacket.data,
565
+ options: {
566
+ queue: item.options?.queue ?? params.queue,
567
+ concurrencyKey: item.options?.concurrencyKey,
568
+ test: taskContext.ctx?.run.isTest,
569
+ payloadType: payloadPacket.dataType,
570
+ idempotencyKey: item.options?.idempotencyKey
571
+ }
572
+ };
573
+ }))
574
+ }, {
575
+ spanParentAsLink: true
576
+ });
577
+ span.setAttribute("messaging.message.id", response2.batchId);
578
+ return response2;
579
+ }, {
580
+ kind: SpanKind.PRODUCER,
521
581
  attributes: {
522
- [SemanticInternalAttributes.STYLE_ICON]: "function",
523
- ...accessoryAttributes({
582
+ [SEMATTRS_MESSAGING_OPERATION]: "publish",
583
+ ["messaging.batch.message_count"]: items.length,
584
+ ["messaging.client_id"]: taskContext.worker?.id,
585
+ [SEMATTRS_MESSAGING_DESTINATION]: params.queue?.name ?? params.id,
586
+ [SEMATTRS_MESSAGING_SYSTEM]: "trigger.dev",
587
+ [SemanticInternalAttributes.STYLE_ICON]: "trigger",
588
+ ...taskMetadata ? accessoryAttributes({
524
589
  items: [
525
590
  {
526
- text: `${attempt}/${opts.maxAttempts}`,
591
+ text: `${taskMetadata.exportName}.batchTrigger()`,
527
592
  variant: "normal"
528
593
  }
529
594
  ],
530
595
  style: "codepath"
531
- })
596
+ }) : {}
532
597
  }
533
598
  });
534
- const contextWithSpanSet = trace.setSpan(context.active(), innerSpan);
535
- try {
536
- const result = await context.with(contextWithSpanSet, async () => {
537
- return fn({
538
- attempt,
539
- maxAttempts: opts.maxAttempts
540
- });
541
- });
542
- innerSpan.end();
543
- return result;
544
- } catch (e) {
545
- if (e instanceof Error || typeof e === "string") {
546
- innerSpan.recordException(e);
547
- } else {
548
- innerSpan.recordException(String(e));
549
- }
550
- innerSpan.setStatus({
551
- code: SpanStatusCode.ERROR
552
- });
553
- const nextRetryDelay = calculateNextRetryDelay(opts, attempt);
554
- if (!nextRetryDelay) {
555
- innerSpan.end();
556
- throw e;
557
- }
558
- innerSpan.setAttribute(SemanticInternalAttributes.RETRY_AT, new Date(Date.now() + nextRetryDelay).toISOString());
559
- innerSpan.setAttribute(SemanticInternalAttributes.RETRY_COUNT, attempt);
560
- innerSpan.setAttribute(SemanticInternalAttributes.RETRY_DELAY, `${nextRetryDelay}ms`);
561
- innerSpan.end();
562
- await runtime.waitForDuration(nextRetryDelay);
563
- } finally {
564
- attempt++;
599
+ return response;
600
+ },
601
+ triggerAndWait: async (payload, options) => {
602
+ const ctx = taskContext.ctx;
603
+ if (!ctx) {
604
+ throw new Error("triggerAndWait can only be used from inside a task.run()");
565
605
  }
566
- }
567
- throw new Error("Max attempts reached");
568
- }, {
569
- attributes: {
570
- [SemanticInternalAttributes.STYLE_ICON]: "arrow-capsule"
571
- }
572
- });
573
- }
574
- __name(onThrow, "onThrow");
575
- var normalizeUrlFromInput = /* @__PURE__ */ __name((input) => {
576
- if (typeof input === "string") {
577
- return new URL(input);
578
- }
579
- if (input instanceof URL) {
580
- return input;
581
- }
582
- return new URL(input.url);
583
- }, "normalizeUrlFromInput");
584
- var normalizeHttpMethod = /* @__PURE__ */ __name((input, init) => {
585
- if (typeof input === "string" || input instanceof URL) {
586
- return (init?.method || "GET").toUpperCase();
587
- }
588
- return (input.method ?? init?.method ?? "GET").toUpperCase();
589
- }, "normalizeHttpMethod");
590
- var fetchHttpHandlerStorage = new AsyncLocalStorage();
591
- var fetchWithInterceptors = /* @__PURE__ */ __name(async (input, init) => {
592
- const handlers = fetchHttpHandlerStorage.getStore();
593
- if (handlers) {
594
- try {
595
- const { getResponse } = await import('msw');
596
- const request = new Request(input, init);
597
- const response = await getResponse(handlers, request);
598
- if (response) {
599
- return response;
606
+ const apiClient = apiClientManager.client;
607
+ if (!apiClient) {
608
+ throw apiClientMissingError();
600
609
  }
601
- } catch (e) {
602
- return fetch(input, init);
603
- }
604
- }
605
- return fetch(input, init);
606
- }, "fetchWithInterceptors");
607
- var _a;
608
- var FetchErrorWithSpan = (_a = class extends Error {
609
- constructor(originalError, span) {
610
- super("Fetch error");
611
- this.originalError = originalError;
612
- this.span = span;
613
- }
614
- }, __name(_a, "FetchErrorWithSpan"), _a);
615
- var MAX_ATTEMPTS = 10;
616
- async function retryFetch(input, init) {
617
- return tracer.startActiveSpan("retry.fetch()", async (span) => {
618
- let attempt = 1;
619
- while (true) {
620
- try {
621
- const abortController = new AbortController();
622
- const timeoutId = init?.timeoutInMs ? setTimeout(() => {
623
- abortController.abort();
624
- }, init?.timeoutInMs) : void 0;
625
- init?.signal?.addEventListener("abort", () => {
626
- abortController.abort();
610
+ const taskMetadata = taskCatalog.getTaskMetadata(params.id);
611
+ const payloadPacket = await stringifyIO(payload);
612
+ return await tracer.startActiveSpan(taskMetadata ? "Trigger" : `${params.id} triggerAndWait()`, async (span) => {
613
+ const response = await apiClient.triggerTask(params.id, {
614
+ payload: payloadPacket.data,
615
+ options: {
616
+ dependentAttempt: ctx.attempt.id,
617
+ lockToVersion: taskContext.worker?.version,
618
+ queue: params.queue,
619
+ concurrencyKey: options?.concurrencyKey,
620
+ test: taskContext.ctx?.run.isTest,
621
+ payloadType: payloadPacket.dataType,
622
+ idempotencyKey: options?.idempotencyKey
623
+ }
627
624
  });
628
- const [response, span2] = await doFetchRequest(input, {
629
- ...init ?? {},
630
- signal: abortController.signal
631
- }, attempt);
632
- if (timeoutId) {
633
- clearTimeout(timeoutId);
634
- }
635
- if (response.ok) {
636
- span2.setAttributes(createFetchResponseAttributes(response));
637
- span2.end();
638
- return response;
639
- }
640
- const nextRetry = await calculateRetryDelayForResponse(resolveDefaults(init?.retry, "byStatus", defaultFetchRetryOptions.byStatus), response, attempt);
641
- if (!nextRetry) {
642
- span2.setAttributes(createFetchResponseAttributes(response));
643
- span2.end();
644
- return response;
625
+ span.setAttribute("messaging.message.id", response.id);
626
+ if (options?.idempotencyKey) {
627
+ const result2 = await apiClient.getRunResult(response.id);
628
+ if (result2) {
629
+ logger.log(`Result reused from previous task run with idempotency key '${options.idempotencyKey}'.`, {
630
+ runId: response.id,
631
+ idempotencyKey: options.idempotencyKey
632
+ });
633
+ return await handleTaskRunExecutionResult(result2);
634
+ }
645
635
  }
646
- if (attempt >= MAX_ATTEMPTS) {
647
- span2.setAttributes(createFetchResponseAttributes(response));
648
- span2.end();
649
- return response;
636
+ const result = await runtime.waitForTask({
637
+ id: response.id,
638
+ ctx
639
+ });
640
+ return await handleTaskRunExecutionResult(result);
641
+ }, {
642
+ kind: SpanKind.PRODUCER,
643
+ attributes: {
644
+ [SemanticInternalAttributes.STYLE_ICON]: "trigger",
645
+ [SEMATTRS_MESSAGING_OPERATION]: "publish",
646
+ ["messaging.client_id"]: taskContext.worker?.id,
647
+ [SEMATTRS_MESSAGING_DESTINATION]: params.queue?.name ?? params.id,
648
+ [SEMATTRS_MESSAGING_SYSTEM]: "trigger.dev",
649
+ ...taskMetadata ? accessoryAttributes({
650
+ items: [
651
+ {
652
+ text: `${taskMetadata.exportName}.triggerAndWait()`,
653
+ variant: "normal"
654
+ }
655
+ ],
656
+ style: "codepath"
657
+ }) : {}
650
658
  }
651
- if (nextRetry.type === "delay") {
652
- span2.setAttribute(SemanticInternalAttributes.RETRY_AT, new Date(Date.now() + nextRetry.value).toISOString());
653
- span2.setAttribute(SemanticInternalAttributes.RETRY_COUNT, attempt);
654
- span2.setAttribute(SemanticInternalAttributes.RETRY_DELAY, `${nextRetry.value}ms`);
655
- span2.end();
656
- await runtime.waitForDuration(nextRetry.value);
657
- } else {
658
- const now = Date.now();
659
- const nextRetryDate = new Date(nextRetry.value);
660
- const isInFuture = nextRetryDate.getTime() > now;
661
- span2.setAttribute(SemanticInternalAttributes.RETRY_AT, new Date(nextRetry.value).toISOString());
662
- span2.setAttribute(SemanticInternalAttributes.RETRY_COUNT, attempt);
663
- if (isInFuture) {
664
- span2.setAttribute(SemanticInternalAttributes.RETRY_DELAY, `${nextRetry.value - now}ms`);
659
+ });
660
+ },
661
+ batchTriggerAndWait: async (items) => {
662
+ const ctx = taskContext.ctx;
663
+ if (!ctx) {
664
+ throw new Error("batchTriggerAndWait can only be used from inside a task.run()");
665
+ }
666
+ const apiClient = apiClientManager.client;
667
+ if (!apiClient) {
668
+ throw apiClientMissingError();
669
+ }
670
+ const taskMetadata = taskCatalog.getTaskMetadata(params.id);
671
+ return await tracer.startActiveSpan(taskMetadata ? "Batch trigger" : `${params.id} batchTriggerAndWait()`, async (span) => {
672
+ const response = await apiClient.batchTriggerTask(params.id, {
673
+ items: await Promise.all(items.map(async (item) => {
674
+ const payloadPacket = await stringifyIO(item.payload);
675
+ return {
676
+ payload: payloadPacket.data,
677
+ options: {
678
+ lockToVersion: taskContext.worker?.version,
679
+ queue: item.options?.queue ?? params.queue,
680
+ concurrencyKey: item.options?.concurrencyKey,
681
+ test: taskContext.ctx?.run.isTest,
682
+ payloadType: payloadPacket.dataType,
683
+ idempotencyKey: item.options?.idempotencyKey
684
+ }
685
+ };
686
+ })),
687
+ dependentAttempt: ctx.attempt.id
688
+ });
689
+ span.setAttribute("messaging.message.id", response.batchId);
690
+ const getBatchResults = /* @__PURE__ */ __name(async () => {
691
+ const hasIdempotencyKey = items.some((item) => item.options?.idempotencyKey);
692
+ if (hasIdempotencyKey) {
693
+ const results = await apiClient.getBatchResults(response.batchId);
694
+ if (results) {
695
+ return results;
696
+ }
665
697
  }
666
- span2.end();
667
- await runtime.waitUntil(new Date(nextRetry.value));
698
+ return {
699
+ id: response.batchId,
700
+ items: []
701
+ };
702
+ }, "getBatchResults");
703
+ const existingResults = await getBatchResults();
704
+ const incompleteRuns = response.runs.filter((runId) => !existingResults.items.some((item) => item.id === runId));
705
+ if (incompleteRuns.length === 0) {
706
+ logger.log(`Results reused from previous task runs because of the provided idempotency keys.`);
707
+ const runs3 = await handleBatchTaskRunExecutionResult(existingResults.items);
708
+ return {
709
+ id: existingResults.id,
710
+ runs: runs3
711
+ };
668
712
  }
669
- } catch (e) {
670
- if (e instanceof FetchErrorWithSpan && e.originalError instanceof Error) {
671
- if (e.originalError.name === "AbortError") {
672
- const nextRetryDelay = calculateNextRetryDelay(resolveDefaults(init?.retry, "timeout", defaultFetchRetryOptions.timeout), attempt);
673
- if (!nextRetryDelay) {
674
- e.span.end();
675
- throw e;
676
- }
677
- if (attempt >= MAX_ATTEMPTS) {
678
- e.span.end();
679
- throw e;
680
- }
681
- e.span.setAttribute(SemanticInternalAttributes.RETRY_AT, new Date(Date.now() + nextRetryDelay).toISOString());
682
- e.span.setAttribute(SemanticInternalAttributes.RETRY_COUNT, attempt);
683
- e.span.setAttribute(SemanticInternalAttributes.RETRY_DELAY, `${nextRetryDelay}ms`);
684
- e.span.end();
685
- await runtime.waitForDuration(nextRetryDelay);
686
- continue;
687
- } else if (e.originalError.name === "TypeError" && "cause" in e.originalError && e.originalError.cause instanceof Error) {
688
- const nextRetryDelay = calculateNextRetryDelay(resolveDefaults(init?.retry, "connectionError", defaultFetchRetryOptions.connectionError), attempt);
689
- if (!nextRetryDelay) {
690
- e.span.end();
691
- throw e;
692
- }
693
- if (attempt >= MAX_ATTEMPTS) {
694
- e.span.end();
695
- throw e;
713
+ const result = await runtime.waitForBatch({
714
+ id: response.batchId,
715
+ runs: incompleteRuns,
716
+ ctx
717
+ });
718
+ const combinedItems = [];
719
+ for (const runId of response.runs) {
720
+ const existingItem = existingResults.items.find((item) => item.id === runId);
721
+ if (existingItem) {
722
+ combinedItems.push(existingItem);
723
+ } else {
724
+ const newItem = result.items.find((item) => item.id === runId);
725
+ if (newItem) {
726
+ combinedItems.push(newItem);
696
727
  }
697
- e.span.setAttribute(SemanticInternalAttributes.RETRY_AT, new Date(Date.now() + nextRetryDelay).toISOString());
698
- e.span.setAttribute(SemanticInternalAttributes.RETRY_COUNT, attempt);
699
- e.span.setAttribute(SemanticInternalAttributes.RETRY_DELAY, `${nextRetryDelay}ms`);
700
- e.span.end();
701
- await runtime.waitForDuration(nextRetryDelay);
702
- continue;
703
728
  }
704
729
  }
705
- if (e instanceof FetchErrorWithSpan) {
706
- e.span.end();
730
+ const runs2 = await handleBatchTaskRunExecutionResult(combinedItems);
731
+ return {
732
+ id: result.id,
733
+ runs: runs2
734
+ };
735
+ }, {
736
+ kind: SpanKind.PRODUCER,
737
+ attributes: {
738
+ [SEMATTRS_MESSAGING_OPERATION]: "publish",
739
+ ["messaging.batch.message_count"]: items.length,
740
+ ["messaging.client_id"]: taskContext.worker?.id,
741
+ [SEMATTRS_MESSAGING_DESTINATION]: params.queue?.name ?? params.id,
742
+ [SEMATTRS_MESSAGING_SYSTEM]: "trigger.dev",
743
+ [SemanticInternalAttributes.STYLE_ICON]: "trigger",
744
+ ...taskMetadata ? accessoryAttributes({
745
+ items: [
746
+ {
747
+ text: `${taskMetadata.exportName}.batchTriggerAndWait()`,
748
+ variant: "normal"
749
+ }
750
+ ],
751
+ style: "codepath"
752
+ }) : {}
707
753
  }
708
- throw e;
709
- } finally {
710
- attempt++;
711
- }
754
+ });
712
755
  }
713
- }, {
714
- attributes: {
715
- [SemanticInternalAttributes.STYLE_ICON]: "arrow-capsule",
716
- ...createFetchAttributes(input, init),
717
- ...createFetchRetryOptionsAttributes(init?.retry)
756
+ };
757
+ taskCatalog.registerTaskMetadata({
758
+ id: params.id,
759
+ packageVersion: version,
760
+ queue: params.queue,
761
+ retry: params.retry ? {
762
+ ...defaultRetryOptions,
763
+ ...params.retry
764
+ } : void 0,
765
+ machine: params.machine,
766
+ fns: {
767
+ run: params.run,
768
+ init: params.init,
769
+ cleanup: params.cleanup,
770
+ middleware: params.middleware,
771
+ handleError: params.handleError,
772
+ onSuccess: params.onSuccess,
773
+ onFailure: params.onFailure,
774
+ onStart: params.onStart
718
775
  }
719
776
  });
777
+ return task3;
720
778
  }
721
- __name(retryFetch, "retryFetch");
722
- var doFetchRequest = /* @__PURE__ */ __name(async (input, init, attemptCount = 0) => {
723
- const httpMethod = normalizeHttpMethod(input, init);
724
- const span = tracer.startSpan(`HTTP ${httpMethod}`, {
725
- attributes: {
726
- [SemanticInternalAttributes.STYLE_ICON]: "world",
727
- ...attemptCount > 1 ? {
728
- ["http.request.resend_count"]: attemptCount - 1
729
- } : {},
730
- ...createFetchAttributes(input, init)
731
- }
779
+ __name(createTask, "createTask");
780
+ async function handleBatchTaskRunExecutionResult(items) {
781
+ const someObjectStoreOutputs = items.some((item) => item.ok && item.outputType === "application/store");
782
+ if (!someObjectStoreOutputs) {
783
+ const results = await Promise.all(items.map(async (item) => {
784
+ return await handleTaskRunExecutionResult(item);
785
+ }));
786
+ return results;
787
+ }
788
+ return await tracer.startActiveSpan("store.downloadPayloads", async (span) => {
789
+ const results = await Promise.all(items.map(async (item) => {
790
+ return await handleTaskRunExecutionResult(item);
791
+ }));
792
+ return results;
793
+ }, {
794
+ kind: SpanKind.INTERNAL,
795
+ [SemanticInternalAttributes.STYLE_ICON]: "cloud-download"
732
796
  });
733
- try {
734
- const response = await fetchWithInterceptors(input, {
735
- ...init,
736
- headers: {
737
- ...init?.headers,
738
- "x-retry-count": attemptCount.toString()
797
+ }
798
+ __name(handleBatchTaskRunExecutionResult, "handleBatchTaskRunExecutionResult");
799
+ async function handleTaskRunExecutionResult(execution) {
800
+ if (execution.ok) {
801
+ const outputPacket = {
802
+ data: execution.output,
803
+ dataType: execution.outputType
804
+ };
805
+ const importedPacket = await conditionallyImportPacket(outputPacket, tracer);
806
+ return {
807
+ ok: true,
808
+ id: execution.id,
809
+ output: await parsePacket(importedPacket)
810
+ };
811
+ } else {
812
+ return {
813
+ ok: false,
814
+ id: execution.id,
815
+ error: createErrorTaskError(execution.error)
816
+ };
817
+ }
818
+ }
819
+ __name(handleTaskRunExecutionResult, "handleTaskRunExecutionResult");
820
+ function apiClientMissingError() {
821
+ const hasBaseUrl = !!apiClientManager.baseURL;
822
+ const hasAccessToken = !!apiClientManager.accessToken;
823
+ if (!hasBaseUrl && !hasAccessToken) {
824
+ return `You need to set the TRIGGER_API_URL and TRIGGER_SECRET_KEY environment variables.`;
825
+ } else if (!hasBaseUrl) {
826
+ return `You need to set the TRIGGER_API_URL environment variable.`;
827
+ } else if (!hasAccessToken) {
828
+ return `You need to set the TRIGGER_SECRET_KEY environment variable.`;
829
+ }
830
+ return `Unknown error`;
831
+ }
832
+ __name(apiClientMissingError, "apiClientMissingError");
833
+
834
+ // src/v3/tasks.ts
835
+ function task(options) {
836
+ return createTask(options);
837
+ }
838
+ __name(task, "task");
839
+ var wait = {
840
+ for: async (options) => {
841
+ return tracer.startActiveSpan(`wait.for()`, async (span) => {
842
+ const durationInMs = calculateDurationInMs(options);
843
+ await runtime.waitForDuration(durationInMs);
844
+ }, {
845
+ attributes: {
846
+ [SemanticInternalAttributes.STYLE_ICON]: "wait",
847
+ ...accessoryAttributes({
848
+ items: [
849
+ {
850
+ text: nameForWaitOptions(options),
851
+ variant: "normal"
852
+ }
853
+ ],
854
+ style: "codepath"
855
+ })
739
856
  }
740
857
  });
741
- span.setAttributes(createFetchResponseAttributes(response));
742
- if (!response.ok) {
743
- span.recordException(`${response.status}: ${response.statusText}`);
744
- span.setStatus({
745
- code: SpanStatusCode.ERROR,
746
- message: `${response.status}: ${response.statusText}`
747
- });
748
- }
749
- return [
750
- response,
751
- span
752
- ];
753
- } catch (e) {
754
- if (typeof e === "string" || e instanceof Error) {
755
- span.recordException(e);
756
- }
757
- span.setStatus({
758
- code: SpanStatusCode.ERROR
858
+ },
859
+ until: async (options) => {
860
+ return tracer.startActiveSpan(`wait.until()`, async (span) => {
861
+ const start = Date.now();
862
+ if (options.throwIfInThePast && options.date < /* @__PURE__ */ new Date()) {
863
+ throw new Error("Date is in the past");
864
+ }
865
+ const durationInMs = options.date.getTime() - start;
866
+ await runtime.waitForDuration(durationInMs);
867
+ }, {
868
+ attributes: {
869
+ [SemanticInternalAttributes.STYLE_ICON]: "wait",
870
+ ...accessoryAttributes({
871
+ items: [
872
+ {
873
+ text: options.date.toISOString(),
874
+ variant: "normal"
875
+ }
876
+ ],
877
+ style: "codepath"
878
+ })
879
+ }
759
880
  });
760
- span.setAttribute(SEMATTRS_HTTP_STATUS_CODE, 0);
761
- span.setAttribute("http.status_text", "This operation was aborted.");
762
- throw new FetchErrorWithSpan(e, span);
763
881
  }
764
- }, "doFetchRequest");
765
- var calculateRetryDelayForResponse = /* @__PURE__ */ __name(async (retry2, response, attemptCount) => {
766
- if (!retry2) {
767
- return;
882
+ };
883
+ function nameForWaitOptions(options) {
884
+ if ("seconds" in options) {
885
+ return options.seconds === 1 ? `1 second` : `${options.seconds} seconds`;
768
886
  }
769
- const strategy = await getRetryStrategyForResponse(response, retry2);
770
- if (!strategy) {
771
- return;
887
+ if ("minutes" in options) {
888
+ return options.minutes === 1 ? `1 minute` : `${options.minutes} minutes`;
772
889
  }
773
- switch (strategy.strategy) {
774
- case "backoff": {
775
- const value = calculateNextRetryDelay({
776
- ...defaultRetryOptions,
777
- ...strategy
778
- }, attemptCount);
779
- if (value) {
780
- return {
781
- type: "delay",
782
- value
783
- };
784
- }
785
- break;
786
- }
787
- case "headers": {
788
- const resetAt = response.headers.get(strategy.resetHeader);
789
- if (typeof resetAt === "string") {
790
- const resetTimestamp = calculateResetAt(resetAt, strategy.resetFormat ?? "unix_timestamp_in_ms");
791
- if (resetTimestamp) {
792
- return {
793
- type: "timestamp",
794
- value: resetTimestamp
795
- };
796
- }
797
- }
798
- break;
799
- }
890
+ if ("hours" in options) {
891
+ return options.hours === 1 ? `1 hour` : `${options.hours} hours`;
800
892
  }
801
- }, "calculateRetryDelayForResponse");
802
- var getRetryStrategyForResponse = /* @__PURE__ */ __name(async (response, retry2) => {
803
- const statusCodes = Object.keys(retry2);
804
- const clonedResponse = response.clone();
805
- for (let i = 0; i < statusCodes.length; i++) {
806
- const statusRange = statusCodes[i];
807
- const strategy = retry2[statusRange];
808
- if (isStatusCodeInRange(response.status, statusRange)) {
809
- if (strategy.bodyFilter) {
810
- const body = safeJsonParse(await clonedResponse.text());
811
- if (!body) {
812
- continue;
813
- }
814
- if (eventFilterMatches(body, strategy.bodyFilter)) {
815
- return strategy;
816
- } else {
817
- continue;
818
- }
819
- }
820
- return strategy;
821
- }
893
+ if ("days" in options) {
894
+ return options.days === 1 ? `1 day` : `${options.days} days`;
822
895
  }
823
- }, "getRetryStrategyForResponse");
824
- var isStatusCodeInRange = /* @__PURE__ */ __name((statusCode, statusRange) => {
825
- if (statusRange === "all") {
826
- return true;
896
+ if ("weeks" in options) {
897
+ return options.weeks === 1 ? `1 week` : `${options.weeks} weeks`;
827
898
  }
828
- if (statusRange.includes(",")) {
829
- const statusCodes = statusRange.split(",").map((s) => s.trim());
830
- return statusCodes.some((s) => isStatusCodeInRange(statusCode, s));
899
+ if ("months" in options) {
900
+ return options.months === 1 ? `1 month` : `${options.months} months`;
831
901
  }
832
- const [start, end] = statusRange.split("-");
833
- if (end) {
834
- return statusCode >= parseInt(start, 10) && statusCode <= parseInt(end, 10);
902
+ if ("years" in options) {
903
+ return options.years === 1 ? `1 year` : `${options.years} years`;
835
904
  }
836
- if (start.endsWith("xx")) {
837
- const prefix = start.slice(0, -2);
838
- const statusCodePrefix = Math.floor(statusCode / 100).toString();
839
- return statusCodePrefix === prefix;
905
+ return "NaN";
906
+ }
907
+ __name(nameForWaitOptions, "nameForWaitOptions");
908
+ function calculateDurationInMs(options) {
909
+ if ("seconds" in options) {
910
+ return options.seconds * 1e3;
840
911
  }
841
- const statusCodeString = statusCode.toString();
842
- const rangePrefix = start.slice(0, -1);
843
- if (start.endsWith("x") && statusCodeString.startsWith(rangePrefix)) {
844
- return true;
912
+ if ("minutes" in options) {
913
+ return options.minutes * 1e3 * 60;
845
914
  }
846
- return statusCode === parseInt(start, 10);
847
- }, "isStatusCodeInRange");
848
- var createAttributesFromHeaders = /* @__PURE__ */ __name((headers) => {
849
- const attributes = {};
850
- const normalizedHeaderKey = /* @__PURE__ */ __name((key) => {
851
- return key.toLowerCase();
852
- }, "normalizedHeaderKey");
853
- headers.forEach((value, key) => {
854
- attributes[`http.response.header.${normalizedHeaderKey(key)}`] = value;
855
- });
856
- return attributes;
857
- }, "createAttributesFromHeaders");
858
- var safeJsonParse = /* @__PURE__ */ __name((json) => {
859
- try {
860
- return JSON.parse(json);
861
- } catch (e) {
862
- return null;
915
+ if ("hours" in options) {
916
+ return options.hours * 1e3 * 60 * 60;
863
917
  }
864
- }, "safeJsonParse");
865
- var interceptFetch = /* @__PURE__ */ __name((...handlers) => {
866
- return {
867
- run: async (fn) => {
868
- const current = fetchHttpHandlerStorage.getStore();
869
- if (current) {
870
- current.push(...handlers);
871
- return fn();
872
- } else {
873
- return fetchHttpHandlerStorage.run(handlers, fn);
874
- }
875
- }
876
- };
877
- }, "interceptFetch");
878
- var resolveDefaults = /* @__PURE__ */ __name((obj, key, defaults) => {
879
- if (!obj) {
880
- return defaults;
918
+ if ("days" in options) {
919
+ return options.days * 1e3 * 60 * 60 * 24;
881
920
  }
882
- if (obj[key] === void 0 || obj[key] === null) {
883
- return defaults;
921
+ if ("weeks" in options) {
922
+ return options.weeks * 1e3 * 60 * 60 * 24 * 7;
884
923
  }
885
- return obj[key];
886
- }, "resolveDefaults");
887
- var createFetchAttributes = /* @__PURE__ */ __name((input, init) => {
888
- const url = normalizeUrlFromInput(input);
889
- const httpMethod = normalizeHttpMethod(input, init);
890
- return {
891
- [SEMATTRS_HTTP_METHOD]: httpMethod,
892
- [SEMATTRS_HTTP_URL]: url.href,
893
- [SEMATTRS_HTTP_HOST]: url.hostname,
894
- ["server.host"]: url.hostname,
895
- ["server.port"]: url.port,
896
- [SEMATTRS_HTTP_SCHEME]: url.protocol.replace(":", ""),
897
- ...accessoryAttributes({
898
- items: [
899
- {
900
- text: url.hostname,
901
- variant: "normal"
902
- }
903
- ],
904
- style: "codepath"
905
- })
906
- };
907
- }, "createFetchAttributes");
908
- var createFetchResponseAttributes = /* @__PURE__ */ __name((response) => {
909
- return {
910
- [SEMATTRS_HTTP_STATUS_CODE]: response.status,
911
- "http.status_text": response.statusText,
912
- [SEMATTRS_HTTP_RESPONSE_CONTENT_LENGTH]: response.headers.get("content-length") || "0",
913
- ...createAttributesFromHeaders(response.headers)
914
- };
915
- }, "createFetchResponseAttributes");
916
- var createFetchRetryOptionsAttributes = /* @__PURE__ */ __name((retry2) => {
917
- const byStatus = resolveDefaults(retry2, "byStatus", defaultFetchRetryOptions.byStatus);
918
- const connectionError = resolveDefaults(retry2, "connectionError", defaultFetchRetryOptions.connectionError);
919
- const timeout = resolveDefaults(retry2, "timeout", defaultFetchRetryOptions.timeout);
920
- return {
921
- ...flattenAttributes(byStatus, "retry.byStatus"),
922
- ...flattenAttributes(connectionError, "retry.connectionError"),
923
- ...flattenAttributes(timeout, "retry.timeout")
924
- };
925
- }, "createFetchRetryOptionsAttributes");
926
- var retry = {
927
- onThrow,
928
- fetch: retryFetch,
929
- interceptFetch
930
- };
924
+ if ("months" in options) {
925
+ return options.months * 1e3 * 60 * 60 * 24 * 30;
926
+ }
927
+ if ("years" in options) {
928
+ return options.years * 1e3 * 60 * 60 * 24 * 365;
929
+ }
930
+ throw new Error("Invalid options");
931
+ }
932
+ __name(calculateDurationInMs, "calculateDurationInMs");
931
933
  var runs = {
932
934
  replay: replayRun,
933
- cancel: cancelRun
935
+ cancel: cancelRun,
936
+ retrieve: retrieveRun
934
937
  };
938
+ async function retrieveRun(runId) {
939
+ const apiClient = apiClientManager.client;
940
+ if (!apiClient) {
941
+ throw apiClientMissingError();
942
+ }
943
+ return await apiClient.retrieveRun(runId);
944
+ }
945
+ __name(retrieveRun, "retrieveRun");
935
946
  async function replayRun(runId) {
936
947
  const apiClient = apiClientManager.client;
937
948
  if (!apiClient) {
@@ -1026,6 +1037,12 @@ async function list(options) {
1026
1037
  }
1027
1038
  __name(list, "list");
1028
1039
 
1029
- export { InMemoryCache, createCache, queue, retry, runs, schedules_exports as schedules, task, wait };
1040
+ // src/v3/index.ts
1041
+ function configure(options) {
1042
+ apiClientManager.setGlobalAPIClientConfiguration(options);
1043
+ }
1044
+ __name(configure, "configure");
1045
+
1046
+ export { InMemoryCache, configure, createCache, queue, retry, runs, schedules_exports as schedules, task, wait };
1030
1047
  //# sourceMappingURL=out.js.map
1031
1048
  //# sourceMappingURL=index.mjs.map