61 lines
2.2 KiB
TypeScript
61 lines
2.2 KiB
TypeScript
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<T extends NativeCapabilityManifest>(manifest: T): T {
|
|
const names = new Set<string>();
|
|
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<boolean> {
|
|
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;
|
|
}
|
|
}
|