@vercel/sandbox 1.9.3 → 1.10.1

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.
Files changed (45) hide show
  1. package/dist/api-client/base-client.cjs +2 -1
  2. package/dist/api-client/base-client.cjs.map +1 -1
  3. package/dist/api-client/base-client.js +2 -1
  4. package/dist/api-client/base-client.js.map +1 -1
  5. package/dist/auth/api.cjs +1 -1
  6. package/dist/auth/api.cjs.map +1 -1
  7. package/dist/auth/api.js +1 -1
  8. package/dist/auth/api.js.map +1 -1
  9. package/dist/auth/index.cjs +0 -1
  10. package/dist/auth/index.d.cts +2 -2
  11. package/dist/auth/index.d.ts +2 -2
  12. package/dist/auth/index.js +2 -2
  13. package/dist/auth/project.cjs +124 -26
  14. package/dist/auth/project.cjs.map +1 -1
  15. package/dist/auth/project.d.cts +9 -13
  16. package/dist/auth/project.d.ts +9 -13
  17. package/dist/auth/project.js +125 -26
  18. package/dist/auth/project.js.map +1 -1
  19. package/dist/filesystem.cjs +499 -0
  20. package/dist/filesystem.cjs.map +1 -0
  21. package/dist/filesystem.d.cts +258 -0
  22. package/dist/filesystem.d.ts +258 -0
  23. package/dist/filesystem.js +497 -0
  24. package/dist/filesystem.js.map +1 -0
  25. package/dist/index.cjs +2 -0
  26. package/dist/index.d.cts +3 -2
  27. package/dist/index.d.ts +3 -2
  28. package/dist/index.js +2 -1
  29. package/dist/sandbox.cjs +2 -0
  30. package/dist/sandbox.cjs.map +1 -1
  31. package/dist/sandbox.d.cts +11 -0
  32. package/dist/sandbox.d.ts +11 -0
  33. package/dist/sandbox.js +2 -0
  34. package/dist/sandbox.js.map +1 -1
  35. package/dist/snapshot.cjs +39 -5
  36. package/dist/snapshot.cjs.map +1 -1
  37. package/dist/snapshot.d.cts +28 -6
  38. package/dist/snapshot.d.ts +28 -6
  39. package/dist/snapshot.js +38 -5
  40. package/dist/snapshot.js.map +1 -1
  41. package/dist/version.cjs +1 -1
  42. package/dist/version.cjs.map +1 -1
  43. package/dist/version.js +1 -1
  44. package/dist/version.js.map +1 -1
  45. package/package.json +1 -1
@@ -5,6 +5,7 @@ const require_with_retry = require('./with-retry.cjs');
5
5
  let undici = require("undici");
6
6
 
7
7
  //#region src/api-client/base-client.ts
