Skip to content
← Public packages

@kody/browser-run

Call Cloudflare Browser Run Quick Actions and reuse shared sessions with createBrowserContext.

src/sessions/close.ts

83 lines · 2.4 KB · TypeScript
import { boolean, object, optional, parse, string } from 'remix/data-schema'
import {
	accountPath,
	browserRunFetch,
	pickAuth,
	requireAccountId,
} from '../client.ts'

const closeInput = object(
	{
		accountId: string(),
		sessionId: string(),
		apiTokenSecret: optional(string()),
		dryRun: optional(boolean()),
		confirm: optional(boolean()),
	},
	{ unknownKeys: 'error' },
)

/**
 * Close a Browser Run session
 * (`DELETE .../devtools/browser/{sessionId}`).
 * Destructive for every client on that session — requires `confirm: true`
 * (or `dryRun: true` to preview). Prefer `context.close()` + `disconnect`
 * for shared-session cleanup instead of closing the whole browser.
 *
 * @param raw.accountId - Cloudflare account id
 * @param raw.sessionId - Session to close
 * @param raw.confirm - Must be true for the live DELETE
 * @param raw.dryRun - Preview without calling CF
 * @param raw.apiTokenSecret - Optional alternate secret name
 * @returns Close status or dry-run preview
 *
 * @example
 * import closeSession from 'kody:@kody/browser-run/sessions/close'
 * const preview = await closeSession({
 *   accountId: 'YOUR_ACCOUNT_ID',
 *   sessionId: '182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e',
 *   dryRun: true,
 * })
 */
export default async function closeSession(raw: unknown = {}) {
	const input = parse(closeInput, raw ?? {})
	const auth = pickAuth(input)
	const accountId = requireAccountId(auth)
	const sessionId = String(input.sessionId).trim()
	if (!sessionId) throw new Error('sessionId is required.')
	const path = accountPath(
		accountId,
		`/browser-rendering/devtools/browser/${encodeURIComponent(sessionId)}`,
	)

	if (input.dryRun === true) {
		return {
			dryRun: true as const,
			method: 'DELETE' as const,
			path,
			accountId,
			sessionId,
			willCloseSharedBrowser: true,
			warning:
				'Closing the session terminates the shared browser for every concurrent client. Prefer context.close() + browser.disconnect() when sharing.',
		}
	}

	if (input.confirm !== true) {
		throw new Error(
			'Refusing to close a Browser Run session without confirm: true. Pass dryRun: true to preview, or confirm: true to DELETE the shared browser.',
		)
	}

	const response = await browserRunFetch(path, { ...auth, method: 'DELETE' })
	return {
		ok: true,
		accountId,
		sessionId,
		status:
			response.kind === 'json'
				? (response.result as { status?: string })?.status ?? response.result
				: response,
		browserMsUsed: response.browserMsUsed,
	}
}