Skip to content

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

Package listing

@kody/supabase

src/create-user.ts

102 lines · 3.3 KB · TypeScript
import { projectAuth } from './setup.ts'
import { assertMutationAllowed, readMutationFlags } from './safety.ts'
import { asLive, projectRequest } from './client.ts'
import { summarizeAuthUser } from './list-users.ts'
import {
	isRecord,
	optionalBoolean,
	optionalString,
	type DryRunResult,
	type JsonRecord,
	type SupabaseAuthInput,
} from './types.ts'

export type CreateUserInput = SupabaseAuthInput & {
	email?: string
	phone?: string
	password?: string
	emailConfirm?: boolean
	phoneConfirm?: boolean
	userMetadata?: JsonRecord
	appMetadata?: JsonRecord
	dryRun?: boolean
	confirm?: boolean
}

/**
 * Create an Auth user. Requires `confirm: true`, or use `dryRun: true`.
 * @example
 * import createUser from 'kody:@kody/supabase/create-user'
 * const preview = await createUser({
 *   projectRef: 'abcdefghijklmnop',
 *   email: 'ada@example.com',
 *   dryRun: true,
 * })
 */
export async function createUser(input: CreateUserInput) {
	const flags = readMutationFlags(input)
	const email = optionalString(input.email, 'email')
	const phone = optionalString(input.phone, 'phone')
	if (!email && !phone) {
		throw new Error('create-user requires email or phone.')
	}
	const body: JsonRecord = {
		email,
		phone,
		password: optionalString(input.password, 'password'),
		email_confirm: optionalBoolean(input.emailConfirm, 'emailConfirm'),
		phone_confirm: optionalBoolean(input.phoneConfirm, 'phoneConfirm'),
		user_metadata: input.userMetadata,
		app_metadata: input.appMetadata,
	}
	const previewBody = { ...body, password: body.password ? '[redacted]' : undefined }
	const allowed = assertMutationAllowed(flags, 'create-user')
	if (allowed.dryRun) {
		const preview: DryRunResult = {
			dryRun: true,
			method: 'POST',
			path: '/auth/v1/admin/users',
			body: previewBody,
		}
		return preview
	}
	const live = asLive<{ data: unknown }>(
		await projectRequest({
			auth: projectAuth(input),
			method: 'POST',
			path: '/auth/v1/admin/users',
			body,
		}),
	)
	return summarizeAuthUser(live.data, true)
}

/**
 * Create an Auth user. Requires `confirm: true`, or use `dryRun: true`.
 * @example
 * import createUser from 'kody:@kody/supabase/create-user'
 * const preview = await createUser({ projectRef: 'abcdefghijklmnop', email: 'ada@example.com', dryRun: true })
 */
export default async function createUserEntrypoint(
	params: Partial<CreateUserInput> & Record<string, unknown> = {},
) {
	return await createUser({
		account: optionalString(params.account, 'account'),
		secretName: optionalString(params.secretName, 'secretName'),
		serviceRoleSecretName: optionalString(
			params.serviceRoleSecretName,
			'serviceRoleSecretName',
		),
		projectRef: optionalString(params.projectRef, 'projectRef'),
		projectUrl: optionalString(params.projectUrl, 'projectUrl'),
		email: optionalString(params.email, 'email'),
		phone: optionalString(params.phone, 'phone'),
		password: optionalString(params.password, 'password'),
		emailConfirm: optionalBoolean(params.emailConfirm, 'emailConfirm'),
		phoneConfirm: optionalBoolean(params.phoneConfirm, 'phoneConfirm'),
		userMetadata: isRecord(params.userMetadata) ? params.userMetadata : undefined,
		appMetadata: isRecord(params.appMetadata) ? params.appMetadata : undefined,
		dryRun: optionalBoolean(params.dryRun, 'dryRun'),
		confirm: optionalBoolean(params.confirm, 'confirm'),
	})
}