← Public packages
@kentcdodds/package-app-kit
Design tokens, PWA install/update, About/version, cache helpers, and optional realtime notes sync for Kody package apps.
src/update-check.ts
94 lines · 2.8 KB · TypeScript
import type { AppVersion } from './version.ts'
import { emptyVersion, normalizeVersion } from './version.ts'
export const UPDATE_CHECK_MIN_INTERVAL_MS = 3000
export type UpdateCheckResult = {
updateAvailable: boolean
kind: 'worker' | 'published' | null
runningSha: string
latestSha: string
latest: AppVersion
hasWaitingWorker: boolean
}
/**
* Gate rapid update checks (visibility storms / focus churn).
*
* @example
* import { shouldRunUpdateCheck } from 'kody:@kentcdodds/package-app-kit/update-check'
* shouldRunUpdateCheck({ visible: true, inFlight: false, lastStartedAt: null, now: Date.now() })
*/
export function shouldRunUpdateCheck(input: {
visible: boolean
inFlight: boolean
lastStartedAt: number | null
now: number
minIntervalMs?: number
}): boolean {
if (!input.visible || input.inFlight) return false
if (input.lastStartedAt == null) return true
const gap = input.minIntervalMs ?? UPDATE_CHECK_MIN_INTERVAL_MS
return input.now - input.lastStartedAt >= gap
}
/**
* Classify whether a waiting SW or a newer published SHA means an update.
*
* @example
* import { describeAvailableUpdate } from 'kody:@kentcdodds/package-app-kit/update-check'
* describeAvailableUpdate({ runningSha: 'aaa', latestSha: 'bbb', hasWaitingWorker: false })
*/
export function describeAvailableUpdate(input: {
runningSha: string
latestSha: string
hasWaitingWorker: boolean
}): 'worker' | 'published' | null {
if (input.hasWaitingWorker) return 'worker'
if (input.runningSha && input.latestSha && input.runningSha !== input.latestSha) {
return 'published'
}
return null
}
/**
* Compare the running client version vs recorded/latest publish metadata.
* Use on visit (toast with refresh) and from About's manual check button.
*
* @param input.runningSha - SHA embedded in the current app shell
* @param input.latest - Recorded version from packageStorage / `/api/version`
* @param input.hasWaitingWorker - Whether a SW is waiting to activate
* @returns `{ updateAvailable, kind, ... }` for toast / About UI
*
* @example
* import checkForUpdate from 'kody:@kentcdodds/package-app-kit/update-check'
* const result = checkForUpdate({
* runningSha: 'aaa111',
* latest: { sha: 'bbb222', message: 'Ship' },
* hasWaitingWorker: false,
* })
*/
export default function checkForUpdate(input: {
runningSha?: string
latest?: Partial<AppVersion> | null
hasWaitingWorker?: boolean
}): UpdateCheckResult {
const runningSha = String(input.runningSha ?? '').trim()
const latest = input.latest ? normalizeVersion(input.latest) : emptyVersion()
const hasWaitingWorker = Boolean(input.hasWaitingWorker)
const kind = describeAvailableUpdate({
runningSha,
latestSha: latest.sha,
hasWaitingWorker,
})
return {
updateAvailable: kind != null,
kind,
runningSha,
latestSha: latest.sha,
latest,
hasWaitingWorker,
}
}