import { requiredAiOperations, type ColineAiRequest, type ColineImageOptions, } from "../capabilities/ai"; import type { ColineCapabilities } from "../capabilities/types"; import { createCapabilityGate, type CapabilityGateConfig } from "../capabilities/gate"; import { APP_CAPABILITY_OPERATIONS } from "../capabilities/operations"; /** Apply the production permission/effect table to every fake call. */ export function guardTestCapabilities( capabilities: ColineCapabilities, config: CapabilityGateConfig, options: { interactive?: boolean; appPrincipal?: boolean; /** Event runs: the actor's capabilities, used for person-scoped reads. */ readAs?: ColineCapabilities; } = {}, ): ColineCapabilities { const gate = createCapabilityGate(config); function wrap(value: object, path: string): object { return new Proxy(value, { get(target, key, receiver): unknown { const child: unknown = Reflect.get(target, key, receiver); if (typeof key !== "string") return child; const operation = path ? `${path}.${key}` : key; if (typeof child === "function") { if (operation === "storage.collection") { return (...args: unknown[]) => wrap(Reflect.apply(child, target, args) as object, "storage.records"); } return async (...args: unknown[]) => { gate.assert(operation); if (operation === "ai.run" || operation === "ai.images.generate") { const request = operation === "ai.run" ? (args[0] as ColineAiRequest) : { kind: "image" as const, input: args[0] as ColineImageOptions }; for (const required of requiredAiOperations(request)) gate.assert(required); } if ( (operation === "storage.records.batch" || operation === "storage.batch") && Array.isArray(args[0]) && (args[0] as Array<{ op?: string }>).some((item) => item?.op === "delete") ) { gate.assert("storage.records.delete"); } if ( !options.interactive && (operation.startsWith("pickers.") || operation.startsWith("navigation.") || operation === "realtime.subscribe" || operation === "tools.invoke" || operation === "files.saveAs" || operation === "connections.connect") ) { throw Object.assign(new Error(`${operation} requires an interactive surface.`), { code: "capability_unavailable", }); } if ( options.appPrincipal && /^(files|members|search\.query|tasks|calendar|messages|references)\./.test( `${operation}.`, ) ) { // Production: event runs read these as the event's actor; writes // stay with the app principal (which cannot do them). const descriptor = (APP_CAPABILITY_OPERATIONS as Record)[operation]; if (options.readAs && descriptor?.mutation === "read") { const segments = operation.split("."); let owner: unknown = options.readAs; for (const segment of segments.slice(0, -1)) owner = (owner as Record)[segment]; const method = (owner as Record)[segments.at(-1)!]; return Reflect.apply(method as (...values: unknown[]) => unknown, owner, args) as unknown; } throw Object.assign(new Error(`${operation} requires a user principal.`), { code: "requires_user_principal", }); } return Reflect.apply(child, target, args) as unknown; }; } return child !== null && typeof child === "object" ? wrap(child, operation) : child; }, }); } return wrap(capabilities, "") as ColineCapabilities; }