{"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, formatRounded} from './helpers.ts';\nexport function run(payload: any, policy: any): any {\n  const now: number = payload.now;\n  const events: any[] = payload && Array.isArray(payload.events) ? payload.events : [];\n  const duplicates: string[] = [];\n  const seen: Set<string> = new Set<string>();\n  const filtered: any[] = [];\n  for (let i = 0; i < events.length; i++) {\n    const ev: any = events[i];\n    const age: number = now - ev.ts;\n    if (age > policy.max_age) continue;\n    if (age === policy.max_age && !policy.inclusive_age) continue;\n    const dedupKey: string = policy.scope_key === 'tenant' ? ev.tenant + '\\u0000' + ev.id : String(ev.id);\n    if (seen.has(dedupKey)) {\n      if (policy.audit_duplicate) duplicates.push(ev.id);\n      continue;\n    }\n    seen.add(dedupKey);\n    filtered.push({ev: ev, idx: i});\n  }\n  const groups: Map<string, any> = new Map<string, any>();\n  for (const item of filtered) {\n    const ev: any = item.ev;\n    const gk: string = ev.tenant + '\\u0000' + ev.entity;\n    let g: any = groups.get(gk);\n    if (!g) {\n      g = {tenant: ev.tenant, entity: ev.entity, items: [], earliest: item.idx};\n      groups.set(gk, g);\n    }\n    g.items.push(item);\n    if (item.idx < g.earliest) g.earliest = item.idx;\n  }\n  const useTs: boolean = policy.latest_by === 'ts' || policy.latest_by === 'time' || policy.latest_by === 'timestamp';\n  const fieldOf = (ev: any): number => useTs ? ev.ts : ev.seq;\n  const results: any[] = [];\n  for (const g of groups.values()) {\n    const byField: Map<number, any> = new Map<number, any>();\n    for (const item of g.items) {\n      const fv: number = fieldOf(item.ev);\n      if (policy.tie_policy === 'last') {\n        byField.set(fv, item);\n      } else if (!byField.has(fv)) {\n        byField.set(fv, item);\n      }\n    }\n    const retained: any[] = Array.from(byField.values());\n    const sorted: any[] = retained.slice().sort((a: any, b: any) => {\n      const d: number = fieldOf(a.ev) - fieldOf(b.ev);\n      if (d !== 0) return d;\n      return a.idx - b.idx;\n    });\n    let seq: number = 0;\n    let value: any = parseDecimal('0');\n    let deleted: boolean = false;\n    let hasDelete: boolean = false;\n    for (const item of retained) {\n      if (item.ev.kind === 'delete') hasDelete = true;\n    }\n    for (const item of sorted) {\n      const ev: any = item.ev;\n      if (policy.gap_policy === 'ignore' && ev.seq !== seq + 1) continue;\n      seq = ev.seq;\n      if (ev.kind === 'delete') {\n        deleted = true;\n      } else if (ev.kind === 'set') {\n        if (!deleted || policy.allow_resurrection) {\n          deleted = false;\n          if (policy.amount_mode === 'delta') {\n            value = addDecimal(value, parseDecimal(ev.amount));\n          } else {\n            value = parseDecimal(ev.amount);\n          }\n        }\n      }\n    }\n    if (policy.delete_wins && hasDelete) deleted = true;\n    results.push({tenant: g.tenant, entity: g.entity, value: formatRounded(value, policy.precision), deleted: deleted, earliest: g.earliest});\n  }\n  if (policy.order === 'key') {\n    results.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    results.sort((a: any, b: any) => a.earliest - b.earliest);\n  }\n  const entities: any[] = results.map((r: any) => ({tenant: r.tenant, entity: r.entity, value: r.value, deleted: r.deleted}));\n  return {entities: entities, 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: string): any {\n  let s: string = String(input).trim();\n  let negative: boolean = false;\n  if (s.startsWith('-')) { negative = true; s = s.slice(1); }\n  else if (s.startsWith('+')) { s = s.slice(1); }\n  const dot: number = s.indexOf('.');\n  let intPart: string = dot === -1 ? s : s.slice(0, dot);\n  let fracPart: string = dot === -1 ? '' : s.slice(dot + 1);\n  if (intPart === '') intPart = '0';\n  const digits: string = intPart + fracPart;\n  let coef: bigint = digits === '' ? BigInt(0) : BigInt(digits);\n  if (negative) coef = -coef;\n  return {coef: coef, scale: fracPart.length};\n}\nfunction pow10(n: number): bigint {\n  return BigInt(10) ** BigInt(n);\n}\nexport function addDecimal(a: any, b: any): any {\n  if (a.scale === b.scale) return {coef: a.coef + b.coef, scale: a.scale};\n  if (a.scale > b.scale) return {coef: a.coef + b.coef * pow10(a.scale - b.scale), scale: a.scale};\n  return {coef: a.coef * pow10(b.scale - a.scale) + b.coef, scale: b.scale};\n}\nexport function formatRounded(value: any, precision: number): string {\n  const p: number = precision;\n  let coef: bigint = value.coef;\n  const scale: number = value.scale;\n  let negative: boolean = false;\n  if (coef < BigInt(0)) { negative = true; coef = -coef; }\n  let rounded: bigint;\n  if (scale <= p) {\n    rounded = coef * pow10(p - scale);\n  } else {\n    const divisor: bigint = pow10(scale - p);\n    const q: bigint = coef / divisor;\n    const r: bigint = coef % divisor;\n    rounded = r * BigInt(2) >= divisor ? q + BigInt(1) : q;\n  }\n  if (rounded === BigInt(0)) negative = false;\n  let s: string = rounded.toString();\n  if (p === 0) return negative ? '-' + s : s;\n  while (s.length <= p) s = '0' + s;\n  const intPart: string = s.slice(0, s.length - p);\n  const fracPart: string = s.slice(s.length - p);\n  const out: string = intPart + '.' + fracPart;\n  return negative ? '-' + out : out;\n}\n"}}