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

native-fn

v1.2.0

Published

TypeScript library bridging Web APIs to native device capabilities — Picture-in-Picture, Fullscreen, Clipboard, Geolocation, Battery, Notifications, Vibration, Platform detection, and more.

Downloads

1,549

Readme

native-fn API Reference

NPM Version NPM Downloads GitHub Repo stars Static Badge

Installation

npm

npm i native-fn

yarn

yarn add native-fn

unpkg

<script src="https://unpkg.com/native-fn"></script>

jsdelivr

<script src="https://cdn.jsdelivr.net/npm/native-fn"></script>

Table of Contents

appearance

value · onChange

Signature

get value(): Appearances

Returns the current color scheme of the device.

Example

console.log(Native.appearance.value); // 'dark' | 'light' | 'unknown'

Returns

Appearances
enum Appearances {
    Unknown = 'unknown',
    Light   = 'light',
    Dark    = 'dark',
}

Signature

onChange(listener: (appearance: Appearances) => void, options?: AddEventListenerOptions): () => void

Subscribes to device color scheme changes.

Example

const unsubscribe = Native.appearance.onChange((appearance) => {
    console.log(appearance); // 'dark' | 'light'
});

unsubscribe();

Returns

() => void
// call to remove the listener
unsubscribe();

badge

supported · set · clear

Signature

get supported(): boolean

Returns whether badge is supported in the current environment.

Example

if (Native.badge.supported) {
    await Native.badge.set(5);
}

Returns

boolean
// true  — badge API support,
// false — no badge support

Signature

set(contents: number): Promise<void>

Sets the app badge count.

Example

await Native.badge.set(5);

Returns

Promise<void>

Throws

throw new NotSupportedError // navigator.setAppBadge unavailable

Signature

clear(): Promise<void>

Clears the app badge.

Example

await Native.badge.clear();

Returns

Promise<void>

Throws

throw new NotSupportedError // navigator.setAppBadge unavailable

battery

supported · value · onChange

Signature

get supported(): boolean

Returns whether battery is supported in the current environment.

Example

if (Native.battery.supported) {
    const battery = await Native.battery.value;

    console.log(battery.level); // 0.0 – 1.0
}

Returns

boolean
// true  — battery API support,
// false — no battery support

Signature

get value(): Promise<BatteryManager>

Returns the current battery status.

Example

const battery = await Native.battery.value;

console.log(battery.level);           // 0.0 – 1.0
console.log(battery.charging);        // true | false
console.log(battery.chargingTime);    // seconds until full
console.log(battery.dischargingTime); // seconds until empty

Returns

Promise<BatteryManager>
interface BatteryManager {
    readonly charging:          boolean;
    readonly chargingTime:      number;
    readonly dischargingTime:   number;
    readonly level:             number;
}

Throws

throw new NotSupportedError // navigator.getBattery unavailable

Signature

onChange(listener: (battery: BatteryManager) => void, options?: AddEventListenerOptions): () => void

Subscribes to battery status changes.

Example

const unsubscribe = Native.battery.onChange((battery) => {
    console.log(battery.level);    // 0.0 – 1.0
    console.log(battery.charging); // true | false
});

unsubscribe();

Returns

() => void
// call to remove the listener
unsubscribe();

clipboard

copy · paste

Signature

copy(item: any): Promise<boolean>

Copies a value to the clipboard. Accepts string, Element, Selection, object, or array.

Flowchart

flowchart TD
    A([clipboard.copy called]) --> B[Convert item to text and html]
    B --> C{Clipboard API available?}
    C -->|yes| D[copyViaClipboardAPI]
    C -->|no| E[copyViaLegacy]
    D --> F{Success?}
    F -->|yes| G([return true])
    F -->|no| E
    E --> H{ClipboardItem + write available?}
    H -->|yes| I[navigator.clipboard.write with text/html + text/plain]
    H -->|no| J{writeText available?}
    J -->|yes| K[navigator.clipboard.writeText]
    J -->|no| L[copyViaSelection via execCommand]
    L --> M{execCommand success?}
    M -->|yes| G
    M -->|no| N[copyViaClipboardData IE fallback]
    N --> O([return boolean])

Example

// String
await Native.clipboard.copy('Hello world');

// DOM element (copies outerHTML + textContent)
await Native.clipboard.copy(document.querySelector('.content'));

// Object → serialized as JSON
await Native.clipboard.copy({ key: 'value' });

// Current selection
await Native.clipboard.copy(window.getSelection());

Returns

Promise<boolean>

Signature

paste(): Promise<string>

Reads the current clipboard content as a string.

Flowchart

