From 85a28b1c53a933cfa35490de34012587e8dc50fa Mon Sep 17 00:00:00 2001 From: sufiyan733 Date: Tue, 22 Sep 2026 01:45:43 +0530 Subject: [PATCH] fix(sdk): resolve ReferenceError and projectRef in envvars.update (#4264) --- .changeset/fix-envvars-update-name.md | 5 + packages/trigger-sdk/src/v3/envvars.test.ts | 215 ++++++++++++++++++++ packages/trigger-sdk/src/v3/envvars.ts | 20 +- 3 files changed, 233 insertions(+), 7 deletions(-) create mode 100644 .changeset/fix-envvars-update-name.md create mode 100644 packages/trigger-sdk/src/v3/envvars.test.ts diff --git a/.changeset/fix-envvars-update-name.md b/.changeset/fix-envvars-update-name.md new file mode 100644 index 00000000000..00c6e1a223f --- /dev/null +++ b/.changeset/fix-envvars-update-name.md @@ -0,0 +1,5 @@ +--- +"@trigger.dev/sdk": patch +--- + +Fix `ReferenceError: name is not defined` when calling `envvars.update()` outside of a task context, and fix incorrect project reference resolution when specifying an explicit project and slug. diff --git a/packages/trigger-sdk/src/v3/envvars.test.ts b/packages/trigger-sdk/src/v3/envvars.test.ts new file mode 100644 index 00000000000..3c4298d880b --- /dev/null +++ b/packages/trigger-sdk/src/v3/envvars.test.ts @@ -0,0 +1,215 @@ +import { createServer, type IncomingMessage, type Server, type ServerResponse } from "node:http"; +import type { AddressInfo } from "node:net"; +import { apiClientManager, taskContext } from "@trigger.dev/core/v3"; +import { afterEach, beforeEach, describe, expect, it } from "vitest"; +import * as envvars from "./envvars.js"; + +type ReceivedRequest = { + method: string; + url: string; + authorization?: string; + body: unknown; +}; + +describe("envvars.update", () => { + let server: Server; + let baseUrl: string; + let requests: ReceivedRequest[]; + + beforeEach(async () => { + requests = []; + server = createServer((request, response) => { + void handleRequest(request, response, requests); + }); + await new Promise((resolve) => { + server.listen(0, "127.0.0.1", () => { + const address = server.address() as AddressInfo; + baseUrl = `http://127.0.0.1:${address.port}`; + resolve(); + }); + }); + taskContext.disable(); + }); + + afterEach(async () => { + taskContext.disable(); + await new Promise((resolve) => server.close(() => resolve())); + }); + + describe("outside task context", () => { + it("updates an environment variable successfully with 4 arguments", async () => { + const key = "tr_prod_sk_0123456789abcdefghijklmn"; + const result = await apiClientManager.runWithConfig( + { baseURL: baseUrl, accessToken: key }, + () => envvars.update("proj_123", "prod", "MY_VAR", { value: "new_value" }) + ); + + expect(result).toEqual({ success: true }); + expect(requests).toEqual([ + { + method: "PUT", + url: "/api/v1/projects/proj_123/envvars/prod/MY_VAR", + authorization: `Bearer ${key}`, + body: { value: "new_value" }, + }, + ]); + }); + + it("throws when slug is missing", () => { + const key = "tr_prod_sk_0123456789abcdefghijklmn"; + expect(() => + apiClientManager.runWithConfig({ baseURL: baseUrl, accessToken: key }, () => + // @ts-expect-error test runtime validation + envvars.update("MY_VAR", { value: "new_value" }) + ) + ).toThrow("slug is required"); + }); + + it("throws when name is missing", () => { + const key = "tr_prod_sk_0123456789abcdefghijklmn"; + expect(() => + apiClientManager.runWithConfig({ baseURL: baseUrl, accessToken: key }, () => + // @ts-expect-error test runtime validation + envvars.update("proj_123", "prod", undefined, { value: "new_value" }) + ) + ).toThrow("name is required"); + }); + + it("throws when params is missing", () => { + const key = "tr_prod_sk_0123456789abcdefghijklmn"; + expect(() => + apiClientManager.runWithConfig({ baseURL: baseUrl, accessToken: key }, () => + // @ts-expect-error test runtime validation + envvars.update("proj_123", "prod", "MY_VAR", undefined) + ) + ).toThrow("params is required"); + }); + }); + + describe("inside task context", () => { + it("updates an environment variable with 2 arguments using context", async () => { + const key = "tr_prod_sk_0123456789abcdefghijklmn"; + taskContext.setGlobalTaskContext({ + ctx: { + project: { id: "proj_ctx_id", ref: "proj_ctx_ref", name: "ctx_proj" }, + environment: { id: "env_ctx_id", slug: "dev", name: "Development", type: "development" }, + organization: { id: "org_ctx_id", slug: "ctx_org", name: "Ctx Org" }, + run: { id: "run_ctx_id", isTest: false }, + task: { id: "task_ctx_id", filePath: "task.ts", exportName: "myTask" }, + }, + } as any); + + const result = await apiClientManager.runWithConfig( + { baseURL: baseUrl, accessToken: key }, + () => envvars.update("CTX_VAR", { value: "ctx_val" }) + ); + + expect(result).toEqual({ success: true }); + expect(requests).toEqual([ + { + method: "PUT", + url: "/api/v1/projects/proj_ctx_ref/envvars/dev/CTX_VAR", + authorization: `Bearer ${key}`, + body: { value: "ctx_val" }, + }, + ]); + }); + + it("updates an environment variable with explicit projectRef, slug, and name", async () => { + const key = "tr_prod_sk_0123456789abcdefghijklmn"; + taskContext.setGlobalTaskContext({ + ctx: { + project: { id: "proj_ctx_id", ref: "proj_ctx_ref", name: "ctx_proj" }, + environment: { id: "env_ctx_id", slug: "dev", name: "Development", type: "development" }, + organization: { id: "org_ctx_id", slug: "ctx_org", name: "Ctx Org" }, + run: { id: "run_ctx_id", isTest: false }, + task: { id: "task_ctx_id", filePath: "task.ts", exportName: "myTask" }, + }, + } as any); + + const result = await apiClientManager.runWithConfig( + { baseURL: baseUrl, accessToken: key }, + () => envvars.update("other_proj", "staging", "OTHER_VAR", { value: "other_val" }) + ); + + expect(result).toEqual({ success: true }); + expect(requests).toEqual([ + { + method: "PUT", + url: "/api/v1/projects/other_proj/envvars/staging/OTHER_VAR", + authorization: `Bearer ${key}`, + body: { value: "other_val" }, + }, + ]); + }); + + it("throws inside task context when 4-arg form is missing name", () => { + const key = "tr_prod_sk_0123456789abcdefghijklmn"; + taskContext.setGlobalTaskContext({ + ctx: { + project: { id: "proj_ctx_id", ref: "proj_ctx_ref", name: "ctx_proj" }, + environment: { id: "env_ctx_id", slug: "dev", name: "Development", type: "development" }, + organization: { id: "org_ctx_id", slug: "ctx_org", name: "Ctx Org" }, + run: { id: "run_ctx_id", isTest: false }, + task: { id: "task_ctx_id", filePath: "task.ts", exportName: "myTask" }, + }, + } as any); + + expect(() => + apiClientManager.runWithConfig({ baseURL: baseUrl, accessToken: key }, () => + // @ts-expect-error test runtime validation + envvars.update("other_proj", "staging", undefined, { value: "other_val" }) + ) + ).toThrow("name is required"); + }); + + it("throws inside task context when 4-arg form is missing params", () => { + const key = "tr_prod_sk_0123456789abcdefghijklmn"; + taskContext.setGlobalTaskContext({ + ctx: { + project: { id: "proj_ctx_id", ref: "proj_ctx_ref", name: "ctx_proj" }, + environment: { id: "env_ctx_id", slug: "dev", name: "Development", type: "development" }, + organization: { id: "org_ctx_id", slug: "ctx_org", name: "Ctx Org" }, + run: { id: "run_ctx_id", isTest: false }, + task: { id: "task_ctx_id", filePath: "task.ts", exportName: "myTask" }, + }, + } as any); + + expect(() => + apiClientManager.runWithConfig({ baseURL: baseUrl, accessToken: key }, () => + // @ts-expect-error test runtime validation + envvars.update("other_proj", "staging", "OTHER_VAR", undefined) + ) + ).toThrow("params is required"); + }); + }); +}); + +async function handleRequest( + request: IncomingMessage, + response: ServerResponse, + requests: ReceivedRequest[] +) { + const chunks: Buffer[] = []; + for await (const chunk of request) { + chunks.push(Buffer.from(chunk)); + } + const rawBody = Buffer.concat(chunks).toString(); + requests.push({ + method: request.method ?? "", + url: request.url ?? "", + authorization: request.headers.authorization, + body: rawBody ? JSON.parse(rawBody) : undefined, + }); + + if (request.method === "PUT" && request.url?.startsWith("/api/v1/projects/")) { + return json(response, { success: true }); + } + + return json(response, { error: "Not found" }, 404); +} + +function json(response: ServerResponse, body: unknown, status = 200) { + response.writeHead(status, { "content-type": "application/json" }); + response.end(JSON.stringify(body)); +} diff --git a/packages/trigger-sdk/src/v3/envvars.ts b/packages/trigger-sdk/src/v3/envvars.ts index 8ff68ab8907..995edf93423 100644 --- a/packages/trigger-sdk/src/v3/envvars.ts +++ b/packages/trigger-sdk/src/v3/envvars.ts @@ -305,12 +305,14 @@ export function update( if (taskContext.ctx) { if (typeof slugOrParams === "string") { - $projectRef = slugOrParams; - $slug = slugOrParams ?? taskContext.ctx.environment.slug; - $name = - typeof nameOrRequestOptions === "string" - ? nameOrRequestOptions - : taskContext.ctx.environment.slug; + $projectRef = projectRefOrName; + $slug = slugOrParams; + + if (typeof nameOrRequestOptions !== "string") { + throw new Error("name is required"); + } + + $name = nameOrRequestOptions; if (!params) { throw new Error("params is required"); @@ -332,13 +334,17 @@ export function update( throw new Error("projectRef is required"); } + if (typeof nameOrRequestOptions !== "string") { + throw new Error("name is required"); + } + if (!params) { throw new Error("params is required"); } $projectRef = projectRefOrName; $slug = slugOrParams; - $name = name!; + $name = nameOrRequestOptions; $params = params; }