npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

ump-monorepo

v0.0.1

Published

A cross-platform framework for building native apps with React-flavoured JSX. Write once, run on Android, iOS, HarmonyOS, and Web.

Readme

UMP — Universal Mobile Platform

A cross-platform framework for building native apps with React-flavoured JSX. Write once, run on Android, iOS, HarmonyOS, and Web.

UMP mirrors the React + React Native split:

  • ump-corereact — platform-agnostic runtime: JSX runtime (auto, via ump-core/jsx-runtime), hooks, render, module / refresh registry.
  • ump-nativereact-native — native components + platform glue (View / Text / Touchable / StyleSheet / web-standard globals / …). It does not re-export ump-core.

A single C++ runtime hosts the reconciler, style, layout (Yoga), render (Skia), event, and HMR client. Hermes / JSC / JSVM / Browser sit behind a JSEngine adapter.

Quick Start

For a guided walkthrough (scaffold → run on Android/iOS/Harmony/H5 → HMR → device build), see docs/tutorials/quickstart.md. For RN migrators, see docs/tutorials/migrate-from-rn.md.

The shell snippets below assume UMP_REPO points at your local clone:

export UMP_REPO=/path/to/your/ump

After cloning, run ump doctor (from ump-cli) to verify the toolchain — it checks for Xcode, Android SDK + NDK 26.1.10909125, Node ≥ 18, and the vendored hermesc binary used to compile bytecode for release builds. It also validates UMP_REPO: a hard fail if it is unset or does not point at a UMP checkout, and a warning if third_party/skia/lib is missing (native platforms/ builds resolve UMP_ROOT from this env and need the Skia slices).

npm install ump-native ump-core
# CLI for scaffolding / dev-server / device run is shipped separately.
# Until ump-cli is published to npm, run it from the repo:
#   node packages/ump-cli/src/index.js <subcommand>
# A `ump` shim that points at the cloned repo is the recommended setup.

The TypeScript / JSX setup uses the automatic JSX runtime (jsx: "react-jsx", jsxImportSource: "ump-core") — no per-file import { createElement, Fragment } from 'ump-core' is required; <View> and <></> resolve against ump-core/jsx-runtime.

// App.tsx
import { useState } from 'ump-core';
import { View, Text, Pressable, StyleSheet } from 'ump-native';

const styles = StyleSheet.create({
    container: { flex: 1, justifyContent: 'center', alignItems: 'center' },
    title: { fontSize: 24, fontWeight: 'bold' },
    button: { padding: 16, backgroundColor: '#4299E1', borderRadius: 8 },
});

export default function App() {
    const [count, setCount] = useState(0);
    return (
        <View style={styles.container}>
            <Text style={styles.title}>Count: {count}</Text>
            <Pressable style={styles.button} onPress={() => setCount(count + 1)}>
                <Text style={{ color: '#FFF' }}>+1</Text>
            </Pressable>
        </View>
    );
}
// index.tsx — entry point
import 'ump-core';                 // initialise module/refresh runtime
import { AppRegistry } from 'ump-native';
import App from './App';

AppRegistry.registerComponent('MyApp', () => App);
AppRegistry.runApplication('MyApp');

Components

Legend: ✅ supported · ⚠️ JS-shape ready, native wiring partial / placeholder · ❌ not yet.

The components table now carries no ⚠️ rows — the per-platform native-wiring follow-ups (originally tracked in docs/superpowers/plans/2026-05-27-native-followup-6-items.md, the trigger list) have all shipped, most recently RefreshControl's pull-to-refresh gesture on all 4 platforms.

Picker / DatePicker previously carried ⚠️ marks waiting on native UIPickerView / Spinner / UIDatePicker hand-offs. They are now ✅: shipping a uniform JS composition (Pressable list / numeric TextInput row) across all native platforms is the intended design for UMP's mini-program / app-shell target — not a "placeholder". Apps that need OS-native pickers can fork the plugin and add a createNativeViewComponent host (see packages/ump-plugins/video/native/ for the shape).

Layout & primitives

