/** * Who may do what. `contract.json` is the table — the roles, and the least * role each verb on each resource needs — and `src/can.ts` is the forge's * evaluator, carried here verbatim. Actions ask `permit` and nothing else * in the app decides who may do what, so the table *is* the policy: it can * be read, compared and audited without running anything. * * A contract that does not read is not a warning. The app does not start. */ import { readFileSync } from "node:fs"; import { decide, readContract, type Actor, type Decision, type Verb, } from "./can.ts"; import type { Who } from "./who.ts"; const read = readContract( readFileSync(new URL("../contract.json", import.meta.url), "utf8"), ); if (!read.ok) throw new Error(`contract.json: ${read.problems.join("; ")}`); export const contract = read.contract; /** The forge's word for who is asking, as the evaluator takes it. */ export const actorOf = (who: Who): Actor => who.role === "anon" ? { id: null, roles: [] } : { id: who.id, roles: [who.role] }; /** May `who` do `verb` to `resource` — to `row`, when there is one? */ export const permit = ( who: Who, resource: string, verb: Verb, row?: Readonly>, ): Decision => decide(contract, resource, verb, actorOf(who), row);