flowchart TD
    A([clipboard.paste called]) --> B{Clipboard API available?}
    B -->|yes| C[pasteViaClipboardAPI]
    B -->|no| D[pasteViaLegacy]
    C --> E{ClipboardItem + read available?}
    E -->|yes| F[navigator.clipboard.read]
    E -->|no| G[navigator.clipboard.readText]
    F --> H{text/html type found?}
    H -->|yes| I([return html string])
    H -->|no| J{text/plain found?}
    J -->|yes| K([return plain string])
    J -->|no| D
    G --> L{Success?}
    L -->|yes| M([return string])
    L -->|no| D
    D --> N[pasteViaSelection via execCommand]
    N --> O{Success?}
    O -->|yes| P([return string])
    O -->|no| Q[pasteViaClipboardData IE fallback]
    Q --> R([return string])

Example

const text = await Native.clipboard.paste();

console.log(text); // HTML string if available, plain text otherwise

Returns

Promise<string>

dimension

value · environment · onChange

Signature

get value(): Dimensions

Returns current viewport dimensions, device pixel ratio, and orientation.

Example

const { innerWidth, innerHeight, outerWidth, outerHeight, scale, orientation } = Native.dimension.value;

console.log(innerWidth, innerHeight); // visible viewport size
console.log(scale);                   // device pixel ratio e.g. 2, 3

if (orientation === Orientation.Portrait) {
    console.log('Portrait mode');
}

Returns

Dimensions
interface Dimensions {
    outerWidth:  number;
    outerHeight: number;
    innerWidth:  number;
    innerHeight: number;
    scale:       number;
    orientation: Orientation;
}

enum Orientation {
    Portrait  = 'portrait',
    Landscape = 'landscape',
    Unknown   = 'unknown',
}

Signature

environment: Environment

Provides access to CSS environment variable values: safe-area-inset, keyboard-inset, titlebar-area, and viewport-segment.

Example

// Safe area insets (e.g. iPhone notch / Dynamic Island)
const inset = Native.dimension.environment.safeAreaInset.value;
console.log(inset.top, inset.bottom, inset.left, inset.right);

// Virtual keyboard height
const kb = Native.dimension.environment.keyboardInset.value;
console.log(kb.height); // 0 when keyboard is hidden

// Subscribe to safe area changes
const unsubscribe = Native.dimension.environment.safeAreaInset.onChange((inset) => {
    document.body.style.paddingBottom = inset.bottom + 'px';
});
unsubscribe();

Returns

Environment
interface Environment {
    safeAreaInset:    EnvironmentPreset<'safe-area-inset'>;
    safeAreaMaxInset: EnvironmentPreset<'safe-area-max-inset'>;
    keyboardInset:    EnvironmentPreset<'keyboard-inset'>;
    titlebarArea:     EnvironmentPreset<'titlebar-area'>;
    viewportSegment:  EnvironmentPreset<'viewport-segment'>;
}

interface EnvironmentPreset<K> {
    get value(): EnvironmentPresetValues<K>;
    onChange(listener: (value: EnvironmentPresetValues<K>) => void, options?: AddEventListenerOptions): () => void;
}

Signature

onChange(listener: (dimension: Dimensions) => void, options?: AddEventListenerOptions): () => void

Subscribes to viewport dimension and orientation changes.

Example

const unsubscribe = Native.dimension.onChange((dimension) => {
    console.log(dimension.innerWidth, dimension.innerHeight);
    console.log(dimension.orientation); // 'portrait' | 'landscape'
});

unsubscribe();

Returns

() => void
// call to remove the listener
unsubscribe();

fullscreen

supported · element · isFullscreen · request · exit · onChange · onError

Signature

get supported(): boolean

Returns whether fullscreen is supported in the current environment.

Example

if (Native.fullscreen.supported) {
    await Native.fullscreen.request();
}

Returns

boolean
// true  — standard or vendor-prefixed fullscreen API detected,
//         or iOS video with webkitSupportsFullscreen
// false — no fullscreen support

Signature

get element(): Element | null

Returns the element currently displayed in fullscreen, or null if not in fullscreen.

Example

const el = Native.fullscreen.element;

if (el !== null) {
    console.log(el.tagName); // e.g. 'VIDEO', 'DIV'
}

Returns

Element | null
// Element — the current fullscreen element
// null    — not in fullscreen

Signature

get isFullscreen(): boolean

Returns whether fullscreen is currently active.

Example

console.log(Native.fullscreen.isFullscreen); // true | false

Returns

boolean

Signature

request(target?: Element, options?: FullscreenOptions): Promise<void>

Requests fullscreen for an element.

Flowchart