| Component | iOS | Android | Harmony | H5 | Notes | |-----------------------|-----|---------|---------|-----|-------| | View | ✅ | ✅ | ✅ | ✅ | Flexbox container | | Text | ✅ | ✅ | ✅ | ✅ | Skia-rendered (not a native TextView). Supports letterSpacing / wordSpacing;native wordSpacing currently recognizes ASCII space only and complex grapheme shaping is not fully browser-equivalent. selectable is honored on h5 (CSS user-select: text) but is a no-op on iOS / Android / Harmony — UMP would need Skia-side selection state + per-platform copy menus, tracked as a roadmap item. A dev console.warn fires when you set it on native. | | Image | ✅ | ✅ | ✅ | ✅ | Async loading + resizeMode | | ScrollView | ✅ | ✅ | ✅ | ✅ | Imperative ScrollViewHandle | | FlatList | ✅ | ✅ | ✅ | ✅ | Virtualised (verified 1000+ items) | | SectionList | ✅ | ✅ | ✅ | ✅ | Section headers + virtualisation (via ump-plugin-sectionlist) | | VirtualizedList | ✅ | ✅ | ✅ | ✅ | Generic data-source-agnostic windowing; backs FlatList / SectionList | | IndexedStack | ✅ | ✅ | ✅ | ✅ | Persisted children, only one shown | | SafeAreaView | ✅ | ✅ | ✅ | ✅ | Edges + insets prop | | KeyboardAvoidingView| ✅ | ✅ | ✅ | ✅ | IME height shrinks layout | | MaskedView | ✅ | ✅ | ✅ | ✅ | Composite mask (Skia layer on native, <svg> clipPath on H5) |

Inputs & controls

| Component | iOS | Android | Harmony | H5 | Notes | |-----------------------|-----|---------|---------|-----|-------| | Pressable | ✅ | ✅ | ✅ | ✅ | pressedStyle, hoverStyle, hitSlop, rippleStyle (Material press ripple — same Skia animator on all 3 native platforms; h5 is a v1 no-op) | | TouchableOpacity | ✅ | ✅ | ✅ | ✅ | RN-shape wrapper over Pressable | | TextInput | ✅ | ✅ | ✅ | ✅ | 8 keyboard props + capitalize / submit / focus. Text value + placeholder inherit letterSpacing / wordSpacing. Harmony maps keyboardType / returnKeyType to inputMethod.TextInputType.{NUMBER,EMAIL_ADDRESS,...} + EnterKeyType.{NEXT,SEARCH,...} via Index.ets UMPImeConfig listener (9a269a8) | | Switch | ✅ | ✅ | ✅ | ✅ | | | Slider | ✅ | ✅ | ✅ | ✅ | (via ump-component) | | Picker / PickerItem| ✅ | ✅ | ✅ | ✅ | Ships a uniform Pressable list across all 4 platforms (H5 included) by design — UMP targets mini-program / app-shell apps where consistent cross-platform UX is preferred over per-OS native pickers. There is no platform branch, so H5 uses the same JS composition (not a native <select>). Apps that need UIPickerView / Spinner / Harmony wheel / <select> can wrap a createNativeViewComponent host (via ump-component) | | DatePicker | ✅ | ✅ | ✅ | ✅ | mode='date'\|'time'\|'datetime'. Ships a numeric-TextInput row across all 4 platforms (H5 included) by design (mini-program UX consistency). There is no platform branch, so H5 uses the same JS composition (not a native <input type="date\|time\|datetime-local">). Apps that need UIDatePicker / android.widget.DatePicker / Harmony wheel / <input type=date> can wrap a native host (via ump-component) | | RefreshControl | ✅ | ✅ | ✅ | ✅ | Spinner UI + controlled refreshing render, and the pull-to-refresh gesture is wired on all 4 platforms: native ScrollNode runs an over-pull state machine (begin_or_continue_overpull / release_overpull) and UMPRuntime fires onRefresh on armed release; H5 ScrollView runs an equivalent rubber-band pull gesture. Controlled — the control retracts only when the app sets refreshing false | | Form / FormField | ✅ | ✅ | ✅ | ✅ | Pure-JS Flutter-style declarative form validation |

Surfaces & overlays

