@caido/server-auth 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","names":["Client","fetchExchange"],"sources":["../src/errors.ts","../src/queries.ts","../src/client.ts","../src/approvers/browser.ts","../src/approvers/pat.ts"],"sourcesContent":["/**\n * Base error class for authentication-related errors.\n */\nexport class AuthenticationError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"AuthenticationError\";\n }\n}\n\n/**\n * Error thrown when the authentication flow fails to start.\n */\nexport class AuthenticationFlowError extends AuthenticationError {\n /** Error code from the API */\n readonly code: string;\n\n constructor(code: string, message: string) {\n super(`${code}: ${message}`);\n this.name = \"AuthenticationFlowError\";\n this.code = code;\n }\n}\n\n/**\n * Error thrown when token refresh fails.\n */\nexport class TokenRefreshError extends AuthenticationError {\n /** Error code from the API */\n readonly code: string;\n\n constructor(code: string, message: string) {\n super(`${code}: ${message}`);\n this.name = \"TokenRefreshError\";\n this.code = code;\n }\n}\n\n/**\n * Error thrown when device approval fails.\n */\nexport class DeviceApprovalError extends AuthenticationError {\n /** HTTP status code if available */\n readonly statusCode: number | undefined;\n\n constructor(message: string, statusCode?: number) {\n super(message);\n this.name = \"DeviceApprovalError\";\n this.statusCode = statusCode;\n }\n}\n\n/**\n * Error thrown when fetching device information fails.\n */\nexport class DeviceInformationError extends AuthenticationError {\n /** HTTP status code if available */\n readonly statusCode: number | undefined;\n\n constructor(message: string, statusCode?: number) {\n super(message);\n this.name = \"DeviceInformationError\";\n this.statusCode = statusCode;\n }\n}\n","import type { DocumentNode } from \"graphql\";\nimport { gql } from \"graphql-tag\";\n\nexport const START_AUTHENTICATION_FLOW: DocumentNode = gql`\n mutation StartAuthenticationFlow {\n startAuthenticationFlow {\n request {\n id\n userCode\n verificationUrl\n expiresAt\n }\n error {\n code\n message\n }\n }\n }\n`;\n\nexport const CREATED_AUTHENTICATION_TOKEN: DocumentNode = gql`\n subscription CreatedAuthenticationToken($requestId: ID!) {\n createdAuthenticationToken(requestId: $requestId) {\n token {\n accessToken\n refreshToken\n expiresAt\n }\n error {\n code\n message\n }\n }\n }\n`;\n\nexport const REFRESH_AUTHENTICATION_TOKEN: DocumentNode = gql`\n mutation RefreshAuthenticationToken($refreshToken: String!) {\n refreshAuthenticationToken(refreshToken: $refreshToken) {\n token {\n accessToken\n refreshToken\n expiresAt\n }\n error {\n code\n message\n }\n }\n }\n`;\n","import { Client, fetchExchange } from \"@urql/core\";\nimport { createClient as createWSClient } from \"graphql-ws\";\n\nimport type { AuthApprover } from \"./approvers/types.js\";\nimport {\n AuthenticationError,\n AuthenticationFlowError,\n TokenRefreshError,\n} from \"./errors.js\";\nimport {\n CREATED_AUTHENTICATION_TOKEN,\n REFRESH_AUTHENTICATION_TOKEN,\n START_AUTHENTICATION_FLOW,\n} from \"./queries.js\";\nimport type {\n AuthenticationRequest,\n AuthenticationToken,\n CreatedAuthenticationTokenResponse,\n RefreshAuthenticationTokenResponse,\n StartAuthenticationFlowResponse,\n} from \"./types.js\";\n\n/**\n * Client for authenticating with a Caido instance.\n *\n * @example\n * ```typescript\n * import { CaidoAuth, BrowserApprover } from \"@caido/auth\";\n *\n * const auth = new CaidoAuth(\n * \"http://localhost:8080\",\n * new BrowserApprover((request) => {\n * console.log(`Visit ${request.verificationUrl}`);\n * })\n * );\n *\n * const token = await auth.startAuthenticationFlow();\n * console.log(\"Access token:\", token.accessToken);\n * ```\n */\nexport class CaidoAuth {\n private readonly instanceUrl: string;\n private readonly graphqlUrl: string;\n private readonly websocketUrl: string;\n private readonly approver: AuthApprover;\n private readonly client: Client;\n\n /**\n * Create a new CaidoAuth client.\n *\n * @param instanceUrl - Base URL of the Caido instance (e.g., \"http://localhost:8080\")\n * @param approver - The approver to use for the authentication flow\n */\n constructor(instanceUrl: string, approver: AuthApprover) {\n this.instanceUrl = instanceUrl.replace(/\\/$/, \"\");\n this.graphqlUrl = `${this.instanceUrl}/graphql`;\n this.websocketUrl = this.getWebsocketUrl();\n this.approver = approver;\n\n this.client = new Client({\n url: this.graphqlUrl,\n exchanges: [fetchExchange],\n });\n }\n\n /**\n * Convert HTTP(S) URL to WS(S) URL for subscriptions.\n */\n private getWebsocketUrl(): string {\n const url = new URL(this.graphqlUrl);\n const scheme = url.protocol === \"https:\" ? \"wss:\" : \"ws:\";\n return `${scheme}//${url.host}/ws/graphql`;\n }\n\n /**\n * Start the device code authentication flow.\n *\n * This method:\n * 1. Initiates the authentication flow via GraphQL mutation\n * 2. Calls the approver with the authentication request\n * 3. Waits for the user to authorize via WebSocket subscription\n * 4. Returns the authentication token once approved\n *\n * @returns The authentication token\n * @throws {AuthenticationFlowError} If the flow fails to start\n * @throws {AuthenticationError} If token retrieval fails\n */\n async startAuthenticationFlow(): Promise<AuthenticationToken> {\n // Step 1: Start the authentication flow\n const result = await this.client\n .mutation<StartAuthenticationFlowResponse>(START_AUTHENTICATION_FLOW, {})\n .toPromise();\n\n if (result.error) {\n throw new AuthenticationFlowError(\"GRAPHQL_ERROR\", result.error.message);\n }\n\n const payload = result.data?.startAuthenticationFlow;\n if (!payload) {\n throw new AuthenticationFlowError(\n \"NO_RESPONSE\",\n \"No response from startAuthenticationFlow\",\n );\n }\n\n if (payload.error) {\n throw new AuthenticationFlowError(\n payload.error.code,\n payload.error.message,\n );\n }\n\n if (!payload.request) {\n throw new AuthenticationFlowError(\n \"NO_REQUEST\",\n \"No authentication request returned\",\n );\n }\n\n const authRequest: AuthenticationRequest = {\n id: payload.request.id,\n userCode: payload.request.userCode,\n verificationUrl: payload.request.verificationUrl,\n expiresAt: new Date(payload.request.expiresAt),\n };\n\n // Step 2: Call the approver\n await this.approver.approve(authRequest);\n\n // Step 3: Wait for the token via subscription\n const token = await this.waitForToken(authRequest.id);\n return token;\n }\n\n /**\n * Subscribe and wait for the authentication token.\n *\n * @param requestId - The authentication request ID\n * @returns The authentication token once the user authorizes\n * @throws {AuthenticationError} If subscription fails or returns an error\n */\n private async waitForToken(requestId: string): Promise<AuthenticationToken> {\n return new Promise<AuthenticationToken>((resolve, reject) => {\n const wsClient = createWSClient({\n url: this.websocketUrl,\n });\n\n const unsubscribe =\n wsClient.subscribe<CreatedAuthenticationTokenResponse>(\n {\n query:\n CREATED_AUTHENTICATION_TOKEN.loc?.source.body ??\n `subscription CreatedAuthenticationToken($requestId: ID!) {\n createdAuthenticationToken(requestId: $requestId) {\n token { accessToken refreshToken expiresAt }\n error { code message }\n }\n }`,\n variables: { requestId },\n },\n {\n next: (result) => {\n const payload = result.data?.createdAuthenticationToken;\n\n if (payload?.error) {\n unsubscribe();\n wsClient.dispose();\n reject(\n new AuthenticationError(\n `${payload.error.code}: ${payload.error.message}`,\n ),\n );\n return;\n }\n\n if (payload?.token) {\n unsubscribe();\n wsClient.dispose();\n resolve({\n accessToken: payload.token.accessToken,\n refreshToken: payload.token.refreshToken,\n expiresAt: new Date(payload.token.expiresAt),\n });\n }\n },\n error: (error) => {\n wsClient.dispose();\n reject(\n new AuthenticationError(\n error instanceof Error ? error.message : String(error),\n ),\n );\n },\n complete: () => {\n wsClient.dispose();\n reject(\n new AuthenticationError(\n \"Subscription ended without receiving token\",\n ),\n );\n },\n },\n );\n });\n }\n\n /**\n * Refresh an access token using a refresh token.\n *\n * @param refreshToken - The refresh token from a previous authentication\n * @returns New authentication token with updated access and refresh tokens\n * @throws {TokenRefreshError} If the refresh fails\n */\n async refreshToken(refreshToken: string): Promise<AuthenticationToken> {\n const result = await this.client\n .mutation<RefreshAuthenticationTokenResponse>(\n REFRESH_AUTHENTICATION_TOKEN,\n { refreshToken },\n )\n .toPromise();\n\n if (result.error) {\n throw new TokenRefreshError(\"GRAPHQL_ERROR\", result.error.message);\n }\n\n const payload = result.data?.refreshAuthenticationToken;\n if (!payload) {\n throw new TokenRefreshError(\n \"NO_RESPONSE\",\n \"No response from refreshAuthenticationToken\",\n );\n }\n\n if (payload.error) {\n throw new TokenRefreshError(payload.error.code, payload.error.message);\n }\n\n if (!payload.token) {\n throw new TokenRefreshError(\"NO_TOKEN\", \"No token returned from refresh\");\n }\n\n return {\n accessToken: payload.token.accessToken,\n refreshToken: payload.token.refreshToken,\n expiresAt: new Date(payload.token.expiresAt),\n };\n }\n}\n","import type { AuthenticationRequest } from \"../types.js\";\n\nimport type { AuthApprover } from \"./types.js\";\n\n/**\n * Callback function that receives the authentication request details.\n * Used to display the verification URL and user code to the user.\n */\nexport type OnRequestCallback = (\n request: AuthenticationRequest,\n) => Promise<void> | void;\n\n/**\n * Browser-based approver that delegates to a callback function.\n * The callback should display the verification URL and user code to the user,\n * who then manually approves the request in their browser.\n *\n * @example\n * ```typescript\n * const approver = new BrowserApprover((request) => {\n * console.log(`Visit ${request.verificationUrl}`);\n * });\n * ```\n */\nexport class BrowserApprover implements AuthApprover {\n private readonly onRequest: OnRequestCallback;\n\n /**\n * Create a new BrowserApprover.\n *\n * @param onRequest - Callback function that will be called with the authentication request\n */\n constructor(onRequest: OnRequestCallback) {\n this.onRequest = onRequest;\n }\n\n /**\n * Approve the authentication request by calling the callback.\n * The actual approval happens when the user visits the URL and enters the code.\n *\n * @param request - The authentication request\n */\n async approve(request: AuthenticationRequest): Promise<void> {\n await this.onRequest(request);\n }\n}\n","import { DeviceApprovalError, DeviceInformationError } from \"../errors.js\";\nimport type { AuthenticationRequest, DeviceInformation } from \"../types.js\";\n\nimport type { AuthApprover } from \"./types.js\";\n\nconst DEFAULT_API_URL = \"https://api.caido.io\";\n\n/**\n * Options for the PATApprover.\n */\nexport interface PATApproverOptions {\n /** The Personal Access Token to use for approval */\n pat: string;\n /** If provided, only approve these scopes. Others will be filtered out. */\n allowedScopes?: string[];\n /** The API URL to use. Defaults to \"https://api.caido.io\" */\n apiUrl?: string;\n}\n\n/**\n * PAT-based approver that automatically approves device code requests.\n * Uses a Personal Access Token to call the Caido API directly.\n *\n * @example\n * ```typescript\n * // Approve all scopes\n * const approver = new PATApprover({ pat: \"caido_xxxxx\" });\n *\n * // Approve only specific scopes\n * const limitedApprover = new PATApprover({\n * pat: \"caido_xxxxx\",\n * allowedScopes: [\"read:projects\", \"write:requests\"],\n * });\n * ```\n */\nexport class PATApprover implements AuthApprover {\n private readonly pat: string;\n private readonly allowedScopes: string[] | undefined;\n private readonly apiUrl: string;\n\n /**\n * Create a new PATApprover.\n *\n * @param options - Configuration options for the approver\n */\n constructor(options: PATApproverOptions) {\n this.pat = options.pat;\n this.allowedScopes = options.allowedScopes;\n this.apiUrl = (options.apiUrl ?? DEFAULT_API_URL).replace(/\\/$/, \"\");\n }\n\n /**\n * Approve the authentication request using the PAT.\n * First fetches device information to get available scopes,\n * then filters scopes if allowedScopes is set,\n * and finally approves the device.\n *\n * @param request - The authentication request\n * @throws {DeviceInformationError} If fetching device information fails\n * @throws {DeviceApprovalError} If approving the device fails\n */\n async approve(request: AuthenticationRequest): Promise<void> {\n // Step 1: Get device information to retrieve available scopes\n const deviceInfo = await this.getDeviceInformation(request.userCode);\n\n // Step 2: Filter scopes if allowedScopes is provided\n let scopesToApprove = deviceInfo.scopes.map((s) => s.name);\n if (this.allowedScopes) {\n scopesToApprove = scopesToApprove.filter((scope) =>\n this.allowedScopes!.includes(scope),\n );\n }\n\n // Step 3: Approve the device with the filtered scopes\n await this.approveDevice(request.userCode, scopesToApprove);\n }\n\n /**\n * Fetch device information from the API.\n *\n * @param userCode - The user code from the authentication request\n * @returns The device information including available scopes\n * @throws {DeviceInformationError} If the request fails\n */\n private async getDeviceInformation(\n userCode: string,\n ): Promise<DeviceInformation> {\n const params = new URLSearchParams();\n params.append(\"user_code\", userCode);\n const url = new URL(`${this.apiUrl}/oauth2/device/information`);\n url.search = params.toString();\n\n const response = await fetch(url, {\n method: \"GET\",\n headers: {\n Authorization: `Bearer ${this.pat}`,\n Accept: \"application/json\",\n },\n });\n\n if (!response.ok) {\n const errorText = await response.text().catch(() => \"Unknown error\");\n throw new DeviceInformationError(\n `Failed to get device information: ${errorText}`,\n response.status,\n );\n }\n\n const data = (await response.json()) as DeviceInformation;\n return data;\n }\n\n /**\n * Approve the device with the specified scopes.\n *\n * @param userCode - The user code from the authentication request\n * @param scopes - The scopes to approve\n * @throws {DeviceApprovalError} If the request fails\n */\n private async approveDevice(\n userCode: string,\n scopes: string[],\n ): Promise<void> {\n const params = new URLSearchParams();\n params.append(\"user_code\", userCode);\n params.append(\"scope\", scopes.join(\",\"));\n const url = new URL(`${this.apiUrl}/oauth2/device/approve`);\n url.search = params.toString();\n\n const response = await fetch(url, {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${this.pat}`,\n \"Content-Type\": \"application/json\",\n Accept: \"application/json\",\n },\n });\n\n if (!response.ok) {\n const errorText = await response.text().catch(() => \"Unknown error\");\n throw new DeviceApprovalError(\n `Failed to approve device: ${errorText}`,\n response.status,\n );\n }\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.cjs","names":["Client","fetchExchange"],"sources":["../src/errors.ts","../src/queries.ts","../src/client.ts","../src/approvers/browser.ts","../src/approvers/pat.ts"],"sourcesContent":["/**\n * Base error class for authentication-related errors.\n */\nexport class AuthenticationError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"AuthenticationError\";\n }\n}\n\n/**\n * Error thrown for errors coming from the Caido cloud API.\n * Used for device approval and device information operations.\n */\nexport class CloudError extends AuthenticationError {\n /** HTTP status code if available */\n readonly statusCode: number | undefined;\n /** Error code from the API if available */\n readonly code: string | undefined;\n /** Reason for the error if available */\n readonly reason: string | undefined;\n\n constructor(\n message: string,\n options?: {\n statusCode?: number;\n code?: string;\n reason?: string;\n },\n ) {\n super(message);\n this.name = \"CloudError\";\n this.statusCode = options?.statusCode;\n this.code = options?.code;\n this.reason = options?.reason;\n }\n}\n\n/**\n * Error thrown for errors coming from the Caido instance.\n * Used for authentication flow and token refresh operations.\n */\nexport class InstanceError extends AuthenticationError {\n /** Error code from the API */\n readonly code: string;\n /** Reason for the error if available */\n readonly reason: string | undefined;\n /** Error message if available */\n readonly errorMessage: string | undefined;\n\n constructor(\n code: string,\n options?: {\n reason?: string;\n message?: string;\n },\n ) {\n const message = options?.reason ?? options?.message ?? code;\n super(message);\n this.name = \"InstanceError\";\n this.code = code;\n this.reason = options?.reason;\n this.errorMessage = options?.message;\n }\n}\n","import type { DocumentNode } from \"graphql\";\nimport { gql } from \"graphql-tag\";\n\nexport const START_AUTHENTICATION_FLOW: DocumentNode = gql`\n mutation StartAuthenticationFlow {\n startAuthenticationFlow {\n request {\n id\n userCode\n verificationUrl\n expiresAt\n }\n error {\n ... on AuthenticationUserError {\n code\n reason\n }\n ... on CloudUserError {\n code\n reason\n }\n ... on InternalUserError {\n code\n message\n }\n ... on OtherUserError {\n code\n }\n }\n }\n }\n`;\n\nexport const CREATED_AUTHENTICATION_TOKEN: DocumentNode = gql`\n subscription CreatedAuthenticationToken($requestId: ID!) {\n createdAuthenticationToken(requestId: $requestId) {\n token {\n accessToken\n expiresAt\n refreshToken\n scopes\n }\n error {\n ... on AuthenticationUserError {\n code\n reason\n }\n ... on InternalUserError {\n code\n message\n }\n ... on OtherUserError {\n code\n }\n }\n }\n }\n`;\n\nexport const REFRESH_AUTHENTICATION_TOKEN: DocumentNode = gql`\n mutation RefreshAuthenticationToken($refreshToken: Token!) {\n refreshAuthenticationToken(refreshToken: $refreshToken) {\n token {\n accessToken\n expiresAt\n refreshToken\n scopes\n }\n error {\n ... on AuthenticationUserError {\n code\n reason\n }\n ... on CloudUserError {\n code\n reason\n }\n ... on InternalUserError {\n code\n message\n }\n ... on OtherUserError {\n code\n }\n }\n }\n }\n`;\n","import { Client, fetchExchange } from \"@urql/core\";\nimport { print } from \"graphql\";\nimport { createClient as createWSClient } from \"graphql-ws\";\n\nimport type { AuthApprover } from \"./approvers/types.js\";\nimport { AuthenticationError, InstanceError } from \"./errors.js\";\nimport {\n CREATED_AUTHENTICATION_TOKEN,\n REFRESH_AUTHENTICATION_TOKEN,\n START_AUTHENTICATION_FLOW,\n} from \"./queries.js\";\nimport type {\n AuthenticationRequest,\n AuthenticationToken,\n CreatedAuthenticationTokenError,\n CreatedAuthenticationTokenResponse,\n RefreshAuthenticationTokenError,\n RefreshAuthenticationTokenResponse,\n StartAuthenticationFlowError,\n StartAuthenticationFlowResponse,\n} from \"./types.js\";\n\n/**\n * Options for configuring the AuthClient.\n */\nexport interface AuthClientOptions {\n /** Base URL of the Caido instance (e.g., \"http://localhost:8080\") */\n instanceUrl: string;\n /** The approver to use for the authentication flow */\n approver: AuthApprover;\n /** Request timeout in milliseconds */\n timeout?: number;\n /** Custom fetch implementation */\n fetch?: typeof globalThis.fetch;\n}\n\ninterface ErrorDetails {\n reason?: string;\n message?: string;\n}\n\n/**\n * Client for authenticating with a Caido instance.\n *\n * @example\n * ```typescript\n * import { AuthClient, BrowserApprover } from \"@caido/auth\";\n *\n * const auth = new AuthClient({\n * instanceUrl: \"http://localhost:8080\",\n * approver: new BrowserApprover((request) => {\n * console.log(`Visit ${request.verificationUrl}`);\n * })\n * });\n *\n * const token = await auth.startAuthenticationFlow();\n * console.log(\"Access token:\", token.accessToken);\n * ```\n */\nexport class AuthClient {\n private readonly instanceUrl: string;\n private readonly graphqlUrl: string;\n private readonly websocketUrl: string;\n private readonly approver: AuthApprover;\n private readonly client: Client;\n private readonly fetchFn: typeof globalThis.fetch | undefined;\n private readonly timeout: number | undefined;\n\n constructor(options: AuthClientOptions) {\n this.instanceUrl = options.instanceUrl.replace(/\\/$/, \"\");\n this.graphqlUrl = `${this.instanceUrl}/graphql`;\n this.websocketUrl = this.getWebsocketUrl();\n this.approver = options.approver;\n this.fetchFn = options.fetch;\n this.timeout = options.timeout;\n\n this.client = new Client({\n url: this.graphqlUrl,\n exchanges: [fetchExchange],\n fetchOptions: () => {\n const fetchOptions: RequestInit = {};\n if (this.timeout !== undefined) {\n fetchOptions.signal = AbortSignal.timeout(this.timeout);\n }\n return fetchOptions;\n },\n fetch: this.fetchFn,\n });\n }\n\n private getWebsocketUrl(): string {\n const url = new URL(this.graphqlUrl);\n const scheme = url.protocol === \"https:\" ? \"wss:\" : \"ws:\";\n return `${scheme}//${url.host}/ws/graphql`;\n }\n\n private extractErrorDetails(\n error:\n | StartAuthenticationFlowError\n | CreatedAuthenticationTokenError\n | RefreshAuthenticationTokenError,\n ): ErrorDetails {\n if (\"reason\" in error) {\n return { reason: error.reason };\n }\n if (\"message\" in error) {\n return { message: error.message };\n }\n return {};\n }\n\n /**\n * Start the device code authentication flow.\n *\n * This method:\n * 1. Initiates the authentication flow via GraphQL mutation\n * 2. Calls the approver with the authentication request\n * 3. Waits for the user to authorize via WebSocket subscription\n * 4. Returns the authentication token once approved\n *\n * @returns The authentication token\n * @throws {InstanceError} If the flow fails to start\n * @throws {AuthenticationError} If token retrieval fails\n */\n async startAuthenticationFlow(): Promise<AuthenticationToken> {\n // Step 1: Start the authentication flow\n const result = await this.client\n .mutation<StartAuthenticationFlowResponse>(START_AUTHENTICATION_FLOW, {})\n .toPromise();\n\n if (result.error) {\n throw new InstanceError(\"GRAPHQL_ERROR\", {\n message: result.error.message,\n });\n }\n\n const payload = result.data?.startAuthenticationFlow;\n if (!payload) {\n throw new InstanceError(\"NO_RESPONSE\", {\n message: \"No response from startAuthenticationFlow\",\n });\n }\n\n if (payload.error) {\n const details = this.extractErrorDetails(payload.error);\n throw new InstanceError(payload.error.code, details);\n }\n\n if (!payload.request) {\n throw new InstanceError(\"NO_REQUEST\", {\n message: \"No authentication request returned\",\n });\n }\n\n const authRequest: AuthenticationRequest = {\n id: payload.request.id,\n userCode: payload.request.userCode,\n verificationUrl: payload.request.verificationUrl,\n expiresAt: new Date(payload.request.expiresAt),\n };\n\n // Step 2: Call the approver\n await this.approver.approve(authRequest);\n\n // Step 3: Wait for the token via subscription\n const token = await this.waitForToken(authRequest.id);\n return token;\n }\n\n private async waitForToken(requestId: string): Promise<AuthenticationToken> {\n return new Promise<AuthenticationToken>((resolve, reject) => {\n const wsClient = createWSClient({\n url: this.websocketUrl,\n });\n\n const unsubscribe =\n wsClient.subscribe<CreatedAuthenticationTokenResponse>(\n {\n query: print(CREATED_AUTHENTICATION_TOKEN),\n variables: { requestId },\n },\n {\n next: (result) => {\n const payload = result.data?.createdAuthenticationToken;\n\n if (payload?.error) {\n unsubscribe();\n wsClient.dispose();\n const details = this.extractErrorDetails(payload.error);\n reject(new InstanceError(payload.error.code, details));\n return;\n }\n\n if (payload?.token) {\n unsubscribe();\n wsClient.dispose();\n resolve({\n accessToken: payload.token.accessToken,\n refreshToken: payload.token.refreshToken,\n expiresAt: new Date(payload.token.expiresAt),\n scopes: payload.token.scopes,\n });\n }\n },\n error: (error) => {\n wsClient.dispose();\n reject(\n new InstanceError(\"SUBSCRIPTION_ERROR\", {\n message:\n error instanceof Error ? error.message : String(error),\n }),\n );\n },\n complete: () => {\n wsClient.dispose();\n reject(\n new InstanceError(\"SUBSCRIPTION_COMPLETE\", {\n message: \"Subscription ended without receiving token\",\n }),\n );\n },\n },\n );\n });\n }\n\n /**\n * Refresh an access token using a refresh token.\n *\n * @param refreshToken - The refresh token from a previous authentication\n * @returns New authentication token with updated access and refresh tokens\n * @throws {InstanceError} If the refresh fails\n */\n async refreshToken(refreshToken: string): Promise<AuthenticationToken> {\n const result = await this.client\n .mutation<RefreshAuthenticationTokenResponse>(\n REFRESH_AUTHENTICATION_TOKEN,\n { refreshToken },\n )\n .toPromise();\n\n if (result.error) {\n throw new InstanceError(\"GRAPHQL_ERROR\", {\n message: result.error.message,\n });\n }\n\n const payload = result.data?.refreshAuthenticationToken;\n if (!payload) {\n throw new InstanceError(\"NO_RESPONSE\", {\n message: \"No response from refreshAuthenticationToken\",\n });\n }\n\n if (payload.error) {\n const details = this.extractErrorDetails(payload.error);\n throw new InstanceError(payload.error.code, details);\n }\n\n if (!payload.token) {\n throw new InstanceError(\"NO_TOKEN\", {\n message: \"No token returned from refresh\",\n });\n }\n\n return {\n accessToken: payload.token.accessToken,\n refreshToken: payload.token.refreshToken,\n expiresAt: new Date(payload.token.expiresAt),\n scopes: payload.token.scopes,\n };\n }\n}\n","import type { AuthenticationRequest } from \"../types.js\";\n\nimport type { AuthApprover } from \"./types.js\";\n\n/**\n * Callback function that receives the authentication request details.\n * Used to display the verification URL and user code to the user.\n */\nexport type OnRequestCallback = (\n request: AuthenticationRequest,\n) => Promise<void> | void;\n\n/**\n * Browser-based approver that delegates to a callback function.\n * The callback should display the verification URL and user code to the user,\n * who then manually approves the request in their browser.\n *\n * @example\n * ```typescript\n * const approver = new BrowserApprover((request) => {\n * console.log(`Visit ${request.verificationUrl}`);\n * });\n * ```\n */\nexport class BrowserApprover implements AuthApprover {\n private readonly onRequest: OnRequestCallback;\n\n /**\n * Create a new BrowserApprover.\n *\n * @param onRequest - Callback function that will be called with the authentication request\n */\n constructor(onRequest: OnRequestCallback) {\n this.onRequest = onRequest;\n }\n\n /**\n * Approve the authentication request by calling the callback.\n * The actual approval happens when the user visits the URL and enters the code.\n *\n * @param request - The authentication request\n */\n async approve(request: AuthenticationRequest): Promise<void> {\n await this.onRequest(request);\n }\n}\n","import { CloudError } from \"../errors.js\";\nimport type { AuthenticationRequest, DeviceInformation } from \"../types.js\";\n\nimport type { AuthApprover } from \"./types.js\";\n\nconst DEFAULT_API_URL = \"https://api.caido.io\";\n\ninterface OAuth2ErrorResponse {\n error?: string;\n error_description?: string;\n}\n\ntype OAuth2ErrorData = OAuth2ErrorResponse | string;\n\ninterface ParsedOAuth2Error {\n errorText: string;\n errorCode: string | undefined;\n errorDescription: string | undefined;\n}\n\ninterface RequestOptions {\n method: string;\n headers: Record<string, string>;\n}\n\n/**\n * Options for the PATApprover.\n */\nexport interface PATApproverOptions {\n /** The Personal Access Token to use for approval */\n pat: string;\n /** If provided, only approve these scopes. Others will be filtered out. */\n allowedScopes?: string[];\n /** The API URL to use. Defaults to \"https://api.caido.io\" */\n apiUrl?: string;\n /** Request timeout in milliseconds */\n timeout?: number;\n /** Custom fetch implementation */\n fetch?: typeof globalThis.fetch;\n}\n\n/**\n * PAT-based approver that automatically approves device code requests.\n * Uses a Personal Access Token to call the Caido API directly.\n *\n * @example\n * ```typescript\n * // Approve all scopes\n * const approver = new PATApprover({ pat: \"caido_xxxxx\" });\n *\n * // Approve only specific scopes\n * const limitedApprover = new PATApprover({\n * pat: \"caido_xxxxx\",\n * allowedScopes: [\"read:projects\", \"write:requests\"],\n * });\n * ```\n */\nexport class PATApprover implements AuthApprover {\n private readonly pat: string;\n private readonly allowedScopes: string[] | undefined;\n private readonly apiUrl: string;\n private readonly fetchFn: typeof globalThis.fetch;\n private readonly timeout: number | undefined;\n\n constructor(options: PATApproverOptions) {\n this.pat = options.pat;\n this.allowedScopes = options.allowedScopes;\n this.apiUrl = (options.apiUrl ?? DEFAULT_API_URL).replace(/\\/$/, \"\");\n this.fetchFn = options.fetch ?? globalThis.fetch;\n this.timeout = options.timeout;\n }\n\n /**\n * Approve the authentication request using the PAT.\n * First fetches device information to get available scopes,\n * then filters scopes if allowedScopes is set,\n * and finally approves the device.\n *\n * @param request - The authentication request\n * @throws {CloudError} If fetching device information or approving the device fails\n */\n async approve(request: AuthenticationRequest): Promise<void> {\n // Step 1: Get device information to retrieve available scopes\n const deviceInfo = await this.getDeviceInformation(request.userCode);\n\n // Step 2: Filter scopes if allowedScopes is provided\n let scopesToApprove = deviceInfo.scopes.map((s) => s.name);\n if (this.allowedScopes) {\n scopesToApprove = scopesToApprove.filter((scope) =>\n this.allowedScopes!.includes(scope),\n );\n }\n\n // Step 3: Approve the device with the filtered scopes\n await this.approveDevice(request.userCode, scopesToApprove);\n }\n\n private async sendRequest(\n url: URL,\n options: RequestOptions,\n ): Promise<Response> {\n const fetchOptions: RequestInit = {\n method: options.method,\n headers: options.headers,\n };\n\n if (this.timeout !== undefined) {\n fetchOptions.signal = AbortSignal.timeout(this.timeout);\n }\n\n return this.fetchFn(url, fetchOptions);\n }\n\n private async parseOAuth2Error(\n response: Response,\n ): Promise<ParsedOAuth2Error> {\n let errorText: string;\n let errorCode: string | undefined;\n let errorDescription: string | undefined;\n\n try {\n const errorData = (await response.json()) as OAuth2ErrorData;\n if (typeof errorData === \"string\") {\n errorText = errorData;\n } else {\n errorCode = errorData.error;\n errorDescription = errorData.error_description;\n errorText = errorDescription ?? errorCode ?? \"Unknown error\";\n }\n } catch {\n errorText = await response.text().catch(() => \"Unknown error\");\n }\n\n return { errorText, errorCode, errorDescription };\n }\n\n private async getDeviceInformation(\n userCode: string,\n ): Promise<DeviceInformation> {\n const params = new URLSearchParams();\n params.append(\"user_code\", userCode);\n const url = new URL(`${this.apiUrl}/oauth2/device/information`);\n url.search = params.toString();\n\n const response = await this.sendRequest(url, {\n method: \"GET\",\n headers: {\n Authorization: `Bearer ${this.pat}`,\n Accept: \"application/json\",\n },\n });\n\n if (!response.ok) {\n const { errorText, errorCode, errorDescription } =\n await this.parseOAuth2Error(response);\n\n throw new CloudError(`Failed to get device information: ${errorText}`, {\n statusCode: response.status,\n code: errorCode,\n reason: errorDescription,\n });\n }\n\n const data = (await response.json()) as DeviceInformation;\n return data;\n }\n\n private async approveDevice(\n userCode: string,\n scopes: string[],\n ): Promise<void> {\n const params = new URLSearchParams();\n params.append(\"user_code\", userCode);\n params.append(\"scope\", scopes.join(\",\"));\n const url = new URL(`${this.apiUrl}/oauth2/device/approve`);\n url.search = params.toString();\n\n const response = await this.sendRequest(url, {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${this.pat}`,\n \"Content-Type\": \"application/json\",\n Accept: \"application/json\",\n },\n });\n\n if (!response.ok) {\n const { errorText, errorCode, errorDescription } =\n await this.parseOAuth2Error(response);\n\n throw new CloudError(`Failed to approve device: ${errorText}`, {\n statusCode: response.status,\n code: errorCode,\n reason: errorDescription,\n });\n }\n }\n}\n"],"mappings":";;;;;;;;;AAGA,IAAa,sBAAb,cAAyC,MAAM;CAC7C,YAAY,SAAiB;AAC3B,QAAM,QAAQ;AACd,OAAK,OAAO;;;;;;;AAQhB,IAAa,aAAb,cAAgC,oBAAoB;;CAElD,AAAS;;CAET,AAAS;;CAET,AAAS;CAET,YACE,SACA,SAKA;AACA,QAAM,QAAQ;AACd,OAAK,OAAO;AACZ,OAAK,aAAa,SAAS;AAC3B,OAAK,OAAO,SAAS;AACrB,OAAK,SAAS,SAAS;;;;;;;AAQ3B,IAAa,gBAAb,cAAmC,oBAAoB;;CAErD,AAAS;;CAET,AAAS;;CAET,AAAS;CAET,YACE,MACA,SAIA;EACA,MAAM,UAAU,SAAS,UAAU,SAAS,WAAW;AACvD,QAAM,QAAQ;AACd,OAAK,OAAO;AACZ,OAAK,OAAO;AACZ,OAAK,SAAS,SAAS;AACvB,OAAK,eAAe,SAAS;;;;;;AC3DjC,MAAa,4BAA0C,eAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA8B1D,MAAa,+BAA6C,eAAG;;;;;;;;;;;;;;;;;;;;;;;;;AA0B7D,MAAa,+BAA6C,eAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACA7D,IAAa,aAAb,MAAwB;CACtB,AAAiB;CACjB,AAAiB;CACjB,AAAiB;CACjB,AAAiB;CACjB,AAAiB;CACjB,AAAiB;CACjB,AAAiB;CAEjB,YAAY,SAA4B;AACtC,OAAK,cAAc,QAAQ,YAAY,QAAQ,OAAO,GAAG;AACzD,OAAK,aAAa,GAAG,KAAK,YAAY;AACtC,OAAK,eAAe,KAAK,iBAAiB;AAC1C,OAAK,WAAW,QAAQ;AACxB,OAAK,UAAU,QAAQ;AACvB,OAAK,UAAU,QAAQ;AAEvB,OAAK,SAAS,IAAIA,kBAAO;GACvB,KAAK,KAAK;GACV,WAAW,CAACC,yBAAc;GAC1B,oBAAoB;IAClB,MAAM,eAA4B,EAAE;AACpC,QAAI,KAAK,YAAY,OACnB,cAAa,SAAS,YAAY,QAAQ,KAAK,QAAQ;AAEzD,WAAO;;GAET,OAAO,KAAK;GACb,CAAC;;CAGJ,AAAQ,kBAA0B;EAChC,MAAM,MAAM,IAAI,IAAI,KAAK,WAAW;AAEpC,SAAO,GADQ,IAAI,aAAa,WAAW,SAAS,MACnC,IAAI,IAAI,KAAK;;CAGhC,AAAQ,oBACN,OAIc;AACd,MAAI,YAAY,MACd,QAAO,EAAE,QAAQ,MAAM,QAAQ;AAEjC,MAAI,aAAa,MACf,QAAO,EAAE,SAAS,MAAM,SAAS;AAEnC,SAAO,EAAE;;;;;;;;;;;;;;;CAgBX,MAAM,0BAAwD;EAE5D,MAAM,SAAS,MAAM,KAAK,OACvB,SAA0C,2BAA2B,EAAE,CAAC,CACxE,WAAW;AAEd,MAAI,OAAO,MACT,OAAM,IAAI,cAAc,iBAAiB,EACvC,SAAS,OAAO,MAAM,SACvB,CAAC;EAGJ,MAAM,UAAU,OAAO,MAAM;AAC7B,MAAI,CAAC,QACH,OAAM,IAAI,cAAc,eAAe,EACrC,SAAS,4CACV,CAAC;AAGJ,MAAI,QAAQ,OAAO;GACjB,MAAM,UAAU,KAAK,oBAAoB,QAAQ,MAAM;AACvD,SAAM,IAAI,cAAc,QAAQ,MAAM,MAAM,QAAQ;;AAGtD,MAAI,CAAC,QAAQ,QACX,OAAM,IAAI,cAAc,cAAc,EACpC,SAAS,sCACV,CAAC;EAGJ,MAAM,cAAqC;GACzC,IAAI,QAAQ,QAAQ;GACpB,UAAU,QAAQ,QAAQ;GAC1B,iBAAiB,QAAQ,QAAQ;GACjC,WAAW,IAAI,KAAK,QAAQ,QAAQ,UAAU;GAC/C;AAGD,QAAM,KAAK,SAAS,QAAQ,YAAY;AAIxC,SADc,MAAM,KAAK,aAAa,YAAY,GAAG;;CAIvD,MAAc,aAAa,WAAiD;AAC1E,SAAO,IAAI,SAA8B,SAAS,WAAW;GAC3D,MAAM,wCAA0B,EAC9B,KAAK,KAAK,cACX,CAAC;GAEF,MAAM,cACJ,SAAS,UACP;IACE,0BAAa,6BAA6B;IAC1C,WAAW,EAAE,WAAW;IACzB,EACD;IACE,OAAO,WAAW;KAChB,MAAM,UAAU,OAAO,MAAM;AAE7B,SAAI,SAAS,OAAO;AAClB,mBAAa;AACb,eAAS,SAAS;MAClB,MAAM,UAAU,KAAK,oBAAoB,QAAQ,MAAM;AACvD,aAAO,IAAI,cAAc,QAAQ,MAAM,MAAM,QAAQ,CAAC;AACtD;;AAGF,SAAI,SAAS,OAAO;AAClB,mBAAa;AACb,eAAS,SAAS;AAClB,cAAQ;OACN,aAAa,QAAQ,MAAM;OAC3B,cAAc,QAAQ,MAAM;OAC5B,WAAW,IAAI,KAAK,QAAQ,MAAM,UAAU;OAC5C,QAAQ,QAAQ,MAAM;OACvB,CAAC;;;IAGN,QAAQ,UAAU;AAChB,cAAS,SAAS;AAClB,YACE,IAAI,cAAc,sBAAsB,EACtC,SACE,iBAAiB,QAAQ,MAAM,UAAU,OAAO,MAAM,EACzD,CAAC,CACH;;IAEH,gBAAgB;AACd,cAAS,SAAS;AAClB,YACE,IAAI,cAAc,yBAAyB,EACzC,SAAS,8CACV,CAAC,CACH;;IAEJ,CACF;IACH;;;;;;;;;CAUJ,MAAM,aAAa,cAAoD;EACrE,MAAM,SAAS,MAAM,KAAK,OACvB,SACC,8BACA,EAAE,cAAc,CACjB,CACA,WAAW;AAEd,MAAI,OAAO,MACT,OAAM,IAAI,cAAc,iBAAiB,EACvC,SAAS,OAAO,MAAM,SACvB,CAAC;EAGJ,MAAM,UAAU,OAAO,MAAM;AAC7B,MAAI,CAAC,QACH,OAAM,IAAI,cAAc,eAAe,EACrC,SAAS,+CACV,CAAC;AAGJ,MAAI,QAAQ,OAAO;GACjB,MAAM,UAAU,KAAK,oBAAoB,QAAQ,MAAM;AACvD,SAAM,IAAI,cAAc,QAAQ,MAAM,MAAM,QAAQ;;AAGtD,MAAI,CAAC,QAAQ,MACX,OAAM,IAAI,cAAc,YAAY,EAClC,SAAS,kCACV,CAAC;AAGJ,SAAO;GACL,aAAa,QAAQ,MAAM;GAC3B,cAAc,QAAQ,MAAM;GAC5B,WAAW,IAAI,KAAK,QAAQ,MAAM,UAAU;GAC5C,QAAQ,QAAQ,MAAM;GACvB;;;;;;;;;;;;;;;;;;ACtPL,IAAa,kBAAb,MAAqD;CACnD,AAAiB;;;;;;CAOjB,YAAY,WAA8B;AACxC,OAAK,YAAY;;;;;;;;CASnB,MAAM,QAAQ,SAA+C;AAC3D,QAAM,KAAK,UAAU,QAAQ;;;;;;ACtCjC,MAAM,kBAAkB;;;;;;;;;;;;;;;;;AAoDxB,IAAa,cAAb,MAAiD;CAC/C,AAAiB;CACjB,AAAiB;CACjB,AAAiB;CACjB,AAAiB;CACjB,AAAiB;CAEjB,YAAY,SAA6B;AACvC,OAAK,MAAM,QAAQ;AACnB,OAAK,gBAAgB,QAAQ;AAC7B,OAAK,UAAU,QAAQ,UAAU,iBAAiB,QAAQ,OAAO,GAAG;AACpE,OAAK,UAAU,QAAQ,SAAS,WAAW;AAC3C,OAAK,UAAU,QAAQ;;;;;;;;;;;CAYzB,MAAM,QAAQ,SAA+C;EAK3D,IAAI,mBAHe,MAAM,KAAK,qBAAqB,QAAQ,SAAS,EAGnC,OAAO,KAAK,MAAM,EAAE,KAAK;AAC1D,MAAI,KAAK,cACP,mBAAkB,gBAAgB,QAAQ,UACxC,KAAK,cAAe,SAAS,MAAM,CACpC;AAIH,QAAM,KAAK,cAAc,QAAQ,UAAU,gBAAgB;;CAG7D,MAAc,YACZ,KACA,SACmB;EACnB,MAAM,eAA4B;GAChC,QAAQ,QAAQ;GAChB,SAAS,QAAQ;GAClB;AAED,MAAI,KAAK,YAAY,OACnB,cAAa,SAAS,YAAY,QAAQ,KAAK,QAAQ;AAGzD,SAAO,KAAK,QAAQ,KAAK,aAAa;;CAGxC,MAAc,iBACZ,UAC4B;EAC5B,IAAI;EACJ,IAAI;EACJ,IAAI;AAEJ,MAAI;GACF,MAAM,YAAa,MAAM,SAAS,MAAM;AACxC,OAAI,OAAO,cAAc,SACvB,aAAY;QACP;AACL,gBAAY,UAAU;AACtB,uBAAmB,UAAU;AAC7B,gBAAY,oBAAoB,aAAa;;UAEzC;AACN,eAAY,MAAM,SAAS,MAAM,CAAC,YAAY,gBAAgB;;AAGhE,SAAO;GAAE;GAAW;GAAW;GAAkB;;CAGnD,MAAc,qBACZ,UAC4B;EAC5B,MAAM,SAAS,IAAI,iBAAiB;AACpC,SAAO,OAAO,aAAa,SAAS;EACpC,MAAM,MAAM,IAAI,IAAI,GAAG,KAAK,OAAO,4BAA4B;AAC/D,MAAI,SAAS,OAAO,UAAU;EAE9B,MAAM,WAAW,MAAM,KAAK,YAAY,KAAK;GAC3C,QAAQ;GACR,SAAS;IACP,eAAe,UAAU,KAAK;IAC9B,QAAQ;IACT;GACF,CAAC;AAEF,MAAI,CAAC,SAAS,IAAI;GAChB,MAAM,EAAE,WAAW,WAAW,qBAC5B,MAAM,KAAK,iBAAiB,SAAS;AAEvC,SAAM,IAAI,WAAW,qCAAqC,aAAa;IACrE,YAAY,SAAS;IACrB,MAAM;IACN,QAAQ;IACT,CAAC;;AAIJ,SADc,MAAM,SAAS,MAAM;;CAIrC,MAAc,cACZ,UACA,QACe;EACf,MAAM,SAAS,IAAI,iBAAiB;AACpC,SAAO,OAAO,aAAa,SAAS;AACpC,SAAO,OAAO,SAAS,OAAO,KAAK,IAAI,CAAC;EACxC,MAAM,MAAM,IAAI,IAAI,GAAG,KAAK,OAAO,wBAAwB;AAC3D,MAAI,SAAS,OAAO,UAAU;EAE9B,MAAM,WAAW,MAAM,KAAK,YAAY,KAAK;GAC3C,QAAQ;GACR,SAAS;IACP,eAAe,UAAU,KAAK;IAC9B,gBAAgB;IAChB,QAAQ;IACT;GACF,CAAC;AAEF,MAAI,CAAC,SAAS,IAAI;GAChB,MAAM,EAAE,WAAW,WAAW,qBAC5B,MAAM,KAAK,iBAAiB,SAAS;AAEvC,SAAM,IAAI,WAAW,6BAA6B,aAAa;IAC7D,YAAY,SAAS;IACrB,MAAM;IACN,QAAQ;IACT,CAAC"}
package/dist/index.d.cts CHANGED
@@ -23,6 +23,8 @@ interface AuthenticationToken {
23
23
  refreshToken: string;
24
24
  /** When the access token expires */
25
25
  expiresAt: Date;
26
+ /** The scopes granted to this token */
27
+ scopes: string[];
26
28
  }
