{"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, isDenyGrant} from './helpers.ts';\n\nexport function run(payload: any, policy: any): any {\n  const make = (allowed: boolean, reason: string, matched: number[]): any => {\n    return {allowed: allowed, reason: reason, matched: matched, audit: (!allowed && policy.audit_denials ? [reason] : [])};\n  };\n  const subject: any = payload.subject || {};\n  const resource: any = payload.resource || {};\n  const now: any = payload.now;\n  const issuedAt: any = payload.issued_at;\n  if (subject.tenant !== resource.tenant) {\n    return make(false, 'tenant', []);\n  }\n  if (now - issuedAt > policy.token_ttl) {\n    return make(false, 'token', []);\n  }\n  if (policy.suspended_denies && subject.suspended) {\n    return make(false, 'suspended', []);\n  }\n  if (policy.owner_bypass && subject.owner) {\n    return make(true, 'owner', []);\n  }\n  const roles = new Set<any>();\n  const subjectRoles: any[] = subject.roles || [];\n  for (const r of subjectRoles) roles.add(r);\n  if (policy.group_union) {\n    const groups: any[] = subject.groups || [];\n    for (const g of groups) roles.add(g);\n  }\n  if (policy.inherit_roles) {\n    const parents: any = payload.role_parents || {};\n    const queue: any[] = Array.from(roles);\n    const seen = new Set<any>(roles);\n    let idx = 0;\n    while (idx < queue.length) {\n      const cur: any = queue[idx];\n      idx += 1;\n      const ps: any[] = parents[cur] || [];\n      for (const p of ps) {\n        if (!seen.has(p)) {\n          seen.add(p);\n          roles.add(p);\n          queue.push(p);\n        }\n      }\n    }\n  }\n  const grants: any[] = payload.grants || [];\n  const requestedAction: any = payload.action;\n  const resourceId: any = resource.id;\n  const matched: number[] = [];\n  const matchedGrants: any[] = [];\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.roles;\n    let roleOk = false;\n    if (Array.isArray(grantRole)) {\n      for (const r of grantRole) if (roles.has(r)) roleOk = true;\n    } else if (roles.has(grantRole)) {\n      roleOk = true;\n    }\n    if (!roleOk) continue;\n    const grantAction: any = grant.action;\n    let actionOk = false;\n    if (policy.action_casefold) {\n      if (typeof grantAction === 'string' && typeof requestedAction === 'string' && asciiLower(grantAction) === asciiLower(requestedAction)) actionOk = true;\n    } else {\n      if (grantAction === requestedAction) actionOk = true;\n    }\n    if (!actionOk && policy.wildcard_action && grantAction === '*') actionOk = true;\n    if (!actionOk) continue;\n    const scope: any = grant.scope !== undefined ? grant.scope : (grant.resource !== undefined ? grant.resource : (grant.resource_id !== undefined ? grant.resource_id : grant.scope_id));\n    let scopeOk = false;\n    if (policy.resource_scope === 'prefix') {\n      if (typeof scope === 'string' && typeof resourceId === 'string' && resourceId.startsWith(scope)) scopeOk = true;\n    } else {\n      if (scope === resourceId) scopeOk = true;\n    }\n    if (!scopeOk) continue;\n    const expires: any = grant.expires !== undefined ? grant.expires : (grant.expiry !== undefined ? grant.expiry : (grant.expires_at !== undefined ? grant.expires_at : null));\n    let expiryOk = false;\n    if (expires === null || expires === undefined) expiryOk = true;\n    else if (policy.expiry_inclusive) expiryOk = now <= expires;\n    else expiryOk = now < expires;\n    if (!expiryOk) continue;\n    matched.push(i);\n    matchedGrants.push(grant);\n  }\n  if (matched.length === 0) {\n    return make(!!policy.default_allow, 'default', []);\n  }\n  let hasAllow = false;\n  let hasDeny = false;\n  for (const g of matchedGrants) {\n    if (isDenyGrant(g)) hasDeny = true;\n    else hasAllow = true;\n  }\n  let allowed = false;\n  if (policy.deny_wins) allowed = hasAllow && !hasDeny;\n  else allowed = hasAllow;\n  return make(allowed, 'grants', matched);\n}\n", "policy.ts": "export const POLICY: any = {\"deny_wins\": true, \"expiry_inclusive\": true, \"inherit_roles\": true, \"owner_bypass\": true, \"action_casefold\": true, \"group_union\": true, \"resource_scope\": \"exact\", \"suspended_denies\": true, \"token_ttl\": 100, \"wildcard_action\": true, \"audit_denials\": true, \"default_allow\": true};\n", "helpers.ts": "export function asciiLower(s: string): string { let out = ''; for (let i = 0; i < s.length; i++) { const c = s.charCodeAt(i); if (c >= 65 && c <= 90) out += String.fromCharCode(c + 32); else out += s.charAt(i); } return out; }\nexport function isDenyGrant(grant: any): boolean { const e: any = grant.effect !== undefined ? grant.effect : (grant.decision !== undefined ? grant.decision : (grant.type !== undefined ? grant.type : 'allow')); return typeof e === 'string' && asciiLower(e) === 'deny'; }\n"}}