8
+ const DEFAULT_AGENT = new undici.Agent({ bodyTimeout: 0 });
8
9
  /**
9
10
  * A base API client that provides a convenience wrapper for fetching where
10
11
  * we can pass query parameters as an object, support retries, debugging
@@ -16,7 +17,7 @@ var BaseClient = class {
16
17
  this.baseUrl = params.baseUrl;
17
18
  this.debug = params.debug ?? process.env.DEBUG_FETCH === "true";
18
19
  this.token = params.token;
19
- this.agent = new undici.Agent({ bodyTimeout: 0 });
20
+ this.agent = DEFAULT_AGENT;
20
21
  }
21
22
  async request(path, opts) {
22
23
  const url = new URL(`${this.baseUrl}${path}`);
@@ -1 +1 @@
1
- {"version":3,"file":"base-client.cjs","names":["withRetry","Agent","value","APIError","json: Data | ErrorData"],"sources":["../../src/api-client/base-client.ts"],"sourcesContent":["import type { Options as RetryOptions } from \"async-retry\";\nimport { APIError } from \"./api-error.js\";\nimport { ZodType } from \"zod\";\nimport { array } from \"../utils/array.js\";\nimport { withRetry, type RequestOptions } from \"./with-retry.js\";\nimport { Agent } from \"undici\";\n\nexport interface RequestParams extends RequestInit {\n headers?: Record<string, string>;\n method?: string;\n onRetry?(error: any, options: RequestOptions): void;\n query?: Record<string, number | string | null | undefined | string[]>;\n retry?: Partial<RetryOptions>;\n}\n\n/**\n * A base API client that provides a convenience wrapper for fetching where\n * we can pass query parameters as an object, support retries, debugging\n * and automatic authorization.\n */\nexport class BaseClient {\n protected token?: string;\n private fetch: ReturnType<typeof withRetry<RequestInit>>;\n private debug: boolean;\n private baseUrl: string;\n private agent: Agent;\n\n constructor(params: {\n debug?: boolean;\n baseUrl: string;\n token?: string;\n fetch?: typeof globalThis.fetch;\n }) {\n this.fetch = withRetry(params.fetch ?? globalThis.fetch);\n this.baseUrl = params.baseUrl;\n this.debug = params.debug ?? process.env.DEBUG_FETCH === \"true\";\n this.token = params.token;\n this.agent = new Agent({\n bodyTimeout: 0, // disable body timeout to allow long logs streaming\n });\n }\n\n protected async request(path: string, opts?: RequestParams) {\n const url = new URL(`${this.baseUrl}${path}`);\n if (opts?.query) {\n for (const [key, value] of Object.entries(opts.query)) {\n array(value).forEach((value) => {\n url.searchParams.append(key, value.toString());\n });\n }\n }\n\n const start = Date.now();\n const response = await this.fetch(url.toString(), {\n ...opts,\n body: opts?.body,\n method: opts?.method || \"GET\",\n headers: this.token\n ? { Authorization: `Bearer ${this.token}`, ...opts?.headers }\n : opts?.headers,\n // @ts-expect-error Node.js' and undici's Agent have different types\n dispatcher: this.agent,\n signal: opts?.signal,\n });\n\n if (this.debug) {\n const duration = Date.now() - start;\n console.log(`[API] ${url} (${response.status}) ${duration}ms`);\n if (response.status === 429) {\n const retry = parseInt(response.headers.get(\"Retry-After\") ?? \"\", 10);\n const hours = Math.floor(retry / 60 / 60);\n const minutes = Math.floor(retry / 60) % 60;\n const seconds = retry % 60;\n console.warn(\n `[API] ${url} Rate Limited, Retry After ${hours}h ${minutes}m ${seconds}s`,\n );\n }\n }\n\n return response;\n }\n}\n\nexport interface Parsed<Data> {\n response: Response;\n text: string;\n json: Data;\n}\n\n/**\n * Extract sandboxId from a sandbox API URL.\n * URLs follow the pattern: /v1/sandboxes/{sandboxId}/...\n */\nfunction extractSandboxId(url: string): string | undefined {\n const match = url.match(/\\/v1\\/sandboxes\\/([^/?]+)/);\n return match?.[1];\n}\n\n/**\n * Allows to read the response text and parse it as JSON casting to the given\n * type. If the response is not ok or cannot be parsed it will return error.\n *\n * @param response Response to parse.\n * @returns Parsed response or error.\n */\nexport async function parse<Data, ErrorData>(\n validator: ZodType<Data>,\n response: Response,\n): Promise<Parsed<Data> | APIError<ErrorData>> {\n const sandboxId = extractSandboxId(response.url);\n\n const text = await response.text().catch((err) => {\n return new APIError<ErrorData>(response, {\n message: `Can't read response text: ${String(err)}`,\n sandboxId,\n });\n });\n\n if (typeof text !== \"string\") {\n return text;\n }\n\n let json: Data | ErrorData;\n\n try {\n json = JSON.parse(text || \"{}\");\n } catch (error) {\n return new APIError<ErrorData>(response, {\n message: `Can't parse JSON: ${String(error)}`,\n text,\n sandboxId,\n });\n }\n\n if (!response.ok) {\n return new APIError<ErrorData>(response, {\n message: `Status code ${response.status} is not ok`,\n json: json as ErrorData,\n text,\n sandboxId,\n });\n }\n\n const validated = validator.safeParse(json);\n if (!validated.success) {\n return new APIError<ErrorData>(response, {\n message: `Response JSON is not valid: ${validated.error}`,\n json: json as ErrorData,\n text,\n sandboxId,\n });\n }\n\n return {\n json: validated.data,\n response,\n text,\n };\n}\n\nexport async function parseOrThrow<Data, ErrorData>(\n validator: ZodType<Data>,\n response: Response,\n): Promise<Parsed<Data>> {\n const result = await parse<Data, ErrorData>(validator, response);\n if (result instanceof APIError) {\n throw result;\n }\n\n return result;\n}\n"],"mappings":";;;;;;;;;;;;AAoBA,IAAa,aAAb,MAAwB;CAOtB,YAAY,QAKT;AACD,OAAK,QAAQA,6BAAU,OAAO,SAAS,WAAW,MAAM;AACxD,OAAK,UAAU,OAAO;AACtB,OAAK,QAAQ,OAAO,SAAS,QAAQ,IAAI,gBAAgB;AACzD,OAAK,QAAQ,OAAO;AACpB,OAAK,QAAQ,IAAIC,aAAM,EACrB,aAAa,GACd,CAAC;;CAGJ,MAAgB,QAAQ,MAAc,MAAsB;EAC1D,MAAM,MAAM,IAAI,IAAI,GAAG,KAAK,UAAU,OAAO;AAC7C,MAAI,MAAM,MACR,MAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,KAAK,MAAM,CACnD,qBAAM,MAAM,CAAC,SAAS,YAAU;AAC9B,OAAI,aAAa,OAAO,KAAKC,QAAM,UAAU,CAAC;IAC9C;EAIN,MAAM,QAAQ,KAAK,KAAK;EACxB,MAAM,WAAW,MAAM,KAAK,MAAM,IAAI,UAAU,EAAE;GAChD,GAAG;GACH,MAAM,MAAM;GACZ,QAAQ,MAAM,UAAU;GACxB,SAAS,KAAK,QACV;IAAE,eAAe,UAAU,KAAK;IAAS,GAAG,MAAM;IAAS,GAC3D,MAAM;GAEV,YAAY,KAAK;GACjB,QAAQ,MAAM;GACf,CAAC;AAEF,MAAI,KAAK,OAAO;GACd,MAAM,WAAW,KAAK,KAAK,GAAG;AAC9B,WAAQ,IAAI,SAAS,IAAI,IAAI,SAAS,OAAO,IAAI,SAAS,IAAI;AAC9D,OAAI,SAAS,WAAW,KAAK;IAC3B,MAAM,QAAQ,SAAS,SAAS,QAAQ,IAAI,cAAc,IAAI,IAAI,GAAG;IACrE,MAAM,QAAQ,KAAK,MAAM,QAAQ,KAAK,GAAG;IACzC,MAAM,UAAU,KAAK,MAAM,QAAQ,GAAG,GAAG;IACzC,MAAM,UAAU,QAAQ;AACxB,YAAQ,KACN,SAAS,IAAI,6BAA6B,MAAM,IAAI,QAAQ,IAAI,QAAQ,GACzE;;;AAIL,SAAO;;;;;;;AAcX,SAAS,iBAAiB,KAAiC;AAEzD,QADc,IAAI,MAAM,4BAA4B,GACrC;;;;;;;;;AAUjB,eAAsB,MACpB,WACA,UAC6C;CAC7C,MAAM,YAAY,iBAAiB,SAAS,IAAI;CAEhD,MAAM,OAAO,MAAM,SAAS,MAAM,CAAC,OAAO,QAAQ;AAChD,SAAO,IAAIC,2BAAoB,UAAU;GACvC,SAAS,6BAA6B,OAAO,IAAI;GACjD;GACD,CAAC;GACF;AAEF,KAAI,OAAO,SAAS,SAClB,QAAO;CAGT,IAAIC;AAEJ,KAAI;AACF,SAAO,KAAK,MAAM,QAAQ,KAAK;UACxB,OAAO;AACd,SAAO,IAAID,2BAAoB,UAAU;GACvC,SAAS,qBAAqB,OAAO,MAAM;GAC3C;GACA;GACD,CAAC;;AAGJ,KAAI,CAAC,SAAS,GACZ,QAAO,IAAIA,2BAAoB,UAAU;EACvC,SAAS,eAAe,SAAS,OAAO;EAClC;EACN;EACA;EACD,CAAC;CAGJ,MAAM,YAAY,UAAU,UAAU,KAAK;AAC3C,KAAI,CAAC,UAAU,QACb,QAAO,IAAIA,2BAAoB,UAAU;EACvC,SAAS,+BAA+B,UAAU;EAC5C;EACN;EACA;EACD,CAAC;AAGJ,QAAO;EACL,MAAM,UAAU;EAChB;EACA;EACD;;AAGH,eAAsB,aACpB,WACA,UACuB;CACvB,MAAM,SAAS,MAAM,MAAuB,WAAW,SAAS;AAChE,KAAI,kBAAkBA,2BACpB,OAAM;AAGR,QAAO"}
1
+ {"version":3,"file":"base-client.cjs","names":["Agent","withRetry","value","APIError","json: Data | ErrorData"],"sources":["../../src/api-client/base-client.ts"],"sourcesContent":["import type { Options as RetryOptions } from \"async-retry\";\nimport { APIError } from \"./api-error.js\";\nimport { ZodType } from \"zod\";\nimport { array } from \"../utils/array.js\";\nimport { withRetry, type RequestOptions } from \"./with-retry.js\";\nimport { Agent } from \"undici\";\n\nexport interface RequestParams extends RequestInit {\n headers?: Record<string, string>;\n method?: string;\n onRetry?(error: any, options: RequestOptions): void;\n query?: Record<string, number | string | null | undefined | string[]>;\n retry?: Partial<RetryOptions>;\n}\n\nconst DEFAULT_AGENT = new Agent({\n bodyTimeout: 0, // disable body timeout to allow long logs streaming\n});\n\n/**\n * A base API client that provides a convenience wrapper for fetching where\n * we can pass query parameters as an object, support retries, debugging\n * and automatic authorization.\n */\nexport class BaseClient {\n protected token?: string;\n private fetch: ReturnType<typeof withRetry<RequestInit>>;\n private debug: boolean;\n private baseUrl: string;\n private agent: Agent;\n\n constructor(params: {\n debug?: boolean;\n baseUrl: string;\n token?: string;\n fetch?: typeof globalThis.fetch;\n }) {\n this.fetch = withRetry(params.fetch ?? globalThis.fetch);\n this.baseUrl = params.baseUrl;\n this.debug = params.debug ?? process.env.DEBUG_FETCH === \"true\";\n this.token = params.token;\n this.agent = DEFAULT_AGENT;\n }\n\n protected async request(path: string, opts?: RequestParams) {\n const url = new URL(`${this.baseUrl}${path}`);\n if (opts?.query) {\n for (const [key, value] of Object.entries(opts.query)) {\n array(value).forEach((value) => {\n url.searchParams.append(key, value.toString());\n });\n }\n }\n\n const start = Date.now();\n const response = await this.fetch(url.toString(), {\n ...opts,\n body: opts?.body,\n method: opts?.method || \"GET\",\n headers: this.token\n ? { Authorization: `Bearer ${this.token}`, ...opts?.headers }\n : opts?.headers,\n // @ts-expect-error Node.js' and undici's Agent have different types\n dispatcher: this.agent,\n signal: opts?.signal,\n });\n\n if (this.debug) {\n const duration = Date.now() - start;\n console.log(`[API] ${url} (${response.status}) ${duration}ms`);\n if (response.status === 429) {\n const retry = parseInt(response.headers.get(\"Retry-After\") ?? \"\", 10);\n const hours = Math.floor(retry / 60 / 60);\n const minutes = Math.floor(retry / 60) % 60;\n const seconds = retry % 60;\n console.warn(\n `[API] ${url} Rate Limited, Retry After ${hours}h ${minutes}m ${seconds}s`,\n );\n }\n }\n\n return response;\n }\n}\n\nexport interface Parsed<Data> {\n response: Response;\n text: string;\n json: Data;\n}\n\n/**\n * Extract sandboxId from a sandbox API URL.\n * URLs follow the pattern: /v1/sandboxes/{sandboxId}/...\n */\nfunction extractSandboxId(url: string): string | undefined {\n const match = url.match(/\\/v1\\/sandboxes\\/([^/?]+)/);\n return match?.[1];\n}\n\n/**\n * Allows to read the response text and parse it as JSON casting to the given\n * type. If the response is not ok or cannot be parsed it will return error.\n *\n * @param response Response to parse.\n * @returns Parsed response or error.\n */\nexport async function parse<Data, ErrorData>(\n validator: ZodType<Data>,\n response: Response,\n): Promise<Parsed<Data> | APIError<ErrorData>> {\n const sandboxId = extractSandboxId(response.url);\n\n const text = await response.text().catch((err) => {\n return new APIError<ErrorData>(response, {\n message: `Can't read response text: ${String(err)}`,\n sandboxId,\n });\n });\n\n if (typeof text !== \"string\") {\n return text;\n }\n\n let json: Data | ErrorData;\n\n try {\n json = JSON.parse(text || \"{}\");\n } catch (error) {\n return new APIError<ErrorData>(response, {\n message: `Can't parse JSON: ${String(error)}`,\n text,\n sandboxId,\n });\n }\n\n if (!response.ok) {\n return new APIError<ErrorData>(response, {\n message: `Status code ${response.status} is not ok`,\n json: json as ErrorData,\n text,\n sandboxId,\n });\n }\n\n const validated = validator.safeParse(json);\n if (!validated.success) {\n return new APIError<ErrorData>(response, {\n message: `Response JSON is not valid: ${validated.error}`,\n json: json as ErrorData,\n text,\n sandboxId,\n });\n }\n\n return {\n json: validated.data,\n response,\n text,\n };\n}\n\nexport async function parseOrThrow<Data, ErrorData>(\n validator: ZodType<Data>,\n response: Response,\n): Promise<Parsed<Data>> {\n const result = await parse<Data, ErrorData>(validator, response);\n if (result instanceof APIError) {\n throw result;\n }\n\n return result;\n}\n"],"mappings":";;;;;;;AAeA,MAAM,gBAAgB,IAAIA,aAAM,EAC9B,aAAa,GACd,CAAC;;;;;;AAOF,IAAa,aAAb,MAAwB;CAOtB,YAAY,QAKT;AACD,OAAK,QAAQC,6BAAU,OAAO,SAAS,WAAW,MAAM;AACxD,OAAK,UAAU,OAAO;AACtB,OAAK,QAAQ,OAAO,SAAS,QAAQ,IAAI,gBAAgB;AACzD,OAAK,QAAQ,OAAO;AACpB,OAAK,QAAQ;;CAGf,MAAgB,QAAQ,MAAc,MAAsB;EAC1D,MAAM,MAAM,IAAI,IAAI,GAAG,KAAK,UAAU,OAAO;AAC7C,MAAI,MAAM,MACR,MAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,KAAK,MAAM,CACnD,qBAAM,MAAM,CAAC,SAAS,YAAU;AAC9B,OAAI,aAAa,OAAO,KAAKC,QAAM,UAAU,CAAC;IAC9C;EAIN,MAAM,QAAQ,KAAK,KAAK;EACxB,MAAM,WAAW,MAAM,KAAK,MAAM,IAAI,UAAU,EAAE;GAChD,GAAG;GACH,MAAM,MAAM;GACZ,QAAQ,MAAM,UAAU;GACxB,SAAS,KAAK,QACV;IAAE,eAAe,UAAU,KAAK;IAAS,GAAG,MAAM;IAAS,GAC3D,MAAM;GAEV,YAAY,KAAK;GACjB,QAAQ,MAAM;GACf,CAAC;AAEF,MAAI,KAAK,OAAO;GACd,MAAM,WAAW,KAAK,KAAK,GAAG;AAC9B,WAAQ,IAAI,SAAS,IAAI,IAAI,SAAS,OAAO,IAAI,SAAS,IAAI;AAC9D,OAAI,SAAS,WAAW,KAAK;IAC3B,MAAM,QAAQ,SAAS,SAAS,QAAQ,IAAI,cAAc,IAAI,IAAI,GAAG;IACrE,MAAM,QAAQ,KAAK,MAAM,QAAQ,KAAK,GAAG;IACzC,MAAM,UAAU,KAAK,MAAM,QAAQ,GAAG,GAAG;IACzC,MAAM,UAAU,QAAQ;AACxB,YAAQ,KACN,SAAS,IAAI,6BAA6B,MAAM,IAAI,QAAQ,IAAI,QAAQ,GACzE;;;AAIL,SAAO;;;;;;;AAcX,SAAS,iBAAiB,KAAiC;AAEzD,QADc,IAAI,MAAM,4BAA4B,GACrC;;;;;;;;;AAUjB,eAAsB,MACpB,WACA,UAC6C;CAC7C,MAAM,YAAY,iBAAiB,SAAS,IAAI;CAEhD,MAAM,OAAO,MAAM,SAAS,MAAM,CAAC,OAAO,QAAQ;AAChD,SAAO,IAAIC,2BAAoB,UAAU;GACvC,SAAS,6BAA6B,OAAO,IAAI;GACjD;GACD,CAAC;GACF;AAEF,KAAI,OAAO,SAAS,SAClB,QAAO;CAGT,IAAIC;AAEJ,KAAI;AACF,SAAO,KAAK,MAAM,QAAQ,KAAK;UACxB,OAAO;AACd,SAAO,IAAID,2BAAoB,UAAU;GACvC,SAAS,qBAAqB,OAAO,MAAM;GAC3C;GACA;GACD,CAAC;;AAGJ,KAAI,CAAC,SAAS,GACZ,QAAO,IAAIA,2BAAoB,UAAU;EACvC,SAAS,eAAe,SAAS,OAAO;EAClC;EACN;EACA;EACD,CAAC;CAGJ,MAAM,YAAY,UAAU,UAAU,KAAK;AAC3C,KAAI,CAAC,UAAU,QACb,QAAO,IAAIA,2BAAoB,UAAU;EACvC,SAAS,+BAA+B,UAAU;EAC5C;EACN;EACA;EACD,CAAC;AAGJ,QAAO;EACL,MAAM,UAAU;EAChB;EACA;EACD;;AAGH,eAAsB,aACpB,WACA,UACuB;CACvB,MAAM,SAAS,MAAM,MAAuB,WAAW,SAAS;AAChE,KAAI,kBAAkBA,2BACpB,OAAM;AAGR,QAAO"}
@@ -4,6 +4,7 @@ import { withRetry } from "./with-retry.js";
4
4
  import { Agent } from "undici";