27
29
  /**
28
30
  * Scope information returned from device information endpoint.
@@ -61,40 +63,47 @@ interface AuthApprover {
61
63
  //#endregion
62
64
  //#region src/client.d.ts
63
65
  /**
66
+ * Options for configuring the AuthClient.
67
+ */
68
+ interface AuthClientOptions {
69
+ /** Base URL of the Caido instance (e.g., "http://localhost:8080") */
70
+ instanceUrl: string;
71
+ /** The approver to use for the authentication flow */
72
+ approver: AuthApprover;
73
+ /** Request timeout in milliseconds */
74
+ timeout?: number;
75
+ /** Custom fetch implementation */
76
+ fetch?: typeof globalThis.fetch;
77
+ }
78
+ /**
64
79
  * Client for authenticating with a Caido instance.
65
80
  *
66
81
  * @example
67
82
  * ```typescript
68
- * import { CaidoAuth, BrowserApprover } from "@caido/auth";
83
+ * import { AuthClient, BrowserApprover } from "@caido/auth";
69
84
  *
70
- * const auth = new CaidoAuth(
71
- * "http://localhost:8080",
72
- * new BrowserApprover((request) => {
85
+ * const auth = new AuthClient({
86
+ * instanceUrl: "http://localhost:8080",
87
+ * approver: new BrowserApprover((request) => {
73
88
  * console.log(`Visit ${request.verificationUrl}`);
74
89
  * })
75
- * );
90
+ * });
76
91
  *
77
92
  * const token = await auth.startAuthenticationFlow();
78
93
  * console.log("Access token:", token.accessToken);
79
94
  * ```
80
95
  */
