@vercel/sandbox
Advanced tools
@@ -38,3 +38,3 @@ import { BaseClient, Parsed, RequestParams } from "./base-client.cjs"; | ||
| timeout: number; | ||
| status: "pending" | "running" | "stopping" | "stopped" | "failed" | "aborted" | "snapshotting"; | ||
| status: "failed" | "aborted" | "pending" | "running" | "stopping" | "stopped" | "snapshotting"; | ||
| requestedAt: number; | ||
@@ -191,3 +191,3 @@ createdAt: number; | ||
| currentSessionId: string; | ||
| status: "pending" | "running" | "stopping" | "stopped" | "failed" | "aborted" | "snapshotting"; | ||
| status: "failed" | "aborted" | "pending" | "running" | "stopping" | "stopped" | "snapshotting"; | ||
| region?: string | undefined; | ||
@@ -307,3 +307,3 @@ vcpus?: number | undefined; | ||
| timeout: number; | ||
| status: "pending" | "running" | "stopping" | "stopped" | "failed" | "aborted" | "snapshotting"; | ||
| status: "failed" | "aborted" | "pending" | "running" | "stopping" | "stopped" | "snapshotting"; | ||
| requestedAt: number; | ||
@@ -508,3 +508,3 @@ createdAt: number; | ||
| timeout: number; | ||
| status: "pending" | "running" | "stopping" | "stopped" | "failed" | "aborted" | "snapshotting"; | ||
| status: "failed" | "aborted" | "pending" | "running" | "stopping" | "stopped" | "snapshotting"; | ||
| requestedAt: number; | ||
@@ -648,3 +648,3 @@ createdAt: number; | ||
| region: string; | ||
| status: "failed" | "created" | "deleted"; | ||
| status: "created" | "deleted" | "failed"; | ||
| sizeBytes: number; | ||
@@ -687,3 +687,3 @@ createdAt: number; | ||
| region: string; | ||
| status: "failed" | "created" | "deleted"; | ||
| status: "created" | "deleted" | "failed"; | ||
| sizeBytes: number; | ||
@@ -701,3 +701,3 @@ createdAt: number; | ||
| region: string; | ||
| status: "failed" | "created" | "deleted"; | ||
| status: "created" | "deleted" | "failed"; | ||
| sizeBytes: number; | ||
@@ -722,3 +722,3 @@ createdAt: number; | ||
| region: string; | ||
| status: "failed" | "created" | "deleted"; | ||
| status: "created" | "deleted" | "failed"; | ||
| sizeBytes: number; | ||
@@ -736,3 +736,3 @@ createdAt: number; | ||
| region: string; | ||
| status: "failed" | "created" | "deleted"; | ||
| status: "created" | "deleted" | "failed"; | ||
| sizeBytes: number; | ||
@@ -828,3 +828,3 @@ createdAt: number; | ||
| currentSessionId: string; | ||
| status: "pending" | "running" | "stopping" | "stopped" | "failed" | "aborted" | "snapshotting"; | ||
| status: "failed" | "aborted" | "pending" | "running" | "stopping" | "stopped" | "snapshotting"; | ||
| region?: string | undefined; | ||
@@ -944,3 +944,3 @@ vcpus?: number | undefined; | ||
| timeout: number; | ||
| status: "pending" | "running" | "stopping" | "stopped" | "failed" | "aborted" | "snapshotting"; | ||
| status: "failed" | "aborted" | "pending" | "running" | "stopping" | "stopped" | "snapshotting"; | ||
| requestedAt: number; | ||
@@ -1073,3 +1073,3 @@ createdAt: number; | ||
| currentSessionId: string; | ||
| status: "pending" | "running" | "stopping" | "stopped" | "failed" | "aborted" | "snapshotting"; | ||
| status: "failed" | "aborted" | "pending" | "running" | "stopping" | "stopped" | "snapshotting"; | ||
| region?: string | undefined; | ||
@@ -1214,3 +1214,3 @@ vcpus?: number | undefined; | ||
| currentSessionId: string; | ||
| status: "pending" | "running" | "stopping" | "stopped" | "failed" | "aborted" | "snapshotting"; | ||
| status: "failed" | "aborted" | "pending" | "running" | "stopping" | "stopped" | "snapshotting"; | ||
| region?: string | undefined; | ||
@@ -1339,3 +1339,3 @@ vcpus?: number | undefined; | ||
| currentSessionId: string; | ||
| status: "pending" | "running" | "stopping" | "stopped" | "failed" | "aborted" | "snapshotting"; | ||
| status: "failed" | "aborted" | "pending" | "running" | "stopping" | "stopped" | "snapshotting"; | ||
| region?: string | undefined; | ||
@@ -1342,0 +1342,0 @@ vcpus?: number | undefined; |
@@ -38,3 +38,3 @@ import { BaseClient, Parsed, RequestParams } from "./base-client.js"; | ||
| timeout: number; | ||
| status: "pending" | "running" | "stopping" | "stopped" | "failed" | "aborted" | "snapshotting"; | ||
| status: "failed" | "aborted" | "pending" | "running" | "stopping" | "stopped" | "snapshotting"; | ||
| requestedAt: number; | ||
@@ -191,3 +191,3 @@ createdAt: number; | ||
| currentSessionId: string; | ||
| status: "pending" | "running" | "stopping" | "stopped" | "failed" | "aborted" | "snapshotting"; | ||
| status: "failed" | "aborted" | "pending" | "running" | "stopping" | "stopped" | "snapshotting"; | ||
| region?: string | undefined; | ||
@@ -307,3 +307,3 @@ vcpus?: number | undefined; | ||
| timeout: number; | ||
| status: "pending" | "running" | "stopping" | "stopped" | "failed" | "aborted" | "snapshotting"; | ||
| status: "failed" | "aborted" | "pending" | "running" | "stopping" | "stopped" | "snapshotting"; | ||
| requestedAt: number; | ||
@@ -508,3 +508,3 @@ createdAt: number; | ||
| timeout: number; | ||
| status: "pending" | "running" | "stopping" | "stopped" | "failed" | "aborted" | "snapshotting"; | ||
| status: "failed" | "aborted" | "pending" | "running" | "stopping" | "stopped" | "snapshotting"; | ||
| requestedAt: number; | ||
@@ -648,3 +648,3 @@ createdAt: number; | ||
| region: string; | ||
| status: "failed" | "created" | "deleted"; | ||
| status: "created" | "deleted" | "failed"; | ||
| sizeBytes: number; | ||
@@ -687,3 +687,3 @@ createdAt: number; | ||
| region: string; | ||
| status: "failed" | "created" | "deleted"; | ||
| status: "created" | "deleted" | "failed"; | ||
| sizeBytes: number; | ||
@@ -701,3 +701,3 @@ createdAt: number; | ||
| region: string; | ||
| status: "failed" | "created" | "deleted"; | ||
| status: "created" | "deleted" | "failed"; | ||
| sizeBytes: number; | ||
@@ -722,3 +722,3 @@ createdAt: number; | ||
| region: string; | ||
| status: "failed" | "created" | "deleted"; | ||
| status: "created" | "deleted" | "failed"; | ||
| sizeBytes: number; | ||
@@ -736,3 +736,3 @@ createdAt: number; | ||
| region: string; | ||
| status: "failed" | "created" | "deleted"; | ||
| status: "created" | "deleted" | "failed"; | ||
| sizeBytes: number; | ||
@@ -828,3 +828,3 @@ createdAt: number; | ||
| currentSessionId: string; | ||
| status: "pending" | "running" | "stopping" | "stopped" | "failed" | "aborted" | "snapshotting"; | ||
| status: "failed" | "aborted" | "pending" | "running" | "stopping" | "stopped" | "snapshotting"; | ||
| region?: string | undefined; | ||
@@ -944,3 +944,3 @@ vcpus?: number | undefined; | ||
| timeout: number; | ||
| status: "pending" | "running" | "stopping" | "stopped" | "failed" | "aborted" | "snapshotting"; | ||
| status: "failed" | "aborted" | "pending" | "running" | "stopping" | "stopped" | "snapshotting"; | ||
| requestedAt: number; | ||
@@ -1073,3 +1073,3 @@ createdAt: number; | ||
| currentSessionId: string; | ||
| status: "pending" | "running" | "stopping" | "stopped" | "failed" | "aborted" | "snapshotting"; | ||
| status: "failed" | "aborted" | "pending" | "running" | "stopping" | "stopped" | "snapshotting"; | ||
| region?: string | undefined; | ||
@@ -1214,3 +1214,3 @@ vcpus?: number | undefined; | ||
| currentSessionId: string; | ||
| status: "pending" | "running" | "stopping" | "stopped" | "failed" | "aborted" | "snapshotting"; | ||
| status: "failed" | "aborted" | "pending" | "running" | "stopping" | "stopped" | "snapshotting"; | ||
| region?: string | undefined; | ||
@@ -1339,3 +1339,3 @@ vcpus?: number | undefined; | ||
| currentSessionId: string; | ||
| status: "pending" | "running" | "stopping" | "stopped" | "failed" | "aborted" | "snapshotting"; | ||
| status: "failed" | "aborted" | "pending" | "running" | "stopping" | "stopped" | "snapshotting"; | ||
| region?: string | undefined; | ||
@@ -1342,0 +1342,0 @@ vcpus?: number | undefined; |
@@ -13,2 +13,4 @@ import { z } from "zod"; | ||
| status: z.ZodEnum<{ | ||
| failed: "failed"; | ||
| aborted: "aborted"; | ||
| pending: "pending"; | ||
@@ -18,4 +20,2 @@ running: "running"; | ||
| stopped: "stopped"; | ||
| failed: "failed"; | ||
| aborted: "aborted"; | ||
| snapshotting: "snapshotting"; | ||
@@ -136,5 +136,5 @@ }>; | ||
| status: z.ZodEnum<{ | ||
| failed: "failed"; | ||
| created: "created"; | ||
| deleted: "deleted"; | ||
| failed: "failed"; | ||
| }>; | ||
@@ -168,2 +168,4 @@ sizeBytes: z.ZodNumber; | ||
| status: z.ZodEnum<{ | ||
| failed: "failed"; | ||
| aborted: "aborted"; | ||
| pending: "pending"; | ||
@@ -173,4 +175,2 @@ running: "running"; | ||
| stopped: "stopped"; | ||
| failed: "failed"; | ||
| aborted: "aborted"; | ||
| snapshotting: "snapshotting"; | ||
@@ -321,5 +321,5 @@ }>; | ||
| status: z.ZodEnum<{ | ||
| failed: "failed"; | ||
| created: "created"; | ||
| deleted: "deleted"; | ||
| failed: "failed"; | ||
| }>; | ||
@@ -339,5 +339,5 @@ sizeBytes: z.ZodNumber; | ||
| status: z.ZodEnum<{ | ||
| failed: "failed"; | ||
| created: "created"; | ||
| deleted: "deleted"; | ||
| failed: "failed"; | ||
| }>; | ||
@@ -361,5 +361,5 @@ sizeBytes: z.ZodNumber; | ||
| status: z.ZodEnum<{ | ||
| failed: "failed"; | ||
| created: "created"; | ||
| deleted: "deleted"; | ||
| failed: "failed"; | ||
| }>; | ||
@@ -382,2 +382,4 @@ sizeBytes: z.ZodNumber; | ||
| status: z.ZodEnum<{ | ||
| failed: "failed"; | ||
| aborted: "aborted"; | ||
| pending: "pending"; | ||
@@ -387,4 +389,2 @@ running: "running"; | ||
| stopped: "stopped"; | ||
| failed: "failed"; | ||
| aborted: "aborted"; | ||
| snapshotting: "snapshotting"; | ||
@@ -500,5 +500,5 @@ }>; | ||
| status: z.ZodEnum<{ | ||
| failed: "failed"; | ||
| created: "created"; | ||
| deleted: "deleted"; | ||
| failed: "failed"; | ||
| }>; | ||
@@ -614,2 +614,4 @@ sizeBytes: z.ZodNumber; | ||
| status: z.ZodEnum<{ | ||
| failed: "failed"; | ||
| aborted: "aborted"; | ||
| pending: "pending"; | ||
@@ -619,4 +621,2 @@ running: "running"; | ||
| stopped: "stopped"; | ||
| failed: "failed"; | ||
| aborted: "aborted"; | ||
| snapshotting: "snapshotting"; | ||
@@ -644,2 +644,4 @@ }>; | ||
| status: z.ZodEnum<{ | ||
| failed: "failed"; | ||
| aborted: "aborted"; | ||
| pending: "pending"; | ||
@@ -649,4 +651,2 @@ running: "running"; | ||
| stopped: "stopped"; | ||
| failed: "failed"; | ||
| aborted: "aborted"; | ||
| snapshotting: "snapshotting"; | ||
@@ -855,2 +855,4 @@ }>; | ||
| status: z.ZodEnum<{ | ||
| failed: "failed"; | ||
| aborted: "aborted"; | ||
| pending: "pending"; | ||
@@ -860,4 +862,2 @@ running: "running"; | ||
| stopped: "stopped"; | ||
| failed: "failed"; | ||
| aborted: "aborted"; | ||
| snapshotting: "snapshotting"; | ||
@@ -880,5 +880,5 @@ }>; | ||
| status: z.ZodEnum<{ | ||
| failed: "failed"; | ||
| created: "created"; | ||
| deleted: "deleted"; | ||
| failed: "failed"; | ||
| }>; | ||
@@ -885,0 +885,0 @@ sizeBytes: z.ZodNumber; |
@@ -13,2 +13,4 @@ import { z } from "zod"; | ||
| status: z.ZodEnum<{ | ||
| failed: "failed"; | ||
| aborted: "aborted"; | ||
| pending: "pending"; | ||
@@ -18,4 +20,2 @@ running: "running"; | ||
| stopped: "stopped"; | ||
| failed: "failed"; | ||
| aborted: "aborted"; | ||
| snapshotting: "snapshotting"; | ||
@@ -136,5 +136,5 @@ }>; | ||
| status: z.ZodEnum<{ | ||
| failed: "failed"; | ||
| created: "created"; | ||
| deleted: "deleted"; | ||
| failed: "failed"; | ||
| }>; | ||
@@ -168,2 +168,4 @@ sizeBytes: z.ZodNumber; | ||
| status: z.ZodEnum<{ | ||
| failed: "failed"; | ||
| aborted: "aborted"; | ||
| pending: "pending"; | ||
@@ -173,4 +175,2 @@ running: "running"; | ||
| stopped: "stopped"; | ||
| failed: "failed"; | ||
| aborted: "aborted"; | ||
| snapshotting: "snapshotting"; | ||
@@ -321,5 +321,5 @@ }>; | ||
| status: z.ZodEnum<{ | ||
| failed: "failed"; | ||
| created: "created"; | ||
| deleted: "deleted"; | ||
| failed: "failed"; | ||
| }>; | ||
@@ -339,5 +339,5 @@ sizeBytes: z.ZodNumber; | ||
| status: z.ZodEnum<{ | ||
| failed: "failed"; | ||
| created: "created"; | ||
| deleted: "deleted"; | ||
| failed: "failed"; | ||
| }>; | ||
@@ -361,5 +361,5 @@ sizeBytes: z.ZodNumber; | ||
| status: z.ZodEnum<{ | ||
| failed: "failed"; | ||
| created: "created"; | ||
| deleted: "deleted"; | ||
| failed: "failed"; | ||
| }>; | ||
@@ -382,2 +382,4 @@ sizeBytes: z.ZodNumber; | ||
| status: z.ZodEnum<{ | ||
| failed: "failed"; | ||
| aborted: "aborted"; | ||
| pending: "pending"; | ||
@@ -387,4 +389,2 @@ running: "running"; | ||
| stopped: "stopped"; | ||
| failed: "failed"; | ||
| aborted: "aborted"; | ||
| snapshotting: "snapshotting"; | ||
@@ -500,5 +500,5 @@ }>; | ||
| status: z.ZodEnum<{ | ||
| failed: "failed"; | ||
| created: "created"; | ||
| deleted: "deleted"; | ||
| failed: "failed"; | ||
| }>; | ||
@@ -614,2 +614,4 @@ sizeBytes: z.ZodNumber; | ||
| status: z.ZodEnum<{ | ||
| failed: "failed"; | ||
| aborted: "aborted"; | ||
| pending: "pending"; | ||
@@ -619,4 +621,2 @@ running: "running"; | ||
| stopped: "stopped"; | ||
| failed: "failed"; | ||
| aborted: "aborted"; | ||
| snapshotting: "snapshotting"; | ||
@@ -644,2 +644,4 @@ }>; | ||
| status: z.ZodEnum<{ | ||
| failed: "failed"; | ||
| aborted: "aborted"; | ||
| pending: "pending"; | ||
@@ -649,4 +651,2 @@ running: "running"; | ||
| stopped: "stopped"; | ||
| failed: "failed"; | ||
| aborted: "aborted"; | ||
| snapshotting: "snapshotting"; | ||
@@ -855,2 +855,4 @@ }>; | ||
| status: z.ZodEnum<{ | ||
| failed: "failed"; | ||
| aborted: "aborted"; | ||
| pending: "pending"; | ||
@@ -860,4 +862,2 @@ running: "running"; | ||
| stopped: "stopped"; | ||
| failed: "failed"; | ||
| aborted: "aborted"; | ||
| snapshotting: "snapshotting"; | ||
@@ -880,5 +880,5 @@ }>; | ||
| status: z.ZodEnum<{ | ||
| failed: "failed"; | ||
| created: "created"; | ||
| deleted: "deleted"; | ||
| failed: "failed"; | ||
| }>; | ||
@@ -885,0 +885,0 @@ sizeBytes: z.ZodNumber; |
@@ -64,3 +64,3 @@ const require_rolldown_runtime = require('../_virtual/rolldown_runtime.cjs'); | ||
| const parsed = DeviceAuthorization.safeParse(json); | ||
| if (!parsed.success) throw new OAuthError(`Failed to parse device authorization response: ${parsed.error.message}`, json); | ||
| if (!parsed.success) throw new OAuthError("Failed to parse device authorization response from the Vercel authorization server.", json); | ||
| return { | ||
@@ -98,3 +98,3 @@ device_code: parsed.data.device_code, | ||
| const processed = TokenSet.safeParse(json); | ||
| if (!processed.success) return [new OAuthError(`Failed to parse token response: ${processed.error.message}`, json)]; | ||
| if (!processed.success) return [new OAuthError("Failed to parse token response from the Vercel authorization server.", json)]; | ||
| return [null, processed.data]; | ||
@@ -144,3 +144,3 @@ }, | ||
| const processed = IntrospectionResponse.safeParse(json); | ||
| if (!processed.success) throw new OAuthError(`Failed to parse introspection response: ${processed.error.message}`, json); | ||
| if (!processed.success) throw new OAuthError("Failed to parse introspection response from the Vercel authorization server.", json); | ||
| return processed.data; | ||
@@ -189,4 +189,3 @@ } | ||
| if (error instanceof TypeError) { | ||
| const message$1 = `Unexpected server response: ${JSON.stringify(response)}`; | ||
| this.cause = new Error(message$1, { cause: error }); | ||
| this.cause = /* @__PURE__ */ new Error("Unexpected response from the Vercel authorization server."); | ||
| this.code = "server_error"; | ||
@@ -193,0 +192,0 @@ return; |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"oauth.cjs","names":["VERSION","z","_as: AuthorizationServerMetadata","message"],"sources":["../../src/auth/oauth.ts"],"sourcesContent":["import os from \"os\";\nimport { z } from \"zod\";\nimport { VERSION } from \"../version.js\";\n\nconst USER_AGENT = `${os.hostname()} @ vercel/sandbox/${VERSION} node-${\n process.version\n} ${os.platform()} (${os.arch()})`;\n\nconst ISSUER = new URL(\"https://vercel.com\");\nconst CLIENT_ID = \"cl_HYyOPBNtFMfHhaUn9L4QPfTZz6TP47bp\";\n\nconst AuthorizationServerMetadata = z.object({\n issuer: z.string().url(),\n device_authorization_endpoint: z.string().url(),\n token_endpoint: z.string().url(),\n revocation_endpoint: z.string().url(),\n jwks_uri: z.string().url(),\n introspection_endpoint: z.string().url(),\n});\ntype AuthorizationServerMetadata = z.infer<typeof AuthorizationServerMetadata>;\nlet _as: AuthorizationServerMetadata;\n\nconst DeviceAuthorization = z.object({\n device_code: z.string(),\n user_code: z.string(),\n verification_uri: z.string().url(),\n verification_uri_complete: z.string().url(),\n expires_in: z.number(),\n interval: z.number(),\n});\n\nconst IntrospectionResponse = z\n .object({\n active: z.literal(true),\n client_id: z.string(),\n session_id: z.string(),\n })\n .or(z.object({ active: z.literal(false) }));\n\n/**\n * Returns the Authorization Server Metadata\n *\n * @see https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderConfigurationRequest\n * @see https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderConfigurationResponse\n */\nasync function authorizationServerMetadata(): Promise<AuthorizationServerMetadata> {\n if (_as) return _as;\n\n const response = await fetch(\n new URL(\".well-known/openid-configuration\", ISSUER),\n {\n headers: { \"Content-Type\": \"application/json\", \"user-agent\": USER_AGENT },\n },\n );\n\n _as = AuthorizationServerMetadata.parse(await response.json());\n return _as;\n}\n\nexport async function OAuth() {\n const as = await authorizationServerMetadata();\n return {\n /**\n * Perform the Device Authorization Request\n *\n * @see https://datatracker.ietf.org/doc/html/rfc8628#section-3.1\n * @see https://datatracker.ietf.org/doc/html/rfc8628#section-3.2\n */\n async deviceAuthorizationRequest(): Promise<DeviceAuthorizationRequest> {\n const response = await fetch(as.device_authorization_endpoint, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/x-www-form-urlencoded\",\n \"user-agent\": USER_AGENT,\n },\n body: new URLSearchParams({\n client_id: CLIENT_ID,\n scope: \"openid offline_access\",\n }),\n });\n\n const json = await response.json();\n const parsed = DeviceAuthorization.safeParse(json);\n\n if (!parsed.success) {\n throw new OAuthError(\n `Failed to parse device authorization response: ${parsed.error.message}`,\n json,\n );\n }\n\n return {\n device_code: parsed.data.device_code,\n user_code: parsed.data.user_code,\n verification_uri: parsed.data.verification_uri,\n verification_uri_complete: parsed.data.verification_uri_complete,\n expiresAt: Date.now() + parsed.data.expires_in * 1000,\n interval: parsed.data.interval,\n };\n },\n /**\n * Perform the Device Access Token Request\n *\n * @see https://datatracker.ietf.org/doc/html/rfc8628#section-3.4\n */\n async deviceAccessTokenRequest(\n device_code: string,\n ): Promise<[Error] | [null, Response]> {\n try {\n return [\n null,\n await fetch(as.token_endpoint, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/x-www-form-urlencoded\",\n \"user-agent\": USER_AGENT,\n },\n body: new URLSearchParams({\n client_id: CLIENT_ID,\n grant_type: \"urn:ietf:params:oauth:grant-type:device_code\",\n device_code,\n }),\n signal: AbortSignal.timeout(10 * 1000),\n }),\n ];\n } catch (error) {\n if (error instanceof Error) return [error];\n return [\n new Error(\"An unknown error occurred. See the logs for details.\", {\n cause: error,\n }),\n ];\n }\n },\n /**\n * Process the Token request Response\n *\n * @see https://datatracker.ietf.org/doc/html/rfc8628#section-3.5\n */\n async processTokenResponse(\n response: Response,\n ): Promise<[OAuthError] | [null, TokenSet]> {\n const json = await response.json();\n const processed = TokenSet.safeParse(json);\n\n if (!processed.success) {\n return [\n new OAuthError(\n `Failed to parse token response: ${processed.error.message}`,\n json,\n ),\n ];\n }\n\n return [null, processed.data];\n },\n /**\n * Perform a Token Revocation Request.\n *\n * @see https://datatracker.ietf.org/doc/html/rfc7009#section-2.1\n * @see https://datatracker.ietf.org/doc/html/rfc7009#section-2.2\n */\n async revokeToken(token: string): Promise<OAuthError | void> {\n const response = await fetch(as.revocation_endpoint, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/x-www-form-urlencoded\",\n \"user-agent\": USER_AGENT,\n },\n body: new URLSearchParams({ token, client_id: CLIENT_ID }),\n });\n\n if (response.ok) return;\n const json = await response.json();\n\n return new OAuthError(\"Revocation request failed\", json);\n },\n /**\n * Perform Refresh Token Request.\n *\n * @see https://datatracker.ietf.org/doc/html/rfc6749#section-6\n */\n async refreshToken(token: string): Promise<TokenSet> {\n const response = await fetch(as.token_endpoint, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/x-www-form-urlencoded\",\n \"user-agent\": USER_AGENT,\n },\n body: new URLSearchParams({\n client_id: CLIENT_ID,\n grant_type: \"refresh_token\",\n refresh_token: token,\n }),\n });\n\n const [tokensError, tokenSet] = await this.processTokenResponse(response);\n if (tokensError) throw tokensError;\n return tokenSet;\n },\n /**\n * Perform Token Introspection Request.\n *\n * @see https://datatracker.ietf.org/doc/html/rfc7662#section-2.1\n */\n async introspectToken(\n token: string,\n ): Promise<z.infer<typeof IntrospectionResponse>> {\n const response = await fetch(as.introspection_endpoint, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/x-www-form-urlencoded\",\n \"user-agent\": USER_AGENT,\n },\n body: new URLSearchParams({ token }),\n });\n\n const json = await response.json();\n const processed = IntrospectionResponse.safeParse(json);\n if (!processed.success) {\n throw new OAuthError(\n `Failed to parse introspection response: ${processed.error.message}`,\n json,\n );\n }\n\n return processed.data;\n },\n };\n}\n\nexport type OAuth = Awaited<ReturnType<typeof OAuth>>;\n\nconst TokenSet = z.object({\n /** The access token issued by the authorization server. */\n access_token: z.string(),\n /** The type of the token issued */\n token_type: z.literal(\"Bearer\"),\n /** The lifetime in seconds of the access token. */\n expires_in: z.number(),\n /** The refresh token, which can be used to obtain new access tokens. */\n refresh_token: z.string().optional(),\n /** The scope of the access token. */\n scope: z.string().optional(),\n});\n\ntype TokenSet = z.infer<typeof TokenSet>;\n\nconst OAuthErrorResponse = z.object({\n error: z.enum([\n \"invalid_request\",\n \"invalid_client\",\n \"invalid_grant\",\n \"unauthorized_client\",\n \"unsupported_grant_type\",\n \"invalid_scope\",\n \"server_error\",\n // Device Authorization Response Errors\n \"authorization_pending\",\n \"slow_down\",\n \"access_denied\",\n \"expired_token\",\n // Revocation Response Errors\n \"unsupported_token_type\",\n ]),\n error_description: z.string().optional(),\n error_uri: z.string().optional(),\n});\n\ntype OAuthErrorResponse = z.infer<typeof OAuthErrorResponse>;\n\nfunction processOAuthErrorResponse(\n json: unknown,\n): OAuthErrorResponse | TypeError {\n try {\n return OAuthErrorResponse.parse(json);\n } catch (error) {\n if (error instanceof z.ZodError) {\n return new TypeError(`Invalid OAuth error response: ${error.message}`);\n }\n return new TypeError(\"Failed to parse OAuth error response\");\n }\n}\n\nclass OAuthError extends Error {\n name = \"OAuthError\";\n code: OAuthErrorResponse[\"error\"];\n cause: Error;\n constructor(message: string, response: unknown) {\n super(message);\n const error = processOAuthErrorResponse(response);\n if (error instanceof TypeError) {\n const message = `Unexpected server response: ${JSON.stringify(response)}`;\n this.cause = new Error(message, { cause: error });\n this.code = \"server_error\";\n return;\n }\n let cause = error.error;\n if (error.error_description) cause += `: ${error.error_description}`;\n if (error.error_uri) cause += ` (${error.error_uri})`;\n\n this.cause = new Error(cause);\n this.code = error.error;\n }\n}\n\nexport function isOAuthError(error: unknown): error is OAuthError {\n return error instanceof OAuthError;\n}\n\nexport interface DeviceAuthorizationRequest {\n /** The device verification code. */\n device_code: string;\n /** The end-user verification code. */\n user_code: string;\n /**\n * The minimum amount of time in seconds that the client\n * SHOULD wait between polling requests to the token endpoint.\n */\n interval: number;\n /** The end-user verification URI on the authorization server. */\n verification_uri: string;\n /**\n * The end-user verification URI on the authorization server,\n * including the `user_code`, without redirection.\n */\n verification_uri_complete: string;\n /**\n * The absolute lifetime of the `device_code` and `user_code`.\n * Calculated from `expires_in`.\n */\n expiresAt: number;\n}\n"],"mappings":"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"} | ||
| {"version":3,"file":"oauth.cjs","names":["VERSION","z","_as: AuthorizationServerMetadata"],"sources":["../../src/auth/oauth.ts"],"sourcesContent":["import os from \"os\";\nimport { z } from \"zod\";\nimport { VERSION } from \"../version.js\";\n\nconst USER_AGENT = `${os.hostname()} @ vercel/sandbox/${VERSION} node-${\n process.version\n} ${os.platform()} (${os.arch()})`;\n\nconst ISSUER = new URL(\"https://vercel.com\");\nconst CLIENT_ID = \"cl_HYyOPBNtFMfHhaUn9L4QPfTZz6TP47bp\";\n\nconst AuthorizationServerMetadata = z.object({\n issuer: z.string().url(),\n device_authorization_endpoint: z.string().url(),\n token_endpoint: z.string().url(),\n revocation_endpoint: z.string().url(),\n jwks_uri: z.string().url(),\n introspection_endpoint: z.string().url(),\n});\ntype AuthorizationServerMetadata = z.infer<typeof AuthorizationServerMetadata>;\nlet _as: AuthorizationServerMetadata;\n\nconst DeviceAuthorization = z.object({\n device_code: z.string(),\n user_code: z.string(),\n verification_uri: z.string().url(),\n verification_uri_complete: z.string().url(),\n expires_in: z.number(),\n interval: z.number(),\n});\n\nconst IntrospectionResponse = z\n .object({\n active: z.literal(true),\n client_id: z.string(),\n session_id: z.string(),\n })\n .or(z.object({ active: z.literal(false) }));\n\n/**\n * Returns the Authorization Server Metadata\n *\n * @see https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderConfigurationRequest\n * @see https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderConfigurationResponse\n */\nasync function authorizationServerMetadata(): Promise<AuthorizationServerMetadata> {\n if (_as) return _as;\n\n const response = await fetch(\n new URL(\".well-known/openid-configuration\", ISSUER),\n {\n headers: { \"Content-Type\": \"application/json\", \"user-agent\": USER_AGENT },\n },\n );\n\n _as = AuthorizationServerMetadata.parse(await response.json());\n return _as;\n}\n\nexport async function OAuth() {\n const as = await authorizationServerMetadata();\n return {\n /**\n * Perform the Device Authorization Request\n *\n * @see https://datatracker.ietf.org/doc/html/rfc8628#section-3.1\n * @see https://datatracker.ietf.org/doc/html/rfc8628#section-3.2\n */\n async deviceAuthorizationRequest(): Promise<DeviceAuthorizationRequest> {\n const response = await fetch(as.device_authorization_endpoint, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/x-www-form-urlencoded\",\n \"user-agent\": USER_AGENT,\n },\n body: new URLSearchParams({\n client_id: CLIENT_ID,\n scope: \"openid offline_access\",\n }),\n });\n\n const json = await response.json();\n const parsed = DeviceAuthorization.safeParse(json);\n\n if (!parsed.success) {\n throw new OAuthError(\n \"Failed to parse device authorization response from the Vercel authorization server.\",\n json,\n );\n }\n\n return {\n device_code: parsed.data.device_code,\n user_code: parsed.data.user_code,\n verification_uri: parsed.data.verification_uri,\n verification_uri_complete: parsed.data.verification_uri_complete,\n expiresAt: Date.now() + parsed.data.expires_in * 1000,\n interval: parsed.data.interval,\n };\n },\n /**\n * Perform the Device Access Token Request\n *\n * @see https://datatracker.ietf.org/doc/html/rfc8628#section-3.4\n */\n async deviceAccessTokenRequest(\n device_code: string,\n ): Promise<[Error] | [null, Response]> {\n try {\n return [\n null,\n await fetch(as.token_endpoint, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/x-www-form-urlencoded\",\n \"user-agent\": USER_AGENT,\n },\n body: new URLSearchParams({\n client_id: CLIENT_ID,\n grant_type: \"urn:ietf:params:oauth:grant-type:device_code\",\n device_code,\n }),\n signal: AbortSignal.timeout(10 * 1000),\n }),\n ];\n } catch (error) {\n if (error instanceof Error) return [error];\n return [\n new Error(\"An unknown error occurred. See the logs for details.\", {\n cause: error,\n }),\n ];\n }\n },\n /**\n * Process the Token request Response\n *\n * @see https://datatracker.ietf.org/doc/html/rfc8628#section-3.5\n */\n async processTokenResponse(\n response: Response,\n ): Promise<[OAuthError] | [null, TokenSet]> {\n const json = await response.json();\n const processed = TokenSet.safeParse(json);\n\n if (!processed.success) {\n return [\n new OAuthError(\n \"Failed to parse token response from the Vercel authorization server.\",\n json,\n ),\n ];\n }\n\n return [null, processed.data];\n },\n /**\n * Perform a Token Revocation Request.\n *\n * @see https://datatracker.ietf.org/doc/html/rfc7009#section-2.1\n * @see https://datatracker.ietf.org/doc/html/rfc7009#section-2.2\n */\n async revokeToken(token: string): Promise<OAuthError | void> {\n const response = await fetch(as.revocation_endpoint, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/x-www-form-urlencoded\",\n \"user-agent\": USER_AGENT,\n },\n body: new URLSearchParams({ token, client_id: CLIENT_ID }),\n });\n\n if (response.ok) return;\n const json = await response.json();\n\n return new OAuthError(\"Revocation request failed\", json);\n },\n /**\n * Perform Refresh Token Request.\n *\n * @see https://datatracker.ietf.org/doc/html/rfc6749#section-6\n */\n async refreshToken(token: string): Promise<TokenSet> {\n const response = await fetch(as.token_endpoint, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/x-www-form-urlencoded\",\n \"user-agent\": USER_AGENT,\n },\n body: new URLSearchParams({\n client_id: CLIENT_ID,\n grant_type: \"refresh_token\",\n refresh_token: token,\n }),\n });\n\n const [tokensError, tokenSet] = await this.processTokenResponse(response);\n if (tokensError) throw tokensError;\n return tokenSet;\n },\n /**\n * Perform Token Introspection Request.\n *\n * @see https://datatracker.ietf.org/doc/html/rfc7662#section-2.1\n */\n async introspectToken(\n token: string,\n ): Promise<z.infer<typeof IntrospectionResponse>> {\n const response = await fetch(as.introspection_endpoint, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/x-www-form-urlencoded\",\n \"user-agent\": USER_AGENT,\n },\n body: new URLSearchParams({ token }),\n });\n\n const json = await response.json();\n const processed = IntrospectionResponse.safeParse(json);\n if (!processed.success) {\n throw new OAuthError(\n \"Failed to parse introspection response from the Vercel authorization server.\",\n json,\n );\n }\n\n return processed.data;\n },\n };\n}\n\nexport type OAuth = Awaited<ReturnType<typeof OAuth>>;\n\nconst TokenSet = z.object({\n /** The access token issued by the authorization server. */\n access_token: z.string(),\n /** The type of the token issued */\n token_type: z.literal(\"Bearer\"),\n /** The lifetime in seconds of the access token. */\n expires_in: z.number(),\n /** The refresh token, which can be used to obtain new access tokens. */\n refresh_token: z.string().optional(),\n /** The scope of the access token. */\n scope: z.string().optional(),\n});\n\ntype TokenSet = z.infer<typeof TokenSet>;\n\nconst OAuthErrorResponse = z.object({\n error: z.enum([\n \"invalid_request\",\n \"invalid_client\",\n \"invalid_grant\",\n \"unauthorized_client\",\n \"unsupported_grant_type\",\n \"invalid_scope\",\n \"server_error\",\n // Device Authorization Response Errors\n \"authorization_pending\",\n \"slow_down\",\n \"access_denied\",\n \"expired_token\",\n // Revocation Response Errors\n \"unsupported_token_type\",\n ]),\n error_description: z.string().optional(),\n error_uri: z.string().optional(),\n});\n\ntype OAuthErrorResponse = z.infer<typeof OAuthErrorResponse>;\n\nfunction processOAuthErrorResponse(\n json: unknown,\n): OAuthErrorResponse | TypeError {\n try {\n return OAuthErrorResponse.parse(json);\n } catch (error) {\n if (error instanceof z.ZodError) {\n return new TypeError(`Invalid OAuth error response: ${error.message}`);\n }\n return new TypeError(\"Failed to parse OAuth error response\");\n }\n}\n\nclass OAuthError extends Error {\n name = \"OAuthError\";\n code: OAuthErrorResponse[\"error\"];\n cause: Error;\n constructor(message: string, response: unknown) {\n super(message);\n const error = processOAuthErrorResponse(response);\n if (error instanceof TypeError) {\n this.cause = new Error(\n \"Unexpected response from the Vercel authorization server.\",\n );\n this.code = \"server_error\";\n return;\n }\n let cause = error.error;\n if (error.error_description) cause += `: ${error.error_description}`;\n if (error.error_uri) cause += ` (${error.error_uri})`;\n\n this.cause = new Error(cause);\n this.code = error.error;\n }\n}\n\nexport function isOAuthError(error: unknown): error is OAuthError {\n return error instanceof OAuthError;\n}\n\nexport interface DeviceAuthorizationRequest {\n /** The device verification code. */\n device_code: string;\n /** The end-user verification code. */\n user_code: string;\n /**\n * The minimum amount of time in seconds that the client\n * SHOULD wait between polling requests to the token endpoint.\n */\n interval: number;\n /** The end-user verification URI on the authorization server. */\n verification_uri: string;\n /**\n * The end-user verification URI on the authorization server,\n * including the `user_code`, without redirection.\n */\n verification_uri_complete: string;\n /**\n * The absolute lifetime of the `device_code` and `user_code`.\n * Calculated from `expires_in`.\n */\n expiresAt: number;\n}\n"],"mappings":"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"} |
@@ -62,3 +62,3 @@ import { VERSION } from "../version.js"; | ||
| const parsed = DeviceAuthorization.safeParse(json); | ||
| if (!parsed.success) throw new OAuthError(`Failed to parse device authorization response: ${parsed.error.message}`, json); | ||
| if (!parsed.success) throw new OAuthError("Failed to parse device authorization response from the Vercel authorization server.", json); | ||
| return { | ||
@@ -96,3 +96,3 @@ device_code: parsed.data.device_code, | ||
| const processed = TokenSet.safeParse(json); | ||
| if (!processed.success) return [new OAuthError(`Failed to parse token response: ${processed.error.message}`, json)]; | ||
| if (!processed.success) return [new OAuthError("Failed to parse token response from the Vercel authorization server.", json)]; | ||
| return [null, processed.data]; | ||
@@ -142,3 +142,3 @@ }, | ||
| const processed = IntrospectionResponse.safeParse(json); | ||
| if (!processed.success) throw new OAuthError(`Failed to parse introspection response: ${processed.error.message}`, json); | ||
| if (!processed.success) throw new OAuthError("Failed to parse introspection response from the Vercel authorization server.", json); | ||
| return processed.data; | ||
@@ -187,4 +187,3 @@ } | ||
| if (error instanceof TypeError) { | ||
| const message$1 = `Unexpected server response: ${JSON.stringify(response)}`; | ||
| this.cause = new Error(message$1, { cause: error }); | ||
| this.cause = /* @__PURE__ */ new Error("Unexpected response from the Vercel authorization server."); | ||
| this.code = "server_error"; | ||
@@ -191,0 +190,0 @@ return; |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"oauth.js","names":["_as: AuthorizationServerMetadata","message"],"sources":["../../src/auth/oauth.ts"],"sourcesContent":["import os from \"os\";\nimport { z } from \"zod\";\nimport { VERSION } from \"../version.js\";\n\nconst USER_AGENT = `${os.hostname()} @ vercel/sandbox/${VERSION} node-${\n process.version\n} ${os.platform()} (${os.arch()})`;\n\nconst ISSUER = new URL(\"https://vercel.com\");\nconst CLIENT_ID = \"cl_HYyOPBNtFMfHhaUn9L4QPfTZz6TP47bp\";\n\nconst AuthorizationServerMetadata = z.object({\n issuer: z.string().url(),\n device_authorization_endpoint: z.string().url(),\n token_endpoint: z.string().url(),\n revocation_endpoint: z.string().url(),\n jwks_uri: z.string().url(),\n introspection_endpoint: z.string().url(),\n});\ntype AuthorizationServerMetadata = z.infer<typeof AuthorizationServerMetadata>;\nlet _as: AuthorizationServerMetadata;\n\nconst DeviceAuthorization = z.object({\n device_code: z.string(),\n user_code: z.string(),\n verification_uri: z.string().url(),\n verification_uri_complete: z.string().url(),\n expires_in: z.number(),\n interval: z.number(),\n});\n\nconst IntrospectionResponse = z\n .object({\n active: z.literal(true),\n client_id: z.string(),\n session_id: z.string(),\n })\n .or(z.object({ active: z.literal(false) }));\n\n/**\n * Returns the Authorization Server Metadata\n *\n * @see https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderConfigurationRequest\n * @see https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderConfigurationResponse\n */\nasync function authorizationServerMetadata(): Promise<AuthorizationServerMetadata> {\n if (_as) return _as;\n\n const response = await fetch(\n new URL(\".well-known/openid-configuration\", ISSUER),\n {\n headers: { \"Content-Type\": \"application/json\", \"user-agent\": USER_AGENT },\n },\n );\n\n _as = AuthorizationServerMetadata.parse(await response.json());\n return _as;\n}\n\nexport async function OAuth() {\n const as = await authorizationServerMetadata();\n return {\n /**\n * Perform the Device Authorization Request\n *\n * @see https://datatracker.ietf.org/doc/html/rfc8628#section-3.1\n * @see https://datatracker.ietf.org/doc/html/rfc8628#section-3.2\n */\n async deviceAuthorizationRequest(): Promise<DeviceAuthorizationRequest> {\n const response = await fetch(as.device_authorization_endpoint, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/x-www-form-urlencoded\",\n \"user-agent\": USER_AGENT,\n },\n body: new URLSearchParams({\n client_id: CLIENT_ID,\n scope: \"openid offline_access\",\n }),\n });\n\n const json = await response.json();\n const parsed = DeviceAuthorization.safeParse(json);\n\n if (!parsed.success) {\n throw new OAuthError(\n `Failed to parse device authorization response: ${parsed.error.message}`,\n json,\n );\n }\n\n return {\n device_code: parsed.data.device_code,\n user_code: parsed.data.user_code,\n verification_uri: parsed.data.verification_uri,\n verification_uri_complete: parsed.data.verification_uri_complete,\n expiresAt: Date.now() + parsed.data.expires_in * 1000,\n interval: parsed.data.interval,\n };\n },\n /**\n * Perform the Device Access Token Request\n *\n * @see https://datatracker.ietf.org/doc/html/rfc8628#section-3.4\n */\n async deviceAccessTokenRequest(\n device_code: string,\n ): Promise<[Error] | [null, Response]> {\n try {\n return [\n null,\n await fetch(as.token_endpoint, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/x-www-form-urlencoded\",\n \"user-agent\": USER_AGENT,\n },\n body: new URLSearchParams({\n client_id: CLIENT_ID,\n grant_type: \"urn:ietf:params:oauth:grant-type:device_code\",\n device_code,\n }),\n signal: AbortSignal.timeout(10 * 1000),\n }),\n ];\n } catch (error) {\n if (error instanceof Error) return [error];\n return [\n new Error(\"An unknown error occurred. See the logs for details.\", {\n cause: error,\n }),\n ];\n }\n },\n /**\n * Process the Token request Response\n *\n * @see https://datatracker.ietf.org/doc/html/rfc8628#section-3.5\n */\n async processTokenResponse(\n response: Response,\n ): Promise<[OAuthError] | [null, TokenSet]> {\n const json = await response.json();\n const processed = TokenSet.safeParse(json);\n\n if (!processed.success) {\n return [\n new OAuthError(\n `Failed to parse token response: ${processed.error.message}`,\n json,\n ),\n ];\n }\n\n return [null, processed.data];\n },\n /**\n * Perform a Token Revocation Request.\n *\n * @see https://datatracker.ietf.org/doc/html/rfc7009#section-2.1\n * @see https://datatracker.ietf.org/doc/html/rfc7009#section-2.2\n */\n async revokeToken(token: string): Promise<OAuthError | void> {\n const response = await fetch(as.revocation_endpoint, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/x-www-form-urlencoded\",\n \"user-agent\": USER_AGENT,\n },\n body: new URLSearchParams({ token, client_id: CLIENT_ID }),\n });\n\n if (response.ok) return;\n const json = await response.json();\n\n return new OAuthError(\"Revocation request failed\", json);\n },\n /**\n * Perform Refresh Token Request.\n *\n * @see https://datatracker.ietf.org/doc/html/rfc6749#section-6\n */\n async refreshToken(token: string): Promise<TokenSet> {\n const response = await fetch(as.token_endpoint, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/x-www-form-urlencoded\",\n \"user-agent\": USER_AGENT,\n },\n body: new URLSearchParams({\n client_id: CLIENT_ID,\n grant_type: \"refresh_token\",\n refresh_token: token,\n }),\n });\n\n const [tokensError, tokenSet] = await this.processTokenResponse(response);\n if (tokensError) throw tokensError;\n return tokenSet;\n },\n /**\n * Perform Token Introspection Request.\n *\n * @see https://datatracker.ietf.org/doc/html/rfc7662#section-2.1\n */\n async introspectToken(\n token: string,\n ): Promise<z.infer<typeof IntrospectionResponse>> {\n const response = await fetch(as.introspection_endpoint, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/x-www-form-urlencoded\",\n \"user-agent\": USER_AGENT,\n },\n body: new URLSearchParams({ token }),\n });\n\n const json = await response.json();\n const processed = IntrospectionResponse.safeParse(json);\n if (!processed.success) {\n throw new OAuthError(\n `Failed to parse introspection response: ${processed.error.message}`,\n json,\n );\n }\n\n return processed.data;\n },\n };\n}\n\nexport type OAuth = Awaited<ReturnType<typeof OAuth>>;\n\nconst TokenSet = z.object({\n /** The access token issued by the authorization server. */\n access_token: z.string(),\n /** The type of the token issued */\n token_type: z.literal(\"Bearer\"),\n /** The lifetime in seconds of the access token. */\n expires_in: z.number(),\n /** The refresh token, which can be used to obtain new access tokens. */\n refresh_token: z.string().optional(),\n /** The scope of the access token. */\n scope: z.string().optional(),\n});\n\ntype TokenSet = z.infer<typeof TokenSet>;\n\nconst OAuthErrorResponse = z.object({\n error: z.enum([\n \"invalid_request\",\n \"invalid_client\",\n \"invalid_grant\",\n \"unauthorized_client\",\n \"unsupported_grant_type\",\n \"invalid_scope\",\n \"server_error\",\n // Device Authorization Response Errors\n \"authorization_pending\",\n \"slow_down\",\n \"access_denied\",\n \"expired_token\",\n // Revocation Response Errors\n \"unsupported_token_type\",\n ]),\n error_description: z.string().optional(),\n error_uri: z.string().optional(),\n});\n\ntype OAuthErrorResponse = z.infer<typeof OAuthErrorResponse>;\n\nfunction processOAuthErrorResponse(\n json: unknown,\n): OAuthErrorResponse | TypeError {\n try {\n return OAuthErrorResponse.parse(json);\n } catch (error) {\n if (error instanceof z.ZodError) {\n return new TypeError(`Invalid OAuth error response: ${error.message}`);\n }\n return new TypeError(\"Failed to parse OAuth error response\");\n }\n}\n\nclass OAuthError extends Error {\n name = \"OAuthError\";\n code: OAuthErrorResponse[\"error\"];\n cause: Error;\n constructor(message: string, response: unknown) {\n super(message);\n const error = processOAuthErrorResponse(response);\n if (error instanceof TypeError) {\n const message = `Unexpected server response: ${JSON.stringify(response)}`;\n this.cause = new Error(message, { cause: error });\n this.code = \"server_error\";\n return;\n }\n let cause = error.error;\n if (error.error_description) cause += `: ${error.error_description}`;\n if (error.error_uri) cause += ` (${error.error_uri})`;\n\n this.cause = new Error(cause);\n this.code = error.error;\n }\n}\n\nexport function isOAuthError(error: unknown): error is OAuthError {\n return error instanceof OAuthError;\n}\n\nexport interface DeviceAuthorizationRequest {\n /** The device verification code. */\n device_code: string;\n /** The end-user verification code. */\n user_code: string;\n /**\n * The minimum amount of time in seconds that the client\n * SHOULD wait between polling requests to the token endpoint.\n */\n interval: number;\n /** The end-user verification URI on the authorization server. */\n verification_uri: string;\n /**\n * The end-user verification URI on the authorization server,\n * including the `user_code`, without redirection.\n */\n verification_uri_complete: string;\n /**\n * The absolute lifetime of the `device_code` and `user_code`.\n * Calculated from `expires_in`.\n */\n expiresAt: number;\n}\n"],"mappings":"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"} | ||
| {"version":3,"file":"oauth.js","names":["_as: AuthorizationServerMetadata"],"sources":["../../src/auth/oauth.ts"],"sourcesContent":["import os from \"os\";\nimport { z } from \"zod\";\nimport { VERSION } from \"../version.js\";\n\nconst USER_AGENT = `${os.hostname()} @ vercel/sandbox/${VERSION} node-${\n process.version\n} ${os.platform()} (${os.arch()})`;\n\nconst ISSUER = new URL(\"https://vercel.com\");\nconst CLIENT_ID = \"cl_HYyOPBNtFMfHhaUn9L4QPfTZz6TP47bp\";\n\nconst AuthorizationServerMetadata = z.object({\n issuer: z.string().url(),\n device_authorization_endpoint: z.string().url(),\n token_endpoint: z.string().url(),\n revocation_endpoint: z.string().url(),\n jwks_uri: z.string().url(),\n introspection_endpoint: z.string().url(),\n});\ntype AuthorizationServerMetadata = z.infer<typeof AuthorizationServerMetadata>;\nlet _as: AuthorizationServerMetadata;\n\nconst DeviceAuthorization = z.object({\n device_code: z.string(),\n user_code: z.string(),\n verification_uri: z.string().url(),\n verification_uri_complete: z.string().url(),\n expires_in: z.number(),\n interval: z.number(),\n});\n\nconst IntrospectionResponse = z\n .object({\n active: z.literal(true),\n client_id: z.string(),\n session_id: z.string(),\n })\n .or(z.object({ active: z.literal(false) }));\n\n/**\n * Returns the Authorization Server Metadata\n *\n * @see https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderConfigurationRequest\n * @see https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderConfigurationResponse\n */\nasync function authorizationServerMetadata(): Promise<AuthorizationServerMetadata> {\n if (_as) return _as;\n\n const response = await fetch(\n new URL(\".well-known/openid-configuration\", ISSUER),\n {\n headers: { \"Content-Type\": \"application/json\", \"user-agent\": USER_AGENT },\n },\n );\n\n _as = AuthorizationServerMetadata.parse(await response.json());\n return _as;\n}\n\nexport async function OAuth() {\n const as = await authorizationServerMetadata();\n return {\n /**\n * Perform the Device Authorization Request\n *\n * @see https://datatracker.ietf.org/doc/html/rfc8628#section-3.1\n * @see https://datatracker.ietf.org/doc/html/rfc8628#section-3.2\n */\n async deviceAuthorizationRequest(): Promise<DeviceAuthorizationRequest> {\n const response = await fetch(as.device_authorization_endpoint, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/x-www-form-urlencoded\",\n \"user-agent\": USER_AGENT,\n },\n body: new URLSearchParams({\n client_id: CLIENT_ID,\n scope: \"openid offline_access\",\n }),\n });\n\n const json = await response.json();\n const parsed = DeviceAuthorization.safeParse(json);\n\n if (!parsed.success) {\n throw new OAuthError(\n \"Failed to parse device authorization response from the Vercel authorization server.\",\n json,\n );\n }\n\n return {\n device_code: parsed.data.device_code,\n user_code: parsed.data.user_code,\n verification_uri: parsed.data.verification_uri,\n verification_uri_complete: parsed.data.verification_uri_complete,\n expiresAt: Date.now() + parsed.data.expires_in * 1000,\n interval: parsed.data.interval,\n };\n },\n /**\n * Perform the Device Access Token Request\n *\n * @see https://datatracker.ietf.org/doc/html/rfc8628#section-3.4\n */\n async deviceAccessTokenRequest(\n device_code: string,\n ): Promise<[Error] | [null, Response]> {\n try {\n return [\n null,\n await fetch(as.token_endpoint, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/x-www-form-urlencoded\",\n \"user-agent\": USER_AGENT,\n },\n body: new URLSearchParams({\n client_id: CLIENT_ID,\n grant_type: \"urn:ietf:params:oauth:grant-type:device_code\",\n device_code,\n }),\n signal: AbortSignal.timeout(10 * 1000),\n }),\n ];\n } catch (error) {\n if (error instanceof Error) return [error];\n return [\n new Error(\"An unknown error occurred. See the logs for details.\", {\n cause: error,\n }),\n ];\n }\n },\n /**\n * Process the Token request Response\n *\n * @see https://datatracker.ietf.org/doc/html/rfc8628#section-3.5\n */\n async processTokenResponse(\n response: Response,\n ): Promise<[OAuthError] | [null, TokenSet]> {\n const json = await response.json();\n const processed = TokenSet.safeParse(json);\n\n if (!processed.success) {\n return [\n new OAuthError(\n \"Failed to parse token response from the Vercel authorization server.\",\n json,\n ),\n ];\n }\n\n return [null, processed.data];\n },\n /**\n * Perform a Token Revocation Request.\n *\n * @see https://datatracker.ietf.org/doc/html/rfc7009#section-2.1\n * @see https://datatracker.ietf.org/doc/html/rfc7009#section-2.2\n */\n async revokeToken(token: string): Promise<OAuthError | void> {\n const response = await fetch(as.revocation_endpoint, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/x-www-form-urlencoded\",\n \"user-agent\": USER_AGENT,\n },\n body: new URLSearchParams({ token, client_id: CLIENT_ID }),\n });\n\n if (response.ok) return;\n const json = await response.json();\n\n return new OAuthError(\"Revocation request failed\", json);\n },\n /**\n * Perform Refresh Token Request.\n *\n * @see https://datatracker.ietf.org/doc/html/rfc6749#section-6\n */\n async refreshToken(token: string): Promise<TokenSet> {\n const response = await fetch(as.token_endpoint, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/x-www-form-urlencoded\",\n \"user-agent\": USER_AGENT,\n },\n body: new URLSearchParams({\n client_id: CLIENT_ID,\n grant_type: \"refresh_token\",\n refresh_token: token,\n }),\n });\n\n const [tokensError, tokenSet] = await this.processTokenResponse(response);\n if (tokensError) throw tokensError;\n return tokenSet;\n },\n /**\n * Perform Token Introspection Request.\n *\n * @see https://datatracker.ietf.org/doc/html/rfc7662#section-2.1\n */\n async introspectToken(\n token: string,\n ): Promise<z.infer<typeof IntrospectionResponse>> {\n const response = await fetch(as.introspection_endpoint, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/x-www-form-urlencoded\",\n \"user-agent\": USER_AGENT,\n },\n body: new URLSearchParams({ token }),\n });\n\n const json = await response.json();\n const processed = IntrospectionResponse.safeParse(json);\n if (!processed.success) {\n throw new OAuthError(\n \"Failed to parse introspection response from the Vercel authorization server.\",\n json,\n );\n }\n\n return processed.data;\n },\n };\n}\n\nexport type OAuth = Awaited<ReturnType<typeof OAuth>>;\n\nconst TokenSet = z.object({\n /** The access token issued by the authorization server. */\n access_token: z.string(),\n /** The type of the token issued */\n token_type: z.literal(\"Bearer\"),\n /** The lifetime in seconds of the access token. */\n expires_in: z.number(),\n /** The refresh token, which can be used to obtain new access tokens. */\n refresh_token: z.string().optional(),\n /** The scope of the access token. */\n scope: z.string().optional(),\n});\n\ntype TokenSet = z.infer<typeof TokenSet>;\n\nconst OAuthErrorResponse = z.object({\n error: z.enum([\n \"invalid_request\",\n \"invalid_client\",\n \"invalid_grant\",\n \"unauthorized_client\",\n \"unsupported_grant_type\",\n \"invalid_scope\",\n \"server_error\",\n // Device Authorization Response Errors\n \"authorization_pending\",\n \"slow_down\",\n \"access_denied\",\n \"expired_token\",\n // Revocation Response Errors\n \"unsupported_token_type\",\n ]),\n error_description: z.string().optional(),\n error_uri: z.string().optional(),\n});\n\ntype OAuthErrorResponse = z.infer<typeof OAuthErrorResponse>;\n\nfunction processOAuthErrorResponse(\n json: unknown,\n): OAuthErrorResponse | TypeError {\n try {\n return OAuthErrorResponse.parse(json);\n } catch (error) {\n if (error instanceof z.ZodError) {\n return new TypeError(`Invalid OAuth error response: ${error.message}`);\n }\n return new TypeError(\"Failed to parse OAuth error response\");\n }\n}\n\nclass OAuthError extends Error {\n name = \"OAuthError\";\n code: OAuthErrorResponse[\"error\"];\n cause: Error;\n constructor(message: string, response: unknown) {\n super(message);\n const error = processOAuthErrorResponse(response);\n if (error instanceof TypeError) {\n this.cause = new Error(\n \"Unexpected response from the Vercel authorization server.\",\n );\n this.code = \"server_error\";\n return;\n }\n let cause = error.error;\n if (error.error_description) cause += `: ${error.error_description}`;\n if (error.error_uri) cause += ` (${error.error_uri})`;\n\n this.cause = new Error(cause);\n this.code = error.error;\n }\n}\n\nexport function isOAuthError(error: unknown): error is OAuthError {\n return error instanceof OAuthError;\n}\n\nexport interface DeviceAuthorizationRequest {\n /** The device verification code. */\n device_code: string;\n /** The end-user verification code. */\n user_code: string;\n /**\n * The minimum amount of time in seconds that the client\n * SHOULD wait between polling requests to the token endpoint.\n */\n interval: number;\n /** The end-user verification URI on the authorization server. */\n verification_uri: string;\n /**\n * The end-user verification URI on the authorization server,\n * including the `user_code`, without redirection.\n */\n verification_uri_complete: string;\n /**\n * The absolute lifetime of the `device_code` and `user_code`.\n * Calculated from `expires_in`.\n */\n expiresAt: number;\n}\n"],"mappings":"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"} |
@@ -15,3 +15,3 @@ const require_rolldown_runtime = require('../_virtual/rolldown_runtime.cjs'); | ||
| slug: zod.z.string(), | ||
| updatedAt: zod.z.number(), | ||
| updatedAt: zod.z.number().optional(), | ||
| membership: zod.z.object({ role: zod.z.string() }), | ||
@@ -21,3 +21,6 @@ billing: zod.z.object({ plan: zod.z.string() }) | ||
| const TeamsSchema = zod.z.object({ | ||
| teams: zod.z.array(TeamSchema), | ||
| teams: zod.z.array(zod.z.unknown()).transform((entries) => entries.flatMap((entry) => { | ||
| const parsed = TeamSchema.safeParse(entry); | ||
| return parsed.success ? [parsed.data] : []; | ||
| })), | ||
| pagination: zod.z.object({ | ||
@@ -100,3 +103,3 @@ count: zod.z.number(), | ||
| if (hobbyOwnerTeams.length === 0) continue; | ||
| const bestHobbyTeam = hobbyOwnerTeams.find((t) => t.slug === username) ?? hobbyOwnerTeams.sort((a, b) => b.updatedAt - a.updatedAt)[0]; | ||
| const bestHobbyTeam = hobbyOwnerTeams.find((t) => t.slug === username) ?? hobbyOwnerTeams.sort((a, b) => (b.updatedAt ?? 0) - (a.updatedAt ?? 0))[0]; | ||
| if (bestHobbyTeam && bestHobbyTeam.id !== defaultTeamId) try { | ||
@@ -121,3 +124,3 @@ return { | ||
| statusCode: 403, | ||
| responseText: `Authenticated as "${username}" but none of the available teams allow sandbox creation. Specify a team explicitly with --scope <team-id-or-slug>.` | ||
| responseText: `Authenticated as "${username}" but none of the available teams allow sandbox creation.` | ||
| }); | ||
@@ -124,0 +127,0 @@ } |
@@ -1,1 +0,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":";;;;;;;AAKA,MAAM,aAAaA,MAAE,OAAO,EAC1B,MAAMA,MAAE,OAAO;CACb,eAAeA,MAAE,QAAQ,CAAC,UAAU;CACpC,UAAUA,MAAE,QAAQ;CACrB,CAAC,EACH,CAAC;AAEF,MAAM,aAAaA,MAAE,OAAO;CAC1B,IAAIA,MAAE,QAAQ;CACd,MAAMA,MAAE,QAAQ;CAChB,WAAWA,MAAE,QAAQ;CACrB,YAAYA,MAAE,OAAO,EACnB,MAAMA,MAAE,QAAQ,EACjB,CAAC;CACF,SAASA,MAAE,OAAO,EAChB,MAAMA,MAAE,QAAQ,EACjB,CAAC;CACH,CAAC;AAEF,MAAM,cAAcA,MAAE,OAAO;CAC3B,OAAOA,MAAE,MAAM,WAAW;CAC1B,YAAYA,MAAE,OAAO;EACnB,OAAOA,MAAE,QAAQ;EACjB,MAAMA,MAAE,QAAQ,CAAC,UAAU;EAC5B,CAAC;CACH,CAAC;AAEF,MAAM,uBAAuB;;AAG7B,SAAS,qBAAqB,GAAqB;AACjD,QAAO,aAAaC,wBAAU,EAAE,SAAS,eAAe,OAAO,EAAE,SAAS,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;AA4B3F,eAAsB,WAAW,MAU9B;CACD,MAAM,gBAAgB,MAAMC,yCAAkB,KAAK,OAAO,QAAQ,KAAK,CAAC;AACxE,KAAI,eAAe;EACjB,MAAM,QAAQ,MAAM,0BAClB,KAAK,OACL,cAAc,QACd,cAAc,UACf;AACD,SAAO;GAAE,GAAG;GAAe,SAAS;GAAO,GAAG;GAAO;;AAGvD,KAAI,KAAK,OACP,QAAO,QAAQ,KAAK,OAAO,KAAK,OAAO;CAOzC,MAAM,EAAE,eAAe,cAJN,MAAMC,qBAAS;EAC9B,OAAO,KAAK;EACZ,UAAU;EACX,CAAC,CAAC,KAAK,WAAW,MAAM,EACoB;AAG7C,KAAI,cACF,KAAI;EACF,MAAM,SAAS,MAAM,QAAQ,KAAK,OAAO,cAAc;AAEvD,MAAI;GACF,MAAM,OAAO,MAAMA,qBAAS;IAC1B,OAAO,KAAK;IACZ,UAAU,aAAa,mBAAmB,cAAc;IACzD,CAAC,CAAC,KAAKH,MAAE,OAAO,EAAE,MAAMA,MAAE,QAAQ,EAAE,CAAC,CAAC,MAAM;AAC7C,UAAO;IAAE,GAAG;IAAQ,UAAU,KAAK;IAAM;UACnC;AACN,UAAO;;UAEF,GAAG;AACV,MAAI,CAAC,qBAAqB,EAAE,CAAE,OAAM;;CAKxC,IAAII,OAAsB;AAC1B,IAAG;EACD,MAAMC,WACJ,SAAS,OACL,uBACA,4BAA4B;EAClC,MAAM,OAAO,MAAMF,qBAAS;GAAE,OAAO,KAAK;GAAO;GAAU,CAAC,CAAC,KAC3D,YAAY,MACb;AAED,SAAO,KAAK,WAAW;EAEvB,MAAM,kBAAkB,KAAK,MAAM,QAChC,MAAM,EAAE,WAAW,SAAS,WAAW,EAAE,QAAQ,SAAS,QAC5D;AACD,MAAI,gBAAgB,WAAW,EAC7B;EAGF,MAAM,gBACJ,gBAAgB,MAAM,MAAM,EAAE,SAAS,SAAS,IAChD,gBAAgB,MAAM,GAAG,MAAM,EAAE,YAAY,EAAE,UAAU,CAAC;AAE5D,MAAI,iBAAiB,cAAc,OAAO,cACxC,KAAI;AAEF,UAAO;IAAE,GADM,MAAM,QAAQ,KAAK,OAAO,cAAc,GAAG;IACtC,UAAU,cAAc;IAAM;WAC3C,GAAG;AACV,OAAI,CAAC,qBAAqB,EAAE,CAAE,OAAM;;UAGjC,SAAS;AAGlB,KAAI;AAEF,SAAO;GAAE,GADM,MAAM,QAAQ,KAAK,OAAO,SAAS;GAC9B,UAAU;GAAU;UACjC,GAAG;AACV,MAAI,CAAC,qBAAqB,EAAE,CAAE,OAAM;;AAGtC,OAAM,IAAIF,oBAAM;EACd,YAAY;EACZ,cAAc,qBAAqB,SAAS;EAC7C,CAAC;;;;;;;;;AAUJ,eAAe,QACb,OACA,QACkE;CAClE,MAAM,YAAY,OAAO,WAAW,QAAQ,GACxC,UAAU,mBAAmB,OAAO,KACpC,QAAQ,mBAAmB,OAAO;CAEtC,IAAI,UAAU;AACd,KAAI;AACF,QAAME,qBAAS;GACb;GACA,UAAU,gBAAgB,mBAAmB,qBAAqB,CAAC,GAAG;GACvE,CAAC;UACK,GAAG;AACV,MAAI,EAAE,aAAaF,wBAAU,EAAE,SAAS,eAAe,IACrD,OAAM;AAGR,QAAME,qBAAS;GACb;GACA,UAAU,iBAAiB;GAC3B,QAAQ;GACR,MAAM,KAAK,UAAU,EACnB,MAAM,sBACP,CAAC;GACH,CAAC;AACF,YAAU;;AAGZ,QAAO;EAAE,WAAW;EAAsB;EAAQ;EAAS;;;;;;;AAQ7D,eAAe,0BACb,OACA,QACA,WACsD;AACtD,KAAI;EACF,MAAM,YAAY,OAAO,WAAW,QAAQ,GACxC,UAAU,mBAAmB,OAAO,KACpC,QAAQ,mBAAmB,OAAO;EACtC,MAAM,CAAC,UAAU,eAAe,MAAM,QAAQ,IAAI,CAChDA,qBAAS;GACP;GACA,UAAU,aAAa,mBAAmB,OAAO;GAClD,CAAC,CAAC,KAAKH,MAAE,OAAO,EAAE,MAAMA,MAAE,QAAQ,EAAE,CAAC,CAAC,MAAM,EAC7CG,qBAAS;GACP;GACA,UAAU,gBAAgB,mBAAmB,UAAU,CAAC,GAAG;GAC5D,CAAC,CAAC,KAAKH,MAAE,OAAO,EAAE,MAAMA,MAAE,QAAQ,EAAE,CAAC,CAAC,MAAM,CAC9C,CAAC;AACF,SAAO;GAAE,UAAU,SAAS;GAAM,aAAa,YAAY;GAAM;SAC3D;AACN,SAAO,EAAE"} | ||
| {"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().optional(),\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 // Parse each team independently and drop any that don't validate, so a single\n // malformed/limited team entry can never break candidate selection for the\n // whole page.\n teams: z.array(z.unknown()).transform((entries) =>\n entries.flatMap((entry) => {\n const parsed = TeamSchema.safeParse(entry);\n return parsed.success ? [parsed.data] : [];\n }),\n ),\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 ?? 0) - (a.updatedAt ?? 0))[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.`,\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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"} |
@@ -14,3 +14,3 @@ import { NotOk } from "./error.js"; | ||
| slug: z.string(), | ||
| updatedAt: z.number(), | ||
| updatedAt: z.number().optional(), | ||
| membership: z.object({ role: z.string() }), | ||
@@ -20,3 +20,6 @@ billing: z.object({ plan: z.string() }) | ||
| const TeamsSchema = z.object({ | ||
| teams: z.array(TeamSchema), | ||
| teams: z.array(z.unknown()).transform((entries) => entries.flatMap((entry) => { | ||
| const parsed = TeamSchema.safeParse(entry); | ||
| return parsed.success ? [parsed.data] : []; | ||
| })), | ||
| pagination: z.object({ | ||
@@ -99,3 +102,3 @@ count: z.number(), | ||
| if (hobbyOwnerTeams.length === 0) continue; | ||
| const bestHobbyTeam = hobbyOwnerTeams.find((t) => t.slug === username) ?? hobbyOwnerTeams.sort((a, b) => b.updatedAt - a.updatedAt)[0]; | ||
| const bestHobbyTeam = hobbyOwnerTeams.find((t) => t.slug === username) ?? hobbyOwnerTeams.sort((a, b) => (b.updatedAt ?? 0) - (a.updatedAt ?? 0))[0]; | ||
| if (bestHobbyTeam && bestHobbyTeam.id !== defaultTeamId) try { | ||
@@ -120,3 +123,3 @@ return { | ||
| statusCode: 403, | ||
| responseText: `Authenticated as "${username}" but none of the available teams allow sandbox creation. Specify a team explicitly with --scope <team-id-or-slug>.` | ||
| responseText: `Authenticated as "${username}" but none of the available teams allow sandbox creation.` | ||
| }); | ||
@@ -123,0 +126,0 @@ } |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"project.js","names":["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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"} | ||
| {"version":3,"file":"project.js","names":["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().optional(),\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 // Parse each team independently and drop any that don't validate, so a single\n // malformed/limited team entry can never break candidate selection for the\n // whole page.\n teams: z.array(z.unknown()).transform((entries) =>\n entries.flatMap((entry) => {\n const parsed = TeamSchema.safeParse(entry);\n return parsed.success ? [parsed.data] : [];\n }),\n ),\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 ?? 0) - (a.updatedAt ?? 0))[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.`,\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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"} |
@@ -378,3 +378,3 @@ import { Paginator } from "./utils/paginator.cjs"; | ||
| currentSessionId: string; | ||
| status: "pending" | "running" | "stopping" | "stopped" | "failed" | "aborted" | "snapshotting"; | ||
| status: "failed" | "aborted" | "pending" | "running" | "stopping" | "stopped" | "snapshotting"; | ||
| region?: string | undefined; | ||
@@ -915,3 +915,3 @@ vcpus?: number | undefined; | ||
| timeout: number; | ||
| status: "pending" | "running" | "stopping" | "stopped" | "failed" | "aborted" | "snapshotting"; | ||
| status: "failed" | "aborted" | "pending" | "running" | "stopping" | "stopped" | "snapshotting"; | ||
| requestedAt: number; | ||
@@ -1042,3 +1042,3 @@ createdAt: number; | ||
| region: string; | ||
| status: "failed" | "created" | "deleted"; | ||
| status: "created" | "deleted" | "failed"; | ||
| sizeBytes: number; | ||
@@ -1045,0 +1045,0 @@ createdAt: number; |
@@ -379,3 +379,3 @@ import { Paginator } from "./utils/paginator.js"; | ||
| currentSessionId: string; | ||
| status: "pending" | "running" | "stopping" | "stopped" | "failed" | "aborted" | "snapshotting"; | ||
| status: "failed" | "aborted" | "pending" | "running" | "stopping" | "stopped" | "snapshotting"; | ||
| region?: string | undefined; | ||
@@ -916,3 +916,3 @@ vcpus?: number | undefined; | ||
| timeout: number; | ||
| status: "pending" | "running" | "stopping" | "stopped" | "failed" | "aborted" | "snapshotting"; | ||
| status: "failed" | "aborted" | "pending" | "running" | "stopping" | "stopped" | "snapshotting"; | ||
| requestedAt: number; | ||
@@ -1043,3 +1043,3 @@ createdAt: number; | ||
| region: string; | ||
| status: "failed" | "created" | "deleted"; | ||
| status: "created" | "deleted" | "failed"; | ||
| sizeBytes: number; | ||
@@ -1046,0 +1046,0 @@ createdAt: number; |
@@ -112,3 +112,3 @@ import { Paginator } from "./utils/paginator.cjs"; | ||
| region: string; | ||
| status: "failed" | "created" | "deleted"; | ||
| status: "created" | "deleted" | "failed"; | ||
| sizeBytes: number; | ||
@@ -151,3 +151,3 @@ createdAt: number; | ||
| region: string; | ||
| status: "failed" | "created" | "deleted"; | ||
| status: "created" | "deleted" | "failed"; | ||
| sizeBytes: number; | ||
@@ -165,3 +165,3 @@ createdAt: number; | ||
| region: string; | ||
| status: "failed" | "created" | "deleted"; | ||
| status: "created" | "deleted" | "failed"; | ||
| sizeBytes: number; | ||
@@ -186,3 +186,3 @@ createdAt: number; | ||
| region: string; | ||
| status: "failed" | "created" | "deleted"; | ||
| status: "created" | "deleted" | "failed"; | ||
| sizeBytes: number; | ||
@@ -200,3 +200,3 @@ createdAt: number; | ||
| region: string; | ||
| status: "failed" | "created" | "deleted"; | ||
| status: "created" | "deleted" | "failed"; | ||
| sizeBytes: number; | ||
@@ -203,0 +203,0 @@ createdAt: number; |
@@ -113,3 +113,3 @@ import { Paginator } from "./utils/paginator.js"; | ||
| region: string; | ||
| status: "failed" | "created" | "deleted"; | ||
| status: "created" | "deleted" | "failed"; | ||
| sizeBytes: number; | ||
@@ -152,3 +152,3 @@ createdAt: number; | ||
| region: string; | ||
| status: "failed" | "created" | "deleted"; | ||
| status: "created" | "deleted" | "failed"; | ||
| sizeBytes: number; | ||
@@ -166,3 +166,3 @@ createdAt: number; | ||
| region: string; | ||
| status: "failed" | "created" | "deleted"; | ||
| status: "created" | "deleted" | "failed"; | ||
| sizeBytes: number; | ||
@@ -187,3 +187,3 @@ createdAt: number; | ||
| region: string; | ||
| status: "failed" | "created" | "deleted"; | ||
| status: "created" | "deleted" | "failed"; | ||
| sizeBytes: number; | ||
@@ -201,3 +201,3 @@ createdAt: number; | ||
| region: string; | ||
| status: "failed" | "created" | "deleted"; | ||
| status: "created" | "deleted" | "failed"; | ||
| sizeBytes: number; | ||
@@ -204,0 +204,0 @@ createdAt: number; |
+1
-1
| //#region src/version.ts | ||
| const VERSION = "2.2.0"; | ||
| const VERSION = "2.2.1"; | ||
@@ -5,0 +5,0 @@ //#endregion |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"version.cjs","names":[],"sources":["../src/version.ts"],"sourcesContent":["// Autogenerated by inject-version.ts\nexport const VERSION = \"2.2.0\";\n"],"mappings":";;AACA,MAAa,UAAU"} | ||
| {"version":3,"file":"version.cjs","names":[],"sources":["../src/version.ts"],"sourcesContent":["// Autogenerated by inject-version.ts\nexport const VERSION = \"2.2.1\";\n"],"mappings":";;AACA,MAAa,UAAU"} |
+1
-1
| //#region src/version.ts | ||
| const VERSION = "2.2.0"; | ||
| const VERSION = "2.2.1"; | ||
@@ -4,0 +4,0 @@ //#endregion |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"version.js","names":[],"sources":["../src/version.ts"],"sourcesContent":["// Autogenerated by inject-version.ts\nexport const VERSION = \"2.2.0\";\n"],"mappings":";AACA,MAAa,UAAU"} | ||
| {"version":3,"file":"version.js","names":[],"sources":["../src/version.ts"],"sourcesContent":["// Autogenerated by inject-version.ts\nexport const VERSION = \"2.2.1\";\n"],"mappings":";AACA,MAAa,UAAU"} |
+1
-1
| { | ||
| "name": "@vercel/sandbox", | ||
| "version": "2.2.0", | ||
| "version": "2.2.1", | ||
| "description": "Software Development Kit for Vercel Sandbox", | ||
@@ -5,0 +5,0 @@ "type": "module", |
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
Found 1 instance in 1 package
1259069
0.08%14673
0.03%68
-1.45%