{"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, isAllowGrant, isDenyGrant} from './helpers.ts';\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 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 - payload.issued_at > 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 roles = new Set<any>();\n  const initialRoles: any[] = Array.isArray(subject.roles) ? subject.roles : [];\n  for (const r of initialRoles) roles.add(r);\n  if (policy.group_union && Array.isArray(subject.groups)) {\n    for (const g of subject.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    let qi = 0;\n    while (qi < queue.length) {\n      const current: any = queue[qi++];\n      const ups: any = parents[current];\n      if (Array.isArray(ups)) {\n        for (const up of ups) {\n          if (!roles.has(up)) { roles.add(up); queue.push(up); }\n        }\n      }\n    }\n  }\n  const grants: any[] = Array.isArray(payload.grants) ? payload.grants : [];\n  const matched: number[] = [];\n  for (let i = 0; i < grants.length; i++) {\n    const grant: any = grants[i] || {};\n    if (!roles.has(grant.role)) continue;\n    let actionOk = false;\n    if (policy.wildcard_action && grant.action === '*') actionOk = true;\n    else if (policy.action_casefold) actionOk = asciiLower(String(grant.action)) === asciiLower(String(payload.action));\n    else actionOk = grant.action === payload.action;\n    if (!actionOk) continue;\n    const scope: any = grant.scope !== undefined && grant.scope !== null ? grant.scope : (grant.resource !== undefined && grant.resource !== null ? grant.resource : '');\n    let scopeOk = false;\n    if (policy.resource_scope === 'exact') scopeOk = scope === resource.id;\n    else scopeOk = typeof resource.id === 'string' && typeof scope === 'string' && resource.id.startsWith(scope);\n    if (!scopeOk) continue;\n    const expires: any = grant.expires === undefined ? null : grant.expires;\n    let expiryOk = false;\n    if (expires === null) 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  }\n  if (matched.length === 0) return make(!!policy.default_allow, 'default', matched);\n  let anyAllow = false;\n  let anyDeny = false;\n  for (const idx of matched) {\n    if (isAllowGrant(grants[idx])) anyAllow = true;\n    if (isDenyGrant(grants[idx])) anyDeny = true;\n  }\n  const allowed: boolean = policy.deny_wins ? (anyAllow && !anyDeny) : anyAllow;\n  return make(allowed, 'grants', matched);\n}\n", "policy.ts": "export const POLICY: any = {\"deny_wins\": false, \"expiry_inclusive\": false, \"inherit_roles\": false, \"owner_bypass\": false, \"action_casefold\": false, \"group_union\": false, \"resource_scope\": \"prefix\", \"suspended_denies\": false, \"token_ttl\": 200, \"wildcard_action\": false, \"audit_denials\": false, \"default_allow\": false};\n", "helpers.ts": "export function asciiLower(value: string): string {\n  let out = '';\n  for (let i = 0; i < value.length; i++) {\n    const code = value.charCodeAt(i);\n    out += (code >= 65 && code <= 90) ? String.fromCharCode(code + 32) : value[i];\n  }\n  return out;\n}\nexport function isAllowGrant(grant: any): boolean {\n  if (!grant) return false;\n  if (typeof grant.allow === 'boolean') return grant.allow;\n  if (typeof grant.allowed === 'boolean') return grant.allowed;\n  if (typeof grant.effect === 'string') return asciiLower(grant.effect) === 'allow';\n  if (typeof grant.decision === 'string') return asciiLower(grant.decision) === 'allow';\n  if (grant.deny === true) return false;\n  return true;\n}\nexport function isDenyGrant(grant: any): boolean {\n  if (!grant) return false;\n  if (typeof grant.allow === 'boolean') return !grant.allow;\n  if (typeof grant.allowed === 'boolean') return !grant.allowed;\n  if (typeof grant.effect === 'string') return asciiLower(grant.effect) === 'deny';\n  if (typeof grant.decision === 'string') return asciiLower(grant.decision) === 'deny';\n  if (grant.deny === true) return true;\n  return false;\n}\n"}}