5
5
 
6
6
  //#region src/api-client/base-client.ts
7
+ const DEFAULT_AGENT = new Agent({ bodyTimeout: 0 });
7
8
  /**
8
9
  * A base API client that provides a convenience wrapper for fetching where
9
10
  * we can pass query parameters as an object, support retries, debugging
@@ -15,7 +16,7 @@ var BaseClient = class {
15
16
  this.baseUrl = params.baseUrl;
16
17
  this.debug = params.debug ?? process.env.DEBUG_FETCH === "true";
17
18
  this.token = params.token;
18
- this.agent = new Agent({ bodyTimeout: 0 });
19
+ this.agent = DEFAULT_AGENT;
19
20
  }
20
21
  async request(path, opts) {
21
22
  const url = new URL(`${this.baseUrl}${path}`);
@@ -1 +1 @@
1
- {"version":3,"file":"base-client.js","names":["value","json: Data | ErrorData"],"sources":["../../src/api-client/base-client.ts"],"sourcesContent":["import type { Options as RetryOptions } from \"async-retry\";\nimport { APIError } from \"./api-error.js\";\nimport { ZodType } from \"zod\";\nimport { array } from \"../utils/array.js\";\nimport { withRetry, type RequestOptions } from \"./with-retry.js\";\nimport { Agent } from \"undici\";\n\nexport interface RequestParams extends RequestInit {\n headers?: Record<string, string>;\n method?: string;\n onRetry?(error: any, options: RequestOptions): void;\n query?: Record<string, number | string | null | undefined | string[]>;\n retry?: Partial<RetryOptions>;\n}\n\n/**\n * A base API client that provides a convenience wrapper for fetching where\n * we can pass query parameters as an object, support retries, debugging\n * and automatic authorization.\n */\nexport class BaseClient {\n protected token?: string;\n private fetch: ReturnType<typeof withRetry<RequestInit>>;\n private debug: boolean;\n private baseUrl: string;\n private agent: Agent;\n\n constructor(params: {\n debug?: boolean;\n baseUrl: string;\n token?: string;\n fetch?: typeof globalThis.fetch;\n }) {\n this.fetch = withRetry(params.fetch ?? globalThis.fetch);\n this.baseUrl = params.baseUrl;\n this.debug = params.debug ?? process.env.DEBUG_FETCH === \"true\";\n this.token = params.token;\n this.agent = new Agent({\n bodyTimeout: 0, // disable body timeout to allow long logs streaming\n });\n }\n\n protected async request(path: string, opts?: RequestParams) {\n const url = new URL(`${this.baseUrl}${path}`);\n if (opts?.query) {\n for (const [key, value] of Object.entries(opts.query)) {\n array(value).forEach((value) => {\n url.searchParams.append(key, value.toString());\n });\n }\n }\n\n const start = Date.now();\n const response = await this.fetch(url.toString(), {\n ...opts,\n body: opts?.body,\n method: opts?.method || \"GET\",\n headers: this.token\n ? { Authorization: `Bearer ${this.token}`, ...opts?.headers }\n : opts?.headers,\n // @ts-expect-error Node.js' and undici's Agent have different types\n dispatcher: this.agent,\n signal: opts?.signal,\n });\n\n if (this.debug) {\n const duration = Date.now() - start;\n console.log(`[API] ${url} (${response.status}) ${duration}ms`);\n if (response.status === 429) {\n const retry = parseInt(response.headers.get(\"Retry-After\") ?? \"\", 10);\n const hours = Math.floor(retry / 60 / 60);\n const minutes = Math.floor(retry / 60) % 60;\n const seconds = retry % 60;\n console.warn(\n `[API] ${url} Rate Limited, Retry After ${hours}h ${minutes}m ${seconds}s`,\n );\n }\n }\n\n return response;\n }\n}\n\nexport interface Parsed<Data> {\n response: Response;\n text: string;\n json: Data;\n}\n\n/**\n * Extract sandboxId from a sandbox API URL.\n * URLs follow the pattern: /v1/sandboxes/{sandboxId}/...\n */\nfunction extractSandboxId(url: string): string | undefined {\n const match = url.match(/\\/v1\\/sandboxes\\/([^/?]+)/);\n return match?.[1];\n}\n\n/**\n * Allows to read the response text and parse it as JSON casting to the given\n * type. If the response is not ok or cannot be parsed it will return error.\n *\n * @param response Response to parse.\n * @returns Parsed response or error.\n */\nexport async function parse<Data, ErrorData>(\n validator: ZodType<Data>,\n response: Response,\n): Promise<Parsed<Data> | APIError<ErrorData>> {\n const sandboxId = extractSandboxId(response.url);\n\n const text = await response.text().catch((err) => {\n return new APIError<ErrorData>(response, {\n message: `Can't read response text: ${String(err)}`,\n sandboxId,\n });\n });\n\n if (typeof text !== \"string\") {\n return text;\n }\n\n let json: Data | ErrorData;\n\n try {\n json = JSON.parse(text || \"{}\");\n } catch (error) {\n return new APIError<ErrorData>(response, {\n message: `Can't parse JSON: ${String(error)}`,\n text,\n sandboxId,\n });\n }\n\n if (!response.ok) {\n return new APIError<ErrorData>(response, {\n message: `Status code ${response.status} is not ok`,\n json: json as ErrorData,\n text,\n sandboxId,\n });\n }\n\n const validated = validator.safeParse(json);\n if (!validated.success) {\n return new APIError<ErrorData>(response, {\n message: `Response JSON is not valid: ${validated.error}`,\n json: json as ErrorData,\n text,\n sandboxId,\n });\n }\n\n return {\n json: validated.data,\n response,\n text,\n };\n}\n\nexport async function parseOrThrow<Data, ErrorData>(\n validator: ZodType<Data>,\n response: Response,\n): Promise<Parsed<Data>> {\n const result = await parse<Data, ErrorData>(validator, response);\n if (result instanceof APIError) {\n throw result;\n }\n\n return result;\n}\n"],"mappings":";;;;;;;;;;;AAoBA,IAAa,aAAb,MAAwB;CAOtB,YAAY,QAKT;AACD,OAAK,QAAQ,UAAU,OAAO,SAAS,WAAW,MAAM;AACxD,OAAK,UAAU,OAAO;AACtB,OAAK,QAAQ,OAAO,SAAS,QAAQ,IAAI,gBAAgB;AACzD,OAAK,QAAQ,OAAO;AACpB,OAAK,QAAQ,IAAI,MAAM,EACrB,aAAa,GACd,CAAC;;CAGJ,MAAgB,QAAQ,MAAc,MAAsB;EAC1D,MAAM,MAAM,IAAI,IAAI,GAAG,KAAK,UAAU,OAAO;AAC7C,MAAI,MAAM,MACR,MAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,KAAK,MAAM,CACnD,OAAM,MAAM,CAAC,SAAS,YAAU;AAC9B,OAAI,aAAa,OAAO,KAAKA,QAAM,UAAU,CAAC;IAC9C;EAIN,MAAM,QAAQ,KAAK,KAAK;EACxB,MAAM,WAAW,MAAM,KAAK,MAAM,IAAI,UAAU,EAAE;GAChD,GAAG;GACH,MAAM,MAAM;GACZ,QAAQ,MAAM,UAAU;GACxB,SAAS,KAAK,QACV;IAAE,eAAe,UAAU,KAAK;IAAS,GAAG,MAAM;IAAS,GAC3D,MAAM;GAEV,YAAY,KAAK;GACjB,QAAQ,MAAM;GACf,CAAC;AAEF,MAAI,KAAK,OAAO;GACd,MAAM,WAAW,KAAK,KAAK,GAAG;AAC9B,WAAQ,IAAI,SAAS,IAAI,IAAI,SAAS,OAAO,IAAI,SAAS,IAAI;AAC9D,OAAI,SAAS,WAAW,KAAK;IAC3B,MAAM,QAAQ,SAAS,SAAS,QAAQ,IAAI,cAAc,IAAI,IAAI,GAAG;IACrE,MAAM,QAAQ,KAAK,MAAM,QAAQ,KAAK,GAAG;IACzC,MAAM,UAAU,KAAK,MAAM,QAAQ,GAAG,GAAG;IACzC,MAAM,UAAU,QAAQ;AACxB,YAAQ,KACN,SAAS,IAAI,6BAA6B,MAAM,IAAI,QAAQ,IAAI,QAAQ,GACzE;;;AAIL,SAAO;;;;;;;AAcX,SAAS,iBAAiB,KAAiC;AAEzD,QADc,IAAI,MAAM,4BAA4B,GACrC;;;;;;;;;AAUjB,eAAsB,MACpB,WACA,UAC6C;CAC7C,MAAM,YAAY,iBAAiB,SAAS,IAAI;CAEhD,MAAM,OAAO,MAAM,SAAS,MAAM,CAAC,OAAO,QAAQ;AAChD,SAAO,IAAI,SAAoB,UAAU;GACvC,SAAS,6BAA6B,OAAO,IAAI;GACjD;GACD,CAAC;GACF;AAEF,KAAI,OAAO,SAAS,SAClB,QAAO;CAGT,IAAIC;AAEJ,KAAI;AACF,SAAO,KAAK,MAAM,QAAQ,KAAK;UACxB,OAAO;AACd,SAAO,IAAI,SAAoB,UAAU;GACvC,SAAS,qBAAqB,OAAO,MAAM;GAC3C;GACA;GACD,CAAC;;AAGJ,KAAI,CAAC,SAAS,GACZ,QAAO,IAAI,SAAoB,UAAU;EACvC,SAAS,eAAe,SAAS,OAAO;EAClC;EACN;EACA;EACD,CAAC;CAGJ,MAAM,YAAY,UAAU,UAAU,KAAK;AAC3C,KAAI,CAAC,UAAU,QACb,QAAO,IAAI,SAAoB,UAAU;EACvC,SAAS,+BAA+B,UAAU;EAC5C;EACN;EACA;EACD,CAAC;AAGJ,QAAO;EACL,MAAM,UAAU;EAChB;EACA;EACD;;AAGH,eAAsB,aACpB,WACA,UACuB;CACvB,MAAM,SAAS,MAAM,MAAuB,WAAW,SAAS;AAChE,KAAI,kBAAkB,SACpB,OAAM;AAGR,QAAO"}
1
+ {"version":3,"file":"base-client.js","names":["value","json: Data | ErrorData"],"sources":["../../src/api-client/base-client.ts"],"sourcesContent":["import type { Options as RetryOptions } from \"async-retry\";\nimport { APIError } from \"./api-error.js\";\nimport { ZodType } from \"zod\";\nimport { array } from \"../utils/array.js\";\nimport { withRetry, type RequestOptions } from \"./with-retry.js\";\nimport { Agent } from \"undici\";\n\nexport interface RequestParams extends RequestInit {\n headers?: Record<string, string>;\n method?: string;\n onRetry?(error: any, options: RequestOptions): void;\n query?: Record<string, number | string | null | undefined | string[]>;\n retry?: Partial<RetryOptions>;\n}\n\nconst DEFAULT_AGENT = new Agent({\n bodyTimeout: 0, // disable body timeout to allow long logs streaming\n});\n\n/**\n * A base API client that provides a convenience wrapper for fetching where\n * we can pass query parameters as an object, support retries, debugging\n * and automatic authorization.\n */\nexport class BaseClient {\n protected token?: string;\n private fetch: ReturnType<typeof withRetry<RequestInit>>;\n private debug: boolean;\n private baseUrl: string;\n private agent: Agent;\n\n constructor(params: {\n debug?: boolean;\n baseUrl: string;\n token?: string;\n fetch?: typeof globalThis.fetch;\n }) {\n this.fetch = withRetry(params.fetch ?? globalThis.fetch);\n this.baseUrl = params.baseUrl;\n this.debug = params.debug ?? process.env.DEBUG_FETCH === \"true\";\n this.token = params.token;\n this.agent = DEFAULT_AGENT;\n }\n\n protected async request(path: string, opts?: RequestParams) {\n const url = new URL(`${this.baseUrl}${path}`);\n if (opts?.query) {\n for (const [key, value] of Object.entries(opts.query)) {\n array(value).forEach((value) => {\n url.searchParams.append(key, value.toString());\n });\n }\n }\n\n const start = Date.now();\n const response = await this.fetch(url.toString(), {\n ...opts,\n body: opts?.body,\n method: opts?.method || \"GET\",\n headers: this.token\n ? { Authorization: `Bearer ${this.token}`, ...opts?.headers }\n : opts?.headers,\n // @ts-expect-error Node.js' and undici's Agent have different types\n dispatcher: this.agent,\n signal: opts?.signal,\n });\n\n if (this.debug) {\n const duration = Date.now() - start;\n console.log(`[API] ${url} (${response.status}) ${duration}ms`);\n if (response.status === 429) {\n const retry = parseInt(response.headers.get(\"Retry-After\") ?? \"\", 10);\n const hours = Math.floor(retry / 60 / 60);\n const minutes = Math.floor(retry / 60) % 60;\n const seconds = retry % 60;\n console.warn(\n `[API] ${url} Rate Limited, Retry After ${hours}h ${minutes}m ${seconds}s`,\n );\n }\n }\n\n return response;\n }\n}\n\nexport interface Parsed<Data> {\n response: Response;\n text: string;\n json: Data;\n}\n\n/**\n * Extract sandboxId from a sandbox API URL.\n * URLs follow the pattern: /v1/sandboxes/{sandboxId}/...\n */\nfunction extractSandboxId(url: string): string | undefined {\n const match = url.match(/\\/v1\\/sandboxes\\/([^/?]+)/);\n return match?.[1];\n}\n\n/**\n * Allows to read the response text and parse it as JSON casting to the given\n * type. If the response is not ok or cannot be parsed it will return error.\n *\n * @param response Response to parse.\n * @returns Parsed response or error.\n */\nexport async function parse<Data, ErrorData>(\n validator: ZodType<Data>,\n response: Response,\n): Promise<Parsed<Data> | APIError<ErrorData>> {\n const sandboxId = extractSandboxId(response.url);\n\n const text = await response.text().catch((err) => {\n return new APIError<ErrorData>(response, {\n message: `Can't read response text: ${String(err)}`,\n sandboxId,\n });\n });\n\n if (typeof text !== \"string\") {\n return text;\n }\n\n let json: Data | ErrorData;\n\n try {\n json = JSON.parse(text || \"{}\");\n } catch (error) {\n return new APIError<ErrorData>(response, {\n message: `Can't parse JSON: ${String(error)}`,\n text,\n sandboxId,\n });\n }\n\n if (!response.ok) {\n return new APIError<ErrorData>(response, {\n message: `Status code ${response.status} is not ok`,\n json: json as ErrorData,\n text,\n sandboxId,\n });\n }\n\n const validated = validator.safeParse(json);\n if (!validated.success) {\n return new APIError<ErrorData>(response, {\n message: `Response JSON is not valid: ${validated.error}`,\n json: json as ErrorData,\n text,\n sandboxId,\n });\n }\n\n return {\n json: validated.data,\n response,\n text,\n };\n}\n\nexport async function parseOrThrow<Data, ErrorData>(\n validator: ZodType<Data>,\n response: Response,\n): Promise<Parsed<Data>> {\n const result = await parse<Data, ErrorData>(validator, response);\n if (result instanceof APIError) {\n throw result;\n }\n\n return result;\n}\n"],"mappings":";;;;;;AAeA,MAAM,gBAAgB,IAAI,MAAM,EAC9B,aAAa,GACd,CAAC;;;;;;AAOF,IAAa,aAAb,MAAwB;CAOtB,YAAY,QAKT;AACD,OAAK,QAAQ,UAAU,OAAO,SAAS,WAAW,MAAM;AACxD,OAAK,UAAU,OAAO;AACtB,OAAK,QAAQ,OAAO,SAAS,QAAQ,IAAI,gBAAgB;AACzD,OAAK,QAAQ,OAAO;AACpB,OAAK,QAAQ;;CAGf,MAAgB,QAAQ,MAAc,MAAsB;EAC1D,MAAM,MAAM,IAAI,IAAI,GAAG,KAAK,UAAU,OAAO;AAC7C,MAAI,MAAM,MACR,MAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,KAAK,MAAM,CACnD,OAAM,MAAM,CAAC,SAAS,YAAU;AAC9B,OAAI,aAAa,OAAO,KAAKA,QAAM,UAAU,CAAC;IAC9C;EAIN,MAAM,QAAQ,KAAK,KAAK;EACxB,MAAM,WAAW,MAAM,KAAK,MAAM,IAAI,UAAU,EAAE;GAChD,GAAG;GACH,MAAM,MAAM;GACZ,QAAQ,MAAM,UAAU;GACxB,SAAS,KAAK,QACV;IAAE,eAAe,UAAU,KAAK;IAAS,GAAG,MAAM;IAAS,GAC3D,MAAM;GAEV,YAAY,KAAK;GACjB,QAAQ,MAAM;GACf,CAAC;AAEF,MAAI,KAAK,OAAO;GACd,MAAM,WAAW,KAAK,KAAK,GAAG;AAC9B,WAAQ,IAAI,SAAS,IAAI,IAAI,SAAS,OAAO,IAAI,SAAS,IAAI;AAC9D,OAAI,SAAS,WAAW,KAAK;IAC3B,MAAM,QAAQ,SAAS,SAAS,QAAQ,IAAI,cAAc,IAAI,IAAI,GAAG;IACrE,MAAM,QAAQ,KAAK,MAAM,QAAQ,KAAK,GAAG;IACzC,MAAM,UAAU,KAAK,MAAM,QAAQ,GAAG,GAAG;IACzC,MAAM,UAAU,QAAQ;AACxB,YAAQ,KACN,SAAS,IAAI,6BAA6B,MAAM,IAAI,QAAQ,IAAI,QAAQ,GACzE;;;AAIL,SAAO;;;;;;;AAcX,SAAS,iBAAiB,KAAiC;AAEzD,QADc,IAAI,MAAM,4BAA4B,GACrC;;;;;;;;;AAUjB,eAAsB,MACpB,WACA,UAC6C;CAC7C,MAAM,YAAY,iBAAiB,SAAS,IAAI;CAEhD,MAAM,OAAO,MAAM,SAAS,MAAM,CAAC,OAAO,QAAQ;AAChD,SAAO,IAAI,SAAoB,UAAU;GACvC,SAAS,6BAA6B,OAAO,IAAI;GACjD;GACD,CAAC;GACF;AAEF,KAAI,OAAO,SAAS,SAClB,QAAO;CAGT,IAAIC;AAEJ,KAAI;AACF,SAAO,KAAK,MAAM,QAAQ,KAAK;UACxB,OAAO;AACd,SAAO,IAAI,SAAoB,UAAU;GACvC,SAAS,qBAAqB,OAAO,MAAM;GAC3C;GACA;GACD,CAAC;;AAGJ,KAAI,CAAC,SAAS,GACZ,QAAO,IAAI,SAAoB,UAAU;EACvC,SAAS,eAAe,SAAS,OAAO;EAClC;EACN;EACA;EACD,CAAC;CAGJ,MAAM,YAAY,UAAU,UAAU,KAAK;AAC3C,KAAI,CAAC,UAAU,QACb,QAAO,IAAI,SAAoB,UAAU;EACvC,SAAS,+BAA+B,UAAU;EAC5C;EACN;EACA;EACD,CAAC;AAGJ,QAAO;EACL,MAAM,UAAU;EAChB;EACA;EACD;;AAGH,eAAsB,aACpB,WACA,UACuB;CACvB,MAAM,SAAS,MAAM,MAAuB,WAAW,SAAS;AAChE,KAAI,kBAAkB,SACpB,OAAM;AAGR,QAAO"}
package/dist/auth/api.cjs CHANGED
@@ -2,7 +2,7 @@ const require_error = require('./error.cjs');
2
2
 
