Upwell

Core API

decide(), explain(), validators, version comparison, rollout, release helpers, sources and simulation.

Everything here is exported from upwell.

Engine

decide

function decide(input: DecisionInput): Decision;

The heart of the library: what kind of update does this install need right now? Pure and synchronous. See Decision engine.

explain

function explain(input: DecisionInput): { decision: Decision; trace: string[] };

Same as decide(), plus every rule the engine evaluated.

DecisionInput

Prop

Type

Helpers

Export
DEFAULT_ENGINE_CONFIG{ snoozeHours: 24, maxPrompts: 3, promptPeriodHours: 168 }
compareInstalled(installed, ref)-1 | 0 | 1, comparing an install to a policy version reference the way the engine does (Android: build; iOS: version, then build)
targetKey(target)The stable key snoozes are scoped to, e.g. "build:340"

Validation

function validatePolicy(input: unknown): ValidationResult<Policy>;
function validateReleases(input: unknown): ValidationResult<ReleasesDocument>;

type ValidationResult<T> =
  | { ok: true; value: T; warnings: string[] }
  | { ok: false; errors: string[]; warnings: string[] };

Never throws. Messages include the path, e.g. policy.android.rollout: must be a number from 0 to 100. See Validation.

Versions

compareVersions

function compareVersions(a: string | number, b: string | number): -1 | 0 | 1;

compareVersions('1.10.0', '1.9.9'); // 1
compareVersions(340, '300');        // 1
compareVersions('1.2', '1.2.0');    // 0
compareVersions('1.0.0-beta', '1.0.0'); // -1

Dotted numeric comparison: missing segments count as 0, a leading v and +build metadata are ignored, and pre-release tags sort before the release (semver rules).

isComparableVersion

function isComparableVersion(v: unknown): boolean;

Whether a value is something compareVersions can handle: a non-negative integer, or a string like 3.4.0, v2, 1.0.0-rc.1.

Rollout

Export
rolloutBucket(installationId, build)Stable 0–99 bucket for an install and a target build
isInRollout(installationId, build, rollout)bucket < rollout. undefined or ≥ 100 means everyone, ≤ 0 means no one
fnv1a(input)The 32-bit FNV-1a hash behind the buckets

Releases

Export
findRelease(doc, { platform, updateIds })The entry whose id matches any of updateIds (case-insensitive) for this platform
releaseHistory(doc, { platform, runtimeVersion })Entries for this platform and runtime, newest first

Sources

Export
jsonUrl(url, options?)Fetches JSON with Cache-Control: no-cache. Options: headers, cacheBust, fetch. See Hosting
staticSource(value)Always returns value
PolicySource, ReleasesSource, Source<T>{ fetch(signal: AbortSignal): Promise<T> }

Platform

Export
createNativePlatform()The device platform backed by the native module, or null when it isn't linked (Expo Go, web, tests)
getUpwellNative()The raw native module, or null
openStoreUrl(url, appId?)Opens a store URL. On Android, Play URLs go through market://
lookupAppStore(options)The iTunes lookup: { version, releaseDate, minimumOsVersion, trackId, trackViewUrl } or null
appStoreLookupUrl(options)The lookup URL, with country and a cache-busting parameter
createMemoryStorage()An in-memory KeyValueStorage

lookupAppStore options: bundleId (required), appStoreId, country, timeoutMs, fetch, now.

Simulation

Export
createSimulation(scenario, options?)Builds { platform, ota, policy, policyError, releases } fakes for a scenario. Call platform.dispose() when done
SIMULATION_SCENARIOSEvery SimulationScenario
SimulatedPlatform, SimulatedOtaThe fake device/store and OTA tool

See Testing and simulation.

On this page