import { createAuthenticatedFetch, kody } from 'kody:runtime' import type { FigmaAuthInput, FigmaAuthMode } from './types.ts' import { requireString } from './types.ts' import { DEFAULT_PAT_SECRET, missingCredentialsMessage } from './setup.ts' import { resolveIntegrationName, resolveSecretName } from './auth-names.ts' export { resolveIntegrationName, resolveSecretName } const authenticatedFetches = new Map() export type ResolvedFigmaAuth = { mode: FigmaAuthMode integrationName: string secretName: string authorization: string } function secretEntries( result: unknown, ): Array<{ name?: string; scope?: string }> { if ( result && typeof result === 'object' && Array.isArray((result as { secrets?: unknown }).secrets) ) { return (result as { secrets: Array<{ name?: string; scope?: string }> }).secrets } return Array.isArray(result) ? result : [] } export async function listUserSecretNames(): Promise> { const listed = await kody.secretList({ scope: 'user' }) const names = new Set() for (const entry of secretEntries(listed)) { if (entry?.name && (entry.scope === 'user' || !entry.scope)) { names.add(entry.name) } } return names } export async function listFigmaIntegrationNames(): Promise> { const listed = await kody.integrationList({}) const integrations = listed && typeof listed === 'object' && Array.isArray((listed as { integrations?: unknown }).integrations) ? (listed as { integrations: Array<{ name?: string }> }).integrations : Array.isArray(listed) ? listed : [] return integrations .map((item) => (typeof item?.name === 'string' ? item.name : '')) .filter((name) => name === 'figma' || name.startsWith('figma-')) } export async function resolveFigmaAuth( input: FigmaAuthInput = {}, ): Promise { const integrationName = resolveIntegrationName(input) const secretName = resolveSecretName(input) const forced = input.auth if (forced !== undefined && forced !== 'oauth' && forced !== 'pat') { throw new Error('auth must be "oauth" or "pat".') } const [secrets, integrations] = await Promise.all([ listUserSecretNames(), listFigmaIntegrationNames(), ]) const hasOauth = integrations.includes(integrationName) const hasPat = secrets.has(secretName) let mode: FigmaAuthMode if (forced === 'oauth') { if (!hasOauth) { throw new Error(missingCredentialsMessage({ integrationName, secretName })) } mode = 'oauth' } else if (forced === 'pat') { if (!hasPat) { throw new Error(missingCredentialsMessage({ integrationName, secretName })) } mode = 'pat' } else if (hasOauth) { mode = 'oauth' } else if (hasPat) { mode = 'pat' } else { throw new Error(missingCredentialsMessage({ integrationName, secretName })) } switch (mode) { case 'oauth': return { mode, integrationName, secretName, authorization: `[managed ${integrationName} OAuth integration]`, } case 'pat': return { mode, integrationName, secretName, authorization: `X-Figma-Token {{secret:${secretName}}}`, } default: { const exhaustive: never = mode throw new Error(`Unsupported Figma auth mode: ${String(exhaustive)}`) } } } async function getOauthFetch(integrationName: string): Promise { let authedFetch = authenticatedFetches.get(integrationName) if (!authedFetch) { try { authedFetch = await createAuthenticatedFetch(integrationName) } catch (error) { const message = error instanceof Error ? error.message : String(error) throw new Error( [ `Figma OAuth integration "${integrationName}" is not usable: ${message}`, missingCredentialsMessage({ integrationName, secretName: DEFAULT_PAT_SECRET, }), ].join(' '), ) } authenticatedFetches.set(integrationName, authedFetch) } return authedFetch } export async function figmaFetch( auth: ResolvedFigmaAuth, url: string, init: RequestInit, ): Promise { switch (auth.mode) { case 'oauth': { const authedFetch = await getOauthFetch(auth.integrationName) return authedFetch(url, init) } case 'pat': { const headers = new Headers(init.headers) headers.set('X-Figma-Token', `{{secret:${auth.secretName}}}`) return fetch(url, { ...init, headers }) } default: { const exhaustive: never = auth.mode throw new Error(`Unsupported Figma auth mode: ${String(exhaustive)}`) } } } export function requireAuthMode(value: unknown): FigmaAuthMode | undefined { if (value === undefined) return undefined const mode = requireString(value, 'auth') if (mode !== 'oauth' && mode !== 'pat') { throw new Error('auth must be "oauth" or "pat".') } return mode }