{"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 { parseDecimalString, addDecimal, formatDecimal } from './helpers.ts';\n\nexport function run(payload: any, policy: any): any {\n  const now = payload ? payload.now : 0;\n  const events = payload && Array.isArray(payload.events) ? payload.events : [];\n  const maxAge = policy.max_age;\n  const inclusiveAge = policy.inclusive_age;\n  const scopeKey = policy.scope_key;\n  const auditDuplicate = policy.audit_duplicate;\n  const latestBy = policy.latest_by;\n  const tiePolicy = policy.tie_policy;\n  const gapPolicy = policy.gap_policy;\n  const allowResurrection = policy.allow_resurrection;\n  const amountMode = policy.amount_mode;\n  const deleteWins = policy.delete_wins;\n  const precision = policy.precision;\n  const order = policy.order;\n  const sortField = latestBy === 'seq' || latestBy === 'sequence' ? 'seq' : 'ts';\n  const duplicates: any[] = [];\n  const seen = new Set<string>();\n  const afterDedup: any[] = [];\n  for (let i = 0; i < events.length; i++) {\n    const ev = events[i];\n    const age = now - ev.ts;\n    if (age > maxAge) continue;\n    if (age === maxAge && !inclusiveAge) continue;\n    const dedupKey = scopeKey === 'tenant' ? String(ev.tenant) + '\\u0000' + String(ev.id) : String(ev.id);\n    if (seen.has(dedupKey)) {\n      if (auditDuplicate) duplicates.push(ev.id);\n      continue;\n    }\n    seen.add(dedupKey);\n    afterDedup.push({ ev: ev, index: i });\n  }\n  const groups = new Map<string, any>();\n  for (const item of afterDedup) {\n    const ev = item.ev;\n    const key = String(ev.tenant) + '\\u0000' + String(ev.entity);\n    let group = groups.get(key);\n    if (!group) {\n      group = { tenant: ev.tenant, entity: ev.entity, minIndex: item.index, items: [] };\n      groups.set(key, group);\n    }\n    if (item.index < group.minIndex) group.minIndex = item.index;\n    group.items.push(item);\n  }\n  const entities: any[] = [];\n  const entityMeta: any[] = [];\n  for (const group of groups.values()) {\n    const bySort = new Map<any, any>();\n    for (const item of group.items) {\n      const sortVal = item.ev[sortField];\n      if (!bySort.has(sortVal)) {\n        bySort.set(sortVal, item);\n      } else if (tiePolicy === 'last') {\n        bySort.set(sortVal, item);\n      }\n    }\n    const retained = Array.from(bySort.values());\n    const hasDelete = retained.some((item: any) => item.ev.kind === 'delete');\n    const sorted = retained.slice().sort((a: any, b: any) => {\n      const av = a.ev[sortField];\n      const bv = b.ev[sortField];\n      if (av < bv) return -1;\n      if (av > bv) return 1;\n      return 0;\n    });\n    let processedSeq = 0;\n    let value: any = { coeff: 0n, scale: 0 };\n    let deleted = false;\n    for (const item of sorted) {\n      const ev = item.ev;\n      if (gapPolicy === 'ignore' && ev.seq !== processedSeq + 1) continue;\n      processedSeq = ev.seq;\n      if (ev.kind === 'delete') {\n        deleted = true;\n      } else if (ev.kind === 'set') {\n        if (!deleted || allowResurrection) {\n          deleted = false;\n          const amount = parseDecimalString(ev.amount === undefined || ev.amount === null ? '0' : String(ev.amount));\n          if (amountMode === 'delta') value = addDecimal(value, amount);\n          else value = amount;\n        }\n      }\n    }\n    if (deleteWins && hasDelete) deleted = true;\n    const entityObj = { tenant: group.tenant, entity: group.entity, value: formatDecimal(value, precision), deleted: deleted };\n    entities.push(entityObj);\n    entityMeta.push({ obj: entityObj, minIndex: group.minIndex });\n  }\n  if (order === 'key') {\n    entities.sort((a: any, b: any) => {\n      if (String(a.tenant) < String(b.tenant)) return -1;\n      if (String(a.tenant) > String(b.tenant)) return 1;\n      if (String(a.entity) < String(b.entity)) return -1;\n      if (String(a.entity) > String(b.entity)) return 1;\n      return 0;\n    });\n  } else {\n    entityMeta.sort((a: any, b: any) => a.minIndex - b.minIndex);\n    const ordered = entityMeta.map((m: any) => m.obj);\n    entities.length = 0;\n    for (const e of ordered) entities.push(e);\n  }\n  return { entities: entities, duplicates: duplicates };\n}\n", "policy.ts": "export const POLICY: any = {\n  allow_resurrection: false,\n  amount_mode: \"delta\",\n  audit_duplicate: false,\n  delete_wins: false,\n  gap_policy: \"ignore\",\n  inclusive_age: false,\n  latest_by: \"timestamp\",\n  max_age: 120,\n  order: \"input\",\n  precision: 2,\n  scope_key: \"global\",\n  tie_policy: \"last\"\n};\n", "helpers.ts": "export interface DecimalValue { coeff: bigint; scale: number }\n\nexport function parseDecimalString(s: string): DecimalValue {\n  let text = String(s).trim();\n  let negative = false;\n  if (text.startsWith('-')) { negative = true; text = text.slice(1); }\n  else if (text.startsWith('+')) { text = text.slice(1); }\n  const dot = text.indexOf('.');\n  let intPart = dot === -1 ? text : text.slice(0, dot);\n  let fracPart = dot === -1 ? '' : text.slice(dot + 1);\n  if (intPart === '') intPart = '0';\n  const digits = intPart + fracPart;\n  let coeff = digits === '' ? 0n : BigInt(digits);\n  if (negative) coeff = -coeff;\n  return { coeff: coeff, scale: fracPart.length };\n}\n\nexport function addDecimal(a: DecimalValue, b: DecimalValue): DecimalValue {\n  if (a.scale === b.scale) return { coeff: a.coeff + b.coeff, scale: a.scale };\n  if (a.scale > b.scale) {\n    const factor = 10n ** BigInt(a.scale - b.scale);\n    return { coeff: a.coeff + b.coeff * factor, scale: a.scale };\n  }\n  const factor = 10n ** BigInt(b.scale - a.scale);\n  return { coeff: a.coeff * factor + b.coeff, scale: b.scale };\n}\n\nexport function formatDecimal(value: DecimalValue, precision: number): string {\n  const negative = value.coeff < 0n;\n  let coeff = negative ? -value.coeff : value.coeff;\n  const scale = value.scale;\n  let rounded: bigint;\n  if (scale <= precision) {\n    rounded = coeff * (10n ** BigInt(precision - scale));\n  } else {\n    const divisor = 10n ** BigInt(scale - precision);\n    const quotient = coeff / divisor;\n    const remainder = coeff % divisor;\n    rounded = remainder * 2n >= divisor ? quotient + 1n : quotient;\n  }\n  const sign = negative && rounded !== 0n ? '-' : '';\n  if (precision === 0) return sign + rounded.toString();\n  let digits = rounded.toString();\n  while (digits.length <= precision) digits = '0' + digits;\n  const intPart = digits.slice(0, digits.length - precision);\n  const fracPart = digits.slice(digits.length - precision);\n  return sign + intPart + '.' + fracPart;\n}\n"}}