Skip to content

Kody is live

Watch the launch video — what Kody is, and why it exists.

← Public packages

@kentcdodds/package-app-kit

Design tokens, PWA install/update, About/version, cache helpers, and optional realtime notes sync for Kody package apps.

starter-fetch/src/client/config.ts

78 lines · 2.5 KB · TypeScript
/** Read kit client config from `<html data-*>` and optional JSON in `[data-pak-config]`. */

export type ClientConfig = {
	appBase: string
	assetBase: string
	clientModuleUrl: string
	hostedUrl: string
	runningSha: string
	appName: string
	appLabel: string
	hintDismissKey: string
	splashDismissKey: string
	versionPath: string
	routes: string[]
	/** Absolute or app-relative SW script URL used for registration. */
	swUrl: string
}

export function readClientConfig(): ClientConfig {
	const root = document.documentElement
	let json: Record<string, unknown> = {}
	const node = document.querySelector('[data-pak-config]')
	if (node) {
		const raw = node.textContent || node.getAttribute('data-pak-config') || ''
		if (raw.trim()) {
			try {
				json = JSON.parse(raw) as Record<string, unknown>
			} catch {
				json = {}
			}
		}
	}
	const appBase = String(json.appBase ?? root.getAttribute('data-app-base') ?? '').replace(
		/\/$/,
		'',
	)
	const assetBase = String(
		json.assetBase ?? root.getAttribute('data-asset-base') ?? (appBase ? `${appBase}/_assets` : ''),
	).replace(/\/$/, '')
	const clientModuleUrl = String(
		json.clientModuleUrl ?? root.getAttribute('data-client-module') ?? '',
	)
	// Prefer platform assets SW when asset base is stamped; else Worker /sw.js interim.
	const swFromAttr = root.getAttribute('data-sw-url') || ''
	const swUrl = String(
		json.swUrl ??
			swFromAttr ??
			(assetBase && root.getAttribute('data-asset-base')
				? `${assetBase}/sw.js`
				: `${appBase}/sw.js`),
	)
	return {
		appBase,
		assetBase,
		clientModuleUrl,
		hostedUrl: String(json.hostedUrl ?? root.getAttribute('data-hosted-url') ?? ''),
		runningSha: String(json.runningSha ?? root.getAttribute('data-running-sha') ?? ''),
		appName: String(json.appName ?? root.getAttribute('data-app-name') ?? 'this app'),
		appLabel: String(json.appLabel ?? root.getAttribute('data-app-label') ?? 'app'),
		hintDismissKey: String(
			json.hintDismissKey ??
				root.getAttribute('data-install-hint-key') ??
				'pak-install-hint-dismissed',
		),
		splashDismissKey: String(
			json.splashDismissKey ??
				root.getAttribute('data-splash-key') ??
				'pak-first-run-splash-dismissed',
		),
		versionPath: String(json.versionPath ?? root.getAttribute('data-version-path') ?? '/api/version'),
		routes: Array.isArray(json.routes) ? json.routes.map(String) : ['/', '/about'],
		swUrl,
	}
}

export function apiUrl(config: ClientConfig, path: string) {
	return `${config.appBase}${path.startsWith('/') ? path : `/${path}`}`
}