Files
WRNexusJS/packages/mobile/src/index.ts
T
2026-07-12 15:55:18 +05:30

92 lines
2.8 KiB
TypeScript

/** @wrnexus/mobile — SSR-safe access to Capacitor's native bridge. */
export { native } from "@wrnexus/native";
export type MobilePlatform = "ios" | "android" | "web" | string;
export interface CapacitorBridge {
isNativePlatform?: () => boolean;
getPlatform?: () => MobilePlatform;
Plugins?: Record<string, unknown>;
}
export class MobileUnavailableError extends Error {
constructor(message = "A Capacitor native runtime is not available") {
super(message);
this.name = "MobileUnavailableError";
}
}
function bridge(): CapacitorBridge | undefined {
if (typeof globalThis === "undefined") return undefined;
return (globalThis as typeof globalThis & { Capacitor?: CapacitorBridge }).Capacitor;
}
const registered = new Map<string, object>();
/** Register a plugin imported by browser-only application code. */
export function registerPlugin<T extends object>(name: string, instance: T): T {
registered.set(name, instance);
return instance;
}
/** True only inside a native Capacitor iOS or Android WebView. SSR-safe. */
export function isNative(): boolean {
return bridge()?.isNativePlatform?.() ?? false;
}
/** Current Capacitor platform, falling back to `web` during SSR and in browsers. */
export function platform(): MobilePlatform {
return bridge()?.getPlatform?.() ?? "web";
}
/** Return an injected Capacitor plugin, or undefined when it is unavailable. */
export function plugin<T extends object>(name: string): T | undefined {
if (!isNative()) return undefined;
return (registered.get(name) ?? bridge()?.Plugins?.[name]) as T | undefined;
}
/** Require an installed native plugin and produce a useful error when absent. */
export function requirePlugin<T extends object>(name: string): T {
const value = plugin<T>(name);
if (!value) {
throw new MobileUnavailableError(
`Capacitor plugin "${name}" is unavailable. Run \`wrnexus mobile add <package>\` and rebuild the native app.`,
);
}
return value;
}
/** Invoke a plugin method without importing native code into an SSR module. */
export async function invoke<TResult = unknown>(
pluginName: string,
method: string,
options?: unknown,
): Promise<TResult> {
const target = requirePlugin<Record<string, unknown>>(pluginName);
const fn = target[method];
if (typeof fn !== "function") {
throw new MobileUnavailableError(`Capacitor plugin "${pluginName}" has no method "${method}".`);
}
return (await fn.call(target, options)) as TResult;
}
/** Run native behavior when available, with an optional SSR/web fallback. */
export async function whenNative<T>(
native: () => T | Promise<T>,
fallback?: () => T | Promise<T>,
): Promise<T | undefined> {
if (isNative()) return native();
return fallback?.();
}
export const mobile = {
isNative,
platform,
registerPlugin,
plugin,
requirePlugin,
invoke,
whenNative,
};