111 lines
3.2 KiB
TypeScript
111 lines
3.2 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,
|
|
};
|
|
export {
|
|
listenDeepLinks,
|
|
parseDeepLink,
|
|
memoryOfflineTaskStore,
|
|
OfflineQueue,
|
|
PushNotifications,
|
|
SecureStorage,
|
|
mobileEnvironment,
|
|
} from "./advanced.ts";
|
|
export type {
|
|
DeepLink,
|
|
DeepLinkSource,
|
|
OfflineTask,
|
|
OfflineTaskStore,
|
|
MobileEnvironment,
|
|
PushAdapter,
|
|
PushRegistration,
|
|
SecureStorageAdapter,
|
|
} from "./advanced.ts";
|