| Component | iOS | Android | Harmony | H5 | Notes | |-----------------------|-----|---------|---------|-----|-------| | Modal | ✅ | ✅ | ✅ | ✅ | Slide / fade animation | | Dialog | ✅ | ✅ | ✅ | ✅ | | | BottomSheet | ✅ | ✅ | ✅ | ✅ | Imperative BottomSheetHandle | | StatusBar | ✅ | ✅ | ✅ | ✅ | Bar style + animation | | ActivityIndicator | ✅ | ✅ | ✅ | ✅ | | | ProgressBar | ✅ | ✅ | ✅ | ✅ | (via ump-component) | | AnimatedSwitcher | ✅ | ✅ | ✅ | ✅ | Cross-fade between children | | Toast | ✅ | ✅ | ✅ | ✅ | Short transient message, 3 durations | | Alert | ✅ | ✅ | ✅ | ✅ | Title + message + buttons; native modal on each platform | | Hero | ✅ | ✅ | ✅ | ✅ | Shared-element morph: source unmount snapshots its rect into HeroController; destination mount morphs from that rect to its own layout via Animated.timing on translate+scale (uniform). 280ms default; snapshots older than 30s drop |

Media & native views

| Component | iOS | Android | Harmony | H5 | Notes | |-----------------------|-----|---------|---------|-----|-------| | Canvas | ✅ | ✅ | ✅ | ✅ | Skia path / paint draw API. 2D text supports letterSpacing / wordSpacing for fillText / strokeText / measureText().width | | Video | ✅ | ✅ | ✅ | ✅ | iOS AVPlayer + Android ExoPlayer via the plugin's native/cpp/video_manager.cpp; Harmony routes through UMPVideoHost.ets (ArkUI native-view registry) over media.AVPlayer (via ump-plugin-video) | | Audio | ✅ | ✅ | ✅ | ✅ | Web-shape HTMLAudioElement / HTMLMediaElement family (the classes themselves stay in ump-native as runtime backbone); native player via the plugin's audio_manager.cpp per platform. controls prop is forwarded but UI must be built with Pressable + ProgressBar (no native chrome) (via ump-plugin-audio) | | WebView | ✅ | ✅ | ✅ | ✅ | iOS WKWebView + Android JNI via the plugin's native/cpp/webview_manager.cpp; Harmony routes through UMPWebViewHost.ets. Security: injectedJavaScript / executeJs are eval-equivalent — never feed onMessage payloads back without sanitisation (via ump-plugin-webview) |

Cross-platform utilities (non-component exports)

| Symbol | Notes | |-------------------------------|-------| | Link | Routes via app scheme (in-app router) or Linking.openUrl (external) | | Vibration | Tactile feedback. iOS pattern arrays degrade to a single tap per platform-hard-limit | | Matrix4 | Column-major 4×4 affine transform helpers (@ohos.matrix4 parity) (via ump-plugin-matrix4) | | LayoutAnimation | RN-shape configureNext surface. v1 records the config; native consumption is a follow-up | | Worker / runOnWorker | Off-main-thread JS. Native ships per-Worker JSEngine isolates (Hermes / JSC / JSVM) and the runOnWorker runtime-worklet sugar; H5 re-exports the browser's native Worker (use postMessage directly — runOnWorker is native-only) (via ump-plugin-worker) |

Accessibility

label / role / state / hint / liveRegion / testID props are accepted on every component. Native AT bridges (TalkBack / VoiceOver / Harmony screen reader) are wired on all three platforms. Harmony's harmony_a11y shim broadcasts announcements + node-level property updates and queries is_screen_reader_active for runtime gating; real-device validation pending.

API surface

Animation

  • Animated, AnimatedValue, Easing, timing, spring, composers (parallel / sequence / stagger / loop / delay), useAnimatedValue, createAnimatedComponent.
  • Reanimated (SharedValue / useSharedValue / useAnimatedStyle, Tier-1) — sugar over AnimatedValue reusing the same C++ ValueRegistry slot. The dev-server worklet plugin rewrites useAnimatedStyle callsites at build time. H5 fallback runs the same surface over requestAnimationFrame with useState-driven re-renders; the build-time worklet plugin is platform-agnostic. (via ump-plugin-reanimated)

Gestures

