Skip to content
← Public packages

@kentcdodds/exchange-threads

Open kody.exchange threads from Kody. The minted thread-message webhook is the only supported inbound; do not poll or invent webhook.site.

src/register.ts

61 lines · 2.0 KB · TypeScript
import { ensureWebhookUrl } from './ensure-webhook.ts'
import {
	connectUrl,
	exchangeFetch,
	getThread,
	readString,
	setWebhookUrl,
	store,
	upsertThread,
} from './shared.ts'

/**
 * Point an existing kody.exchange thread at this package's minted webhook URL.
 * Prefer `./create`, which attaches the webhook by default. Use this for
 * threads created before that default, or for guest tokens.
 *
 * Guest kody.exchange has no `/v1/threads/:id/webhook`. A guest token uses
 * `PUT /v1/webhook`. Account-owned threads use
 * `PUT /api/threads/:id/webhook`.
 * @example
 * import register from 'kody:@kentcdodds/exchange-threads/register'
 * const owned = await register({ threadId: 'th_1' })
 * // => { ok: true, threadId: 'th_1', webhookAttached: true }
 * const guest = await register({ threadId: 'th_1', token: 'kx_live_…' })
 * // => { ok: true, threadId: 'th_1', webhookAttached: true }
 */
export default async function register(
	params: { threadId?: string; token?: string; url?: string } = {},
) {
	const threadId = readString(params.threadId)
	if (!threadId) return { ok: false, error: 'threadId is required.' }
	const bucket = await store()
	const url = readString(params.url) ?? (await ensureWebhookUrl(bucket)).url
	if (!url) {
		return { ok: false, error: 'could not mint the inbound webhook URL.' }
	}
	await setWebhookUrl(bucket, url)
	const token = readString(params.token)
	const response = await exchangeFetch({
		path: token ? '/v1/webhook' : `/api/threads/${threadId}/webhook`,
		method: 'PUT',
		body: { url },
		token: token ?? undefined,
	})
	if (!response.ok) {
		return {
			ok: false,
			status: response.status,
			error: response.json,
			connectUrl: token ? undefined : connectUrl(),
		}
	}
	const previous = await getThread(bucket, threadId)
	await upsertThread(bucket, {
		id: threadId,
		token: token ?? previous?.token ?? '',
		registeredAt: previous?.registeredAt ?? new Date().toISOString(),
		lastAt: previous?.lastAt ?? null,
	})
	return { ok: true, threadId, webhookAttached: true }
}