{"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 {parseDecimal, addDecimal, formatDecimal} from './helpers.ts';\nexport function run(payload: any, policy: any): any {\n  const duplicates: any[] = [];\n  const entities: any[] = [];\n  if (!payload || !Array.isArray(payload.events)) { return {entities: [], duplicates: []}; }\n  const now = payload.now;\n  const maxAge = policy.max_age;\n  const inclusiveAge = !!policy.inclusive_age;\n  const seen = new Set<string>();\n  const retained: any[] = [];\n  for (let i = 0; i < payload.events.length; i++) {\n    const ev = payload.events[i];\n    const age = now - ev.ts;\n    if (age > maxAge || (!inclusiveAge && age === maxAge)) { continue; }\n    const dedupKey = policy.scope_key === 'tenant' ? JSON.stringify([ev.tenant, ev.id]) : JSON.stringify([ev.id]);\n    if (seen.has(dedupKey)) {\n      if (policy.audit_duplicate) { duplicates.push(ev.id); }\n      continue;\n    }\n    seen.add(dedupKey);\n    retained.push({ev: ev, index: i});\n  }\n  const groups = new Map<string, any>();\n  for (const item of retained) {\n    const ev = item.ev;\n    const gk = JSON.stringify([ev.tenant, ev.entity]);\n    let g = groups.get(gk);\n    if (!g) { g = {tenant: ev.tenant, entity: ev.entity, items: [], earliest: item.index}; groups.set(gk, g); }\n    g.items.push(item);\n    if (item.index < g.earliest) { g.earliest = item.index; }\n  }\n  const sortField = (policy.latest_by === 'ts' || policy.latest_by === 'timestamp' || policy.latest_by === 'time') ? 'ts' : 'seq';\n  for (const g of groups.values()) {\n    const byKey = new Map<any, any>();\n    for (const item of g.items) {\n      const k = item.ev[sortField];\n      if (!byKey.has(k)) { byKey.set(k, item); } else if (policy.tie_policy === 'last') { byKey.set(k, item); }\n    }\n    const kept: any[] = Array.from(byKey.values());\n    const hasDelete = kept.some((it: any) => it.ev.kind === 'delete');\n    const sorted = kept.slice().sort((a: any, b: any) => a.ev[sortField] - b.ev[sortField]);\n    let currentSeq = 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 (policy.gap_policy === 'ignore') {\n        if (ev.seq !== currentSeq + 1) { continue; }\n      }\n      currentSeq = ev.seq;\n      if (ev.kind === 'delete') { deleted = true; } else if (ev.kind === 'set') {\n        if (!deleted || policy.allow_resurrection) {\n          deleted = false;\n          const amt = parseDecimal(ev.amount);\n          if (policy.amount_mode === 'delta') { value = addDecimal(value, amt); } else { value = amt; }\n        }\n      }\n    }\n    if (policy.delete_wins && hasDelete) { deleted = true; }\n    const precision = typeof policy.precision === 'number' ? policy.precision : 0;\n    entities.push({tenant: g.tenant, entity: g.entity, value: formatDecimal(value, precision), deleted: deleted, earliest: g.earliest});\n  }\n  if (policy.order === 'key') {\n    entities.sort((a: any, b: any) => {\n      if (a.tenant < b.tenant) return -1;\n      if (a.tenant > b.tenant) return 1;\n      if (a.entity < b.entity) return -1;\n      if (a.entity > b.entity) return 1;\n      return 0;\n    });\n  } else {\n    entities.sort((a: any, b: any) => a.earliest - b.earliest);\n  }\n  const outEntities = entities.map((e: any) => ({tenant: e.tenant, entity: e.entity, value: e.value, deleted: e.deleted}));\n  return {entities: outEntities, duplicates: duplicates};\n}\n", "policy.ts": "export const POLICY: any = {\"allow_resurrection\": true, \"amount_mode\": \"absolute\", \"audit_duplicate\": true, \"delete_wins\": true, \"gap_policy\": \"accept\", \"inclusive_age\": true, \"latest_by\": \"sequence\", \"max_age\": 60, \"order\": \"key\", \"precision\": 0, \"scope_key\": \"tenant\", \"tie_policy\": \"first\"};\n", "helpers.ts": "export function parseDecimal(input: any): any {\n  let s = String(input).trim();\n  let negative = false;\n  if (s.startsWith('-')) { negative = true; s = s.slice(1); } else if (s.startsWith('+')) { s = s.slice(1); }\n  let exp = 0;\n  const eIdx = s.search(/[eE]/);\n  if (eIdx >= 0) { exp = parseInt(s.slice(eIdx + 1), 10); s = s.slice(0, eIdx); }\n  const dot = s.indexOf('.');\n  let intPart = dot >= 0 ? s.slice(0, dot) : s;\n  let fracPart = dot >= 0 ? s.slice(dot + 1) : '';\n  if (intPart === '') { intPart = '0'; }\n  let coeffStr = intPart + fracPart;\n  coeffStr = coeffStr.replace(/^0+(?=\\d)/, '');\n  let scale = fracPart.length;\n  let coeff = BigInt(coeffStr === '' ? '0' : coeffStr);\n  if (exp > 0) {\n    if (exp >= scale) { coeff = coeff * (10n ** BigInt(exp - scale)); scale = 0; } else { scale = scale - exp; }\n  } else if (exp < 0) { scale = scale - exp; }\n  if (negative) { coeff = -coeff; }\n  return {coeff: coeff, scale: scale};\n}\nexport function addDecimal(a: any, b: any): any {\n  const scale = Math.max(a.scale, b.scale);\n  const ac = a.coeff * (10n ** BigInt(scale - a.scale));\n  const bc = b.coeff * (10n ** BigInt(scale - b.scale));\n  return {coeff: ac + bc, scale: scale};\n}\nexport function formatDecimal(v: any, precision: number): string {\n  let coeff = v.coeff;\n  const scale = v.scale;\n  let negative = false;\n  if (coeff < 0n) { negative = true; coeff = -coeff; }\n  let scaled: bigint;\n  if (scale > precision) {\n    const divisor = 10n ** BigInt(scale - precision);\n    const q = coeff / divisor;\n    const r = coeff % divisor;\n    scaled = (r * 2n >= divisor) ? q + 1n : q;\n  } else {\n    scaled = coeff * (10n ** BigInt(precision - scale));\n  }\n  let digits = scaled.toString();\n  let result: string;\n  if (precision === 0) { result = digits; } else {\n    digits = digits.padStart(precision + 1, '0');\n    result = digits.slice(0, digits.length - precision) + '.' + digits.slice(digits.length - precision);\n  }\n  return negative && scaled !== 0n ? '-' + result : result;\n}\n"}}