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