flowchart TD
    A([Fullscreen.request called]) --> B{target defined?}
    B -->|no| C[getDefaultTarget]
    B -->|yes| D{api available?}
    C --> D
    D -->|yes| E[Call element api.request]
    D -->|no| F[fallbackToIOSVideo]
    E --> G{Promise returned?}
    G -->|yes| H{Resolved?}
    H -->|yes| I([resolve])
    H -->|no| F
    G -->|no| I
    F --> J{iOS + VIDEO + webkitSupportsFullscreen?}
    J -->|yes| K[video.webkitEnterFullscreen]
    K --> I
    J -->|no| L([Throw NotSupportedError])

Example

// Default: documentElement on desktop, first video on iOS
await Native.fullscreen.request();

// Specific element
await Native.fullscreen.request(document.getElementById('player'));

// With options
await Native.fullscreen.request(element, { navigationUI: 'hide' });

Returns

Promise<void>

Throws

throw new NotSupportedError // element does not support fullscreen
throw new NotSupportedError // iOS video lacks webkitEnterFullscreen
throw new InvalidStateError // iOS video not yet played

Signature

exit(): Promise<void>

Exits fullscreen.

Flowchart

flowchart TD
    A([Fullscreen.exit called]) --> B{api available?}
    B -->|yes| C[Call document api.exit]
    B -->|no| D[fallbackToIOSVideo]
    C --> E{Promise returned?}
    E -->|yes| F{Resolved?}
    F -->|yes| G([resolve])
    F -->|no| D
    E -->|no| G
    D --> H{iOS + displaying fullscreen video found?}
    H -->|yes| I[webkitExitFullscreen]
    I --> G
    H -->|no| J{getElement null?}
    J -->|yes| G
    J -->|no| K([Throw NotSupportedError])

Example

await Native.fullscreen.exit();

Returns

Promise<void>

Throws

throw new NotSupportedError // failed to exit fullscreen

Signature

onChange(listener: (payload: FullscreenEventPayload) => void, options?: AddEventListenerOptions): () => void

Subscribes to fullscreen state changes.

Example

const unsubscribe = Native.fullscreen.onChange((payload) => {
    console.log(payload.isFullscreen); // true | false
    console.log(payload.element);      // Element
    console.log(payload.nativeEvent);  // Event
});

unsubscribe();

Returns

() => void
// call to remove the listener
unsubscribe();

Signature

onError(listener: (payload: FullscreenEventPayload) => void, options?: AddEventListenerOptions): () => void

Subscribes to fullscreen errors.

Example

const unsubscribe = Native.fullscreen.onError((payload) => {
    console.log(payload.isFullscreen); // boolean
    console.log(payload.element);      // Element
    console.log(payload.nativeEvent);  // Event
});

unsubscribe();

Returns

() => void
// call to remove the listener
unsubscribe();

geolocation

supported · value · onChange

Signature

get supported(): boolean

Returns whether geolocation is supported in the current environment.

Example

if (Native.geolocation.supported) {
    const coords = await Native.geolocation.value;

    console.log(coords.latitude, coords.longitude);
}

Returns

boolean
// true  — geolocation API support,
// false — no geolocation support

Signature

get value(): Promise<GeolocationCoordinates>

Returns the current geographic coordinates. Falls back to IP-based location if the Geolocation API is unavailable or permission is denied.

Flowchart

flowchart TD
    A([geolocation.value called]) --> B{navigator.geolocation supported?}
    B -->|no| F[Fallback to ip-api.com]
    B -->|yes| C[Permission.request geolocation]
    C --> D{Permission granted?}
    D -->|no| F
    D -->|yes| E[getCurrentPosition]
    E --> G{Success?}
    G -->|yes| H([Resolve GeolocationCoordinates])
    G -->|no| F
    F --> I{ip-api.com responded?}
    I -->|yes| J([Resolve approximate coordinates])
    I -->|no| K([Throw original error])

Example

const coords = await Native.geolocation.value;

console.log(coords.latitude, coords.longitude);
console.log(coords.accuracy); // -1 when resolved via IP fallback

Returns

Promise<GeolocationCoordinates>
interface GeolocationCoordinates {
    readonly latitude:         number;
    readonly longitude:        number;
    readonly accuracy:         number;  // -1 when resolved via IP fallback
    readonly altitude:         number | null;
    readonly altitudeAccuracy: number | null;
    readonly heading:          number | null;
    readonly speed:            number | null;
}

Throws

throw new NotSupportedError // geolocation unavailable and IP fallback failed
throw new PermissionNotGrantedError // permission denied and IP fallback failed

Signature

