{"files":{"main.ts":"import {run} from './core.ts';\nimport {POLICY} from './policy.ts';\nexport function evaluate(payload: any): any { return run(payload, POLICY); }\n","policy.ts":"export const POLICY: any = {\n  deny_wins: false,\n  expiry_inclusive: false,\n  inherit_roles: false,\n  owner_bypass: false,\n  action_casefold: false,\n  group_union: false,\n  resource_scope: 'prefix',\n  suspended_denies: false,\n  token_ttl: 200,\n  wildcard_action: false,\n  audit_denials: false,\n  default_allow: false\n};\n","helpers.ts":"export function expandRoles(initial: string[], parents: any, inherit: boolean): Set<string> {\n  const seen = new Set<string>(initial);\n  if (!inherit) return seen;\n  const stack = [...initial];\n  while (stack.length) {\n    const r = stack.pop() as string;\n    const ps = (parents && Object.prototype.hasOwnProperty.call(parents, r)) ? parents[r] : [];\n    for (const p of (ps || [])) {\n      if (!seen.has(p)) { seen.add(p); stack.push(p); }\n    }\n  }\n  return seen;\n}\n","core.ts":"import {expandRoles} from './helpers.ts';\n\nexport function run(payload: any, policy: any): any {\n  const subject = payload.subject || {};\n  const resource = payload.resource || {};\n  const now = payload.now;\n  const fin = (allowed: boolean, reason: string, matched: number[]) => ({\n    allowed,\n    reason,\n    matched,\n    audit: (!allowed && policy.audit_denials) ? [reason] : []\n  });\n  if (subject.tenant !== resource.tenant) return fin(false, 'tenant', []);\n  if (now - payload.issued_at > policy.token_ttl) return fin(false, 'token', []);\n  if (policy.suspended_denies && subject.suspended) return fin(false, 'suspended', []);\n  if (policy.owner_bypass && subject.owner) return fin(true, 'owner', []);\n  let initial: string[] = [...(subject.roles || [])];\n  if (policy.group_union) initial = initial.concat(subject.groups || []);\n  const roles = expandRoles(initial, payload.role_parents, !!policy.inherit_roles);\n  const lower = (s: string) => String(s).replace(/[A-Z]/g, (c) => c.toLowerCase());\n  const grants = payload.grants || [];\n  const matched: number[] = [];\n  let anyAllow = false;\n  let anyDeny = false;\n  const act = payload.action;\n  const rid = String(resource.id);\n  grants.forEach((g: any, i: number) => {\n    if (!roles.has(g.role)) return;\n    let am = policy.action_casefold ? lower(g.action) === lower(act) : g.action === act;\n    if (!am && policy.wildcard_action && g.action === '*') am = true;\n    if (!am) return;\n    const sc = String(g.scope);\n    const sm = policy.resource_scope === 'exact' ? sc === rid : rid.startsWith(sc);\n    if (!sm) return;\n    const e = g.expires;\n    if (!(e === null || e === undefined || (policy.expiry_inclusive ? now <= e : now < e))) return;\n    matched.push(i);\n    if (g.effect === 'deny') anyDeny = true; else anyAllow = true;\n  });\n  if (matched.length === 0) return fin(!!policy.default_allow, 'default', []);\n  const allowed = policy.deny_wins ? (anyAllow && !anyDeny) : anyAllow;\n  return fin(allowed, 'grants', matched);\n}\n"}}