/** * @fileoverview Thin wrapper around `update-notifier` that checks npm * hourly for a newer version of opensip-cli (see UPDATE_CHECK_INTERVAL_MS). * * `update-notifier` is used purely as the *fetcher* — it runs the throttled * (hourly), detached, non-blocking network check. It is NOT used as the * display source, because its `check()` deletes the cached result the instant * it's read, which would make the notice show at most once per fetch cycle. * Instead the newest known version is mirrored into a sticky store * (`update-state.ts`) that {@link checkForUpdate} consults on EVERY run, so * the notice persists until the running version catches up. See * `update-state.ts` for the rationale in full. * * Two consumers, one resolved result: * - {@link checkForUpdate} returns the newer version string (if any) so * the banner can surface it inline as `( available)`. * - {@link formatUpdateNag} builds the stderr one-liner shown for * bannerless JSON output. * The bootstrap calls `checkForUpdate` once and decides which surface to use. * * Outbound policy (ADR-0073): default-on TTY product update I/O — not * telemetry. Stores only `{ latest }` in update-state; no user paths or secrets. * * Design goals: * - Silent by default when there's nothing to report. * - Persistent while a genuinely newer release exists; self-clearing the * run after the user upgrades. * - Opt-out via OPENSIP_NO_UPDATE (and honours the upstream * NO_UPDATE_NOTIFIER flag for convention). * - Non-blocking: the check runs in a child process; the current * command never waits on network I/O. * * Suppressed when: * - Env var OPENSIP_NO_UPDATE=1 (our flag) * - Env var NO_UPDATE_NOTIFIER=1 (convention for the npm package) * - Env var CI set (update-notifier suppresses by default) * - stdout is not a TTY (scripts, pipelines) */ import { type UpdateNotifier } from 'update-notifier'; /** * How often the detached background fetch may hit npm to learn the latest * published version. `update-notifier` throttles its network check to this * interval; the sticky store (`update-state.ts`) then drives *display* on * every run. This is therefore the worst-case DETECTION latency: a freshly * published release becomes visible within one interval of going live (on the * run after the next fetch completes — the fetch is detached, so never the * same run). * * Set to 1 hour rather than the conventional 24h: the fetch is non-blocking, * so a shorter interval costs the user nothing at the command line and only a * modest amount of extra npm traffic (≤1 request/hour/user), while shrinking * the "I published but the CLI still says up-to-date" window from a day to an * hour. One named constant so the two call sites can't drift. */ export declare const UPDATE_CHECK_INTERVAL_MS: number; export interface NotifyOptions { readonly name: string; readonly version: string; /** Override stderr writer (for tests). */ readonly write?: (s: string) => void; } export interface CheckForUpdateOptions { readonly name: string; readonly version: string; /** * Override the sticky update-state file path (for tests). Defaults to * `~/.opensip-cli/update-state.json`. */ readonly stateFile?: string; } /** * Strict semver "is `latest` newer than `current`". Compares the numeric * MAJOR.MINOR.PATCH core and prerelease identifiers according to SemVer. * * This is the guard the nag relies on: the previous `latest !== current` * check fired on ANY difference, so running a build AHEAD of npm's `latest` * (a local dev build, or a prerelease) wrongly prompted a "downgrade". We * only notify when there is a genuinely newer release to move TO. */ export declare function isNewerVersion(latest: string, current: string): boolean; /** * Resolve whether a newer published version is available, scheduling the * hourly background fetch as a side effect. Returns the newer version * string (e.g. `1.0.1`) when one is known, or `undefined` when up-to-date, * opted-out, or non-TTY. * * Unlike `update-notifier`'s own delete-on-read result, this is **sticky**: * the newest version the hourly check observes is mirrored into a small store * (`update-state.ts`) that is read on EVERY run, so the notice persists until * the running version catches up — at which point the store is cleared and * the notice stops on its own. * * Never throws: a malformed cache or notifier failure degrades to "no update * known" rather than breaking the command. Callers decide how to surface it: * the banner inline for human output, or {@link formatUpdateNag} on stderr for * bannerless JSON output. */ export declare function checkForUpdate(opts: CheckForUpdateOptions): string | undefined; /** * Build the stderr update-nag line for the bannerless `--json` path. Human TTY * output surfaces the same information in the banner box. */ export declare function formatUpdateNag(current: string, latest: string): string; /** * Run the update check. Returns the notifier instance (so tests can * assert) or null if skipped. */ export declare function maybeNotify(opts: NotifyOptions): UpdateNotifier | null; //# sourceMappingURL=update-notifier.d.ts.map