onChange(listener: (coordinates: GeolocationCoordinates) => void, options?: AddEventListenerOptions): () => void

Subscribes to geographic position changes.

Example

const unsubscribe = Native.geolocation.onChange((coords) => {
    console.log(coords.latitude, coords.longitude);
    console.log(coords.accuracy); // meters
});

unsubscribe();

Returns

() => void
// call to remove the listener
unsubscribe();

notification

supported · send

Signature

get supported(): boolean

Returns whether notification is supported in the current environment.

Example

if (Native.notification.supported) {
    await Native.notification.send({
        title: 'Hello',
        body:  'You have a new message.',
        icon:  '/icon.png',
    });
}

Returns

boolean
// true  — notification API support,
// false — no notification support

Signature

send(options: NotificationOptions): Promise<Notification>

Sends a native notification after requesting permission.

Example

// Basic notification
const notification = await Native.notification.send({
    title: 'Hello',
    body:  'You have a new message.',
    icon:  '/icon.png',
});

// With event handlers
const notification = await Native.notification.send({
    title:   'Download complete',
    body:    'your-file.zip is ready.',
    onClick: () => window.focus(),
    onClose: () => console.log('dismissed'),
});

// Close the notification programmatically.
notification.close();

Returns

Promise<Notification>
interface NotificationOptions {
    title:               string;
    badge?:              string;
    body?:               string;
    data?:               any;
    dir?:                NotificationDirection;
    icon?:               string;
    lang?:               string;
    requireInteraction?: boolean;
    silent?:             boolean | null;
    tag?:                string;
    onClick?:            (event: Event) => any;
    onClose?:            (event: Event) => any;
    onError?:            (event: Event) => any;
    onShow?:             (event: Event) => any;
}

Throws

throw new NotSupportedError // window.Notification unavailable
throw new PermissionNotGrantedError // notification permission denied

open

app · telephone · message · mail · file · directory · setting · camera · contact · share · calendar

Signature

app(options: AppOpenOptions): Promise<AppOpenState>

Opens a native app. Falls back through multiple URL strategies until one succeeds.

Flowchart

flowchart TD
    A([Native.open.app called]) --> B[Detect current OS]
    B --> C[Extract app info by OS]
    C --> D[Build URL priority list]
    D --> E[tryOpenURL sequentially]
    E --> F{Success?}
    F -->|yes| G([Resolve AppOpenState])
    F -->|no| H{URLs remaining?}
    H -->|yes| E
    H -->|no| I([Throw URLOpenError])

    subgraph tryOpenURL
        T1([Register blur and focus events]) --> T2[Open URL via href / iframe / cordova]
        T2 --> T3{blur fired?}
        T3 -->|yes| T4([resolve])
        T3 -->|no| T5([reject via timeout])
    end

    E -.->|delegates to| T1

Example

try {
    const result = await Native.open.app({
        android: {
            scheme: 'ms-excel://',
            packageName: 'com.microsoft.office.excel',
            allowAppStore: true,
            // allowWebStore: false,
            // intent: 'intent://#Intent;scheme=ms-excel;...',
            // fallback: 'https://www.microsoft.com/ko-kr/microsoft-365/excel',
            // timeout: 1000,
        },
        ios: {
            scheme: 'ms-excel://',
            trackId: '586683407',
            allowAppStore: true,
            // allowWebStore: false,
            // universal: '',
            // bundleId: 'com.microsoft.Office.Excel',
            // fallback: 'https://www.microsoft.com/ko-kr/microsoft-365/excel',
            // timeout: 2000,
        },
        windows: {
            scheme: 'ms-excel://',
            productId: 'cfq7ttc0pr28',
            allowAppStore: true,
            // allowWebStore: false,
            // fallback: 'https://www.microsoft.com/ko-kr/microsoft-365/excel',
            // timeout: 750,
        },
        macos: {
            scheme: 'ms-excel://',
            trackId: '462058435',
            allowAppStore: true,
            // allowWebStore: false,
            // bundleId: 'com.microsoft.Excel',
            // fallback: 'https://www.microsoft.com/ko-kr/microsoft-365/excel',
            // timeout: 750,
        },
    });

    switch (result) {
        AppOpenState.Intent:
            console.log('Opened via Android intent.');    break;
            AppOpenState.Universal:
            console.log('Opened via Universal Link.');    break;
            AppOpenState.Scheme:
            console.log('Opened via custom scheme.');     break;
            AppOpenState.Fallback:
            console.log('Opened via fallback URL.');      break;
            AppOpenState.Store:
            console.log('Redirected to App Store.');      break;
    }
} catch (e) {
    if (e instanceof Native.open.Errors.URLOpenError) {
        console.error('All URLs exhausted:', e.message);
    }
}

