import type { KitAuthInput, KitAuthMode } from './types.ts' import { optionalString, requireString } from './types.ts' export const KIT_API_BASE_URL = 'https://api.kit.com/v4' export const KIT_AUTHORIZE_URL = 'https://api.kit.com/v4/oauth/authorize' export const KIT_TOKEN_URL = 'https://api.kit.com/v4/oauth/token' export const KIT_REQUIRED_HOSTS = ['api.kit.com'] as const export const KIT_REQUIRED_HOST = 'api.kit.com' export const KIT_CALLBACK_URL = 'https://kody.codes/connect/oauth' export const KIT_OAUTH_APP_URL = 'https://developers.kit.com/kit-app-store/building-apps#creating-your-app' export const KIT_API_KEY_DASHBOARD_URL = 'https://app.kit.com/account_settings/developer_settings' export const KIT_AUTH_DOCS_URL = 'https://developers.kit.com/api-reference/authentication' export const DEFAULT_INTEGRATION_NAME = 'kit' export const DEFAULT_API_KEY_SECRET = 'kitApiKey' export const DEFAULT_OAUTH_SCOPES = ['public'] as const export const KIT_SCOPES = { public: 'Default Kit App Store scope. Fine-grained scopes are not generally available yet.', read: 'Read access to Kit API v4 when the app requests it.', write: 'Write access to Kit API v4 when the app requests it.', } as const export function resolveIntegrationName(input: KitAuthInput = {}): string { const explicit = optionalString(input.integrationName, 'integrationName') ?? optionalString(input.integration, 'integration') if (explicit) return explicit const account = optionalString(input.account, 'account') if (!account || account === 'default' || account === 'kit') { return DEFAULT_INTEGRATION_NAME } if (account.startsWith('kit-')) return account return `kit-${account}` } export function resolveSecretName(input: KitAuthInput = {}): string { const explicit = optionalString(input.secretName, 'secretName') if (explicit) return explicit const integrationName = resolveIntegrationName(input) if (integrationName === DEFAULT_INTEGRATION_NAME) return DEFAULT_API_KEY_SECRET const suffix = integrationName.startsWith('kit-') ? integrationName.slice('kit-'.length) : integrationName return `${DEFAULT_API_KEY_SECRET}-${suffix}` } export function oauthConnectUrl(provider: string = DEFAULT_INTEGRATION_NAME): string { const name = requireString(provider, 'provider') const params = new URLSearchParams({ provider: name, authorizeUrl: KIT_AUTHORIZE_URL, tokenUrl: KIT_TOKEN_URL, apiBaseUrl: KIT_API_BASE_URL, scopes: DEFAULT_OAUTH_SCOPES.join(' '), flow: 'confidential', pkce: 'false', allowedHosts: KIT_REQUIRED_HOSTS.join(','), dashboardUrl: KIT_OAUTH_APP_URL, }) return `https://kody.codes/connect/oauth?${params.toString()}` } export function apiKeySetupUrl(secretName: string = DEFAULT_API_KEY_SECRET): string { const name = requireString(secretName, 'secretName') const params = new URLSearchParams({ name, description: 'Kit v4 API key for subscribers, forms, tags, broadcasts, and sequences', allowedHosts: KIT_REQUIRED_HOSTS.join(','), scope: 'user', }) return `https://kody.codes/account/secrets/new?${params.toString()}` } export function reconnectUrl(provider: string = DEFAULT_INTEGRATION_NAME): string { return `https://kody.codes/connect/oauth?provider=${encodeURIComponent(provider)}` } export function missingCredentialsMessage(options: { integrationName: string secretName: string }): string { return [ 'Kit credentials are missing.', 'API key (recommended for personal automation): create a v4 key at', KIT_API_KEY_DASHBOARD_URL, 'then save it (do not paste the value in chat):', apiKeySetupUrl(options.secretName), 'OAuth (Kit App Store apps): create an app at', KIT_OAUTH_APP_URL, `with redirect URI ${KIT_CALLBACK_URL}, then connect:`, oauthConnectUrl(options.integrationName), `Required API host: ${KIT_REQUIRED_HOST}. Auth docs: ${KIT_AUTH_DOCS_URL}.`, ].join(' ') } export function nextStepForAuth(options: { authMode: KitAuthMode integrationName: string secretName: string }): string { switch (options.authMode) { case 'oauth': return [ `Reconnect the "${options.integrationName}" OAuth integration.`, reconnectUrl(options.integrationName), `Full BYO connect URL: ${oauthConnectUrl(options.integrationName)}`, ].join(' ') case 'apiKey': return [ `Save a Kit v4 API key as ${options.secretName} and approve host ${KIT_REQUIRED_HOST}.`, KIT_API_KEY_DASHBOARD_URL, apiKeySetupUrl(options.secretName), ].join(' ') default: { const exhaustive: never = options.authMode throw new Error(`Unsupported Kit auth mode: ${String(exhaustive)}`) } } } export function isMutatingMethod(method: string): boolean { const normalized = method.toUpperCase() return ( normalized === 'POST' || normalized === 'PUT' || normalized === 'PATCH' || normalized === 'DELETE' ) } export const OAUTH_CONNECT_URL = oauthConnectUrl() export const API_KEY_SETUP_URL = apiKeySetupUrl()