Gesture recognizers: pan, press, scale, pinch, rotate. GestureArena arbitrates between contenders. Multi-touch wiring verified across all three native platforms — Harmony's NAPI touchPoints[] loop, Android's ACTION_POINTER_DOWN/UP + getPointerCount() JNI fan-out, and iOS's event.allTouches with stable UITouch*pointer_id mapping via NSMapTable. Real-device multi-pointer recognizer validation still pending.

Streams

ReadableStream, WritableStream, TransformStream and their default-controller / writer types — installed onto globalThis non-overwriting.

Workers

Worker (postMessage / terminate, structured-clone payloads, postTaskWithTransfer for ArrayBuffer hand-off, runOnWorker for runtime worklet dispatch).

On native, each Worker owns its own JSEngine instance constructed on the worker thread (Hermes / JSC / JSVM per platform) — main-thread state is never visible from the worker. isAlive() reports the engine-ready / terminated state; terminate() joins the thread and tears the engine down before the next spawn.

On H5, the browser's native Worker is re-exported unchanged; pass a data:application/javascript,... URL or a worker module URL and use the standard postMessage / onmessage pair. The runOnWorker sugar is native-only — calling it on H5 throws (use a real Worker + postMessage in browsers).

Web Standards globals

All of these install on globalThis as a side-effect of importing ump-native, non-overwriting (a host-provided implementation always wins). Each is also re-exported as a named import.

| Group | APIs | |--------------|------| | Network | fetch, Headers, Request, Response, AbortController, AbortSignal, Blob, File, FormData. WebSocket (via ump-plugin-websocket); EventSource (via ump-plugin-eventsource) | | Storage | localStorage, sessionStorage, Storage. indexedDB family — IDBDatabase, IDBObjectStore, IDBTransaction, IDBCursor (via ump-plugin-indexeddb) | | Streams | ReadableStream, WritableStream, TransformStream (+ controllers / writers), TextEncoderStream, TextDecoderStream | | DOM events | EventTarget, Event, CustomEvent, MessageEvent, ErrorEvent, PromiseRejectionEvent. CloseEvent (via ump-plugin-websocket) | | Files | FileReader, URL.createObjectURL / revokeObjectURL | | Encoding | TextEncoder, TextDecoder, atob, btoa | | Crypto | crypto.getRandomValues, crypto.randomUUID, crypto.subtle.digest (SHA-1 / -256 / -384 / -512) | | URL | URL, URLSearchParams | | Scheduling | setTimeout / setInterval / clearTimeout / clearInterval, requestAnimationFrame / cancelAnimationFrame, requestIdleCallback / cancelIdleCallback, queueMicrotask | | Performance | performance.now/mark/measure/getEntries…, PerformanceObserver (via ump-plugin-performance) | | Observers | MutationObserver. IntersectionObserver, ResizeObserver (via ump-plugin-observers) | | Channels | MessageChannel, MessagePort. BroadcastChannel¹ (via ump-plugin-broadcast-channel) | | Cloning | structuredClone | | Memory | WeakRef, FinalizationRegistry | | Canvas | OffscreenCanvas, ImageBitmap, createImageBitmap, Element.animate() (Web Animations API) | | Media (peer) | RTCPeerConnection, RTCSessionDescription, RTCIceCandidate, RTCDataChannel (JS-shape only, native peer connection is a follow-up) (via ump-plugin-webrtc) | | Notifications| Notification (JS shim — request permission + show; permission gate per platform) (via ump-plugin-notification) | | History | globalThis.history (pushState / replaceState / back / forward / go / popstate) | | Viewport | globalThis.matchMedia, innerWidth, innerHeight, devicePixelRatio, visualViewport | | Connectivity | navigator.onLine + online / offline events | | navigator | clipboard, connection, geolocation, vibrate, userAgent, language, permissions, share | | Internationalization | Intl (FormatJS-backed polyfill on hosts where Intl is missing or broken — Hermes 0.82) |

¹ BroadcastChannel on native is single-realm: messages reach peers in the same JS realm only. The web spec also fans out across same-origin tabs / iframes / workers — that cross-realm delivery is a no-op on Android / iOS / Harmony because there is no equivalent IPC layer. Note: each Worker (see Worker.ts) runs in its own JSI isolate with its own module graph, so a channel created in a worker does NOT see messages from one on the main thread; use the worker handle's postMessage/onmessage pair for thread-to-thread comms. Use a platform bridge (Android ContentProvider, iOS CFMessagePort, Harmony RPC) for cross-process messaging. On the H5 peer the browser's native BroadcastChannel is re-exported and works across tabs as expected.