Returns

Promise<AppOpenState>
enum AppOpenState {
    Intent    = 'Intent',
    Universal = 'Universal',
    Scheme    = 'Scheme',
    Fallback  = 'Fallback',
    Store     = 'Store',
}

Throws

throw new URLOpenError // all candidate URLs were tried and none succeeded

Signature

telephone(options: TelephoneOptions): Promise<void>

Opens the native phone dialer.

Example

await Native.open.telephone({ to: '+821012345678' });

Returns

Promise<void>

Throws

throw new URLOpenError // failed to open the phone dialer

Signature

message(options: MessageOptions): Promise<void>

Opens the native SMS app.

Example

// With pre-filled body
await Native.open.message({
    to:   '+821012345678',
    body: 'Hello from Native.open!',
});

Returns

Promise<void>

Throws

throw new URLOpenError // failed to open the SMS app

Signature

mail(options: MailOptions): Promise<void>

Opens the native mail client.

Example

// Single recipient
await Native.open.mail({
    to:      '[email protected]',
    subject: 'Greetings',
    body:    'Hi there!',
});

// Multiple recipients with cc / bcc
await Native.open.mail({
    to:      ['[email protected]', '[email protected]'],
    cc:      '[email protected]',
    bcc:     '[email protected]',
    subject: 'Greetings',
    body:    'Hi there!',
});

Returns

Promise<void>

Throws

throw new URLOpenError // failed to open the mail client

Signature

file(options?: FileOptions): Promise<File[]>

Opens a file picker dialog.

Example

// Single file
const [file] = await Native.open.file({
    accept: ['.pdf'],
});

// Multiple files with type filter
const files = await Native.open.file({
    multiple: true,
    accept:   ['.png', '.jpg', 'image/webp'],
    startIn:  ExplorerStartIn.Pictures,
});

Returns

Promise<File[]>

Throws

throw new UserCancelledError // user dismissed the picker
throw new NotSupportedError // showOpenFilePicker and input fallback both unavailable

Signature

directory(options?: DirectoryOptions): Promise<FileWithPath[]>

Opens a directory picker and returns all files with their relative paths.

Example

// Read-only
const entries = await Native.open.directory();

// Read-write
const entries = await Native.open.directory({
    mode: DirectoryExploreMode.ReadWrite,
});

entries.forEach(({ file, relativePath }) => {
    console.log(relativePath, file.size); // 'src/index.ts', 1024
});

Returns

Promise<FileWithPath[]>
interface FileWithPath {
    file:         File;
    relativePath: string;
}

Throws

throw new NotSupportedError // showDirectoryPicker and webkitdirectory both unavailable
throw new UserCancelledError // user dismissed the picker

Signature

setting(type: SettingType): Promise<void>

Opens a system settings screen. iOS is unsupported.

Example

// General settings
await Native.open.setting(SettingType.General);

// Accessibility settings
await Native.open.setting(SettingType.Accessibility);

// Battery settings (Android 5.1+)
await Native.open.setting(SettingType.Battery);

Returns

Promise<void>

Throws

throw new URLOpenError // canOpenSetting() returned false
throw new URLOpenError // all setting URLs failed

Signature

camera(options?: CameraOptions): Promise<File[]>

Opens the device camera.

Example

// Rear-facing photo (default)
const [photo] = await Native.open.camera();

// Front-facing video
const [video] = await Native.open.camera({
    type:    CameraType.Video,
    capture: CaptureType.User,
});

Returns

Promise<File[]>

Throws

throw new UserCancelledError // user dismissed the camera UI

Signature

contact(options?: ContactOptions): Promise<Contact[]>

Opens the native contact picker.

Example

// Single contact
const [contact] = await Native.open.contact();
console.log(contact.name, contact.tel);

// Multiple contacts
const contacts = await Native.open.contact({ multiple: true });
contacts.forEach((c) => console.log(c.name, c.email));

Returns

Promise<Contact[]>
interface Contact {
    name?:    string;
    email?:   string;
    tel?:     string;
    address?: string;
    icon?:    Blob[];
}

Throws

throw new NotSupportedError // navigator.contacts unavailable

Signature

share(options: ShareData): Promise<void>

Opens the native OS share sheet.

Example

// Share a URL
await Native.open.share({
    title: 'Check this out',
    url:   'https://example.com',
});

// Share text and URL
await Native.open.share({
    title: 'Check this out',
    text:  'Shared via Native.open',
    url:   'https://example.com',
});

Returns

Promise<void>

Throws

