Skip to content

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

Package listing

@kody/bluesky

src/smoke-test.ts

74 lines · 2.5 KB · TypeScript
import { kody } from 'kody:runtime'
import { smokeTest, tryDefaultHandle } from './client.ts'
import {
	APP_PASSWORD_DOCS_URL,
	APP_PASSWORD_SECRET,
	APP_PASSWORD_SETUP_URL,
	AUTH_HOST,
	DEFAULT_AUTH_SERVICE,
	DEFAULT_PUBLIC_APPVIEW,
	HANDLE_STORAGE_KEY,
	PDS_HOST_PATTERN,
} from './setup.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 : []
}

/**
 * Verify Bluesky auth and read the default profile without returning session tokens.
 * Without a saved app password or handle this still returns `{ ok: true, live: false }`
 * plus the prefilled secret URL so the package is forkable before anyone connects.
 * @example
 * import smokeTest from 'kody:@kody/bluesky/smoke-test'
 * const result = await smokeTest()
 * // => { ok: true, live: false, setup: { ... } }
 */
export default async function smokeTestEntrypoint(params: Record<string, unknown> = {}) {
	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)
		}
	}

	const identifier =
		(typeof params.identifier === 'string' && params.identifier.trim()) ||
		(typeof params.handle === 'string' && params.handle.trim()) ||
		(await tryDefaultHandle())
	const hasSecret = names.has(APP_PASSWORD_SECRET)
	const setup = {
		secretUrl: APP_PASSWORD_SETUP_URL,
		appPasswordDocs: APP_PASSWORD_DOCS_URL,
		hosts: [AUTH_HOST, PDS_HOST_PATTERN],
		handleStorageKey: HANDLE_STORAGE_KEY,
		defaultAuthService: DEFAULT_AUTH_SERVICE,
		defaultPublicAppview: DEFAULT_PUBLIC_APPVIEW,
		secrets: { [APP_PASSWORD_SECRET]: hasSecret },
		hasHandle: Boolean(identifier),
		nextSteps: [
			'Create an app password at ' + APP_PASSWORD_DOCS_URL + '. Do not paste it into chat.',
			APP_PASSWORD_SETUP_URL,
			'Approve hosts bsky.social and *.bsky.network after the secret is saved.',
			'Fork this package, then packageStorage().set("blueskyHandle", "yourhandle.bsky.social").',
		],
	}

	if (!hasSecret || !identifier || params.setupOnly === true) {
		return {
			ok: true,
			live: false,
			method: 'com.atproto.server.createSession',
			host: AUTH_HOST,
			handle: identifier || null,
			setup,
		}
	}

	const live = await smokeTest({ ...params, identifier })
	return { ...live, setup: { ...setup, nextSteps: [] } }
}