Strategy in one line: prefer web naming and shapes; only break out of the ES-module pattern when the spec demands a global (fetch / requestAnimationFrame / similar). Both module imports (import { fetch } from 'ump-native') and the global (globalThis.fetch) work, with the host-provided implementation always winning when present.

Architecture

┌───────────────────────────────────────────────────────────┐
│  User code  ·  JSX/TSX  ·  StyleSheet                     │
├───────────────────────────────────────────────────────────┤
│  ump-native     Components · Web-standard globals         │
│                 Animated · Reanimated · Gesture · Streams │
├───────────────────────────────────────────────────────────┤
│  ump-core       createElement · hooks · render · Fragment │
│                 module-runtime · refresh-runtime          │
├───────────────────────────────────────────────────────────┤
│  ump-dev-server esbuild + __ump_define plugin             │
│                 refresh-inject · worklet · ws HMR server  │
├───────────────────────────────────────────────────────────┤
│  C++ Runtime    Reconciler · Style · Yoga · Skia · Event  │
│                 HmrClient (libwebsockets) · ValueRegistry │
├──────────┬─────────┬───────────┬──────────────────────────┤
│ Android  │  iOS    │ HarmonyOS │  H5 (Web)                │
│ Hermes   │  JSC    │  JSVM     │  Browser                 │
│ EGL+Skia │Metal+Skia│Vulkan+Skia│ DOM+CSS                 │
│          │         │  +raster  │                          │
└──────────┴─────────┴───────────┴──────────────────────────┘

+raster on HarmonyOS: when Vulkan is unavailable (emulator) the adapter falls back to CPU rasterisation directly into the OHNativeWindow buffer via SkSurfaces::WrapPixels. Real devices keep the Vulkan path.

The plugin model (ump-plugin-core) lets host apps register router / storage / session-storage / biometrics / image-picker / share / filesystem / permissions modules behind a uniform UMPPlugin interface — autolinked into the native build by ump autolink and into JS at runtime via PluginManager.

Project layout

runtime/              C++ core (platform-agnostic)
├── core/             Types, CommandQueue, BundleVerifier, UpdateManager
├── engine/           JSEngine abstraction + Hermes/JSC/JSVM adapters
├── reconciler/       Fiber tree, Diff, Hooks, JS Bridge, ValueRegistry
├── style/            CSS parser, StyleStore
├── layout/           Yoga Flexbox wrapper
├── render/           RenderNode, ViewNode, TextNode, Pipeline
├── event/            Touch, HitTest, Gesture recognizers, GestureArena
├── hmr/              C++ HMR client (libwebsockets, UMP_ENABLE_HMR)
└── platform/         PlatformAdapter + Android/iOS/HarmonyOS adapters

platforms/
├── android/          Native Android app (Skia)
├── ios/              Native iOS app (Metal + Skia)
├── harmony/          HarmonyOS app (Vulkan + Skia)
└── h5/               Web app (DOM + CSS, esbuild via ump-dev-server)

packages/
├── ump-core/         Platform-agnostic runtime (createElement, hooks, render)
├── ump-native/       Native components + web-standard globals
├── ump-h5-native/    H5 mirror of ump-native (DOM-backed)
├── ump-test-utils/   Snapshot + dts-surface helpers (private)
├── ump-dev-server/   esbuild build + Fast Refresh HMR + worklet plugin
├── ump-cli/          ump create/dev/build/run/doctor/publish/bundle-analyze
└── ump-plugins/      router / storage / biometrics / permissions /
                      svg / video / webview / audio / worker / webrtc /
                      notification / eventsource / websocket / observers /
                      indexeddb / performance / reanimated / sectionlist /
                      matrix4 / picker / slider / datepicker / progressbar /
                      broadcast-channel / …

examples/             60+ demo apps (counter, FlatList, Reanimated, Worker,
                      Canvas, Video, WebView, BottomSheet, plugin/API demos, …)

scripts/              Build helpers
└── build-skia-ios.sh Cross-compile Skia for ios-arm64 + ios-arm64-sim

CLI