throw new NotSupportedError // navigator.share unavailable or canShare() false
throw new UserCancelledError // user dismissed the share sheet

Signature

calendar(options: CalendarOptions): void

Generates an RFC 5545 .ics file and triggers a download to open in the default calendar app.

Example

// Basic event
Native.open.calendar({
    title:       'Team Sync',
    description: 'Weekly alignment meeting',
    location:    'Seoul, Korea',
    startDate:   new Date('2026-04-01T10:00:00Z'),
    endDate:     new Date('2026-04-01T11:00:00Z'),
});

// Recurring event with alarm
Native.open.calendar({
    title:     'Weekly Standup',
    startDate: new Date('2026-04-01T09:00:00Z'),
    endDate:   new Date('2026-04-01T09:15:00Z'),
    alarm:     [{ minutes: 10, before: true }],
    recur:     { frequency: 'WEEKLY', byDay: ['MO'], count: 12 },
});

// All-day event
Native.open.calendar({
    title:     'Company Holiday',
    allDay:    true,
    startDate: new Date('2026-05-05T00:00:00Z'),
    endDate:   new Date('2026-05-05T00:00:00Z'),
});

Returns

void

permission

supported · request · check

Signature

get supported(): boolean

Returns whether permission is supported in the current environment.

Example

if (Native.permission.supported) {
    const state = await Native.permission.check(PermissionType.Geolocation);
}

Returns

boolean
// true  — permission API support,
// false — no permission support

Signature

request(type: PermissionType): Promise<PermissionState>

Requests a permission. Resolves immediately if already granted.

Flowchart

flowchart TD
    A([permission.request called]) --> B[check current state]
    B --> C{Already granted?}
    C -->|yes| D([Resolve Grant])
    C -->|no| E{PermissionType?}
    E -->|Notification| F[Notification.requestPermission]
    E -->|Geolocation| G[getCurrentPosition to trigger prompt]
    E -->|Camera| H[getUserMedia video=true]
    E -->|ClipboardRead| I[clipboard.read]
    E -->|Microphone| J[getUserMedia audio=true]
    E -->|MIDI| k[requestMIDIAccess]
    E -->|unknown| L([Resolve Unsupported])
    F & G & H & I & J --> M[check state again]
    M --> N([Resolve PermissionState])

Example

const state = await Native.permission.request(PermissionType.Notification);

switch (state) {
    case PermissionState.Grant:
        console.log('Permission granted.');  break;
    case PermissionState.Denied:
        console.log('Permission denied.');   break;
    case PermissionState.Prompt:
        console.log('Not yet decided.');     break;
    case PermissionState.Unsupported:
        console.log('Not supported.');       break;
}

Returns

Promise<PermissionState>
enum PermissionType {
    Notification  = 'notifications',
    Geolocation   = 'geolocation',
    Camera        = 'camera',
    ClipboardRead = 'clipboard-read',
    Microphone    = 'microphone',
    MIDI          = 'midi',
}

enum PermissionState {
    Grant       = 'grant',
    Denied      = 'denied',
    Prompt      = 'prompt',
    Unsupported = 'unsupported',
}

Signature

check(type: PermissionType): Promise<PermissionState>

Returns the current permission state without triggering a prompt.

Example

// Check before accessing a feature
const state = await Native.permission.check(PermissionType.Geolocation);

if (state === PermissionState.Grant) {
    const coords = await Native.geolocation.value;
}

Returns

Promise<PermissionState>
enum PermissionState {
    Grant       = 'grant',
    Denied      = 'denied',
    Prompt      = 'prompt',
    Unsupported = 'unsupported',
}

pip

supported · element · isPip · request · exit · onChange · onError

Signature

get supported(): boolean

Returns whether Picture-in-Picture is supported in the current environment.

Example

if (Native.pip.supported) {
    await Native.pip.request();
}

Returns

boolean
// true  — document.pictureInPictureEnabled is true,
//         or a video with webkitSupportsPresentationMode('picture-in-picture') exists
// false — no PiP support

Signature

get element(): HTMLVideoElement | null

Returns the video element currently in Picture-in-Picture, or null if not active.

Example

const el = Native.pip.element;

if (el !== null) {
    console.log(el.src); // currently PiP video source
}

Returns

HTMLVideoElement | null
// HTMLVideoElement — the current PiP video
// null            — not in PiP

Signature

get isPip(): boolean

Returns whether Picture-in-Picture is currently active.

Example

console.log(Native.pip.isPip); // true | false

Returns

boolean

Signature

request(target?: HTMLVideoElement): Promise<void>

Requests Picture-in-Picture for a video element.

Flowchart

