import type { MutationClass } from "../manifest/effect"; import type { AppPermissionV2 } from "../manifest/permissions"; /** * The capability operation descriptor table (plan §8, §10.3). * * Every capability call is classified once, here: which permission it * requires and which mutation class it is. The unified guard in apps/web * and every transport-side gate consume this single table — there is no * second classification anywhere. * * Notes on classifications: * - `pickers.*` and `navigation.*` need no permission: they are * host-mediated consent (the user's pick IS the grant, plan §7.2.3). * - `ai.generate` is `read`: metered and billed, but not a workspace * mutation. * - `storage.kv.delete` is `write` (ephemeral app state); * `storage.records.delete` is `destructive` (user-meaningful work). */ export interface AppCapabilityDescriptor { permission: AppPermissionV2 | null; mutation: MutationClass; } export const APP_CAPABILITY_OPERATIONS = { "workspace.get": { permission: null, mutation: "read" }, "members.list": { permission: "members.read", mutation: "read" }, "members.get": { permission: "members.read", mutation: "read" }, "files.list": { permission: "files.read", mutation: "read" }, "files.get": { permission: "files.read", mutation: "read" }, "files.getDocument": { permission: "files.read", mutation: "read" }, "files.create": { permission: "files.write", mutation: "write" }, "files.update": { permission: "files.write", mutation: "write" }, "files.updateDocument": { permission: "files.write", mutation: "write" }, "files.trash": { permission: "files.write", mutation: "destructive" }, "drives.getAppDrive": { permission: "drives.app", mutation: "read" }, "storage.kv.get": { permission: "storage.app", mutation: "read" }, "storage.kv.list": { permission: "storage.app", mutation: "read" }, "storage.kv.set": { permission: "storage.app", mutation: "write" }, "storage.kv.delete": { permission: "storage.app", mutation: "write" }, "storage.records.get": { permission: "storage.app", mutation: "read" }, "storage.records.query": { permission: "storage.app", mutation: "read" }, "storage.records.insert": { permission: "storage.app", mutation: "write" }, "storage.records.update": { permission: "storage.app", mutation: "write" }, "storage.records.delete": { permission: "storage.app", mutation: "destructive" }, "search.query": { permission: "search.query", mutation: "read" }, "search.index.upsert": { permission: "search.index", mutation: "write" }, "search.index.delete": { permission: "search.index", mutation: "write" }, "notifications.create": { permission: "notifications.write", mutation: "write" }, "ai.generate": { permission: "ai.generate", mutation: "read" }, "pickers.file": { permission: null, mutation: "read" }, "pickers.member": { permission: null, mutation: "read" }, "pickers.confirm": { permission: null, mutation: "read" }, "navigation.openFile": { permission: null, mutation: "read" }, "navigation.openAppHome": { permission: null, mutation: "read" }, "navigation.navigate": { permission: null, mutation: "read" }, "navigation.openReference": { permission: null, mutation: "read" }, "realtime.subscribe": { permission: "realtime.subscribe", mutation: "read" }, "events.emit": { permission: "events.emit", mutation: "write" }, } as const satisfies Record; export type AppCapabilityOperation = keyof typeof APP_CAPABILITY_OPERATIONS; export function isAppCapabilityOperation( value: string, ): value is AppCapabilityOperation { return value in APP_CAPABILITY_OPERATIONS; }