| Command | Description | |---------|-------------| | ump create <name> | Scaffold a new app project | | ump create-plugin <name> | Scaffold a new plugin project | | ump dev -p <platform> | Dev server with Fast Refresh HMR | | ump build -p <platform> | Production build | | ump run -p <platform> | Build and run on device/emulator | | ump doctor | Check development environment | | ump publish | Package signed bundle for dynamic update | | ump autolink | Regenerate native manifests from plugins | | ump plugin-lint | Lint plugin name / ump.native shape / events | | ump plugin-link [path] | Pack a local plugin and install it | | ump bundle-analyze | Per-package + per-module byte breakdown via esbuild metafile |

Fast Refresh

RN-style module-level reload that preserves useState when component signatures are unchanged. See docs/hmr.md for the full reload semantics.

node packages/ump-dev-server/bin.js             # default port 8081
node packages/ump-dev-server/bin.js --hmr-port=8765

Both Android and HarmonyOS emulators reach the host dev-server through 10.0.2.2. Harmony additionally needs ohos.permission.INTERNET in module.json5, otherwise socket() returns EPERM.

TypeScript

The three ship packages (ump-core / ump-native / ump-h5-native) all build to dist/:

cd packages/ump-core      && npm run build:types
cd packages/ump-native    && npm run build:types
cd packages/ump-h5-native && npm run build:types

