import { createAuthenticatedFetch, kody } from 'kody:runtime' import type { IntercomAuthInput, IntercomAuthMode } from './types.ts' import { requireString } from './types.ts' import { DEFAULT_INTEGRATION_NAME, DEFAULT_TOKEN_SECRET, missingCredentialsMessage, resolveApiBaseUrl, resolveIntegrationName, resolveRegion, resolveSecretName, } from './setup.ts' const authenticatedFetches = new Map() export type ResolvedIntercomAuth = { mode: IntercomAuthMode integrationName: string secretName: string apiBaseUrl: string region: ReturnType 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 listIntercomIntegrationNames(): 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 === 'intercom' || name.startsWith('intercom-')) } export async function resolveIntercomAuth( input: IntercomAuthInput = {}, ): Promise { const integrationName = resolveIntegrationName(input) const secretName = resolveSecretName(input) const apiBaseUrl = resolveApiBaseUrl(input) const region = resolveRegion(input) const forced = input.auth if (forced !== undefined && forced !== 'oauth' && forced !== 'accessToken') { throw new Error('auth must be "oauth" or "accessToken".') } const [secrets, integrations] = await Promise.all([ listUserSecretNames(), listIntercomIntegrationNames(), ]) const hasOauth = integrations.includes(integrationName) const hasToken = secrets.has(secretName) let mode: IntercomAuthMode if (forced === 'oauth') { if (!hasOauth) { throw new Error(missingCredentialsMessage({ integrationName, secretName, region })) } mode = 'oauth' } else if (forced === 'accessToken') { if (!hasToken) { throw new Error(missingCredentialsMessage({ integrationName, secretName, region })) } mode = 'accessToken' } else if (hasOauth) { mode = 'oauth' } else if (hasToken) { mode = 'accessToken' } else { throw new Error(missingCredentialsMessage({ integrationName, secretName, region })) } switch (mode) { case 'oauth': return { mode, integrationName, secretName, apiBaseUrl, region, authorization: `[managed ${integrationName} OAuth integration]`, } case 'accessToken': return { mode, integrationName, secretName, apiBaseUrl, region, authorization: `Bearer {{secret:${secretName}}}`, } default: { const exhaustive: never = mode throw new Error(`Unsupported Intercom 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( [ `Intercom OAuth integration "${integrationName}" is not usable: ${message}`, missingCredentialsMessage({ integrationName, secretName: DEFAULT_TOKEN_SECRET, }), ].join(' '), ) } authenticatedFetches.set(integrationName, authedFetch) } return authedFetch } export async function intercomFetch( auth: ResolvedIntercomAuth, url: string, init: RequestInit, ): Promise { switch (auth.mode) { case 'oauth': { const authedFetch = await getOauthFetch(auth.integrationName) return authedFetch(url, init) } case 'accessToken': { const headers = new Headers(init.headers) headers.set('Authorization', `Bearer {{secret:${auth.secretName}}}`) return fetch(url, { ...init, headers }) } default: { const exhaustive: never = auth.mode throw new Error(`Unsupported Intercom auth mode: ${String(exhaustive)}`) } } } export function requireAuthMode(value: unknown): IntercomAuthMode | undefined { if (value === undefined) return undefined const mode = requireString(value, 'auth') if (mode !== 'oauth' && mode !== 'accessToken') { throw new Error('auth must be "oauth" or "accessToken".') } return mode } export function parseAuth(input: Record): IntercomAuthInput { const regionValue = optionalStringSafe(input.region, 'region') return { integrationName: optionalStringSafe(input.integrationName, 'integrationName'), integration: optionalStringSafe(input.integration, 'integration'), account: optionalStringSafe(input.account, 'account'), secretName: optionalStringSafe(input.secretName, 'secretName'), auth: requireAuthMode(input.auth), region: regionValue as IntercomAuthInput['region'], apiBaseUrl: optionalStringSafe(input.apiBaseUrl, 'apiBaseUrl'), apiVersion: optionalStringSafe(input.apiVersion, 'apiVersion'), } } function optionalStringSafe(value: unknown, label: string): string | undefined { if (value === undefined || value === null) return undefined return requireString(value, label) } export { DEFAULT_INTEGRATION_NAME, DEFAULT_TOKEN_SECRET }