first commit
This commit is contained in:
@@ -0,0 +1,91 @@
|
||||
/** @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,
|
||||
};
|
||||
Reference in New Issue
Block a user