Each plugin under packages/ump-plugins/* builds to its own lib/ via npm run build (the pretest hook auto-builds before tests).

Each package.json exposes main: dist/index.js, types: dist/index.d.ts. prepublishOnly rebuilds dist/ before publish so consumers always install JS, not TS. dist/ is gitignored — when working from a fresh clone, run build:types in each package before running tests / typecheck across workspaces.

Status

UMP has reached broad React-Native + Web-Standards parity at the JS surface (Hermes 0.82.1 vendored on Android). Touch / Layout / Render / Style / IME backbone is production-ready. The Tier-7 P0/P1 backlog of "JS-shape-ready, native partial" gaps has been cleared in a 17-PR perf round and a 9-PR native-depth round: Worker per-instance isolates, iOS/Android/Harmony multi-touch dispatch, Harmony a11y / IME / KeyboardAvoidingView, Reanimated H5 fallback, and Video / WebView native players are all landed (Harmony Video/WebView route through the ArkUI native-view registry — UMPVideoHost.ets / UMPWebViewHost.ets over media.AVPlayer / webview.WebviewController — rather than the plugin-side C++ managers used on iOS/Android, since OHOS exposes these APIs only at the ArkTS layer; sources live under packages/ump-plugins/{video,webview}/native/ after the carve-out). The one remaining bring-up step is the iOS Skia .a cross-compile — a one-time local run of scripts/build-skia-ios.sh (now fully automated) before the iOS target can link.

Detailed audit reports live under docs/superpowers/audits/. The most recent passes are:

  • 2026-05-26-error-dx-prod-test-perf.md — P1/P2 dev experience.
  • 2026-05-27-security-api-docs-memory-parity.md — security + API stability + memory + parity.
  • 2026-05-27-tier7-recheck.md — re-verification of the Tier-7 follow-up status (95 commits since the 2026-05-22 native-depth pass).

Verified results

  • H5 (Chrome, Puppeteer): counter PASS, FlatList virtualises 1000 items down to 23 visible, zero console errors.
  • Android (emulator, adb): counter / TextInput / Switch / FlatList render; useState preserved across Fast Refresh.
  • HarmonyOS (emulator 6.0.0.112, hdc): counter renders, CPU raster fallback engages when Vulkan unavailable, Fast Refresh end-to-end verified, no fault logs.
  • iOS (Simulator, JavaScriptCore): counter renders and is interactive (onPress increments on tap). Built via the CMake-generated Xcode project; Skia iOS slices are produced once with scripts/build-skia-ios.sh (see iOS — bring-up checklist).

Android — bring-up checklist

# Required: Android SDK + a specific NDK pinned by the runtime CMake.
# `ump doctor` will tell you if either is missing.
sdkmanager "platforms;android-34" "ndk;26.1.10909125"

# Optional but recommended: speeds up cold first build.
export ANDROID_HOME=$HOME/Library/Android/sdk     # macOS default
# export ANDROID_HOME=$HOME/Android/Sdk            # Linux default

# Connect a device or boot an emulator, then:
ump dev -p android        # dev-server + adb push, HMR enabled
ump build -p android      # release APK with hermesc bytecode

platforms/android/app/build.gradle.kts picks the NDK version from ANDROID_NDK_HOME (falling back to the default SDK path). If NDK 26.1.10909125 isn't installed, the configure step fails with a gradle-side message pointing at the missing component.

iOS — bring-up checklist

sudo xcode-select -s /Applications/Xcode.app/Contents/Developer

# One-time prep already done locally; if you fresh-clone:
mkdir -p ~/work/skia-build && cd ~/work/skia-build
git clone --depth 50 --branch chrome/m149 https://skia.googlesource.com/skia
cd skia && python3 tools/git-sync-deps
brew install gn ninja

# Produce third_party/skia/lib/ios-arm64{,-sim}/*.a (~20 min)
bash $UMP_REPO/scripts/build-skia-ios.sh

# Generate the Xcode project with the iOS toolchain flags (there is no
# checked-in .xcodeproj — CMake produces it), then build / run:
cmake -G Xcode -S platforms/ios -B platforms/ios/build \
    -DCMAKE_SYSTEM_NAME=iOS -DCMAKE_OSX_SYSROOT=iphonesimulator \
    -DCMAKE_OSX_ARCHITECTURES=arm64 -DCMAKE_BUILD_TYPE=Debug -DUMP_ENABLE_HMR=OFF
cmake --build platforms/ios/build --config Debug
xcrun simctl install booted platforms/ios/build/Debug-iphonesimulator/UMPApp.app
xcrun simctl launch booted com.ump.app
# (or open platforms/ios/build/UMPApp.xcodeproj in Xcode and Run)

platforms/ios/CMakeLists.txt picks ios-arm64-sim or ios-arm64 automatically based on CMAKE_OSX_SYSROOT. If libskia.a is missing the project still configures (with a warning); only the link step fails.

HarmonyOS — bring-up checklist

# Required: HarmonyOS SDK (HMS) + DevEco Studio.
# Download from https://developer.huawei.com/consumer/en/deveco-studio/
# Then expose:
export DEVECO_SDK_HOME=/Applications/DevEco-Studio.app/Contents/sdk
# Linux: ~/Huawei/DevEcoStudio/sdk

# Open the Harmony project in DevEco:
deveco-studio platforms/harmony/

# Or build from CLI via hvigorw (bundled with the SDK):
cd platforms/harmony && hvigorw assembleHap

platforms/harmony/entry/src/main/module.json5 declares the runtime permissions UMP relies on (ohos.permission.INTERNET, ohos.permission.GET_NETWORK_INFO, plus camera / location / mic / read-media when the corresponding plugin is active). Edit that file — not config.json — to add or remove permissions.

The Harmony peer ships a Vulkan-backed Skia path; on the emulator Vulkan is unavailable and the runtime falls back to +raster automatically. No manual flag needed.

Plugins

import { PluginManager } from 'ump-plugin-core';
import { routerPlugin } from 'ump-plugin-router';
import { sessionStoragePlugin } from 'ump-plugin-session-storage';

const pm = new PluginManager();
pm.register(routerPlugin({ routes: [/* … */] }));
pm.register(sessionStoragePlugin({ prefix: 'app' }));

pm.runAppDidMount(app);

Persistent key/value storage uses the web-standard globalThis.localStorage directly from ump-native — no plugin required. Use ump-plugin-session-storage for prefix-scoped session-lifetime storage on top of globalThis.sessionStorage.

Each plugin declares ump-core as a peerDependency so a single runtime instance is shared across the host app and its plugins. See packages/ump-plugins/ for the full list.

Reference docs

详细 API 文档:

  • docs/components/ — 每个组件的 props / 方法 / 示例 / 平台差异。
  • docs/api/ — Animated / Linking / Vibration 等 ump-native 工具,web-standards 偏离点,以及 plugin 暴露的非组件 API。
  • docs/css.md — StyleSheet 实际支持的 CSS 属性、单位、颜色、变换、过渡 / 动画、阴影,以及不支持的属性清单。

License

MIT