{"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';\n\nexport function run(payload: any, policy: any): any {\n  const now: number = payload && payload.now !== undefined ? payload.now : 0;\n  const events: any[] = payload && Array.isArray(payload.events) ? payload.events : [];\n  const maxAge: number = policy.max_age;\n  const inclusiveAge: boolean = !!policy.inclusive_age;\n  const auditDuplicate: boolean = !!policy.audit_duplicate;\n  const scopeKey: string = policy.scope_key;\n  const latestBy: string = policy.latest_by;\n  const tiePolicy: string = policy.tie_policy;\n  const gapPolicy: string = policy.gap_policy;\n  const allowResurrection: boolean = !!policy.allow_resurrection;\n  const amountMode: string = policy.amount_mode;\n  const deleteWins: boolean = !!policy.delete_wins;\n  const precision: number = typeof policy.precision === 'number' ? policy.precision : 0;\n  const order: string = policy.order;\n  const duplicates: any[] = [];\n  const seen = new Set<string>();\n  type Entry = { ev: any; idx: number };\n  const filtered: Entry[] = [];\n  for (let i = 0; i < events.length; i++) {\n    const ev = events[i];\n    const age = now - ev.ts;\n    const discard = inclusiveAge ? age > maxAge : age >= maxAge;\n    if (discard) continue;\n    const key = scopeKey === 'tenant' ? JSON.stringify([ev.tenant, ev.id]) : JSON.stringify([ev.id]);\n    if (seen.has(key)) {\n      if (auditDuplicate) duplicates.push(ev.id);\n      continue;\n    }\n    seen.add(key);\n    filtered.push({ ev: ev, idx: i });\n  }\n  type Group = { tenant: any; entity: any; entries: Entry[]; earliest: number };\n  const groups = new Map<string, Group>();\n  for (const entry of filtered) {\n    const gk = JSON.stringify([entry.ev.tenant, entry.ev.entity]);\n    let g = groups.get(gk);\n    if (!g) {\n      g = { tenant: entry.ev.tenant, entity: entry.ev.entity, entries: [], earliest: entry.idx };\n      groups.set(gk, g);\n    }\n    g.entries.push(entry);\n    if (entry.idx < g.earliest) g.earliest = entry.idx;\n  }\n  const sortField: string = latestBy === 'seq' || latestBy === 'sequence' ? 'seq' : 'ts';\n  type Out = { tenant: any; entity: any; value: string; deleted: boolean; earliest: number };\n  const out: Out[] = [];\n  for (const g of groups.values()) {\n    const byTie = new Map<any, Entry>();\n    for (const entry of g.entries) {\n      const tieVal = entry.ev[sortField];\n      if (tiePolicy === 'first') {\n        if (!byTie.has(tieVal)) byTie.set(tieVal, entry);\n      } else {\n        byTie.set(tieVal, entry);\n      }\n    }\n    const retained: Entry[] = Array.from(byTie.values());\n    retained.sort((a, b) => {\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    const hasDelete = retained.some((e) => e.ev.kind === 'delete');\n    let prevSeq = 0;\n    let value = { coeff: 0n, scale: 0 };\n    let deleted = false;\n    for (const entry of retained) {\n      const ev = entry.ev;\n      if (gapPolicy === 'ignore') {\n        if (ev.seq !== prevSeq + 1) continue;\n      }\n      prevSeq = 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 amt = parseDecimal(ev.amount !== undefined && ev.amount !== null ? ev.amount : '0');\n          if (amountMode === 'absolute') {\n            value = amt;\n          } else {\n            value = addDecimal(value, amt);\n          }\n        }\n      }\n    }\n    if (deleteWins && hasDelete) deleted = true;\n    out.push({ tenant: g.tenant, entity: g.entity, value: formatDecimal(value, precision), deleted: deleted, earliest: g.earliest });\n  }\n  if (order === 'key') {\n    out.sort((a, b) => {\n      const at = String(a.tenant);\n      const bt = String(b.tenant);\n      if (at < bt) return -1;\n      if (at > bt) return 1;\n      const ae = String(a.entity);\n      const be = String(b.entity);\n      if (ae < be) return -1;\n      if (ae > be) return 1;\n      return 0;\n    });\n  } else {\n    out.sort((a, b) => a.earliest - b.earliest);\n  }\n  const entities = out.map((e) => ({ tenant: e.tenant, entity: e.entity, value: e.value, deleted: e.deleted }));\n  return { entities: entities, duplicates: duplicates };\n}\n", "policy.ts": "export const POLICY: any = {\"allow_resurrection\": false, \"amount_mode\": \"delta\", \"audit_duplicate\": false, \"delete_wins\": false, \"gap_policy\": \"ignore\", \"inclusive_age\": false, \"latest_by\": \"timestamp\", \"max_age\": 120, \"order\": \"input\", \"precision\": 2, \"scope_key\": \"global\", \"tie_policy\": \"last\"};\n", "helpers.ts": "export type Decimal = { coeff: bigint; scale: number };\n\nfunction pow10(n: number): bigint {\n  return 10n ** BigInt(n);\n}\n\nexport function parseDecimal(input: any): Decimal {\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) || 0; s = s.slice(0, eIdx); }\n  let intPart = s;\n  let fracPart = '';\n  const dot = s.indexOf('.');\n  if (dot >= 0) { intPart = s.slice(0, dot); fracPart = s.slice(dot + 1); }\n  if (intPart === '') intPart = '0';\n  const digits = intPart + fracPart;\n  let coeff = digits === '' ? 0n : BigInt(digits);\n  let scale = fracPart.length - exp;\n  if (scale < 0) { coeff = coeff * pow10(-scale); scale = 0; }\n  if (negative) coeff = -coeff;\n  return { coeff: coeff, scale: scale };\n}\n\nexport function addDecimal(a: Decimal, b: Decimal): Decimal {\n  if (a.scale === b.scale) return { coeff: a.coeff + b.coeff, scale: a.scale };\n  if (a.scale > b.scale) return { coeff: a.coeff + b.coeff * pow10(a.scale - b.scale), scale: a.scale };\n  return { coeff: a.coeff * pow10(b.scale - a.scale) + b.coeff, scale: b.scale };\n}\n\nexport function formatDecimal(value: Decimal, precision: number): string {\n  let coeff = value.coeff;\n  const scale = value.scale;\n  let scaled: bigint;\n  if (scale <= precision) {\n    scaled = coeff * pow10(precision - scale);\n  } else {\n    const divisor = pow10(scale - precision);\n    const negative = coeff < 0n;\n    const absCoeff = negative ? -coeff : coeff;\n    let q = absCoeff / divisor;\n    const r = absCoeff % divisor;\n    if (r * 2n >= divisor) q = q + 1n;\n    scaled = negative ? -q : q;\n  }\n  const negative = scaled < 0n;\n  const absScaled = negative ? -scaled : scaled;\n  const sign = negative ? '-' : '';\n  if (precision === 0) return sign + absScaled.toString();\n  const denom = pow10(precision);\n  const intPart = absScaled / denom;\n  const fracPart = absScaled % denom;\n  return sign + intPart.toString() + '.' + fracPart.toString().padStart(precision, '0');\n}\n"}}