Skip to content

Built for people who want to own their automations. Join the waitlist for an invite.

Package listing

@kody/resend

src/smoke-test.ts

127 lines · 3.6 KB · TypeScript
import { kody } from 'kody:runtime'
import { listDomains } from './domains.ts'
import { listEmails, sendEmail } from './emails.ts'
import {
	API_HOST,
	DEFAULT_AUTH_NAME,
	oauthConnectUrl,
	resolveIntegrationName,
	resolveSecretName,
	secretSetupUrl,
	type ResendAuthOptions,
} from './resend-core.ts'

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 : []
}

async function listUserSecretNames(): Promise<Set<string>> {
	try {
		const listed = await kody.secret_list({ scope: 'user' })
		const names = new Set<string>()
		for (const entry of secretEntries(listed)) {
			if (entry?.name && (entry.scope === 'user' || !entry.scope)) names.add(entry.name)
		}
		return names
	} catch {
		return new Set()
	}
}

async function listIntegrationNames(): Promise<Set<string>> {
	try {
		const listed = await kody.integration_list({})
		const names = new Set<string>()
		const integrations =
			listed && typeof listed === 'object' && Array.isArray((listed as { integrations?: unknown }).integrations)
				? (listed as { integrations: Array<{ name?: string }> }).integrations
				: []
		for (const entry of integrations) {
			if (entry?.name) names.add(entry.name)
		}
		return names
	} catch {
		return new Set()
	}
}

/**
 * Local dry-run self-check plus an optional live domains + recent-email read.
 *
 * Without a `resend` secret or OAuth integration this still returns
 * `{ ok: true, live: false }` and the prefilled setup URLs.
 *
 * @example
 * import smokeTest from 'kody:@kody/resend/smoke-test'
 * const result = await smokeTest()
 */
export async function smokeTest(input: ResendAuthOptions = {}) {
	const preview = await sendEmail({
		from: 'Kody <hello@example.com>',
		to: 'person@example.com',
		subject: 'kody resend smoke',
		text: 'dry-run only',
		dryRun: true,
	})
	if (!('dryRun' in preview) || preview.dryRun !== true || preview.path !== '/emails') {
		throw new Error('sendEmail dryRun self-check failed.')
	}

	const secretName = resolveSecretName(input)
	const explicitIntegration = resolveIntegrationName(input)
	const secretNames = await listUserSecretNames()
	const integrationNames = await listIntegrationNames()
	const hasSecret = secretNames.has(secretName)
	const inferredIntegration =
		explicitIntegration ??
		(integrationNames.has(secretName) && !hasSecret ? secretName : undefined)
	const liveAuth: ResendAuthOptions = inferredIntegration
		? { ...input, integration: inferredIntegration }
		: input

	if (!hasSecret && !inferredIntegration) {
		return {
			ok: true,
			live: false,
			selfCheck: { dryRunSendEmail: true },
			secretName,
			setup: {
				auth: 'api-key-or-oauth',
				hosts: [API_HOST],
				nextSteps: [
					'Save a Resend API key (re_…) as ' + secretName + '.',
					secretSetupUrl(secretName),
					'Approve host ' + API_HOST + ' on that secret.',
					'Optional OAuth (Dynamic Client Registration, no dashboard app): ' +
						oauthConnectUrl(secretName === DEFAULT_AUTH_NAME ? DEFAULT_AUTH_NAME : secretName),
				],
			},
		}
	}

	const [domains, emails] = await Promise.all([
		listDomains(liveAuth),
		listEmails({ ...liveAuth, limit: 5 }),
	])

	return {
		ok: true,
		live: true,
		selfCheck: { dryRunSendEmail: true },
		secretName,
		integration: inferredIntegration ?? null,
		domainCount: domains.length,
		domains,
		recentEmailCount: emails.length,
		sampleEmail: emails[0] ?? null,
		setup: {
			auth: inferredIntegration ? 'oauth' : 'api-key',
			hosts: [API_HOST],
		},
	}
}

export default smokeTest