{"files": {"main.ts": "import {run} from './core.ts';\nimport {POLICY} from './policy.ts';\nexport function evaluate(payload: any): any { return run(payload, POLICY); }\n", "core.ts": "import {asciiLower, expandRoles, grantIsAllow, matchesActionValue} from './helpers.ts';\n\nexport function run(payload: any, policy: any): any {\n  const subject: any = (payload && payload.subject) || {};\n  const resource: any = (payload && payload.resource) || {};\n  const now: any = payload ? payload.now : undefined;\n  const issuedAt: any = payload ? payload.issued_at : undefined;\n  const requestedAction: any = payload ? payload.action : undefined;\n  const grants: any[] = (payload && Array.isArray(payload.grants)) ? payload.grants : [];\n  const roleParents: any = (payload && payload.role_parents) || {};\n  const make = (allowed: boolean, reason: string, matched: number[]): any => ({allowed: allowed, reason: reason, matched: matched, audit: (!allowed && policy.audit_denials) ? [reason] : []});\n  if (subject.tenant !== resource.tenant) { return make(false, 'tenant', []); }\n  if (now - issuedAt > policy.token_ttl) { return make(false, 'token', []); }\n  if (policy.suspended_denies && subject.suspended) { return make(false, 'suspended', []); }\n  if (policy.owner_bypass && subject.owner) { return make(true, 'owner', []); }\n  const initialRoles: any[] = [];\n  const subjectRoles: any[] = Array.isArray(subject.roles) ? subject.roles : [];\n  for (const r of subjectRoles) { initialRoles.push(r); }\n  if (policy.group_union) {\n    const subjectGroups: any[] = Array.isArray(subject.groups) ? subject.groups : [];\n    for (const g of subjectGroups) { initialRoles.push(g); }\n  }\n  const roles: Set<any> = expandRoles(initialRoles, roleParents, !!policy.inherit_roles);\n  const matched: number[] = [];\n  let hasAllow = false;\n  let hasDeny = false;\n  for (let i = 0; i < grants.length; i++) {\n    const grant: any = grants[i] || {};\n    const grantRole: any = grant.role !== undefined ? grant.role : (grant.role_id !== undefined ? grant.role_id : grant.role_name);\n    if (!roles.has(grantRole)) { continue; }\n    const grantAction: any = grant.action;\n    let actionOk = false;\n    if (policy.wildcard_action && grantAction === '*') { actionOk = true; } else if (matchesActionValue(grantAction, requestedAction, !!policy.action_casefold)) { actionOk = true; }\n    if (!actionOk) { continue; }\n    const grantScope: any = grant.scope !== undefined ? grant.scope : (grant.resource !== undefined ? grant.resource : grant.resource_id);\n    let scopeOk = false;\n    if (policy.resource_scope === 'prefix') { scopeOk = typeof resource.id === 'string' && typeof grantScope === 'string' && resource.id.startsWith(grantScope); } else { scopeOk = grantScope === resource.id; }\n    if (!scopeOk) { continue; }\n    const expires: any = grant.expires !== undefined ? grant.expires : (grant.expires_at !== undefined ? grant.expires_at : (grant.expiry !== undefined ? grant.expiry : null));\n    if (expires !== null && expires !== undefined) {\n      if (policy.expiry_inclusive) { if (!(now <= expires)) { continue; } } else { if (!(now < expires)) { continue; } }\n    }\n    matched.push(i);\n    if (grantIsAllow(grant)) { hasAllow = true; } else { hasDeny = true; }\n  }\n  if (matched.length === 0) { return make(!!policy.default_allow, 'default', []); }\n  let allowed = false;\n  if (policy.deny_wins) { allowed = hasAllow && !hasDeny; } else { allowed = hasAllow; }\n  return make(allowed, 'grants', matched);\n}\n", "policy.ts": "export const POLICY: any = {\n  \"action_casefold\": true,\n  \"audit_denials\": true,\n  \"default_allow\": true,\n  \"deny_wins\": true,\n  \"expiry_inclusive\": true,\n  \"group_union\": true,\n  \"inherit_roles\": true,\n  \"owner_bypass\": true,\n  \"resource_scope\": \"exact\",\n  \"suspended_denies\": true,\n  \"token_ttl\": 100,\n  \"wildcard_action\": true\n};\n", "helpers.ts": "export function asciiLower(value: any): string {\n  const s: string = typeof value === 'string' ? value : String(value === null || value === undefined ? '' : value);\n  let out = '';\n  for (let i = 0; i < s.length; i++) {\n    const code = s.charCodeAt(i);\n    if (code >= 65 && code <= 90) { out += String.fromCharCode(code + 32); } else { out += s[i]; }\n  }\n  return out;\n}\n\nexport function matchesActionValue(grantAction: any, requestedAction: any, casefold: boolean): boolean {\n  if (casefold) { return asciiLower(grantAction) === asciiLower(requestedAction); }\n  return grantAction === requestedAction;\n}\n\nexport function expandRoles(initial: any[], roleParents: any, inherit: boolean): Set<any> {\n  const seen = new Set<any>();\n  const queue: any[] = [];\n  for (const r of initial) { if (!seen.has(r)) { seen.add(r); queue.push(r); } }\n  if (!inherit) { return seen; }\n  let index = 0;\n  while (index < queue.length) {\n    const role: any = queue[index];\n    index++;\n    const parentsRaw: any = roleParents ? roleParents[role] : undefined;\n    let parents: any[] = [];\n    if (Array.isArray(parentsRaw)) { parents = parentsRaw; } else if (parentsRaw !== null && parentsRaw !== undefined) { parents = [parentsRaw]; }\n    for (const parent of parents) { if (!seen.has(parent)) { seen.add(parent); queue.push(parent); } }\n  }\n  return seen;\n}\n\nexport function grantIsAllow(grant: any): boolean {\n  if (grant && typeof grant.allow === 'boolean') { return grant.allow; }\n  if (grant && typeof grant.allowed === 'boolean') { return grant.allowed; }\n  const fields: string[] = ['effect', 'decision', 'type', 'action_type', 'access', 'result'];\n  for (const field of fields) {\n    if (grant && typeof grant[field] === 'string') {\n      const v = asciiLower(grant[field]);\n      if (v === 'allow' || v === 'allowed' || v === 'permit') { return true; }\n      if (v === 'deny' || v === 'denied' || v === 'forbid' || v === 'forbidden') { return false; }\n    }\n  }\n  if (grant && typeof grant.deny === 'boolean') { return !grant.deny; }\n  return true;\n}\n"}}