diff --git a/.changeset/heavy-eyes-own.md b/.changeset/heavy-eyes-own.md new file mode 100644 index 000000000..4765ed51f --- /dev/null +++ b/.changeset/heavy-eyes-own.md @@ -0,0 +1,5 @@ +--- +"@trigger.dev/cli": patch +--- + +[dev] Better handle errors when registering endpoint diff --git a/.changeset/lucky-crews-matter.md b/.changeset/lucky-crews-matter.md new file mode 100644 index 000000000..ef58a1d00 --- /dev/null +++ b/.changeset/lucky-crews-matter.md @@ -0,0 +1,5 @@ +--- +"@trigger.dev/sdk": patch +--- + +Fixed responses from the PING action to match expected schema diff --git a/examples/nextjs-clerk/package.json b/examples/nextjs-clerk/package.json index 0b0df7ce9..a0215a6ca 100644 --- a/examples/nextjs-clerk/package.json +++ b/examples/nextjs-clerk/package.json @@ -20,8 +20,8 @@ "react-dom": "^18.2.0", "tailwindcss": "3.3.2", "typescript": "5.1.6", - "@trigger.dev/sdk": "^2.0.0-next.4", - "@trigger.dev/nextjs": "^1.0.0-next.5" + "@trigger.dev/sdk": "workspace:*", + "@trigger.dev/nextjs": "workspace:*" }, "devDependencies": { "@trigger.dev/cli": "workspace:*" diff --git a/examples/nextjs-clerk/src/app/api/trigger/route.ts b/examples/nextjs-clerk/src/app/api/trigger/route.ts index bde606eec..f7f24556f 100644 --- a/examples/nextjs-clerk/src/app/api/trigger/route.ts +++ b/examples/nextjs-clerk/src/app/api/trigger/route.ts @@ -23,6 +23,4 @@ new Job(client, { }, }); -export const { POST, dynamic } = createAppRoute(client, { - path: "/api/trigger", -}); +export const { POST, dynamic } = createAppRoute(client); diff --git a/examples/nextjs-clerk/src/middleware.ts b/examples/nextjs-clerk/src/middleware.ts index 126a58657..3955aad55 100644 --- a/examples/nextjs-clerk/src/middleware.ts +++ b/examples/nextjs-clerk/src/middleware.ts @@ -1,6 +1,8 @@ import { authMiddleware } from "@clerk/nextjs"; -export default authMiddleware(); +export default authMiddleware({ + publicRoutes: ["/api/trigger"], +}); export const config = { matcher: ["/((?!.*\\..*|_next).*)", "/", "/(api|trpc)(.*)"], diff --git a/examples/nextjs-clerk/tsconfig.json b/examples/nextjs-clerk/tsconfig.json index 0c7555fa7..398a4ac1b 100644 --- a/examples/nextjs-clerk/tsconfig.json +++ b/examples/nextjs-clerk/tsconfig.json @@ -20,7 +20,27 @@ } ], "paths": { - "@/*": ["./src/*"] + "@/*": ["./src/*"], + "@trigger.dev/sdk": ["../../packages/trigger-sdk/src/index"], + "@trigger.dev/sdk/*": ["../../packages/trigger-sdk/src/*"], + "@trigger.dev/nextjs": ["../../packages/nextjs/src/index"], + "@trigger.dev/nextjs/*": ["../../packages/nextjs/src/*"], + "@trigger.dev/internal": ["../../packages/internal/src/index"], + "@trigger.dev/internal/*": ["../../packages/internal/src/*"], + "@trigger.dev/integration-kit": [ + "../../packages/integration-kit/src/index" + ], + "@trigger.dev/integration-kit/*": [ + "../../packages/integration-kit/src/*" + ], + "@trigger.dev/github": ["../../integrations/github/src/index"], + "@trigger.dev/github/*": ["../../integrations/github/src/*"], + "@trigger.dev/slack": ["../../integrations/slack/src/index"], + "@trigger.dev/slack/*": ["../../integrations/slack/src/*"], + "@trigger.dev/openai": ["../../integrations/openai/src/index"], + "@trigger.dev/openai/*": ["../../integrations/openai/src/*"], + "@trigger.dev/resend": ["../../integrations/resend/src/index"], + "@trigger.dev/resend/*": ["../../integrations/resend/src/*"] } }, "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], diff --git a/packages/cli/src/commands/dev.ts b/packages/cli/src/commands/dev.ts index 7485b23d2..9a6835f72 100644 --- a/packages/cli/src/commands/dev.ts +++ b/packages/cli/src/commands/dev.ts @@ -17,9 +17,6 @@ export const DevCommandOptionsSchema = z.object({ const throttleTimeMs = 1000; const formattedDate = new Intl.DateTimeFormat("en", { - year: "numeric", - month: "short", - day: "numeric", hour: "numeric", minute: "numeric", second: "numeric", @@ -42,7 +39,7 @@ export async function devCommand(path: string, anyOptions: any) { ); process.exit(1); } - logger.success(`✔️ Detected valid Next.js project`); + logger.success(`✔️ [trigger.dev] Detected valid Next.js project`); // Read from package.json to get the endpointId const endpointId = await getEndpointIdFromPackageJson(resolvedPath); @@ -52,40 +49,51 @@ export async function devCommand(path: string, anyOptions: any) { ); process.exit(1); } - logger.success(`✔️ Detected TriggerClient id: ${endpointId}`); + logger.success(`✔️ [trigger.dev] Detected TriggerClient id: ${endpointId}`); // Read from .env.local to get the TRIGGER_API_KEY and TRIGGER_API_URL - const { apiKey, apiUrl } = await getTriggerApiDetails( - resolvedPath, - options.envFile - ); - logger.success(`✔️ Found API Key in ${options.envFile} file`); - const apiClient = new TriggerApi(apiKey, apiUrl); + const { apiUrl } = await getTriggerApiDetails(resolvedPath, options.envFile); + logger.success(`✔️ [trigger.dev] Found API Key in ${options.envFile} file`); // Setup tunnel - const tunnelSpinner = ora(`🚇 Creating tunnel`).start(); - const tunnelUrl = await createTunnel(options.port); - tunnelSpinner.succeed(`🚇 Created tunnel: ${tunnelUrl}`); + const endpointUrl = await resolveEndpointUrl(apiUrl, options.port); - const connectingSpinner = ora(`Connecting to Trigger.dev...`).start(); + const connectingSpinner = ora( + `[trigger.dev] Connecting to Trigger.dev...` + ).start(); //refresh function let attemptCount = 0; const refresh = async () => { - const result = await refreshEndpoint(apiClient, endpointId, tunnelUrl); + const refreshedEndpointId = await getEndpointIdFromPackageJson( + resolvedPath + ); + + // Read from .env.local to get the TRIGGER_API_KEY and TRIGGER_API_URL + const { apiKey, apiUrl } = await getTriggerApiDetails( + resolvedPath, + options.envFile + ); + + const apiClient = new TriggerApi(apiKey, apiUrl); + + const result = await refreshEndpoint( + apiClient, + refreshedEndpointId ?? endpointId, + endpointUrl + ); if (result.success) { attemptCount = 0; - connectingSpinner.stop(); - logger.success( - `🔄 Updated your Jobs ${formattedDate.format( - new Date(result.data.updatedAt) - )}` + connectingSpinner.succeed( + `[trigger.dev] 🔄 Refreshed ${ + refreshedEndpointId ?? endpointId + } ${formattedDate.format(new Date(result.data.updatedAt))}` ); } else { attemptCount++; - if (attemptCount === 10) { - logger.error(`🚨 Failed to connect: ${result.error}`); + if (attemptCount === 10 || !result.retryable) { + connectingSpinner.fail(`🚨 Failed to connect: ${result.error}`); logger.info(`Will attempt again on the next file change…`); return; } @@ -169,6 +177,21 @@ async function getTriggerApiDetails(path: string, envFile: string) { return { apiKey, apiUrl }; } +async function resolveEndpointUrl(apiUrl: string, port: number) { + const apiURL = new URL(apiUrl); + + if (apiURL.hostname === "localhost") { + return `http://localhost:${port}`; + } + + // Setup tunnel + const tunnelSpinner = ora(`🚇 Creating tunnel`).start(); + const tunnelUrl = await createTunnel(port); + tunnelSpinner.succeed(`🚇 Created tunnel: ${tunnelUrl}`); + + return tunnelUrl; +} + async function createTunnel(port: number) { try { return await ngrok.connect(port); @@ -190,15 +213,23 @@ async function refreshEndpoint( }); if (!response.ok) { - return { success: false as const, error: response.error }; + return { + success: false as const, + error: response.error, + retryable: response.retryable, + }; } return { success: true as const, data: response.data }; } catch (e) { if (e instanceof Error) { - return { success: false as const, error: e.message }; + return { success: false as const, error: e.message, retryable: true }; } else { - return { success: false as const, error: "Unknown error" }; + return { + success: false as const, + error: "Unknown error", + retryable: true, + }; } } } diff --git a/packages/cli/src/commands/init.ts b/packages/cli/src/commands/init.ts index c76820007..3f195e9ca 100644 --- a/packages/cli/src/commands/init.ts +++ b/packages/cli/src/commands/init.ts @@ -139,7 +139,7 @@ async function printNextSteps( logger.info("Next steps:"); logger.info(` 1. Run your Next.js project locally with 'npm run dev'`); logger.info( - ` 2. Run 'npx @trigger.dev/cli dev' to watch for changes and automatically register Trigger.dev jobs` + ` 2. Run 'npx @trigger.dev/cli@latest dev' to watch for changes and automatically register Trigger.dev jobs` ); logger.info(` 3. View your jobs at ${projectUrl}`); diff --git a/packages/cli/src/utils/triggerApi.ts b/packages/cli/src/utils/triggerApi.ts index 94761a24f..e82d7d414 100644 --- a/packages/cli/src/utils/triggerApi.ts +++ b/packages/cli/src/utils/triggerApi.ts @@ -23,7 +23,9 @@ export type EndpointResponse = ok: true; data: EndpointData; } - | { ok: false; error: string }; + | { ok: false; error: string; retryable: boolean }; + +const RETRYABLE_PATTERN = /Could not connect to endpoint/i; const WhoamiResponseSchema = z.object({ id: z.string(), @@ -97,27 +99,31 @@ export class TriggerApi { ok: false, error: "An unknown issue occurred when registering with Trigger.dev", + retryable: true, }; } - const parsedJson = z.object({ message: z.string() }).safeParse(rawJson); + const parsedJson = z.object({ error: z.string() }).safeParse(rawJson); if (!parsedJson.success) { return { ok: false, error: "An unknown issue occurred when registering with Trigger.dev", + retryable: true, }; } return { ok: false, - error: parsedJson.data.message, + error: parsedJson.data.error, + retryable: RETRYABLE_PATTERN.test(parsedJson.data.error), }; } else { return { ok: false, error: "An unknown issue occurred when registering with Trigger.dev", + retryable: true, }; } } diff --git a/packages/trigger-sdk/src/triggerClient.ts b/packages/trigger-sdk/src/triggerClient.ts index 4dbdebad5..c1f390e72 100644 --- a/packages/trigger-sdk/src/triggerClient.ts +++ b/packages/trigger-sdk/src/triggerClient.ts @@ -160,7 +160,7 @@ export class TriggerClient { status: 200, body: { ok: false, - message: "Missing endpoint ID", + error: "Missing endpoint ID", }, }; } @@ -170,7 +170,7 @@ export class TriggerClient { status: 200, body: { ok: false, - message: `Endpoint ID mismatch error. Expected ${this.id}, got ${endpointId}`, + error: `Endpoint ID mismatch error. Expected ${this.id}, got ${endpointId}`, }, }; } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b9c7fca7e..9471a74c8 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -462,8 +462,8 @@ importers: specifiers: '@clerk/nextjs': ^4.21.12 '@trigger.dev/cli': workspace:* - '@trigger.dev/nextjs': ^1.0.0-next.5 - '@trigger.dev/sdk': ^2.0.0-next.4 + '@trigger.dev/nextjs': workspace:* + '@trigger.dev/sdk': workspace:* '@types/node': 20.3.2 '@types/react': ^18.0.21 '@types/react-dom': ^18.0.6 @@ -476,8 +476,8 @@ importers: typescript: 5.1.6 dependencies: '@clerk/nextjs': 4.21.12_5v7kwk6wz2hvmegftnptprfjvm - '@trigger.dev/nextjs': 1.0.0-next.5_yuyr3hquo7tgjdmhxnzkuafg3m - '@trigger.dev/sdk': 2.0.0-next.5 + '@trigger.dev/nextjs': link:../../packages/nextjs + '@trigger.dev/sdk': link:../../packages/trigger-sdk '@types/node': 20.3.2 '@types/react': 18.0.26 '@types/react-dom': 18.0.10 @@ -10694,46 +10694,6 @@ packages: react-dom: 18.2.0_react@18.2.0 dev: false - /@trigger.dev/nextjs/1.0.0-next.5_yuyr3hquo7tgjdmhxnzkuafg3m: - resolution: {integrity: sha512-41/RvyfuR3cPDCKAAhWOJMNw3KgA9uho7vcg8Q0ri8NCObsvxbStpnb5lpvNsUVOTyB7qy3tHULVJhKANczQqQ==} - engines: {node: '>=18'} - peerDependencies: - '@trigger.dev/sdk': ^2.0.0-next.2 - next: ^13.3.1 - dependencies: - '@trigger.dev/sdk': 2.0.0-next.5 - debug: 4.3.4 - next: 13.4.7_biqbaboplfbrettd7655fr4n2y - transitivePeerDependencies: - - supports-color - dev: false - - /@trigger.dev/sdk/2.0.0-next.5: - resolution: {integrity: sha512-ERnhWSiWSO1sOkKVZfFP+bmbDOtgyviahY7tVP/8LD0cJb/uz5zSufcqHRHhVCXU423u24tJcYYJcXKNyoSmHQ==} - engines: {node: '>=18'} - dependencies: - chalk: 5.2.0 - debug: 4.3.4 - evt: 2.4.13 - get-caller-file: 2.0.5 - git-remote-origin-url: 4.0.0 - git-repo-info: 2.1.1 - node-fetch: 2.6.11 - slug: 6.1.0 - terminal-link: 3.0.0 - ulid: 2.3.0 - uuid: 9.0.0 - ws: 8.12.0 - zod: 3.21.4 - zod-error: 1.1.0 - zod-to-json-schema: 3.20.2_zod@3.21.4 - transitivePeerDependencies: - - bufferutil - - encoding - - supports-color - - utf-8-validate - dev: false - /@tsconfig/node10/1.0.9: resolution: {integrity: sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==} @@ -25800,14 +25760,6 @@ packages: zod: 3.20.2 dev: false - /zod-to-json-schema/3.20.2_zod@3.21.4: - resolution: {integrity: sha512-qka3UAXmVXD8z5SHkRU89UyHp8JHJW7zc8RQCpt7QloJOn6uijwkjUm8o+M/cF1IysmKc5gxac/QeDikaQMdzQ==} - peerDependencies: - zod: ^3.20.0 - dependencies: - zod: 3.21.4 - dev: false - /zod/3.20.2: resolution: {integrity: sha512-1MzNQdAvO+54H+EaK5YpyEy0T+Ejo/7YLHS93G3RnYWh5gaotGHwGeN/ZO687qEDU2y4CdStQYXVHIgrUl5UVQ==} dev: false