flowchart TD
    A([Pip.request called]) --> B{target defined?}
    B -->|no| C[getDefaultTarget]
    B -->|yes| D{target is video element?}
    C --> D
    D -->|no| Z([Throw NotSupportedError])
    D -->|yes| E{requestPictureInPicture available?}
    E -->|yes| F[Call video.requestPictureInPicture]
    E -->|no| G[fallbackToWebkit]
    F --> H{Promise returned?}
    H -->|yes| I{Resolved?}
    I -->|yes| J([resolve])
    I -->|no| G
    H -->|no| J
    G --> K{webkitSupportsPresentationMode PIP?}
    K -->|yes| L[webkitSetPresentationMode picture-in-picture]
    L --> J
    K -->|no| M([Throw NotSupportedError])

Example

// Default: first video element
await Native.pip.request();

// Specific video element
await Native.pip.request(document.querySelector('video#player'));

Returns

Promise<void>

Throws

throw new NotSupportedError // target is not a video element
throw new NotSupportedError // PiP disabled on this element (disablePictureInPicture)
throw new NotSupportedError // requestPictureInPicture and webkitSetPresentationMode both unavailable
throw new InvalidStateError // PiP transition already in progress

Signature

exit(): Promise<void>

Exits Picture-in-Picture.

Flowchart

flowchart TD
    A([Pip.exit called]) --> B{exitPictureInPicture available?}
    B -->|yes| C[Call document.exitPictureInPicture]
    B -->|no| D[fallbackToWebkit]
    C --> E{Promise returned?}
    E -->|yes| F{Resolved?}
    F -->|yes| G([resolve])
    F -->|no| D
    E -->|no| G
    D --> H{webkit PiP video found?}
    H -->|yes| I[webkitSetPresentationMode inline]
    I --> G
    H -->|no| J{any video in PIP mode found?}
    J -->|yes| I
    J -->|no| K{pictureInPictureElement null?}
    K -->|yes| G
    K -->|no| L([Throw NotSupportedError])

Example

await Native.pip.exit();

Returns

Promise<void>

Throws

throw new NotSupportedError // failed to exit PiP

Signature

onChange(listener: (payload: PipEventPayload) => void, options?: AddEventListenerOptions): () => void

Subscribes to Picture-in-Picture state changes.

Example

const unsubscribe = Native.pip.onChange((payload) => {
    console.log(payload.isPip);       // true | false
    console.log(payload.element);     // HTMLVideoElement
    console.log(payload.nativeEvent); // Event
});

unsubscribe();

Returns

() => void
// call to remove the listener
unsubscribe();

Signature

onError(listener: (payload: PipEventPayload) => void, options?: AddEventListenerOptions): () => void

Subscribes to Picture-in-Picture errors.

Example

const unsubscribe = Native.pip.onError((payload) => {
    console.log(payload.isPip);       // boolean
    console.log(payload.element);     // HTMLVideoElement
    console.log(payload.nativeEvent); // Event
});

unsubscribe();

Returns

() => void
// call to remove the listener
unsubscribe();

platform

os · browser · engine · device · locale · gpu · userAgent · ready · isWebview · isNode · isStandalone

Signature

get os(): NameVersionPair<OS>

Returns the detected OS name and version.

Example

const { name, version } = Native.platform.os;

switch (name) {
    case OS.iOS:
        console.log('iOS', version);     break;
    case OS.Android:
        console.log('Android', version); break;
    case OS.Windows:
        console.log('Windows', version); break;
    case OS.MacOS:
        console.log('macOS', version);   break;
}

Returns

NameVersionPair<OS>
interface NameVersionPair<T> {
    name:    T;
    version: string;
}

enum OS {
    Unknown = 'Unknown',
    Android = 'Android',
    iOS     = 'iOS',
    Windows = 'Windows',
    MacOS   = 'MacOS',
}

Signature

get browser(): NameVersionPair<Browsers>

Returns the detected browser name and version.

Example

const { name, version } = Native.platform.browser;

if (name === Browsers.Safari) {
    console.log('Safari', version); // e.g. '17.0'
}

Returns

NameVersionPair<Browsers>
enum Browsers {
    Unknown         = 'Unknown',
    Chrome          = 'Chrome',
    Safari          = 'Safari',
    Edge            = 'Edge',
    Firefox         = 'Firefox',
    Opera           = 'Opera',
    IE              = 'IE',
    SamsungInternet = 'SamsungInternet',
}

Signature

get engine(): NameVersionPair<Engines>

Returns the detected rendering engine name and version.

Example

const { name, version } = Native.platform.engine;

if (name === Engines.Blink) {
    console.log('Blink', version); // e.g. '120.0.6099.62'
}

