Skip to content

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

Package listing

@kody/kit

src/forms.ts

117 lines · 3.7 KB · TypeScript
import { kitListAll, kitRequest, unwrapRecord } from './client.ts'
import { mutationPreview } from './safety.ts'
import { parseAuthInput } from './auth.ts'
import { formSummary, segmentSummary, subscriberSummary } from './models.ts'
import type { FormSummary, JsonRecord, KitAuthInput, MutationInput, SegmentSummary } from './types.ts'
import {
	clampInt,
	compactRecord,
	optionalBoolean,
	optionalString,
	requireId,
	requireRecord,
	requireString,
} from './types.ts'

/**
 * List signup forms. Pass `maxItems` (default 25). Form ids come from the
 * caller's Kit account — this package has no baked-in form ids.
 * @example
 * import { listForms } from 'kody:@kody/kit/forms'
 * const forms = await listForms({ maxItems: 20 })
 */
export async function listForms(
	input: KitAuthInput & { maxItems?: number } = {},
): Promise<Array<FormSummary>> {
	const items = await kitListAll<JsonRecord>({
		...input,
		path: '/forms',
		itemKey: 'forms',
		maxItems: clampInt(input.maxItems, 1, 500, 25),
	})
	return items.map((item) => formSummary(item)).filter((item): item is FormSummary => Boolean(item))
}

/**
 * Add a subscriber to a form by email. Requires `confirm: true`, or use `dryRun: true`.
 * @example
 * import { addSubscriberToForm } from 'kody:@kody/kit/forms'
 * const preview = await addSubscriberToForm({
 *   formId: 123,
 *   email_address: 'ada@example.com',
 *   dryRun: true,
 * })
 */
export async function addSubscriberToForm(
	input: KitAuthInput & MutationInput & { formId: number | string; email_address: string; referrer?: string },
) {
	const formId = requireId(input.formId, 'formId')
	const email = requireString(input.email_address, 'email_address')
	const body = compactRecord({
		email_address: email,
		referrer: input.referrer,
	})
	const preview = mutationPreview(input, {
		method: 'POST',
		path: `/forms/${formId}/subscribers`,
		body,
	})
	if (preview) return preview
	const result = await kitRequest<JsonRecord>({
		...input,
		method: 'POST',
		path: `/forms/${formId}/subscribers`,
		body,
		confirm: true,
	})
	return subscriberSummary(unwrapRecord(result.data, 'subscriber', 'addSubscriberToForm'))
}

/**
 * List segments (read-only in Kit v4).
 * @example
 * import { listSegments } from 'kody:@kody/kit/forms'
 * const segments = await listSegments()
 */
export async function listSegments(
	input: KitAuthInput & { maxItems?: number } = {},
): Promise<Array<SegmentSummary>> {
	const items = await kitListAll<JsonRecord>({
		...input,
		path: '/segments',
		itemKey: 'segments',
		maxItems: clampInt(input.maxItems, 1, 500, 25),
	})
	return items
		.map((item) => segmentSummary(item))
		.filter((item): item is SegmentSummary => Boolean(item))
}

/**
 * Form and segment helpers. Defaults to `list`.
 * @example
 * import forms from 'kody:@kody/kit/forms'
 * const all = await forms({ action: 'list' })
 */
export default async function formsEntrypoint(params: Record<string, unknown> = {}) {
	const input = requireRecord(params, 'forms')
	const auth = parseAuthInput(input)
	const action = optionalString(input.action, 'action') ?? 'list'
	switch (action) {
		case 'list':
			return listForms({ ...auth, maxItems: input.maxItems as number | undefined })
		case 'add-subscriber':
			return addSubscriberToForm({
				...auth,
				formId: requireId(input.formId, 'formId'),
				email_address: requireString(input.email_address, 'email_address'),
				referrer: optionalString(input.referrer, 'referrer'),
				confirm: optionalBoolean(input.confirm, 'confirm'),
				dryRun: optionalBoolean(input.dryRun, 'dryRun'),
			})
		case 'segments':
			return listSegments({ ...auth, maxItems: input.maxItems as number | undefined })
		default:
			throw new Error('forms action must be one of: list, add-subscriber, segments.')
	}
}