diff --git a/apps/webapp/app/routes/api.v1.tasks.$taskId.trigger.ts b/apps/webapp/app/routes/api.v1.tasks.$taskId.trigger.ts index 2582e9df16..bf393a9d8a 100644 --- a/apps/webapp/app/routes/api.v1.tasks.$taskId.trigger.ts +++ b/apps/webapp/app/routes/api.v1.tasks.$taskId.trigger.ts @@ -11,6 +11,7 @@ import { prisma } from "~/db.server"; import { env } from "~/env.server"; import { ApiAuthenticationResultSuccess, getOneTimeUseToken } from "~/services/apiAuth.server"; import { logger } from "~/services/logger.server"; +import { extractJwtSigningSecretKey } from "~/services/realtime/jwtAuth.server"; import { determineRealtimeStreamsVersion } from "~/services/realtime/v1StreamsGlobal.server"; import { createActionApiRoute } from "~/services/routeBuilders/apiBuilder.server"; import { resolveIdempotencyKeyTTL } from "~/utils/idempotencyKeys.server"; @@ -85,7 +86,7 @@ const { action, loader } = createActionApiRoute( isCached: false, }), buildResponseHeaders: async (responseBody, cachedEntity) => { - return await responseHeaders(cachedEntity, authentication, triggerClient); + return await responseHeaders(cachedEntity, authentication); }, }); @@ -140,7 +141,12 @@ const { action, loader } = createActionApiRoute( await saveRequestIdempotency(requestIdempotencyKey, "trigger", result.run.id); - const $responseHeaders = await responseHeaders(result.run, authentication, triggerClient); + const $responseHeaders = await responseHeaders(result.run, authentication); + + logger.debug("responseHeaders authentication", { + authentication, + responseHeaders: $responseHeaders, + }); return json( { @@ -170,39 +176,26 @@ const { action, loader } = createActionApiRoute( async function responseHeaders( run: Pick, - authentication: ApiAuthenticationResultSuccess, - triggerClient?: string | null + authentication: ApiAuthenticationResultSuccess ): Promise> { const { environment, realtime } = authentication; - const claimsHeader = JSON.stringify({ + const claims = { sub: environment.id, pub: true, + scopes: [`read:runs:${run.friendlyId}`], realtime, - }); - - if (triggerClient === "browser") { - const claims = { - sub: environment.id, - pub: true, - scopes: [`read:runs:${run.friendlyId}`], - realtime, - }; - - const jwt = await internal_generateJWT({ - secretKey: environment.apiKey, - payload: claims, - expirationTime: "1h", - }); + }; - return { - "x-trigger-jwt-claims": claimsHeader, - "x-trigger-jwt": jwt, - }; - } + const jwt = await internal_generateJWT({ + secretKey: extractJwtSigningSecretKey(environment), + payload: claims, + expirationTime: "1h", + }); return { - "x-trigger-jwt-claims": claimsHeader, + "x-trigger-jwt-claims": JSON.stringify(claims), + "x-trigger-jwt": jwt, }; } diff --git a/apps/webapp/app/routes/api.v1.tasks.batch.ts b/apps/webapp/app/routes/api.v1.tasks.batch.ts index d21d277c47..4a10ade0e6 100644 --- a/apps/webapp/app/routes/api.v1.tasks.batch.ts +++ b/apps/webapp/app/routes/api.v1.tasks.batch.ts @@ -17,6 +17,7 @@ import { import { OutOfEntitlementError } from "~/v3/services/triggerTask.server"; import { HeadersSchema } from "./api.v1.tasks.$taskId.trigger"; import { determineRealtimeStreamsVersion } from "~/services/realtime/v1StreamsGlobal.server"; +import { extractJwtSigningSecretKey } from "~/services/realtime/jwtAuth.server"; const { action, loader } = createActionApiRoute( { @@ -163,7 +164,7 @@ async function responseHeaders( }; const jwt = await generateJWT({ - secretKey: environment.apiKey, + secretKey: extractJwtSigningSecretKey(environment), payload: claims, expirationTime: "1h", }); diff --git a/apps/webapp/app/routes/api.v2.tasks.batch.ts b/apps/webapp/app/routes/api.v2.tasks.batch.ts index 252439b7bf..02cbb594c1 100644 --- a/apps/webapp/app/routes/api.v2.tasks.batch.ts +++ b/apps/webapp/app/routes/api.v2.tasks.batch.ts @@ -19,6 +19,7 @@ import { BatchProcessingStrategy } from "~/v3/services/batchTriggerV3.server"; import { OutOfEntitlementError } from "~/v3/services/triggerTask.server"; import { HeadersSchema } from "./api.v1.tasks.$taskId.trigger"; import { determineRealtimeStreamsVersion } from "~/services/realtime/v1StreamsGlobal.server"; +import { extractJwtSigningSecretKey } from "~/services/realtime/jwtAuth.server"; const { action, loader } = createActionApiRoute( { @@ -178,7 +179,7 @@ async function responseHeaders( }; const jwt = await generateJWT({ - secretKey: environment.apiKey, + secretKey: extractJwtSigningSecretKey(environment), payload: claims, expirationTime: "1h", }); diff --git a/apps/webapp/app/services/apiAuth.server.ts b/apps/webapp/app/services/apiAuth.server.ts index aac0ecb6a6..989aeef8e9 100644 --- a/apps/webapp/app/services/apiAuth.server.ts +++ b/apps/webapp/app/services/apiAuth.server.ts @@ -236,6 +236,8 @@ async function authenticateApiKeyWithFailure( case "PUBLIC_JWT": { const validationResults = await validatePublicJwtKey(result.apiKey); + logger.debug("validatePublicJwtKey", { validationResults }); + if (!validationResults.ok) { return validationResults; } diff --git a/apps/webapp/app/services/realtime/jwtAuth.server.ts b/apps/webapp/app/services/realtime/jwtAuth.server.ts index d5950d99d3..4f94575a7c 100644 --- a/apps/webapp/app/services/realtime/jwtAuth.server.ts +++ b/apps/webapp/app/services/realtime/jwtAuth.server.ts @@ -2,6 +2,7 @@ import { json } from "@remix-run/server-runtime"; import { validateJWT } from "@trigger.dev/core/v3/jwt"; import { findEnvironmentById } from "~/models/runtimeEnvironment.server"; import { AuthenticatedEnvironment } from "../apiAuth.server"; +import { logger } from "../logger.server"; export type ValidatePublicJwtKeySuccess = { ok: true; @@ -38,6 +39,8 @@ export async function validatePublicJwtKey(token: string): Promise=16.0.0} - peerDependencies: - eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 - dependencies: - eslint: 9.39.1 - eslint-visitor-keys: 3.4.3 - - /@eslint-community/regexpp@4.12.2: - resolution: {integrity: sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==} - engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} - /@eslint-community/regexpp@4.5.1: resolution: {integrity: sha512-Z5ba73P98O1KUYCCJTUeVpja9RcGoMdncZ6T49FCUl2lN38JtCJ+3WgIDBv0AuY4WChU5PmtJmOCTlN6FZTFKQ==} engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} dev: true - /@eslint/config-array@0.21.1: - resolution: {integrity: sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - dependencies: - '@eslint/object-schema': 2.1.7 - debug: 4.4.3 - minimatch: 3.1.2 - transitivePeerDependencies: - - supports-color - - /@eslint/config-helpers@0.4.2: - resolution: {integrity: sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - dependencies: - '@eslint/core': 0.17.0 - - /@eslint/core@0.17.0: - resolution: {integrity: sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - dependencies: - '@types/json-schema': 7.0.15 - /@eslint/eslintrc@1.4.1: resolution: {integrity: sha512-XXrH9Uarn0stsyldqDYq8r++mROmWRI1xKMXa640Bb//SY1+ECYX6VzT6Lcx5frD0V30XieqJ0oX9I2Xj5aoMA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -7865,38 +7821,6 @@ packages: strip-json-comments: 3.1.1 transitivePeerDependencies: - supports-color - dev: true - - /@eslint/eslintrc@3.3.1: - resolution: {integrity: sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - dependencies: - ajv: 6.12.6 - debug: 4.4.3 - espree: 10.4.0 - globals: 14.0.0 - ignore: 5.3.2 - import-fresh: 3.3.1 - js-yaml: 4.1.1 - minimatch: 3.1.2 - strip-json-comments: 3.1.1 - transitivePeerDependencies: - - supports-color - - /@eslint/js@9.39.1: - resolution: {integrity: sha512-S26Stp4zCy88tH94QbBv3XCuzRQiZ9yXofEILmglYTh/Ug/a9/umqvgFtYBAo3Lp0nsI/5/qH1CCrbdK3AP1Tw==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - - /@eslint/object-schema@2.1.7: - resolution: {integrity: sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - - /@eslint/plugin-kit@0.4.1: - resolution: {integrity: sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - dependencies: - '@eslint/core': 0.17.0 - levn: 0.4.1 /@fal-ai/serverless-client@0.15.0: resolution: {integrity: sha512-4Vuocu0342OijAN6xO/lwohDV7h90LbkTnOAEwH+pYvMFVC6RYmHS4GILc/wnOWBTw+iFlZFEKlljEVolkjVfg==} @@ -8235,17 +8159,6 @@ packages: - utf-8-validate dev: true - /@humanfs/core@0.19.1: - resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==} - engines: {node: '>=18.18.0'} - - /@humanfs/node@0.16.7: - resolution: {integrity: sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==} - engines: {node: '>=18.18.0'} - dependencies: - '@humanfs/core': 0.19.1 - '@humanwhocodes/retry': 0.4.3 - /@humanwhocodes/config-array@0.11.8: resolution: {integrity: sha512-UybHIJzJnR5Qc/MsD9Kr+RpO2h+/P1GhOwdiLPXK5TWk5sgTdu88bTD9UP+CKbPPh5Rni1u0GjAdYQLemG8g+g==} engines: {node: '>=10.10.0'} @@ -8255,7 +8168,6 @@ packages: minimatch: 3.1.2 transitivePeerDependencies: - supports-color - dev: true /@humanwhocodes/module-importer@1.0.1: resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} @@ -8263,11 +8175,6 @@ packages: /@humanwhocodes/object-schema@1.2.1: resolution: {integrity: sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==} - dev: true - - /@humanwhocodes/retry@0.4.3: - resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==} - engines: {node: '>=18.18'} /@iconify/types@2.0.0: resolution: {integrity: sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==} @@ -19572,6 +19479,7 @@ packages: /@types/estree@1.0.8: resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} + dev: true /@types/eventsource@1.1.15: resolution: {integrity: sha512-XQmGcbnxUNa06HR3VBVkc9+A2Vpi9ZyLJcdS5dwaQQ/4ZMWFO+5c90FnMUpbtMZwB/FChoYHwuVg8TvkECacTA==} @@ -20992,7 +20900,6 @@ packages: acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 dependencies: acorn: 8.12.1 - dev: true /acorn-jsx@5.3.2(acorn@8.14.1): resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} @@ -21000,14 +20907,6 @@ packages: acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 dependencies: acorn: 8.14.1 - dev: true - - /acorn-jsx@5.3.2(acorn@8.15.0): - resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} - peerDependencies: - acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 - dependencies: - acorn: 8.15.0 /acorn-node@1.8.2: resolution: {integrity: sha512-8mt+fslDufLYntIoPAaIMUe/lrbrehIiwmR3t2k9LljIzoigEPF27eLk2hy8zSGzmR/ogr7zbRKINMo1u0yh5A==} @@ -21047,6 +20946,7 @@ packages: resolution: {integrity: sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==} engines: {node: '>=0.4.0'} hasBin: true + dev: true /agent-base@6.0.2: resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==} @@ -23535,17 +23435,6 @@ packages: ms: 2.1.3 supports-color: 10.0.0 - /debug@4.4.3: - resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} - engines: {node: '>=6.0'} - peerDependencies: - supports-color: '*' - peerDependenciesMeta: - supports-color: - optional: true - dependencies: - ms: 2.1.3 - /decamelize-keys@1.1.1: resolution: {integrity: sha512-WiPxgEirIV0/eIOMcnFBA3/IJZAZqKnwAwWyvvdi4lsr1WCN22nhdf/3db3DoZcUjTV2SqfzIwNyp6y2xs3nmg==} engines: {node: '>=0.10.0'} @@ -23833,7 +23722,6 @@ packages: engines: {node: '>=6.0.0'} dependencies: esutils: 2.0.3 - dev: true /dom-accessibility-api@0.5.15: resolution: {integrity: sha512-8o+oVqLQZoruQPYy3uAAQtc6YbtSiRq5aPJBhJ82YTJRHvI6ofhYAkC81WmjFTnfUbqg6T3aCglIpU9p/5e7Cw==} @@ -24755,7 +24643,6 @@ packages: '@esbuild/win32-arm64': 0.25.1 '@esbuild/win32-ia32': 0.25.1 '@esbuild/win32-x64': 0.25.1 - dev: true /escalade@3.2.0: resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} @@ -24797,22 +24684,22 @@ packages: eslint: 8.31.0 dev: true - /eslint-config-prettier@9.0.0(eslint@9.39.1): + /eslint-config-prettier@9.0.0(eslint@8.31.0): resolution: {integrity: sha512-IcJsTkJae2S35pRsRAwoCE+925rJJStOdkKnLVgtE+tEpqU0EVVM7OqrwxqgptKdX29NUwC82I5pXsGFIgSevw==} hasBin: true peerDependencies: eslint: '>=7.0.0' dependencies: - eslint: 9.39.1 + eslint: 8.31.0 dev: false - /eslint-config-turbo@1.10.12(eslint@9.39.1): + /eslint-config-turbo@1.10.12(eslint@8.31.0): resolution: {integrity: sha512-z3jfh+D7UGYlzMWGh+Kqz++hf8LOE96q3o5R8X4HTjmxaBWlLAWG+0Ounr38h+JLR2TJno0hU9zfzoPNkR9BdA==} peerDependencies: eslint: '>6.6.0' dependencies: - eslint: 9.39.1 - eslint-plugin-turbo: 1.10.12(eslint@9.39.1) + eslint: 8.31.0 + eslint-plugin-turbo: 1.10.12(eslint@8.31.0) dev: false /eslint-import-resolver-node@0.3.7: @@ -25084,13 +24971,13 @@ packages: - typescript dev: true - /eslint-plugin-turbo@1.10.12(eslint@9.39.1): + /eslint-plugin-turbo@1.10.12(eslint@8.31.0): resolution: {integrity: sha512-uNbdj+ohZaYo4tFJ6dStRXu2FZigwulR1b3URPXe0Q8YaE7thuekKNP+54CHtZPH9Zey9dmDx5btAQl9mfzGOw==} peerDependencies: eslint: '>6.6.0' dependencies: dotenv: 16.0.3 - eslint: 9.39.1 + eslint: 8.31.0 dev: false /eslint-plugin-turbo@2.0.5(eslint@8.31.0): @@ -25102,15 +24989,6 @@ packages: eslint: 8.31.0 dev: true - /eslint-plugin-turbo@2.0.5(eslint@9.39.1): - resolution: {integrity: sha512-nCTXZdaKmdRybBdjnMrDFG+ppLc9toUqB01Hf0pfhkQw8OoC29oJIVPsCSvuL/W58RKD02CNEUrwnVt57t36IQ==} - peerDependencies: - eslint: '>6.6.0' - dependencies: - dotenv: 16.0.3 - eslint: 9.39.1 - dev: true - /eslint-scope@5.1.1: resolution: {integrity: sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==} engines: {node: '>=8.0.0'} @@ -25124,14 +25002,6 @@ packages: dependencies: esrecurse: 4.3.0 estraverse: 5.3.0 - dev: true - - /eslint-scope@8.4.0: - resolution: {integrity: sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - dependencies: - esrecurse: 4.3.0 - estraverse: 5.3.0 /eslint-utils@2.1.0: resolution: {integrity: sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==} @@ -25148,7 +25018,6 @@ packages: dependencies: eslint: 8.31.0 eslint-visitor-keys: 2.1.0 - dev: true /eslint-visitor-keys@1.3.0: resolution: {integrity: sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==} @@ -25158,25 +25027,14 @@ packages: /eslint-visitor-keys@2.1.0: resolution: {integrity: sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==} engines: {node: '>=10'} - dev: true /eslint-visitor-keys@3.3.0: resolution: {integrity: sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - dev: true /eslint-visitor-keys@3.4.2: resolution: {integrity: sha512-8drBzUEyZ2llkpCA67iYrgEssKDUu68V8ChqqOfFupIaG/LCVPUT+CoGJpT77zJprs4T/W7p07LP7zAIMuweVw==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - dev: true - - /eslint-visitor-keys@3.4.3: - resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - - /eslint-visitor-keys@4.2.1: - resolution: {integrity: sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} /eslint@8.31.0: resolution: {integrity: sha512-0tQQEVdmPZ1UtUKXjX7EMm9BlgJ08G90IhWh0PKDCb3ZLsgAOHI8fYSIzYVZej92zsgq+ft0FGsxhJ3xo2tbuA==} @@ -25224,67 +25082,11 @@ packages: text-table: 0.2.0 transitivePeerDependencies: - supports-color - dev: true - - /eslint@9.39.1: - resolution: {integrity: sha512-BhHmn2yNOFA9H9JmmIVKJmd288g9hrVRDkdoIgRCRuSySRUHH7r/DI6aAXW9T1WwUuY3DFgrcaqB+deURBLR5g==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - hasBin: true - peerDependencies: - jiti: '*' - peerDependenciesMeta: - jiti: - optional: true - dependencies: - '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.1) - '@eslint-community/regexpp': 4.12.2 - '@eslint/config-array': 0.21.1 - '@eslint/config-helpers': 0.4.2 - '@eslint/core': 0.17.0 - '@eslint/eslintrc': 3.3.1 - '@eslint/js': 9.39.1 - '@eslint/plugin-kit': 0.4.1 - '@humanfs/node': 0.16.7 - '@humanwhocodes/module-importer': 1.0.1 - '@humanwhocodes/retry': 0.4.3 - '@types/estree': 1.0.8 - ajv: 6.12.6 - chalk: 4.1.2 - cross-spawn: 7.0.6 - debug: 4.4.3 - escape-string-regexp: 4.0.0 - eslint-scope: 8.4.0 - eslint-visitor-keys: 4.2.1 - espree: 10.4.0 - esquery: 1.6.0 - esutils: 2.0.3 - fast-deep-equal: 3.1.3 - file-entry-cache: 8.0.0 - find-up: 5.0.0 - glob-parent: 6.0.2 - ignore: 5.3.2 - imurmurhash: 0.1.4 - is-glob: 4.0.3 - json-stable-stringify-without-jsonify: 1.0.1 - lodash.merge: 4.6.2 - minimatch: 3.1.2 - natural-compare: 1.4.0 - optionator: 0.9.4 - transitivePeerDependencies: - - supports-color /esm-env@1.2.2: resolution: {integrity: sha512-Epxrv+Nr/CaL4ZcFGPJIYLWFom+YeV1DqMLHJoEd9SYRxNbaFruBwfEX/kkHUJf55j2+TUbmDcmuilbP1TmXHA==} dev: true - /espree@10.4.0: - resolution: {integrity: sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - dependencies: - acorn: 8.15.0 - acorn-jsx: 5.3.2(acorn@8.15.0) - eslint-visitor-keys: 4.2.1 - /espree@9.4.1: resolution: {integrity: sha512-XwctdmTO6SIvCzd9810yyNzIrOrqNYV9Koizx4C/mRhf9uq0o4yHoCEU/670pOxOL/MSraektvSAji79kX90Vg==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -25292,7 +25094,6 @@ packages: acorn: 8.12.1 acorn-jsx: 5.3.2(acorn@8.12.1) eslint-visitor-keys: 3.4.2 - dev: true /espree@9.6.0: resolution: {integrity: sha512-1FH/IiruXZ84tpUlm0aCUEwMl2Ho5ilqVh0VvQXw+byAz/4SAciyHLlfmL5WYqsvD38oymdUwBss0LtK8m4s/A==} @@ -25301,7 +25102,6 @@ packages: acorn: 8.14.1 acorn-jsx: 5.3.2(acorn@8.14.1) eslint-visitor-keys: 3.4.2 - dev: true /esprima@4.0.1: resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} @@ -25313,13 +25113,6 @@ packages: engines: {node: '>=0.10'} dependencies: estraverse: 5.3.0 - dev: true - - /esquery@1.6.0: - resolution: {integrity: sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==} - engines: {node: '>=0.10'} - dependencies: - estraverse: 5.3.0 /esrap@2.1.2: resolution: {integrity: sha512-DgvlIQeowRNyvLPWW4PT7Gu13WznY288Du086E751mwwbsgr29ytBiYeLzAGIo0qk3Ujob0SDk8TiSaM5WQzNg==} @@ -25871,13 +25664,6 @@ packages: engines: {node: ^10.12.0 || >=12.0.0} dependencies: flat-cache: 3.0.4 - dev: true - - /file-entry-cache@8.0.0: - resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} - engines: {node: '>=16.0.0'} - dependencies: - flat-cache: 4.0.1 /file-selector@0.6.0: resolution: {integrity: sha512-QlZ5yJC0VxHxQQsQhXvBaC7VRJ2uaxTf+Tfpu4Z/OcVQJVpZO+DGU0rkoVW5ce2SccxugvpBJoMvUs59iILYdw==} @@ -25983,21 +25769,9 @@ packages: dependencies: flatted: 3.2.7 rimraf: 3.0.2 - dev: true - - /flat-cache@4.0.1: - resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==} - engines: {node: '>=16'} - dependencies: - flatted: 3.3.3 - keyv: 4.5.4 /flatted@3.2.7: resolution: {integrity: sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==} - dev: true - - /flatted@3.3.3: - resolution: {integrity: sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==} /follow-redirects@1.15.9: resolution: {integrity: sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==} @@ -26344,7 +26118,6 @@ packages: resolution: {integrity: sha512-ZAqrLlu18NbDdRaHq+AKXzAmqIUPswPWKUchfytdAjiRFnCe5ojG2bstg6mRiZabkKfCoL/e98pbBELIV/YCeA==} dependencies: resolve-pkg-maps: 1.0.0 - dev: true /get-uri@6.0.1: resolution: {integrity: sha512-7ZqONUVqaabogsYNWlYj0t3YZaL6dhuEueZXGF+/YVmf6dHmaFg8/6psJKqhx9QykIDKzpGcy2cn4oV4YC7V/Q==} @@ -26480,11 +26253,6 @@ packages: engines: {node: '>=8'} dependencies: type-fest: 0.20.2 - dev: true - - /globals@14.0.0: - resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==} - engines: {node: '>=18'} /globals@15.15.0: resolution: {integrity: sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg==} @@ -27082,10 +26850,6 @@ packages: resolution: {integrity: sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==} engines: {node: '>= 4'} - /ignore@5.3.2: - resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} - engines: {node: '>= 4'} - /import-fresh@3.3.0: resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==} engines: {node: '>=6'} @@ -27093,13 +26857,6 @@ packages: parent-module: 1.0.1 resolve-from: 4.0.0 - /import-fresh@3.3.1: - resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} - engines: {node: '>=6'} - dependencies: - parent-module: 1.0.1 - resolve-from: 4.0.0 - /import-in-the-middle@1.11.0: resolution: {integrity: sha512-5DimNQGoe0pLUHbR9qK84iWaWjjbsxiqXnw6Qz64+azRgleqv9k2kTt5fw7QsOpmaGYtuxxursnPPsnTKEx10Q==} dependencies: @@ -27465,7 +27222,6 @@ packages: /is-path-inside@3.0.3: resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==} engines: {node: '>=8'} - dev: true /is-plain-obj@1.1.0: resolution: {integrity: sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==} @@ -27776,7 +27532,6 @@ packages: /js-sdsl@4.2.0: resolution: {integrity: sha512-dyBIzQBDkCqCu+0upx25Y2jGdbTGxE9fshMsCdK0ViOongpV+n5tXRcZY9v7CaVQ79AGS9KA1KHtojxiM7aXSQ==} - dev: true /js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} @@ -27794,12 +27549,6 @@ packages: dependencies: argparse: 2.0.1 - /js-yaml@4.1.1: - resolution: {integrity: sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==} - hasBin: true - dependencies: - argparse: 2.0.1 - /jsbn@0.1.1: resolution: {integrity: sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==} dev: false @@ -27830,9 +27579,6 @@ packages: resolution: {integrity: sha512-CuUqjv0FUZIdXkHPI8MezCnFCdaTAacej1TZYulLoAg1h/PhwkdXFN4V/gzY4g+fMBCOV2xF+rp7t2XD2ns/NQ==} dev: true - /json-buffer@3.0.1: - resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} - /json-parse-better-errors@1.0.2: resolution: {integrity: sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==} dev: true @@ -27993,11 +27739,6 @@ packages: json-buffer: 3.0.0 dev: true - /keyv@4.5.4: - resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} - dependencies: - json-buffer: 3.0.1 - /khroma@2.1.0: resolution: {integrity: sha512-Ls993zuzfayK269Svk9hzpeGUKob/sIgZzyHYdjQoAdQetRKpOLj+k/QQQ/6Qi0Yz65mlROrfd+Ev+1+7dz9Kw==} dev: false @@ -30805,18 +30546,6 @@ packages: prelude-ls: 1.2.1 type-check: 0.4.0 word-wrap: 1.2.3 - dev: true - - /optionator@0.9.4: - resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} - engines: {node: '>= 0.8.0'} - dependencies: - deep-is: 0.1.4 - fast-levenshtein: 2.0.6 - levn: 0.4.1 - prelude-ls: 1.2.1 - type-check: 0.4.0 - word-wrap: 1.2.5 /ora@5.4.1: resolution: {integrity: sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==} @@ -32517,7 +32246,7 @@ packages: scheduler: 0.26.0 dev: false - /react-email@2.1.2(eslint@9.39.1): + /react-email@2.1.2(eslint@8.31.0): resolution: {integrity: sha512-HBHhpzEE5es9YUoo7VSj6qy1omjwndxf3/Sb44UJm/uJ2AjmqALo2yryux0CjW9QAVfitc9rxHkLvIb9H87QQw==} engines: {node: '>=18.0.0'} hasBin: true @@ -32543,8 +32272,8 @@ packages: commander: 11.1.0 debounce: 2.0.0 esbuild: 0.19.11 - eslint-config-prettier: 9.0.0(eslint@9.39.1) - eslint-config-turbo: 1.10.12(eslint@9.39.1) + eslint-config-prettier: 9.0.0(eslint@8.31.0) + eslint-config-turbo: 1.10.12(eslint@8.31.0) framer-motion: 10.17.4(react-dom@18.2.0)(react@18.3.1) glob: 10.3.4 log-symbols: 4.1.0 @@ -33170,7 +32899,6 @@ packages: /regexpp@3.2.0: resolution: {integrity: sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==} engines: {node: '>=8'} - dev: true /registry-auth-token@4.2.2: resolution: {integrity: sha512-PC5ZysNb42zpFME6D/XlIgtNGdTl8bBOCw90xQLVMpzuuubJKYDWFAEuUNc+Cn8Z8724tg2SDhDRrkVEsqfDMg==} @@ -33516,7 +33244,6 @@ packages: /resolve-pkg-maps@1.0.0: resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} - dev: true /resolve.exports@2.0.2: resolution: {integrity: sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg==} @@ -33595,7 +33322,6 @@ packages: hasBin: true dependencies: glob: 7.2.3 - dev: true /rimraf@5.0.7: resolution: {integrity: sha512-nV6YcJo5wbLW77m+8KjH8aB/7/rxQy9SZ0HY5shnwULfS+9nmTtVXAJET5NdZmCzA4fPI/Hm1wo/Po/4mopOdg==} @@ -35476,7 +35202,6 @@ packages: /text-table@0.2.0: resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==} - dev: true /thenify-all@1.6.0: resolution: {integrity: sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==} @@ -35983,7 +35708,6 @@ packages: get-tsconfig: 4.7.6 optionalDependencies: fsevents: 2.3.3 - dev: true /tsx@4.7.1: resolution: {integrity: sha512-8d6VuibXHtlN5E3zFkgY8u4DX7Y3Z27zvvPKVmLon/D4AjuKzarkUBTLDBgj9iTQ0hg5xM7c/mYiRVM+HETf0g==} @@ -36097,7 +35821,6 @@ packages: /type-fest@0.20.2: resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==} engines: {node: '>=10'} - dev: true /type-fest@0.6.0: resolution: {integrity: sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==} @@ -37355,11 +37078,6 @@ packages: /word-wrap@1.2.3: resolution: {integrity: sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==} engines: {node: '>=0.10.0'} - dev: true - - /word-wrap@1.2.5: - resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} - engines: {node: '>=0.10.0'} /wrap-ansi@6.2.0: resolution: {integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==} diff --git a/references/issue-2687/.env.example b/references/issue-2687/.env.example new file mode 100644 index 0000000000..bec8f7a1b8 --- /dev/null +++ b/references/issue-2687/.env.example @@ -0,0 +1,3 @@ +TRIGGER_SECRET_KEY=tr_dev_... +TRIGGER_API_URL=https://api.trigger.dev +TRIGGER_BRANCH= diff --git a/references/issue-2687/README.md b/references/issue-2687/README.md new file mode 100644 index 0000000000..3f522e048b --- /dev/null +++ b/references/issue-2687/README.md @@ -0,0 +1,28 @@ +# Issue 2687 Reproduction + +This reference project reproduces the issue where `Realtime` returns 401 when using `createTriggerPublicToken`. + +## Setup + +1. Make sure your Trigger.dev instance is running (webapp). +2. Copy `.env.example` to `.env` and fill in your details: + +```bash +cp .env.example .env +``` + +3. Edit `.env` to set your `TRIGGER_SECRET_KEY` and `TRIGGER_API_URL`. + +## Running the reproduction + +Run the reproduction script: + +```bash +pnpm run repro +``` + +## What it does + +1. Generates a `triggerPublicToken` for `issue-2687-task`. +2. Triggers the task using this token. +3. Attempts to connect to the Realtime endpoint for the resulting run using the same token. diff --git a/references/issue-2687/package.json b/references/issue-2687/package.json new file mode 100644 index 0000000000..53b49cb8ee --- /dev/null +++ b/references/issue-2687/package.json @@ -0,0 +1,19 @@ +{ + "name": "references-issue-2687", + "private": true, + "type": "module", + "devDependencies": { + "trigger.dev": "workspace:*" + }, + "dependencies": { + "@trigger.dev/sdk": "workspace:*", + "dotenv": "^16.4.5", + "tsx": "^4.0.0" + }, + "scripts": { + "dev": "trigger dev", + "deploy": "trigger deploy", + "repro": "tsx src/repro.ts" + } +} + diff --git a/references/issue-2687/src/repro.ts b/references/issue-2687/src/repro.ts new file mode 100644 index 0000000000..4c2be6a840 --- /dev/null +++ b/references/issue-2687/src/repro.ts @@ -0,0 +1,81 @@ +import "dotenv/config"; +import { auth, tasks, runs, configure } from "@trigger.dev/sdk/v3"; + +const taskId = "issue-2687-task"; + +async function main() { + if (!process.env.TRIGGER_SECRET_KEY) { + console.error("TRIGGER_SECRET_KEY is not set. Please set it to your project's secret key."); + process.exit(1); + } + + const apiUrl = process.env.TRIGGER_API_URL || "https://api.trigger.dev"; + const branch = process.env.TRIGGER_PREVIEW_BRANCH; + const secretKey = process.env.TRIGGER_SECRET_KEY; + + console.log(`Using API URL: ${apiUrl}`); + console.log(`Using Secret Key: ${secretKey}`); + if (branch) { + console.log(`Using Branch: ${branch}`); + } + + // 1. Generate the public token (Server-side) + // We need the secret key for this part, so we use the environment variable which the SDK picks up automatically for this call. + console.log("Generating public token..."); + try { + // Ensure we are using the correct API URL for the generation as well, if it matters (it validates against the env). + configure({ baseURL: apiUrl, accessToken: secretKey, previewBranch: branch }); + + const token = await auth.createTriggerPublicToken(taskId); + console.log("Token generated."); + + // 2. Trigger the task using the public token + console.log(`Triggering task ${taskId}...`); + + // Use auth.withAuth to temporarily scope the SDK to the public token + await auth.withAuth( + { accessToken: token, baseURL: apiUrl, previewBranch: branch }, + async () => { + const handle = await tasks.trigger(taskId, { foo: "bar" }); + console.log(`Task triggered. Run ID: ${handle.id}`); + + let tokenToUse = token; + if (handle.publicAccessToken) { + console.log("Received publicAccessToken in handle, using it for realtime."); + console.log(`Public Access Token: ${handle.publicAccessToken}`); + tokenToUse = handle.publicAccessToken; + } else { + console.log("Using initial token for subsequent requests."); + } + + // 3. Access Run details (Simulating Realtime/Read access) + // If the token changed (which it might if the API returns a specific read-only token), we should use that. + + if (tokenToUse !== token) { + await auth.withAuth( + { accessToken: tokenToUse, baseURL: apiUrl, previewBranch: branch }, + async () => { + console.log(`Subscribing to run ${handle.id} with new token...`); + for await (const run of runs.subscribeToRun(handle.id)) { + console.log(`Run update received. Status: ${run.status}`); + break; + } + } + ); + } else { + console.log(`Subscribing to run ${handle.id} with initial token...`); + for await (const run of runs.subscribeToRun(handle.id)) { + console.log(`Run update received. Status: ${run.status}`); + break; + } + } + + console.log("Realtime/Read access verified."); + } + ); + } catch (error) { + console.error("Error:", error); + } +} + +main(); diff --git a/references/issue-2687/src/trigger/task.ts b/references/issue-2687/src/trigger/task.ts new file mode 100644 index 0000000000..990a1b46b2 --- /dev/null +++ b/references/issue-2687/src/trigger/task.ts @@ -0,0 +1,9 @@ +import { task } from "@trigger.dev/sdk/v3"; + +export const myTask = task({ + id: "issue-2687-task", + run: async (payload: any) => { + console.log("Task running with payload:", payload); + return { message: "Hello World" }; + }, +}); diff --git a/references/issue-2687/trigger.config.ts b/references/issue-2687/trigger.config.ts new file mode 100644 index 0000000000..0a3d35cb36 --- /dev/null +++ b/references/issue-2687/trigger.config.ts @@ -0,0 +1,17 @@ +import { defineConfig } from "@trigger.dev/sdk/v3"; + +export default defineConfig({ + project: process.env.TRIGGER_PROJECT_REF!, + logLevel: "log", + maxDuration: 3600, + retries: { + enabledInDev: true, + default: { + maxAttempts: 3, + minTimeoutInMs: 1000, + maxTimeoutInMs: 10000, + factor: 2, + randomize: true, + }, + }, +});