{"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: true,\n  expiry_inclusive: true,\n  inherit_roles: true,\n  owner_bypass: true,\n  action_casefold: true,\n  group_union: true,\n  resource_scope: 'exact',\n  suspended_denies: true,\n  token_ttl: 100,\n  wildcard_action: true,\n  audit_denials: true,\n  default_allow: true\n};\n","helpers.ts":"export function expandedRoles(subject: any, parents: any, policy: any): Set<string> {\n  const roles = new Set<string>(subject.roles);\n  if (policy.group_union) {\n    for (const group of subject.groups) roles.add(group);\n  }\n  if (policy.inherit_roles) {\n    const pending = Array.from(roles);\n    while (pending.length > 0) {\n      const role = pending.pop()!;\n      const inherited = Object.prototype.hasOwnProperty.call(parents, role) ? parents[role] : [];\n      for (const parent of inherited) {\n        if (!roles.has(parent)) {\n          roles.add(parent);\n          pending.push(parent);\n        }\n      }\n    }\n  }\n  return roles;\n}\n\nexport function grantDecisions(grant: any): {allow: boolean; deny: boolean} {\n  const matchingFields = new Set(['role', 'action', 'scope', 'expires']);\n  let allow = false;\n  let deny = false;\n  for (const key of Object.keys(grant)) {\n    if (matchingFields.has(key)) continue;\n    const value = grant[key];\n    if (value === 'allow' || value === true) allow = true;\n    if (value === 'deny' || value === false) deny = true;\n  }\n  return {allow, deny};\n}\n","core.ts":"import {expandedRoles, grantDecisions} from './helpers.ts';\n\nexport function run(payload: any, policy: any): any {\n  const {subject, resource, now, issued_at, action, grants, role_parents} = payload;\n  function result(allowed: boolean, reason: string, matched: number[] = []): any {\n    return {\n      allowed,\n      reason,\n      matched,\n      audit: !allowed && policy.audit_denials ? [reason] : []\n    };\n  }\n\n  if (subject.tenant !== resource.tenant) return result(false, 'tenant');\n  if (now - issued_at > policy.token_ttl) return result(false, 'token');\n  if (policy.suspended_denies && subject.suspended) return result(false, 'suspended');\n  if (policy.owner_bypass && subject.owner) return result(true, 'owner');\n\n  const roles = expandedRoles(subject, role_parents, policy);\n  const requestedAction = policy.action_casefold ? action.toLowerCase() : action;\n  const matched: number[] = [];\n  let anyAllow = false;\n  let anyDeny = false;\n\n  for (let index = 0; index < grants.length; index++) {\n    const grant = grants[index];\n    if (!roles.has(grant.role)) continue;\n    const grantAction = policy.action_casefold ? grant.action.toLowerCase() : grant.action;\n    if (grantAction !== requestedAction && !(policy.wildcard_action && grant.action === '*')) continue;\n    const scopeMatches = policy.resource_scope === 'exact'\n      ? grant.scope === resource.id\n      : resource.id.startsWith(grant.scope);\n    if (!scopeMatches) continue;\n    if (grant.expires !== null) {\n      const valid = policy.expiry_inclusive ? now <= grant.expires : now < grant.expires;\n      if (!valid) continue;\n    }\n    matched.push(index);\n    const decision = grantDecisions(grant);\n    anyAllow = anyAllow || decision.allow;\n    anyDeny = anyDeny || decision.deny;\n  }\n\n  if (matched.length === 0) return result(policy.default_allow, 'default');\n  return result(anyAllow && (!policy.deny_wins || !anyDeny), 'grants', matched);\n}\n"}}