81
- declare class CaidoAuth {
96
+ declare class AuthClient {
82
97
  private readonly instanceUrl;
83
98
  private readonly graphqlUrl;
84
99
  private readonly websocketUrl;
85
100
  private readonly approver;
86
101
  private readonly client;
87
- /**
88
- * Create a new CaidoAuth client.
89
- *
90
- * @param instanceUrl - Base URL of the Caido instance (e.g., "http://localhost:8080")
91
- * @param approver - The approver to use for the authentication flow
92
- */
93
- constructor(instanceUrl: string, approver: AuthApprover);
94
- /**
95
- * Convert HTTP(S) URL to WS(S) URL for subscriptions.
96
- */
102
+ private readonly fetchFn;
103
+ private readonly timeout;
104
+ constructor(options: AuthClientOptions);
97
105
  private getWebsocketUrl;
106
+ private extractErrorDetails;
98
107
  /**
99
108
  * Start the device code authentication flow.
100
109
  *
@@ -105,24 +114,17 @@ declare class CaidoAuth {
105
114
  * 4. Returns the authentication token once approved
106
115
  *
107
116
  * @returns The authentication token
108
- * @throws {AuthenticationFlowError} If the flow fails to start
117
+ * @throws {InstanceError} If the flow fails to start
109
118
  * @throws {AuthenticationError} If token retrieval fails
110
119
  */
111
120
  startAuthenticationFlow(): Promise<AuthenticationToken>;
112
- /**
113
- * Subscribe and wait for the authentication token.
114
- *
115
- * @param requestId - The authentication request ID
116
- * @returns The authentication token once the user authorizes
117
- * @throws {AuthenticationError} If subscription fails or returns an error
118
- */
119
121
  private waitForToken;
120
122
  /**
121
123
  * Refresh an access token using a refresh token.
122
124
  *
123
125
  * @param refreshToken - The refresh token from a previous authentication
124
126
  * @returns New authentication token with updated access and refresh tokens
125
- * @throws {TokenRefreshError} If the refresh fails
127
+ * @throws {InstanceError} If the refresh fails
126
128
  */
127
129
  refreshToken(refreshToken: string): Promise<AuthenticationToken>;
128
130
  }
@@ -135,36 +137,37 @@ declare class AuthenticationError extends Error {
135
137
  constructor(message: string);
136
138
  }
137
139
  /**
138
- * Error thrown when the authentication flow fails to start.
140
+ * Error thrown for errors coming from the Caido cloud API.
141
+ * Used for device approval and device information operations.
139
142
  */
140
- declare class AuthenticationFlowError extends AuthenticationError {
141
- /** Error code from the API */
142
- readonly code: string;
143
- constructor(code: string, message: string);
144
- }
145
- /**
146
- * Error thrown when token refresh fails.
147
- */
148
- declare class TokenRefreshError extends AuthenticationError {
149
- /** Error code from the API */
150
- readonly code: string;
151
- constructor(code: string, message: string);
152
- }
153
- /**
154
- * Error thrown when device approval fails.
155
- */
156
- declare class DeviceApprovalError extends AuthenticationError {
143
+ declare class CloudError extends AuthenticationError {
157
144
  /** HTTP status code if available */
158
145
  readonly statusCode: number | undefined;
159
- constructor(message: string, statusCode?: number);
146
+ /** Error code from the API if available */
147
+ readonly code: string | undefined;
148
+ /** Reason for the error if available */
149
+ readonly reason: string | undefined;
150
+ constructor(message: string, options?: {
151
+ statusCode?: number;
152
+ code?: string;
153
+ reason?: string;
154
+ });
160
155
  }
161
156
  /**
162
- * Error thrown when fetching device information fails.
157
+ * Error thrown for errors coming from the Caido instance.
158
+ * Used for authentication flow and token refresh operations.
163
159
  */
164
- declare class DeviceInformationError extends AuthenticationError {
165
- /** HTTP status code if available */
166
- readonly statusCode: number | undefined;
167
- constructor(message: string, statusCode?: number);
160
+ declare class InstanceError extends AuthenticationError {
161
+ /** Error code from the API */
162
+ readonly code: string;
163
+ /** Reason for the error if available */
164
+ readonly reason: string | undefined;
165
+ /** Error message if available */
166
+ readonly errorMessage: string | undefined;
167
+ constructor(code: string, options?: {
168
+ reason?: string;
169
+ message?: string;
170
+ });
168
171
  }
169
172
  //#endregion
170
173
  //#region src/approvers/browser.d.ts
@@ -213,6 +216,10 @@ interface PATApproverOptions {
213
216
  allowedScopes?: string[];
214
217
  /** The API URL to use. Defaults to "https://api.caido.io" */
215
218
  apiUrl?: string;
219
+ /** Request timeout in milliseconds */
220
+ timeout?: number;
221
+ /** Custom fetch implementation */
222
+ fetch?: typeof globalThis.fetch;
216
223
  }
217
224
  /**
218
225
  * PAT-based approver that automatically approves device code requests.
@@ -234,11 +241,8 @@ declare class PATApprover implements AuthApprover {
234
241
  private readonly pat;
235
242
  private readonly allowedScopes;
236
243
  private readonly apiUrl;
237
- /**
238
- * Create a new PATApprover.
239
- *
240
- * @param options - Configuration options for the approver
241
- */
244
+ private readonly fetchFn;
245
+ private readonly timeout;
242
246
  constructor(options: PATApproverOptions);
243
247
  /**
244
248
  * Approve the authentication request using the PAT.
@@ -247,27 +251,14 @@ declare class PATApprover implements AuthApprover {
247
251
  * and finally approves the device.
248
252
  *
249
253
  * @param request - The authentication request
250
- * @throws {DeviceInformationError} If fetching device information fails
251
- * @throws {DeviceApprovalError} If approving the device fails
254
+ * @throws {CloudError} If fetching device information or approving the device fails
252
255
  */
253
256
  approve(request: AuthenticationRequest): Promise<void>;
254
- /**
255
- * Fetch device information from the API.
256
- *
257
- * @param userCode - The user code from the authentication request
258
- * @returns The device information including available scopes
259
- * @throws {DeviceInformationError} If the request fails
260
- */
257
+ private sendRequest;
258
+ private parseOAuth2Error;
261
259
  private getDeviceInformation;
262
- /**
263
- * Approve the device with the specified scopes.
264
- *
265
- * @param userCode - The user code from the authentication request
266
- * @param scopes - The scopes to approve
267
- * @throws {DeviceApprovalError} If the request fails
268
- */
269
260
  private approveDevice;
270
261
  }
271
262
  //#endregion
272
- export { type AuthApprover, AuthenticationError, AuthenticationFlowError, type AuthenticationRequest, type AuthenticationToken, BrowserApprover, CaidoAuth, DeviceApprovalError, type DeviceInformation, DeviceInformationError, type DeviceScope, type OnRequestCallback, PATApprover, type PATApproverOptions, TokenRefreshError };
263
+ export { type AuthApprover, AuthClient, type AuthClientOptions, AuthenticationError, type AuthenticationRequest, type AuthenticationToken, BrowserApprover, CloudError, type DeviceInformation, type DeviceScope, InstanceError, type OnRequestCallback, PATApprover, type PATApproverOptions };
273
264
  //# sourceMappingURL=index.d.cts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.cts","names":[],"sources":["../src/types.ts","../src/approvers/types.ts","../src/client.ts","../src/errors.ts","../src/approvers/browser.ts","../src/approvers/pat.ts"],"mappings":";;AAIA;;;UAAiB,qBAAA;;EAEf,EAAA;;EAEA,QAAA;;EAEA,eAAA;EAEW;EAAX,SAAA,EAAW,IAAA;AAAA;;;;UAMI,mBAAA;;EAEf,WAAA;;EAEA,YAAA;EAQF;EANE,SAAA,EAAW,IAAA;AAAA;;;AAgBb;UAViB,WAAA;;EAEf,IAAA;;EAEA,WAAA;AAAA;;;;UAMe,iBAAA;;EAEf,QAAA;ECpCe;EDsCf,MAAA,EAAQ,WAAA;AAAA;;;AAxCV;;;;AAAA,UCEiB,YAAA;;;;;;;ADYjB;ECJE,OAAA,CAAQ,OAAA,EAAS,qBAAA,GAAwB,OAAA;AAAA;;;;;;;;;;;;;ADI3C;;;;;;;;cEsBa,SAAA;EAAA,iBACM,WAAA;EAAA,iBACA,UAAA;EAAA,iBACA,YAAA;EAAA,iBACA,QAAA;EAAA,iBACA,MAAA;EFXjB;AAMF;;;;;EEaE,WAAA,CAAY,WAAA,UAAqB,QAAA,EAAU,YAAA;;;;UAenC,eAAA;;;AD9DV;;;;;;;;;;;ECiFE,uBAAA,CAAA,GAAiC,OAAA,CAAQ,mBAAA;;AA/C3C;;;;;;UAqGgB,YAAA;;;;;;;;EAwEd,YAAA,CAAmB,YAAA,WAAuB,OAAA,CAAQ,mBAAA;AAAA;;;;AFjNpD;;cGDa,mBAAA,SAA4B,KAAA;EACvC,WAAA,CAAY,OAAA;AAAA;;;;cASD,uBAAA,SAAgC,mBAAA;;WAElC,IAAA;EAET,WAAA,CAAY,IAAA,UAAc,OAAA;AAAA;;;;cAUf,iBAAA,SAA0B,mBAAA;;WAE5B,IAAA;EAET,WAAA,CAAY,IAAA,UAAc,OAAA;AAAA;AHD5B;;;AAAA,cGWa,mBAAA,SAA4B,mBAAA;EHPvC;EAAA,SGSS,UAAA;EAET,WAAA,CAAY,OAAA,UAAiB,UAAA;AAAA;;;;cAUlB,sBAAA,SAA+B,mBAAA;;WAEjC,UAAA;EAET,WAAA,CAAY,OAAA,UAAiB,UAAA;AAAA;;;;;;;KCnDnB,iBAAA,IACV,OAAA,EAAS,qBAAA,KACN,OAAA;;;;;;AJQL;;;;;;;cIMa,eAAA,YAA2B,YAAA;EAAA,iBACrB,SAAA;EJDN;AAMb;;;;EIEE,WAAA,CAAY,SAAA,EAAW,iBAAA;EJQzB;;;;;;EIEE,OAAA,CAAc,OAAA,EAAS,qBAAA,GAAwB,OAAA;AAAA;;;;;;UChChC,kBAAA;;EAEf,GAAA;;EAEA,aAAA;;EAEA,MAAA;AAAA;;;;;;;;;;ALcF;;;;;AAUA;;cKLa,WAAA,YAAuB,YAAA;EAAA,iBACjB,GAAA;EAAA,iBACA,aAAA;EAAA,iBACA,MAAA;;;;;;EAOjB,WAAA,CAAY,OAAA,EAAS,kBAAA;EJvCvB;;;;;;;;;;EIuDE,OAAA,CAAc,OAAA,EAAS,qBAAA,GAAwB,OAAA;;;AHrBjD;;;;;UG4CgB,oBAAA;;;;;;;;UAmCA,aAAA;AAAA"}
1
+ {"version":3,"file":"index.d.cts","names":[],"sources":["../src/types.ts","../src/approvers/types.ts","../src/client.ts","../src/errors.ts","../src/approvers/browser.ts","../src/approvers/pat.ts"],"mappings":";;AAIA;;;UAAiB,qBAAA;;EAEf,EAAA;;EAEA,QAAA;;EAEA,eAAA;EAEW;EAAX,SAAA,EAAW,IAAA;AAAA;;;;UAMI,mBAAA;;EAEf,WAAA;;EAEA,YAAA;EAIA;EAFA,SAAA,EAAW,IAAA;EAQI;EANf,MAAA;AAAA;;AAgBF;;UAViB,WAAA;EAcP;EAZR,IAAA;;EAEA,WAAA;AAAA;;;;UAMe,iBAAA;ECpCjB;EDsCE,QAAA;;EAEA,MAAA,EAAQ,WAAA;AAAA;;;AA1CV;;;;AAAA,UCEiB,YAAA;;;;;;;ADYjB;ECJE,OAAA,CAAQ,OAAA,EAAS,qBAAA,GAAwB,OAAA;AAAA;;;;;;UCW1B,iBAAA;;EAEf,WAAA;;EAEA,QAAA,EAAU,YAAA;;EAEV,OAAA;EFbF;EEeE,KAAA,UAAe,UAAA,CAAW,KAAA;AAAA;;;;;;;;;AFD5B;;;;;AAUA;;;;;cEiBa,UAAA;EAAA,iBACM,WAAA;EAAA,iBACA,UAAA;EAAA,iBACA,YAAA;EAAA,iBACA,QAAA;EAAA,iBACA,MAAA;EAAA,iBACA,OAAA;EAAA,iBACA,OAAA;EAEjB,WAAA,CAAY,OAAA,EAAS,iBAAA;EAAA,QAsBb,eAAA;EAAA,QAMA,mBAAA;;;;;;;;;;AAvEV;;;;EAmGE,uBAAA,CAAA,GAAiC,OAAA,CAAQ,mBAAA;EAAA,QA6C3B,YAAA;;;;;;;;EAgEd,YAAA,CAAmB,YAAA,WAAuB,OAAA,CAAQ,mBAAA;AAAA;;;;AFrOpD;;cGDa,mBAAA,SAA4B,KAAA;EACvC,WAAA,CAAY,OAAA;AAAA;;;;;cAUD,UAAA,SAAmB,mBAAA;EHFnB;EAAA,SGIF,UAAA;EHEM;EAAA,SGAN,IAAA;EHME;EAAA,SGJF,MAAA;EAET,WAAA,CACE,OAAA,UACA,OAAA;IACE,UAAA;IACA,IAAA;IACA,MAAA;EAAA;AAAA;AHKN;;;;AAAA,cGUa,aAAA,SAAsB,mBAAA;EHAnC;EAAA,SGEW,IAAA;;WAEA,MAAA;;WAEA,YAAA;EAET,WAAA,CACE,IAAA,UACA,OAAA;IACE,MAAA;IACA,OAAA;EAAA;AAAA;;;;;;;KC9CM,iBAAA,IACV,OAAA,EAAS,qBAAA,KACN,OAAA;;;;;;AJQL;;;;;;;cIMa,eAAA,YAA2B,YAAA;EAAA,iBACrB,SAAA;;;AJOnB;;;EIAE,WAAA,CAAY,SAAA,EAAW,iBAAA;EJIvB;AAMF;;;;;EIAE,OAAA,CAAc,OAAA,EAAS,qBAAA,GAAwB,OAAA;AAAA;;;;;;UCdhC,kBAAA;;EAEf,GAAA;;EAEA,aAAA;;EAEA,MAAA;ELhBF;EKkBE,OAAA;;EAEA,KAAA,UAAe,UAAA,CAAW,KAAA;AAAA;;;;;;;ALN5B;;;;;AAUA;;;;;cKea,WAAA,YAAuB,YAAA;EAAA,iBACjB,GAAA;EAAA,iBACA,aAAA;EAAA,iBACA,MAAA;EAAA,iBACA,OAAA;EAAA,iBACA,OAAA;EAEjB,WAAA,CAAY,OAAA,EAAS,kBAAA;EJ1DvB;;;;;;;;;EI2EE,OAAA,CAAc,OAAA,EAAS,qBAAA,GAAwB,OAAA;EAAA,QAgBjC,WAAA;EAAA,QAgBA,gBAAA;EAAA,QAuBA,oBAAA;EAAA,QA+BA,aAAA;AAAA"}
package/dist/index.d.mts CHANGED
@@ -23,6 +23,8 @@ interface AuthenticationToken {
23
23
  refreshToken: string;
24
24
  /** When the access token expires */
25
25
  expiresAt: Date;
26
+ /** The scopes granted to this token */
27
+ scopes: string[];
26
28
  }
27
29
  /**
28
30
  * Scope information returned from device information endpoint.
@@ -61,40 +63,47 @@ interface AuthApprover {
61
63
  //#endregion
62
64
  //#region src/client.d.ts
63
65
  /**
66
+ * Options for configuring the AuthClient.
67
+ */
68
+ interface AuthClientOptions {
69
+ /** Base URL of the Caido instance (e.g., "http://localhost:8080") */
70
+ instanceUrl: string;
71
+ /** The approver to use for the authentication flow */
72
+ approver: AuthApprover;
73
+ /** Request timeout in milliseconds */
74
+ timeout?: number;
75
+ /** Custom fetch implementation */
76
+ fetch?: typeof globalThis.fetch;
77
+ }
78
+ /**
64
79
  * Client for authenticating with a Caido instance.
65
80
  *
66
81
  * @example
67
82
  * ```typescript
68
- * import { CaidoAuth, BrowserApprover } from "@caido/auth";
83
+ * import { AuthClient, BrowserApprover } from "@caido/auth";
69
84
  *
70
- * const auth = new CaidoAuth(
71
- * "http://localhost:8080",
72
- * new BrowserApprover((request) => {
85
+ * const auth = new AuthClient({
86
+ * instanceUrl: "http://localhost:8080",
87
+ * approver: new BrowserApprover((request) => {
73
88
  * console.log(`Visit ${request.verificationUrl}`);
74
89
  * })
75
- * );
90
+ * });
76
91
  *
77
92
  * const token = await auth.startAuthenticationFlow();
78
93
  * console.log("Access token:", token.accessToken);
79
94
  * ```
80
95
  */
81
- declare class CaidoAuth {
96
+ declare class AuthClient {
82
97
  private readonly instanceUrl;
83
98
  private readonly graphqlUrl;
84
99
  private readonly websocketUrl;
85
100
  private readonly approver;
86
101
  private readonly client;
87
- /**
88
- * Create a new CaidoAuth client.
89
- *
90
- * @param instanceUrl - Base URL of the Caido instance (e.g., "http://localhost:8080")
91
- * @param approver - The approver to use for the authentication flow
92
- */
93
- constructor(instanceUrl: string, approver: AuthApprover);
94
- /**
95
- * Convert HTTP(S) URL to WS(S) URL for subscriptions.
96
- */
102
+ private readonly fetchFn;
103
+ private readonly timeout;
104
+ constructor(options: AuthClientOptions);
97
105
  private getWebsocketUrl;
106
+ private extractErrorDetails;
98
107
  /**
99
108
  * Start the device code authentication flow.
100
109
  *
@@ -105,24 +114,17 @@ declare class CaidoAuth {
105
114
  * 4. Returns the authentication token once approved
106
115
  *
107
116
  * @returns The authentication token
108
- * @throws {AuthenticationFlowError} If the flow fails to start
117
+ * @throws {InstanceError} If the flow fails to start
109
118
  * @throws {AuthenticationError} If token retrieval fails
110
119
  */
111
120
  startAuthenticationFlow(): Promise<AuthenticationToken>;
112
- /**
113
- * Subscribe and wait for the authentication token.
114
- *
115
- * @param requestId - The authentication request ID
116
- * @returns The authentication token once the user authorizes
117
- * @throws {AuthenticationError} If subscription fails or returns an error
118
- */
119
121
  private waitForToken;
120
122
  /**
121
123
  * Refresh an access token using a refresh token.
122
124
  *
123
125
  * @param refreshToken - The refresh token from a previous authentication
124
126
  * @returns New authentication token with updated access and refresh tokens
125
- * @throws {TokenRefreshError} If the refresh fails
127
+ * @throws {InstanceError} If the refresh fails
126
128
  */
127
129
  refreshToken(refreshToken: string): Promise<AuthenticationToken>;
128
130
  }
@@ -135,36 +137,37 @@ declare class AuthenticationError extends Error {
135
137
  constructor(message: string);
136
138
  }
137
139
  /**
138
- * Error thrown when the authentication flow fails to start.
140
+ * Error thrown for errors coming from the Caido cloud API.
141
+ * Used for device approval and device information operations.
139
142
  */
140
- declare class AuthenticationFlowError extends AuthenticationError {
141
- /** Error code from the API */
142
- readonly code: string;
143
- constructor(code: string, message: string);
144
- }
145
- /**
146
- * Error thrown when token refresh fails.
147
- */
148
- declare class TokenRefreshError extends AuthenticationError {
149
- /** Error code from the API */
150
- readonly code: string;
151
- constructor(code: string, message: string);
152
- }
153
- /**
154
- * Error thrown when device approval fails.
155
- */
156
- declare class DeviceApprovalError extends AuthenticationError {
143
+ declare class CloudError extends AuthenticationError {
157
144
  /** HTTP status code if available */
158
145
  readonly statusCode: number | undefined;
159
- constructor(message: string, statusCode?: number);
146
+ /** Error code from the API if available */
147
+ readonly code: string | undefined;
148
+ /** Reason for the error if available */
149
+ readonly reason: string | undefined;
150
+ constructor(message: string, options?: {
151
+ statusCode?: number;
152
+ code?: string;
153
+ reason?: string;
154
+ });
160
155
  }
161
156
  /**
162
- * Error thrown when fetching device information fails.
157
+ * Error thrown for errors coming from the Caido instance.
158
+ * Used for authentication flow and token refresh operations.
163
159
  */
164
- declare class DeviceInformationError extends AuthenticationError {
165
- /** HTTP status code if available */
166
- readonly statusCode: number | undefined;
167
- constructor(message: string, statusCode?: number);
160
+ declare class InstanceError extends AuthenticationError {
161
+ /** Error code from the API */
162
+ readonly code: string;
163
+ /** Reason for the error if available */
164
+ readonly reason: string | undefined;
165
+ /** Error message if available */
166
+ readonly errorMessage: string | undefined;
167
+ constructor(code: string, options?: {
168
+ reason?: string;
169
+ message?: string;
170
+ });
168
171
  }
169
172
  //#endregion
170
173
  //#region src/approvers/browser.d.ts
@@ -213,6 +216,10 @@ interface PATApproverOptions {
213
216
  allowedScopes?: string[];
214
217
  /** The API URL to use. Defaults to "https://api.caido.io" */
215
218
  apiUrl?: string;
219
+ /** Request timeout in milliseconds */
220
+ timeout?: number;
221
+ /** Custom fetch implementation */
222
+ fetch?: typeof globalThis.fetch;
216
223
  }
217
224
  /**
218
225
  * PAT-based approver that automatically approves device code requests.
@@ -234,11 +241,8 @@ declare class PATApprover implements AuthApprover {
234
241
  private readonly pat;
235
242
  private readonly allowedScopes;
236
243
  private readonly apiUrl;
237
- /**
238
- * Create a new PATApprover.
239
- *
240
- * @param options - Configuration options for the approver
241
- */
244
+ private readonly fetchFn;
245
+ private readonly timeout;
242
246
  constructor(options: PATApproverOptions);
243
247
  /**
244
248
  * Approve the authentication request using the PAT.
@@ -247,27 +251,14 @@ declare class PATApprover implements AuthApprover {
247
251
  * and finally approves the device.
248
252
  *
249
253
  * @param request - The authentication request
250
- * @throws {DeviceInformationError} If fetching device information fails
251
- * @throws {DeviceApprovalError} If approving the device fails
254
+ * @throws {CloudError} If fetching device information or approving the device fails
252
255
  */
253
256
  approve(request: AuthenticationRequest): Promise<void>;
254
- /**
255
- * Fetch device information from the API.
256
- *
257
- * @param userCode - The user code from the authentication request
258
- * @returns The device information including available scopes
259
- * @throws {DeviceInformationError} If the request fails
260
- */
257
+ private sendRequest;
258
+ private parseOAuth2Error;
261
259
  private getDeviceInformation;
262
- /**
263
- * Approve the device with the specified scopes.
264
- *
265
- * @param userCode - The user code from the authentication request
266
- * @param scopes - The scopes to approve
267
- * @throws {DeviceApprovalError} If the request fails
268
- */
269
260
  private approveDevice;
270
261
  }
271
262
  //#endregion
272
- export { type AuthApprover, AuthenticationError, AuthenticationFlowError, type AuthenticationRequest, type AuthenticationToken, BrowserApprover, CaidoAuth, DeviceApprovalError, type DeviceInformation, DeviceInformationError, type DeviceScope, type OnRequestCallback, PATApprover, type PATApproverOptions, TokenRefreshError };
263
+ export { type AuthApprover, AuthClient, type AuthClientOptions, AuthenticationError, type AuthenticationRequest, type AuthenticationToken, BrowserApprover, CloudError, type DeviceInformation, type DeviceScope, InstanceError, type OnRequestCallback, PATApprover, type PATApproverOptions };
273
264
  //# sourceMappingURL=index.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.mts","names":[],"sources":["../src/types.ts","../src/approvers/types.ts","../src/client.ts","../src/errors.ts","../src/approvers/browser.ts","../src/approvers/pat.ts"],"mappings":";;AAIA;;;UAAiB,qBAAA;;EAEf,EAAA;;EAEA,QAAA;;EAEA,eAAA;EAEW;EAAX,SAAA,EAAW,IAAA;AAAA;;;;UAMI,mBAAA;;EAEf,WAAA;;EAEA,YAAA;EAQF;EANE,SAAA,EAAW,IAAA;AAAA;;;AAgBb;UAViB,WAAA;;EAEf,IAAA;;EAEA,WAAA;AAAA;;;;UAMe,iBAAA;;EAEf,QAAA;ECpCe;EDsCf,MAAA,EAAQ,WAAA;AAAA;;;AAxCV;;;;AAAA,UCEiB,YAAA;;;;;;;ADYjB;ECJE,OAAA,CAAQ,OAAA,EAAS,qBAAA,GAAwB,OAAA;AAAA;;;;;;;;;;;;;ADI3C;;;;;;;;cEsBa,SAAA;EAAA,iBACM,WAAA;EAAA,iBACA,UAAA;EAAA,iBACA,YAAA;EAAA,iBACA,QAAA;EAAA,iBACA,MAAA;EFXjB;AAMF;;;;;EEaE,WAAA,CAAY,WAAA,UAAqB,QAAA,EAAU,YAAA;;;;UAenC,eAAA;;;AD9DV;;;;;;;;;;;ECiFE,uBAAA,CAAA,GAAiC,OAAA,CAAQ,mBAAA;;AA/C3C;;;;;;UAqGgB,YAAA;;;;;;;;EAwEd,YAAA,CAAmB,YAAA,WAAuB,OAAA,CAAQ,mBAAA;AAAA;;;;AFjNpD;;cGDa,mBAAA,SAA4B,KAAA;EACvC,WAAA,CAAY,OAAA;AAAA;;;;cASD,uBAAA,SAAgC,mBAAA;;WAElC,IAAA;EAET,WAAA,CAAY,IAAA,UAAc,OAAA;AAAA;;;;cAUf,iBAAA,SAA0B,mBAAA;;WAE5B,IAAA;EAET,WAAA,CAAY,IAAA,UAAc,OAAA;AAAA;AHD5B;;;AAAA,cGWa,mBAAA,SAA4B,mBAAA;EHPvC;EAAA,SGSS,UAAA;EAET,WAAA,CAAY,OAAA,UAAiB,UAAA;AAAA;;;;cAUlB,sBAAA,SAA+B,mBAAA;;WAEjC,UAAA;EAET,WAAA,CAAY,OAAA,UAAiB,UAAA;AAAA;;;;;;;KCnDnB,iBAAA,IACV,OAAA,EAAS,qBAAA,KACN,OAAA;;;;;;AJQL;;;;;;;cIMa,eAAA,YAA2B,YAAA;EAAA,iBACrB,SAAA;EJDN;AAMb;;;;EIEE,WAAA,CAAY,SAAA,EAAW,iBAAA;EJQzB;;;;;;EIEE,OAAA,CAAc,OAAA,EAAS,qBAAA,GAAwB,OAAA;AAAA;;;;;;UChChC,kBAAA;;EAEf,GAAA;;EAEA,aAAA;;EAEA,MAAA;AAAA;;;;;;;;;;ALcF;;;;;AAUA;;cKLa,WAAA,YAAuB,YAAA;EAAA,iBACjB,GAAA;EAAA,iBACA,aAAA;EAAA,iBACA,MAAA;;;;;;EAOjB,WAAA,CAAY,OAAA,EAAS,kBAAA;EJvCvB;;;;;;;;;;EIuDE,OAAA,CAAc,OAAA,EAAS,qBAAA,GAAwB,OAAA;;;AHrBjD;;;;;UG4CgB,oBAAA;;;;;;;;UAmCA,aAAA;AAAA"}
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../src/types.ts","../src/approvers/types.ts","../src/client.ts","../src/errors.ts","../src/approvers/browser.ts","../src/approvers/pat.ts"],"mappings":";;AAIA;;;UAAiB,qBAAA;;EAEf,EAAA;;EAEA,QAAA;;EAEA,eAAA;EAEW;EAAX,SAAA,EAAW,IAAA;AAAA;;;;UAMI,mBAAA;;EAEf,WAAA;;EAEA,YAAA;EAIA;EAFA,SAAA,EAAW,IAAA;EAQI;EANf,MAAA;AAAA;;AAgBF;;UAViB,WAAA;EAcP;EAZR,IAAA;;EAEA,WAAA;AAAA;;;;UAMe,iBAAA;ECpCjB;EDsCE,QAAA;;EAEA,MAAA,EAAQ,WAAA;AAAA;;;AA1CV;;;;AAAA,UCEiB,YAAA;;;;;;;ADYjB;ECJE,OAAA,CAAQ,OAAA,EAAS,qBAAA,GAAwB,OAAA;AAAA;;;;;;UCW1B,iBAAA;;EAEf,WAAA;;EAEA,QAAA,EAAU,YAAA;;EAEV,OAAA;EFbF;EEeE,KAAA,UAAe,UAAA,CAAW,KAAA;AAAA;;;;;;;;;AFD5B;;;;;AAUA;;;;;cEiBa,UAAA;EAAA,iBACM,WAAA;EAAA,iBACA,UAAA;EAAA,iBACA,YAAA;EAAA,iBACA,QAAA;EAAA,iBACA,MAAA;EAAA,iBACA,OAAA;EAAA,iBACA,OAAA;EAEjB,WAAA,CAAY,OAAA,EAAS,iBAAA;EAAA,QAsBb,eAAA;EAAA,QAMA,mBAAA;;;;;;;;;;AAvEV;;;;EAmGE,uBAAA,CAAA,GAAiC,OAAA,CAAQ,mBAAA;EAAA,QA6C3B,YAAA;;;;;;;;EAgEd,YAAA,CAAmB,YAAA,WAAuB,OAAA,CAAQ,mBAAA;AAAA;;;;AFrOpD;;cGDa,mBAAA,SAA4B,KAAA;EACvC,WAAA,CAAY,OAAA;AAAA;;;;;cAUD,UAAA,SAAmB,mBAAA;EHFnB;EAAA,SGIF,UAAA;EHEM;EAAA,SGAN,IAAA;EHME;EAAA,SGJF,MAAA;EAET,WAAA,CACE,OAAA,UACA,OAAA;IACE,UAAA;IACA,IAAA;IACA,MAAA;EAAA;AAAA;AHKN;;;;AAAA,cGUa,aAAA,SAAsB,mBAAA;EHAnC;EAAA,SGEW,IAAA;;WAEA,MAAA;;WAEA,YAAA;EAET,WAAA,CACE,IAAA,UACA,OAAA;IACE,MAAA;IACA,OAAA;EAAA;AAAA;;;;;;;KC9CM,iBAAA,IACV,OAAA,EAAS,qBAAA,KACN,OAAA;;;;;;AJQL;;;;;;;cIMa,eAAA,YAA2B,YAAA;EAAA,iBACrB,SAAA;;;AJOnB;;;EIAE,WAAA,CAAY,SAAA,EAAW,iBAAA;EJIvB;AAMF;;;;;EIAE,OAAA,CAAc,OAAA,EAAS,qBAAA,GAAwB,OAAA;AAAA;;;;;;UCdhC,kBAAA;;EAEf,GAAA;;EAEA,aAAA;;EAEA,MAAA;ELhBF;EKkBE,OAAA;;EAEA,KAAA,UAAe,UAAA,CAAW,KAAA;AAAA;;;;;;;ALN5B;;;;;AAUA;;;;;cKea,WAAA,YAAuB,YAAA;EAAA,iBACjB,GAAA;EAAA,iBACA,aAAA;EAAA,iBACA,MAAA;EAAA,iBACA,OAAA;EAAA,iBACA,OAAA;EAEjB,WAAA,CAAY,OAAA,EAAS,kBAAA;EJ1DvB;;;;;;;;;EI2EE,OAAA,CAAc,OAAA,EAAS,qBAAA,GAAwB,OAAA;EAAA,QAgBjC,WAAA;EAAA,QAgBA,gBAAA;EAAA,QAuBA,oBAAA;EAAA,QA+BA,aAAA;AAAA"}