import { registered, supports } from "./registry.ts"; import type { NativeTarget } from "./types.ts"; export type NativePermission = "camera" | "geolocation" | "microphone" | "notifications" | "photos" | "storage" | (string & {}); export interface NativeCapabilityManifestEntry { name: string; description?: string; permissions?: NativePermission[]; targets?: NativeTarget[]; optional?: boolean; } export interface NativeCapabilityManifest { name: string; version?: string; capabilities: NativeCapabilityManifestEntry[]; } export function defineNativeManifest(manifest: T): T { const names = new Set(); for (const capability of manifest.capabilities) { if (!/^[A-Za-z][A-Za-z0-9.-]*$/.test(capability.name)) throw new TypeError(`Invalid native capability: ${capability.name}`); if (names.has(capability.name)) throw new Error(`Duplicate native capability: ${capability.name}`); names.add(capability.name); } return manifest; } export function inspectNativeCapabilities( target?: NativeTarget, ): Array<{ name: string; supported: boolean }> { return registered().map((name) => ({ name, supported: supports(name, target) })); } export function missingNativeCapabilities( manifest: NativeCapabilityManifest, target?: NativeTarget, ): NativeCapabilityManifestEntry[] { return manifest.capabilities.filter( (capability) => !capability.optional && (!capability.targets || !target || capability.targets.includes(target)) && !supports(capability.name, target), ); } export interface PermissionAdapter { query(name: NativePermission): Promise<"granted" | "denied" | "prompt" | "unavailable">; request?(name: NativePermission): Promise<"granted" | "denied">; } export class PermissionManager { constructor(private readonly adapter: PermissionAdapter) {} query(name: NativePermission) { return this.adapter.query(name); } async ensure(name: NativePermission): Promise { const state = await this.adapter.query(name); if (state === "granted") return true; if (state === "prompt" && this.adapter.request) return (await this.adapter.request(name)) === "granted"; return false; } }