3
3
  //#region src/auth/api.ts
4
4
  async function fetchApi(opts) {
5
- const x = await fetch(`https://api.vercel.com${opts.endpoint}`, {
5
+ const x = await fetch(`https://vercel.com/api${opts.endpoint}`, {
6
6
  method: opts.method,
7
7
  body: opts.body,
8
8
  headers: {
@@ -1 +1 @@
1
- {"version":3,"file":"api.cjs","names":["NotOk"],"sources":["../../src/auth/api.ts"],"sourcesContent":["import { NotOk } from \"./error.js\";\n\nexport async function fetchApi(opts: {\n token: string;\n endpoint: string;\n method?: string;\n body?: string;\n}): Promise<unknown> {\n const x = await fetch(`https://api.vercel.com${opts.endpoint}`, {\n method: opts.method,\n body: opts.body,\n headers: {\n Authorization: `Bearer ${opts.token}`,\n \"Content-Type\": \"application/json\",\n },\n });\n if (!x.ok) {\n let message = await x.text();\n\n try {\n const { error } = JSON.parse(message);\n message = `${error.code.toUpperCase()}: ${error.message}`;\n } catch {}\n\n throw new NotOk({\n responseText: message,\n statusCode: x.status,\n });\n }\n return (await x.json()) as unknown;\n}\n"],"mappings":";;;AAEA,eAAsB,SAAS,MAKV;CACnB,MAAM,IAAI,MAAM,MAAM,yBAAyB,KAAK,YAAY;EAC9D,QAAQ,KAAK;EACb,MAAM,KAAK;EACX,SAAS;GACP,eAAe,UAAU,KAAK;GAC9B,gBAAgB;GACjB;EACF,CAAC;AACF,KAAI,CAAC,EAAE,IAAI;EACT,IAAI,UAAU,MAAM,EAAE,MAAM;AAE5B,MAAI;GACF,MAAM,EAAE,UAAU,KAAK,MAAM,QAAQ;AACrC,aAAU,GAAG,MAAM,KAAK,aAAa,CAAC,IAAI,MAAM;UAC1C;AAER,QAAM,IAAIA,oBAAM;GACd,cAAc;GACd,YAAY,EAAE;GACf,CAAC;;AAEJ,QAAQ,MAAM,EAAE,MAAM"}
1
+ {"version":3,"file":"api.cjs","names":["NotOk"],"sources":["../../src/auth/api.ts"],"sourcesContent":["import { NotOk } from \"./error.js\";\n\nexport async function fetchApi(opts: {\n token: string;\n endpoint: string;\n method?: string;\n body?: string;\n}): Promise<unknown> {\n const x = await fetch(`https://vercel.com/api${opts.endpoint}`, {\n method: opts.method,\n body: opts.body,\n headers: {\n Authorization: `Bearer ${opts.token}`,\n \"Content-Type\": \"application/json\",\n },\n });\n if (!x.ok) {\n let message = await x.text();\n\n try {\n const { error } = JSON.parse(message);\n message = `${error.code.toUpperCase()}: ${error.message}`;\n } catch {}\n\n throw new NotOk({\n responseText: message,\n statusCode: x.status,\n });\n }\n return (await x.json()) as unknown;\n}\n"],"mappings":";;;AAEA,eAAsB,SAAS,MAKV;CACnB,MAAM,IAAI,MAAM,MAAM,yBAAyB,KAAK,YAAY;EAC9D,QAAQ,KAAK;EACb,MAAM,KAAK;EACX,SAAS;GACP,eAAe,UAAU,KAAK;GAC9B,gBAAgB;GACjB;EACF,CAAC;AACF,KAAI,CAAC,EAAE,IAAI;EACT,IAAI,UAAU,MAAM,EAAE,MAAM;AAE5B,MAAI;GACF,MAAM,EAAE,UAAU,KAAK,MAAM,QAAQ;AACrC,aAAU,GAAG,MAAM,KAAK,aAAa,CAAC,IAAI,MAAM;UAC1C;AAER,QAAM,IAAIA,oBAAM;GACd,cAAc;GACd,YAAY,EAAE;GACf,CAAC;;AAEJ,QAAQ,MAAM,EAAE,MAAM"}
package/dist/auth/api.js CHANGED
@@ -2,7 +2,7 @@ import { NotOk } from "./error.js";
2
2
 
3
3
  //#region src/auth/api.ts
4
4
  async function fetchApi(opts) {
5
- const x = await fetch(`https://api.vercel.com${opts.endpoint}`, {
5
+ const x = await fetch(`https://vercel.com/api${opts.endpoint}`, {
6
6
  method: opts.method,
7
7
  body: opts.body,
8
8
  headers: {
@@ -1 +1 @@
1
- {"version":3,"file":"api.js","names":[],"sources":["../../src/auth/api.ts"],"sourcesContent":["import { NotOk } from \"./error.js\";\n\nexport async function fetchApi(opts: {\n token: string;\n endpoint: string;\n method?: string;\n body?: string;\n}): Promise<unknown> {\n const x = await fetch(`https://api.vercel.com${opts.endpoint}`, {\n method: opts.method,\n body: opts.body,\n headers: {\n Authorization: `Bearer ${opts.token}`,\n \"Content-Type\": \"application/json\",\n },\n });\n if (!x.ok) {\n let message = await x.text();\n\n try {\n const { error } = JSON.parse(message);\n message = `${error.code.toUpperCase()}: ${error.message}`;\n } catch {}\n\n throw new NotOk({\n responseText: message,\n statusCode: x.status,\n });\n }\n return (await x.json()) as unknown;\n}\n"],"mappings":";;;AAEA,eAAsB,SAAS,MAKV;CACnB,MAAM,IAAI,MAAM,MAAM,yBAAyB,KAAK,YAAY;EAC9D,QAAQ,KAAK;EACb,MAAM,KAAK;EACX,SAAS;GACP,eAAe,UAAU,KAAK;GAC9B,gBAAgB;GACjB;EACF,CAAC;AACF,KAAI,CAAC,EAAE,IAAI;EACT,IAAI,UAAU,MAAM,EAAE,MAAM;AAE5B,MAAI;GACF,MAAM,EAAE,UAAU,KAAK,MAAM,QAAQ;AACrC,aAAU,GAAG,MAAM,KAAK,aAAa,CAAC,IAAI,MAAM;UAC1C;AAER,QAAM,IAAI,MAAM;GACd,cAAc;GACd,YAAY,EAAE;GACf,CAAC;;AAEJ,QAAQ,MAAM,EAAE,MAAM"}
1
+ {"version":3,"file":"api.js","names":[],"sources":["../../src/auth/api.ts"],"sourcesContent":["import { NotOk } from \"./error.js\";\n\nexport async function fetchApi(opts: {\n token: string;\n endpoint: string;\n method?: string;\n body?: string;\n}): Promise<unknown> {\n const x = await fetch(`https://vercel.com/api${opts.endpoint}`, {\n method: opts.method,\n body: opts.body,\n headers: {\n Authorization: `Bearer ${opts.token}`,\n \"Content-Type\": \"application/json\",\n },\n });\n if (!x.ok) {\n let message = await x.text();\n\n try {\n const { error } = JSON.parse(message);\n message = `${error.code.toUpperCase()}: ${error.message}`;\n } catch {}\n\n throw new NotOk({\n responseText: message,\n statusCode: x.status,\n });\n }\n return (await x.json()) as unknown;\n}\n"],"mappings":";;;AAEA,eAAsB,SAAS,MAKV;CACnB,MAAM,IAAI,MAAM,MAAM,yBAAyB,KAAK,YAAY;EAC9D,QAAQ,KAAK;EACb,MAAM,KAAK;EACX,SAAS;GACP,eAAe,UAAU,KAAK;GAC9B,gBAAgB;GACjB;EACF,CAAC;AACF,KAAI,CAAC,EAAE,IAAI;EACT,IAAI,UAAU,MAAM,EAAE,MAAM;AAE5B,MAAI;GACF,MAAM,EAAE,UAAU,KAAK,MAAM,QAAQ;AACrC,aAAU,GAAG,MAAM,KAAK,aAAa,CAAC,IAAI,MAAM;UAC1C;AAER,QAAM,IAAI,MAAM;GACd,cAAc;GACd,YAAY,EAAE;GACf,CAAC;;AAEJ,QAAQ,MAAM,EAAE,MAAM"}
@@ -8,5 +8,4 @@ exports.getAuth = require_file.getAuth;
8
8
  exports.inferScope = require_project.inferScope;
9
9
  exports.isOAuthError = require_oauth.isOAuthError;
10
10
  exports.pollForToken = require_poll_for_token.pollForToken;
11
- exports.selectTeam = require_project.selectTeam;
12
11
  exports.updateAuthConfig = require_file.updateAuthConfig;
@@ -1,5 +1,5 @@
1
1
  import { getAuth, updateAuthConfig } from "./file.cjs";
2
2
  import { DeviceAuthorizationRequest, OAuth, isOAuthError } from "./oauth.cjs";
3
3
  import { pollForToken } from "./poll-for-token.cjs";
4
- import { inferScope, selectTeam } from "./project.cjs";
5
- export { DeviceAuthorizationRequest, OAuth, getAuth, inferScope, isOAuthError, pollForToken, selectTeam, updateAuthConfig };
4
+ import { inferScope } from "./project.cjs";
5
+ export { DeviceAuthorizationRequest, OAuth, getAuth, inferScope, isOAuthError, pollForToken, updateAuthConfig };
@@ -1,5 +1,5 @@
1
1
  import { getAuth, updateAuthConfig } from "./file.js";
2
2
  import { DeviceAuthorizationRequest, OAuth, isOAuthError } from "./oauth.js";
3
3
  import { pollForToken } from "./poll-for-token.js";
4
- import { inferScope, selectTeam } from "./project.js";
5
- export { DeviceAuthorizationRequest, OAuth, getAuth, inferScope, isOAuthError, pollForToken, selectTeam, updateAuthConfig };
4
+ import { inferScope } from "./project.js";
5
+ export { DeviceAuthorizationRequest, OAuth, getAuth, inferScope, isOAuthError, pollForToken, updateAuthConfig };
@@ -1,6 +1,6 @@
1
1
  import { getAuth, updateAuthConfig } from "./file.js";
2
2
  import { OAuth, isOAuthError } from "./oauth.js";
3
3
  import { pollForToken } from "./poll-for-token.js";
4
- import { inferScope, selectTeam } from "./project.js";
4
+ import { inferScope } from "./project.js";
5
5
 
6
- export { OAuth, getAuth, inferScope, isOAuthError, pollForToken, selectTeam, updateAuthConfig };
6
+ export { OAuth, getAuth, inferScope, isOAuthError, pollForToken, updateAuthConfig };
@@ -5,24 +5,47 @@ const require_linked_project = require('./linked-project.cjs');
5
5
  let zod = require("zod");
6
6
 
7
7
  //#region src/auth/project.ts
8
- const TeamsSchema = zod.z.object({ teams: zod.z.array(zod.z.object({ slug: zod.z.string() })).min(1, `No teams found. Please create a team first.`) });
8
+ const UserSchema = zod.z.object({ user: zod.z.object({
9
+ defaultTeamId: zod.z.string().nullable(),
10
+ username: zod.z.string()
11
+ }) });
12
+ const TeamSchema = zod.z.object({
13
+ id: zod.z.string(),
14
+ slug: zod.z.string(),
15
+ updatedAt: zod.z.number(),
16
+ membership: zod.z.object({ role: zod.z.string() }),
17
+ billing: zod.z.object({ plan: zod.z.string() })
18
+ });
19
+ const TeamsSchema = zod.z.object({
20
+ teams: zod.z.array(TeamSchema),
21
+ pagination: zod.z.object({
22
+ count: zod.z.number(),
23
+ next: zod.z.number().nullable()
24
+ })
25
+ });
9
26
  const DEFAULT_PROJECT_NAME = "vercel-sandbox-default-project";
27
+ /** Status codes that mean "this team can't be used, try the next one". */
28
+ function isSkippableTeamError(e) {
29
+ return e instanceof require_error.NotOk && (e.response.statusCode === 402 || e.response.statusCode === 403);
30
+ }
10
31
  /**
11
32
  * Resolves the team and project scope for sandbox operations.
12
33
  *
13
34
  * First checks for a locally linked project in `.vercel/project.json`.
14
35
  * If found, uses the `projectId` and `orgId` from there.
15
36
  *
16
- * Otherwise, if `teamId` is not provided, selects the first available team for the account.
17
- * Ensures a default project exists within the team, creating it if necessary.
37
+ * Otherwise, if `teamId` is not provided, builds an ordered list of candidate
38
+ * teams to try: the user's `defaultTeamId` first (if set), then hobby-plan
39
+ * teams where the user has an OWNER role (preferring the personal team matching
40
+ * the username, then the most recently updated). Tries each candidate until one
41
+ * succeeds.
18
42
  *
19
43
  * @param opts.token - Vercel API authentication token.
20
- * @param opts.teamId - Optional team slug. If omitted, the first team is selected.
44
+ * @param opts.teamId - Optional team slug. If omitted, candidate teams are resolved automatically.
21
45
  * @param opts.cwd - Optional directory to search for `.vercel/project.json`. Defaults to `process.cwd()`.
22
46
  * @returns The resolved scope with `projectId`, `teamId`, and whether the project was `created`.
23
47
  *
24
48
  * @throws {NotOk} If the API returns an error other than 404 when checking the project.
25
- * @throws {ZodError} If no teams exist for the account.
26
49
  *
27
50
  * @example
28
51
  * ```ts
@@ -32,22 +55,89 @@ const DEFAULT_PROJECT_NAME = "vercel-sandbox-default-project";
32
55
  */
33
56
  async function inferScope(opts) {
34
57
  const linkedProject = await require_linked_project.readLinkedProject(opts.cwd ?? process.cwd());
35
- if (linkedProject) return {
36
- ...linkedProject,
37
- created: false
38
- };
39
- const teamId = opts.teamId ?? await selectTeam(opts.token);
58
+ if (linkedProject) {
59
+ const slugs = await resolveLinkedProjectSlugs(opts.token, linkedProject.teamId, linkedProject.projectId);
60
+ return {
61
+ ...linkedProject,
62
+ created: false,
63
+ ...slugs
64
+ };
65
+ }
66
+ if (opts.teamId) return tryTeam(opts.token, opts.teamId);
67
+ const { defaultTeamId, username } = (await require_api.fetchApi({
68
+ token: opts.token,
69
+ endpoint: "/v2/user"
70
+ }).then(UserSchema.parse)).user;
71
+ if (defaultTeamId) try {
72
+ const result = await tryTeam(opts.token, defaultTeamId);
73
+ try {
74
+ const team = await require_api.fetchApi({
75
+ token: opts.token,
76
+ endpoint: `/v2/teams/${encodeURIComponent(defaultTeamId)}`
77
+ }).then(zod.z.object({ slug: zod.z.string() }).parse);
78
+ return {
79
+ ...result,
80
+ teamSlug: team.slug
81
+ };
82
+ } catch {
83
+ return result;
84
+ }
85
+ } catch (e) {
86
+ if (!isSkippableTeamError(e)) throw e;
87
+ }
88
+ let next = null;
89
+ do {
90
+ const endpoint = next === null ? "/v2/teams?limit=20" : `/v2/teams?limit=20&until=${next}`;
91
+ const page = await require_api.fetchApi({
92
+ token: opts.token,
93
+ endpoint
94
+ }).then(TeamsSchema.parse);
95
+ next = page.pagination.next;
96
+ const hobbyOwnerTeams = page.teams.filter((t) => t.membership.role === "OWNER" && t.billing.plan === "hobby");
97
+ if (hobbyOwnerTeams.length === 0) continue;
98
+ const bestHobbyTeam = hobbyOwnerTeams.find((t) => t.slug === username) ?? hobbyOwnerTeams.sort((a, b) => b.updatedAt - a.updatedAt)[0];
99
+ if (bestHobbyTeam && bestHobbyTeam.id !== defaultTeamId) try {
100
+ return {
101
+ ...await tryTeam(opts.token, bestHobbyTeam.id),
102
+ teamSlug: bestHobbyTeam.slug
103
+ };
104
+ } catch (e) {
105
+ if (!isSkippableTeamError(e)) throw e;
106
+ }
107
+ } while (next !== null);
108
+ try {
109
+ return {
110
+ ...await tryTeam(opts.token, username),
111
+ teamSlug: username
112
+ };
113
+ } catch (e) {
114
+ if (!isSkippableTeamError(e)) throw e;
115
+ }
116
+ throw new require_error.NotOk({
117
+ statusCode: 403,
118
+ responseText: `Authenticated as "${username}" but none of the available teams allow sandbox creation. Specify a team explicitly with --scope <team-id-or-slug>.`
119
+ });
120
+ }
121
+ /**
122
+ * Attempts to use a specific team for sandbox operations by checking for
123
+ * (or creating) the default project within that team.
124
+ *
125
+ * @returns The resolved scope if the team is usable.
126
+ * @throws {NotOk} On authorization or other API errors.
127
+ */
128
+ async function tryTeam(token, teamId) {
129
+ const teamParam = teamId.startsWith("team_") ? `teamId=${encodeURIComponent(teamId)}` : `slug=${encodeURIComponent(teamId)}`;
40
130
  let created = false;
41
131
  try {
42
132
  await require_api.fetchApi({
43
- token: opts.token,
44
- endpoint: `/v2/projects/${encodeURIComponent(DEFAULT_PROJECT_NAME)}?slug=${encodeURIComponent(teamId)}`
133
+ token,
134
+ endpoint: `/v2/projects/${encodeURIComponent(DEFAULT_PROJECT_NAME)}?${teamParam}`
45
135
  });
46
136
  } catch (e) {
47
137
  if (!(e instanceof require_error.NotOk) || e.response.statusCode !== 404) throw e;
48
138
  await require_api.fetchApi({
49
- token: opts.token,
50
- endpoint: `/v11/projects?slug=${encodeURIComponent(teamId)}`,
139
+ token,
140
+ endpoint: `/v11/projects?${teamParam}`,
51
141
  method: "POST",
52
142
  body: JSON.stringify({ name: DEFAULT_PROJECT_NAME })
53
143
  });
@@ -60,21 +150,29 @@ async function inferScope(opts) {
60
150
  };
61
151
  }
62
152
  /**
63
- * Selects a team for the current token by querying the Teams API and
64
- * returning the slug of the first team in the result set.
65
- *
66
- * @param token - Authentication token used to call the Vercel API.
67
- * @returns A promise that resolves to the first team's slug.
153
+ * Best-effort resolution of team slug and project name for a linked project.
154
+ * Both IDs may be opaque (e.g. `team_xxx`, `prj_xxx`), so we fetch the
155
+ * human-readable names from the API in parallel.
68
156
  */
69
- async function selectTeam(token) {
70
- const { teams: [team] } = await require_api.fetchApi({
71
- token,
72
- endpoint: "/v2/teams?limit=1"
73
- }).then(TeamsSchema.parse);
74
- return team.slug;
157
+ async function resolveLinkedProjectSlugs(token, teamId, projectId) {
158
+ try {
159
+ const teamParam = teamId.startsWith("team_") ? `teamId=${encodeURIComponent(teamId)}` : `slug=${encodeURIComponent(teamId)}`;
160
+ const [teamData, projectData] = await Promise.all([require_api.fetchApi({
161
+ token,
162
+ endpoint: `/v2/teams/${encodeURIComponent(teamId)}`
163
+ }).then(zod.z.object({ slug: zod.z.string() }).parse), require_api.fetchApi({
164
+ token,
165
+ endpoint: `/v2/projects/${encodeURIComponent(projectId)}?${teamParam}`
166
+ }).then(zod.z.object({ name: zod.z.string() }).parse)]);
167
+ return {
168
+ teamSlug: teamData.slug,
169
+ projectSlug: projectData.name
170
+ };
171
+ } catch {
172
+ return {};
173
+ }
75
174
  }
76
175
 
77
176
  //#endregion
78
177
  exports.inferScope = inferScope;
79
- exports.selectTeam = selectTeam;
80
178
  //# sourceMappingURL=project.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"project.cjs","names":["z","readLinkedProject","fetchApi","NotOk"],"sources":["../../src/auth/project.ts"],"sourcesContent":["import { z } from \"zod\";\nimport { fetchApi } from \"./api.js\";\nimport { NotOk } from \"./error.js\";\nimport { readLinkedProject } from \"./linked-project.js\";\n\nconst TeamsSchema = z.object({\n teams: z\n .array(\n z.object({\n slug: z.string(),\n }),\n )\n .min(1, `No teams found. Please create a team first.`),\n});\n\nconst DEFAULT_PROJECT_NAME = \"vercel-sandbox-default-project\";\n\n/**\n * Resolves the team and project scope for sandbox operations.\n *\n * First checks for a locally linked project in `.vercel/project.json`.\n * If found, uses the `projectId` and `orgId` from there.\n *\n * Otherwise, if `teamId` is not provided, selects the first available team for the account.\n * Ensures a default project exists within the team, creating it if necessary.\n *\n * @param opts.token - Vercel API authentication token.\n * @param opts.teamId - Optional team slug. If omitted, the first team is selected.\n * @param opts.cwd - Optional directory to search for `.vercel/project.json`. Defaults to `process.cwd()`.\n * @returns The resolved scope with `projectId`, `teamId`, and whether the project was `created`.\n *\n * @throws {NotOk} If the API returns an error other than 404 when checking the project.\n * @throws {ZodError} If no teams exist for the account.\n *\n * @example\n * ```ts\n * const scope = await inferScope({ token: \"vercel_...\" });\n * // => { projectId: \"vercel-sandbox-default-project\", teamId: \"my-team\", created: false }\n * ```\n */\nexport async function inferScope(opts: {\n token: string;\n teamId?: string;\n cwd?: string;\n}): Promise<{ projectId: string; teamId: string; created: boolean }> {\n const linkedProject = await readLinkedProject(opts.cwd ?? process.cwd());\n if (linkedProject) {\n return { ...linkedProject, created: false };\n }\n\n const teamId = opts.teamId ?? (await selectTeam(opts.token));\n\n let created = false;\n try {\n await fetchApi({\n token: opts.token,\n endpoint: `/v2/projects/${encodeURIComponent(DEFAULT_PROJECT_NAME)}?slug=${encodeURIComponent(teamId)}`,\n });\n } catch (e) {\n if (!(e instanceof NotOk) || e.response.statusCode !== 404) {\n throw e;\n }\n\n await fetchApi({\n token: opts.token,\n endpoint: `/v11/projects?slug=${encodeURIComponent(teamId)}`,\n method: \"POST\",\n body: JSON.stringify({\n name: DEFAULT_PROJECT_NAME,\n }),\n });\n created = true;\n }\n\n return { projectId: DEFAULT_PROJECT_NAME, teamId, created };\n}\n\n/**\n * Selects a team for the current token by querying the Teams API and\n * returning the slug of the first team in the result set.\n *\n * @param token - Authentication token used to call the Vercel API.\n * @returns A promise that resolves to the first team's slug.\n */\nexport async function selectTeam(token: string) {\n const {\n teams: [team],\n } = await fetchApi({ token, endpoint: \"/v2/teams?limit=1\" }).then(\n TeamsSchema.parse,\n );\n return team.slug;\n}\n"],"mappings":";;;;;;;AAKA,MAAM,cAAcA,MAAE,OAAO,EAC3B,OAAOA,MACJ,MACCA,MAAE,OAAO,EACP,MAAMA,MAAE,QAAQ,EACjB,CAAC,CACH,CACA,IAAI,GAAG,8CAA8C,EACzD,CAAC;AAEF,MAAM,uBAAuB;;;;;;;;;;;;;;;;;;;;;;;;AAyB7B,eAAsB,WAAW,MAIoC;CACnE,MAAM,gBAAgB,MAAMC,yCAAkB,KAAK,OAAO,QAAQ,KAAK,CAAC;AACxE,KAAI,cACF,QAAO;EAAE,GAAG;EAAe,SAAS;EAAO;CAG7C,MAAM,SAAS,KAAK,UAAW,MAAM,WAAW,KAAK,MAAM;CAE3D,IAAI,UAAU;AACd,KAAI;AACF,QAAMC,qBAAS;GACb,OAAO,KAAK;GACZ,UAAU,gBAAgB,mBAAmB,qBAAqB,CAAC,QAAQ,mBAAmB,OAAO;GACtG,CAAC;UACK,GAAG;AACV,MAAI,EAAE,aAAaC,wBAAU,EAAE,SAAS,eAAe,IACrD,OAAM;AAGR,QAAMD,qBAAS;GACb,OAAO,KAAK;GACZ,UAAU,sBAAsB,mBAAmB,OAAO;GAC1D,QAAQ;GACR,MAAM,KAAK,UAAU,EACnB,MAAM,sBACP,CAAC;GACH,CAAC;AACF,YAAU;;AAGZ,QAAO;EAAE,WAAW;EAAsB;EAAQ;EAAS;;;;;;;;;AAU7D,eAAsB,WAAW,OAAe;CAC9C,MAAM,EACJ,OAAO,CAAC,UACN,MAAMA,qBAAS;EAAE;EAAO,UAAU;EAAqB,CAAC,CAAC,KAC3D,YAAY,MACb;AACD,QAAO,KAAK"}
1
+ {"version":3,"file":"project.cjs","names":["z","NotOk","readLinkedProject","fetchApi","next: number | null","endpoint: string"],"sources":["../../src/auth/project.ts"],"sourcesContent":["import { z } from \"zod\";\nimport { fetchApi } from \"./api.js\";\nimport { NotOk } from \"./error.js\";\nimport { readLinkedProject } from \"./linked-project.js\";\n\nconst UserSchema = z.object({\n user: z.object({\n defaultTeamId: z.string().nullable(),\n username: z.string(),\n }),\n});\n\nconst TeamSchema = z.object({\n id: z.string(),\n slug: z.string(),\n updatedAt: z.number(),\n membership: z.object({\n role: z.string(),\n }),\n billing: z.object({\n plan: z.string(),\n }),\n});\n\nconst TeamsSchema = z.object({\n teams: z.array(TeamSchema),\n pagination: z.object({\n count: z.number(),\n next: z.number().nullable(),\n }),\n});\n\nconst DEFAULT_PROJECT_NAME = \"vercel-sandbox-default-project\";\n\n/** Status codes that mean \"this team can't be used, try the next one\". */\nfunction isSkippableTeamError(e: unknown): boolean {\n return e instanceof NotOk && (e.response.statusCode === 402 || e.response.statusCode === 403);\n}\n\n/**\n * Resolves the team and project scope for sandbox operations.\n *\n * First checks for a locally linked project in `.vercel/project.json`.\n * If found, uses the `projectId` and `orgId` from there.\n *\n * Otherwise, if `teamId` is not provided, builds an ordered list of candidate\n * teams to try: the user's `defaultTeamId` first (if set), then hobby-plan\n * teams where the user has an OWNER role (preferring the personal team matching\n * the username, then the most recently updated). Tries each candidate until one\n * succeeds.\n *\n * @param opts.token - Vercel API authentication token.\n * @param opts.teamId - Optional team slug. If omitted, candidate teams are resolved automatically.\n * @param opts.cwd - Optional directory to search for `.vercel/project.json`. Defaults to `process.cwd()`.\n * @returns The resolved scope with `projectId`, `teamId`, and whether the project was `created`.\n *\n * @throws {NotOk} If the API returns an error other than 404 when checking the project.\n *\n * @example\n * ```ts\n * const scope = await inferScope({ token: \"vercel_...\" });\n * // => { projectId: \"vercel-sandbox-default-project\", teamId: \"my-team\", created: false }\n * ```\n */\nexport async function inferScope(opts: {\n token: string;\n teamId?: string;\n cwd?: string;\n}): Promise<{\n projectId: string;\n teamId: string;\n created: boolean;\n teamSlug?: string;\n projectSlug?: string;\n}> {\n const linkedProject = await readLinkedProject(opts.cwd ?? process.cwd());\n if (linkedProject) {\n const slugs = await resolveLinkedProjectSlugs(\n opts.token,\n linkedProject.teamId,\n linkedProject.projectId,\n );\n return { ...linkedProject, created: false, ...slugs };\n }\n\n if (opts.teamId) {\n return tryTeam(opts.token, opts.teamId);\n }\n\n const userData = await fetchApi({\n token: opts.token,\n endpoint: \"/v2/user\",\n }).then(UserSchema.parse);\n const { defaultTeamId, username } = userData.user;\n\n // 1. Try defaultTeamId first\n if (defaultTeamId) {\n try {\n const result = await tryTeam(opts.token, defaultTeamId);\n // Resolve team slug (best-effort)\n try {\n const team = await fetchApi({\n token: opts.token,\n endpoint: `/v2/teams/${encodeURIComponent(defaultTeamId)}`,\n }).then(z.object({ slug: z.string() }).parse);\n return { ...result, teamSlug: team.slug };\n } catch {\n return result;\n }\n } catch (e) {\n if (!isSkippableTeamError(e)) throw e;\n }\n }\n\n // 2. Paginate teams in pages of 20, try best hobby team per page\n let next: number | null = null;\n do {\n const endpoint: string =\n next === null\n ? \"/v2/teams?limit=20\"\n : `/v2/teams?limit=20&until=${next}`;\n const page = await fetchApi({ token: opts.token, endpoint }).then(\n TeamsSchema.parse,\n );\n\n next = page.pagination.next;\n\n const hobbyOwnerTeams = page.teams.filter(\n (t) => t.membership.role === \"OWNER\" && t.billing.plan === \"hobby\",\n );\n if (hobbyOwnerTeams.length === 0) {\n continue;\n }\n\n const bestHobbyTeam =\n hobbyOwnerTeams.find((t) => t.slug === username) ??\n hobbyOwnerTeams.sort((a, b) => b.updatedAt - a.updatedAt)[0];\n\n if (bestHobbyTeam && bestHobbyTeam.id !== defaultTeamId) {\n try {\n const result = await tryTeam(opts.token, bestHobbyTeam.id);\n return { ...result, teamSlug: bestHobbyTeam.slug };\n } catch (e) {\n if (!isSkippableTeamError(e)) throw e;\n }\n }\n } while (next !== null);\n\n // 3. Fall back to username as personal team\n try {\n const result = await tryTeam(opts.token, username);\n return { ...result, teamSlug: username };\n } catch (e) {\n if (!isSkippableTeamError(e)) throw e;\n }\n\n throw new NotOk({\n statusCode: 403,\n responseText: `Authenticated as \"${username}\" but none of the available teams allow sandbox creation. Specify a team explicitly with --scope <team-id-or-slug>.`,\n });\n}\n\n/**\n * Attempts to use a specific team for sandbox operations by checking for\n * (or creating) the default project within that team.\n *\n * @returns The resolved scope if the team is usable.\n * @throws {NotOk} On authorization or other API errors.\n */\nasync function tryTeam(\n token: string,\n teamId: string,\n): Promise<{ projectId: string; teamId: string; created: boolean }> {\n const teamParam = teamId.startsWith(\"team_\")\n ? `teamId=${encodeURIComponent(teamId)}`\n : `slug=${encodeURIComponent(teamId)}`;\n\n let created = false;\n try {\n await fetchApi({\n token,\n endpoint: `/v2/projects/${encodeURIComponent(DEFAULT_PROJECT_NAME)}?${teamParam}`,\n });\n } catch (e) {\n if (!(e instanceof NotOk) || e.response.statusCode !== 404) {\n throw e;\n }\n\n await fetchApi({\n token,\n endpoint: `/v11/projects?${teamParam}`,\n method: \"POST\",\n body: JSON.stringify({\n name: DEFAULT_PROJECT_NAME,\n }),\n });\n created = true;\n }\n\n return { projectId: DEFAULT_PROJECT_NAME, teamId, created };\n}\n\n/**\n * Best-effort resolution of team slug and project name for a linked project.\n * Both IDs may be opaque (e.g. `team_xxx`, `prj_xxx`), so we fetch the\n * human-readable names from the API in parallel.\n */\nasync function resolveLinkedProjectSlugs(\n token: string,\n teamId: string,\n projectId: string,\n): Promise<{ teamSlug?: string; projectSlug?: string }> {\n try {\n const teamParam = teamId.startsWith(\"team_\")\n ? `teamId=${encodeURIComponent(teamId)}`\n : `slug=${encodeURIComponent(teamId)}`;\n const [teamData, projectData] = await Promise.all([\n fetchApi({\n token,\n endpoint: `/v2/teams/${encodeURIComponent(teamId)}`,\n }).then(z.object({ slug: z.string() }).parse),\n fetchApi({\n token,\n endpoint: `/v2/projects/${encodeURIComponent(projectId)}?${teamParam}`,\n }).then(z.object({ name: z.string() }).parse),\n ]);\n return { teamSlug: teamData.slug, projectSlug: projectData.name };\n } catch {\n return {};\n }\n}\n"],"mappings":"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"}
@@ -5,16 +5,18 @@
5
5
  * First checks for a locally linked project in `.vercel/project.json`.
6
6
  * If found, uses the `projectId` and `orgId` from there.
7
7
  *
8
- * Otherwise, if `teamId` is not provided, selects the first available team for the account.
9
- * Ensures a default project exists within the team, creating it if necessary.
8
+ * Otherwise, if `teamId` is not provided, builds an ordered list of candidate
9
+ * teams to try: the user's `defaultTeamId` first (if set), then hobby-plan
10
+ * teams where the user has an OWNER role (preferring the personal team matching
11
+ * the username, then the most recently updated). Tries each candidate until one
12
+ * succeeds.
10
13
  *
11
14
  * @param opts.token - Vercel API authentication token.
12
- * @param opts.teamId - Optional team slug. If omitted, the first team is selected.
15
+ * @param opts.teamId - Optional team slug. If omitted, candidate teams are resolved automatically.
13
16
  * @param opts.cwd - Optional directory to search for `.vercel/project.json`. Defaults to `process.cwd()`.
14
17
  * @returns The resolved scope with `projectId`, `teamId`, and whether the project was `created`.
15
18
  *
16
19
  * @throws {NotOk} If the API returns an error other than 404 when checking the project.
17
- * @throws {ZodError} If no teams exist for the account.
18
20
  *
19
21
  * @example
20
22
  * ```ts
@@ -30,15 +32,9 @@ declare function inferScope(opts: {
30
32
  projectId: string;
31
33
  teamId: string;
32
34
  created: boolean;
35
+ teamSlug?: string;
36
+ projectSlug?: string;
33
37
  }>;
34
- /**
35
- * Selects a team for the current token by querying the Teams API and
36
- * returning the slug of the first team in the result set.
37
- *
38
- * @param token - Authentication token used to call the Vercel API.
39
- * @returns A promise that resolves to the first team's slug.
40
- */
41
- declare function selectTeam(token: string): Promise<string>;
42
38
  //#endregion
43
- export { inferScope, selectTeam };
39
+ export { inferScope };
44
40
  //# sourceMappingURL=project.d.cts.map
@@ -5,16 +5,18 @@
5
5
  * First checks for a locally linked project in `.vercel/project.json`.
6
6
  * If found, uses the `projectId` and `orgId` from there.
7
7
  *
8
- * Otherwise, if `teamId` is not provided, selects the first available team for the account.
9
- * Ensures a default project exists within the team, creating it if necessary.
8
+ * Otherwise, if `teamId` is not provided, builds an ordered list of candidate
9
+ * teams to try: the user's `defaultTeamId` first (if set), then hobby-plan
10
+ * teams where the user has an OWNER role (preferring the personal team matching
11
+ * the username, then the most recently updated). Tries each candidate until one
12
+ * succeeds.
10
13
  *
11
14
  * @param opts.token - Vercel API authentication token.
12
- * @param opts.teamId - Optional team slug. If omitted, the first team is selected.
15
+ * @param opts.teamId - Optional team slug. If omitted, candidate teams are resolved automatically.
13
16
  * @param opts.cwd - Optional directory to search for `.vercel/project.json`. Defaults to `process.cwd()`.
14
17
  * @returns The resolved scope with `projectId`, `teamId`, and whether the project was `created`.
15
18
  *
16
19
  * @throws {NotOk} If the API returns an error other than 404 when checking the project.
17
- * @throws {ZodError} If no teams exist for the account.
18
20
  *
19
21
  * @example
20
22
  * ```ts
@@ -30,15 +32,9 @@ declare function inferScope(opts: {
30
32
  projectId: string;
31
33
  teamId: string;
32
34
  created: boolean;
35
+ teamSlug?: string;
36
+ projectSlug?: string;
33
37
  }>;
34
- /**
35
- * Selects a team for the current token by querying the Teams API and
36
- * returning the slug of the first team in the result set.
37
- *
38
- * @param token - Authentication token used to call the Vercel API.
39
- * @returns A promise that resolves to the first team's slug.
40
- */
41
- declare function selectTeam(token: string): Promise<string>;
42
38
  //#endregion
43
- export { inferScope, selectTeam };
39
+ export { inferScope };
44
40
  //# sourceMappingURL=project.d.ts.map