Returns

NameVersionPair<Engines>
enum Engines {
    Unknown  = 'Unknown',
    EdgeHTML = 'EdgeHTML',
    ArkWeb   = 'ArkWeb',
    Blink    = 'Blink',
    Presto   = 'Presto',
    WebKit   = 'WebKit',
    Trident  = 'Trident',
    Gecko    = 'Gecko',
}

Signature

get device(): Devices

Returns the device category: Mobile, Desktop, or Unknown.

Example

if (Native.platform.device === Devices.Mobile) {
    console.log('Running on a mobile device');
}

Returns

Devices
enum Devices {
    Unknown = 'Unknown',
    Mobile  = 'Mobile',
    Desktop = 'Desktop',
}

Signature

get locale(): Locale

Returns the current locale, timezone, UTC offset, and text direction.

Example

const { language, languages, timezone, offset, isRTL } = Native.platform.locale;

console.log(language);   // 'ko-KR'
console.log(languages);  // ['ko-KR', 'en-US']
console.log(timezone);   // 'Asia/Seoul'
console.log(offset);     // 540  (UTC+9 in minutes)
console.log(isRTL);      // false

Returns

Locale
interface Locale {
    language:  string | null;
    languages: string[];
    timezone:  string | null;
    offset:    number;
    isRTL:     boolean;
}

Signature

get gpu(): GPU

Returns GPU information. Await Native.platform.ready for guaranteed complete data.

Example

// Guaranteed complete data
await Native.platform.ready;
const { vendor, architecture, device, description } = Native.platform.gpu;

console.log(vendor);       // 'apple'
console.log(architecture); // 'common-3'
console.log(device);       // 'Apple M2'

Returns

GPU
interface GPU {
    vendor?:       string;
    architecture?: string;
    device?:       string;
    description?:  string;
}

Signature

get userAgent(): string
set userAgent(value: string)

Gets or sets the User-Agent string used for all platform detection. The setter invalidates all parsed caches.

Example

// Read current UA
console.log(Native.platform.userAgent);

// Override for testing
Native.platform.userAgent = 'Mozilla/5.0 (iPhone; CPU iPhone OS 17_0 like Mac OS X)...';
console.log(Native.platform.os.name); // 'iOS'

// Restore original UA
Native.platform.userAgent = originalUA;
await Native.platform.ready;

Returns

string

Signature

get ready(): Promise<void>

Resolves when all async platform detection has settled.

Example

await Native.platform.ready;

// All values now reflect high-entropy and WebGPU data
console.log(Native.platform.os.name);
console.log(Native.platform.browser.name);
console.log(Native.platform.gpu.vendor);

Returns

Promise<void>

Signature

get isWebview(): boolean

Returns true when running inside a native WebView.

Example

if (Native.platform.isWebview) {
    console.log('Running inside a native WebView');
}

Returns

boolean

Signature

get isNode(): boolean

Returns true when running in a Node.js environment.

Example

if (Native.platform.isNode) {
    console.log('Running in Node.js');
}

Returns

boolean

Signature

get isStandalone(): boolean

Returns true when running as an installed PWA.

Example

if (Native.platform.isStandalone) {
    console.log('Running as installed PWA');
}

Returns

boolean

theme

value

Signature

get value(): string | undefined
set value(color: string | undefined)

Gets or sets the browser theme color via the meta theme-color tag.

Example

// Read
console.log(Native.theme.value); // '#ffffff' | undefined

// Set
Native.theme.value = '#1a1a2e';

// Remove
Native.theme.value = undefined;

Returns

string | undefined

vibration

supported · run · stop

Signature

get supported(): boolean

Returns whether vibration is supported in the current environment.

Example

if (Native.vibration.supported) {
    Native.vibration.run([100, 50, 200]);
}

Returns

boolean
// true  — vibration API support,
// false — no vibration support

Signature

run(pattern: number | number[]): boolean

Triggers device vibration. Pass a number for a single pulse or an array to define a pattern.

Example

// Single pulse — 200ms
Native.vibration.run(200);

// Pattern — vibrate 100ms, pause 50ms, vibrate 200ms
Native.vibration.run([100, 50, 200]);

// Stop any ongoing vibration
Native.vibration.run(0);

Returns

boolean

Throws

throw new NotSupportedError // navigator.vibrate unavailable

Signature

stop(): boolean

Stops any ongoing vibration.

Example

Native.vibration.stop();

Returns

boolean
// true  — stop request accepted by the browser
// false — document is hidden or vibration is unsupported

Throws

throw new NotSupportedError // navigator.vibrate unavailable