{"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 {addDecimal, formatDecimal, parseDecimal, zeroDecimal} from './helpers.ts';\nexport function run(payload: any, policy: any): any {\n  const now: any = payload ? payload.now : undefined;\n  const rawEvents: any[] = payload && Array.isArray(payload.events) ? payload.events : [];\n  const indexed: any[] = rawEvents.map((event: any, index: number) => ({event: event, index: index}));\n  const maxAge: any = policy.max_age;\n  const inclusiveAge: boolean = policy.inclusive_age === true;\n  const afterAge: any[] = [];\n  for (const item of indexed) {\n    const age: any = now - item.event.ts;\n    if (age > maxAge) continue;\n    if (!inclusiveAge && age === maxAge) continue;\n    afterAge.push(item);\n  }\n  const duplicates: any[] = [];\n  const seen: Set<string> = new Set();\n  const deduped: any[] = [];\n  for (const item of afterAge) {\n    const key: string = policy.scope_key === 'tenant' ? JSON.stringify([item.event.tenant, item.event.id]) : JSON.stringify([item.event.id]);\n    if (seen.has(key)) {\n      if (policy.audit_duplicate === true) duplicates.push(item.event.id);\n      continue;\n    }\n    seen.add(key);\n    deduped.push(item);\n  }\n  const groups: Map<string, any> = new Map();\n  for (const item of deduped) {\n    const key: string = JSON.stringify([item.event.tenant, item.event.entity]);\n    let group: any = groups.get(key);\n    if (!group) {\n      group = {tenant: item.event.tenant, entity: item.event.entity, earliest: item.index, items: []};\n      groups.set(key, group);\n    }\n    if (item.index < group.earliest) group.earliest = item.index;\n    group.items.push(item);\n  }\n  const sortField: string = (policy.latest_by === 'timestamp' || policy.latest_by === 'ts') ? 'ts' : 'seq';\n  const entities: any[] = [];\n  for (const group of groups.values()) {\n    const bySort: Map<string, any> = new Map();\n    for (const item of group.items) {\n      const sortKey: string = JSON.stringify(item.event[sortField]);\n      if (!bySort.has(sortKey)) {\n        bySort.set(sortKey, item);\n      } else if (policy.tie_policy === 'last') {\n        bySort.set(sortKey, item);\n      }\n    }\n    const retained: any[] = Array.from(bySort.values());\n    retained.sort((a: any, b: any) => {\n      const av: any = a.event[sortField];\n      const bv: any = b.event[sortField];\n      if (av < bv) return -1;\n      if (av > bv) return 1;\n      return a.index - b.index;\n    });\n    let processedSeq: number = 0;\n    let value: any = zeroDecimal();\n    let deleted: boolean = false;\n    for (const item of retained) {\n      const event: any = item.event;\n      if (policy.gap_policy === 'ignore') {\n        if (event.seq !== processedSeq + 1) continue;\n      }\n      processedSeq = event.seq;\n      if (event.kind === 'delete') {\n        deleted = true;\n      } else if (event.kind === 'set') {\n        if (!deleted || policy.allow_resurrection === true) {\n          deleted = false;\n          const amount: any = parseDecimal(event.amount === undefined || event.amount === null ? '0' : event.amount);\n          if (policy.amount_mode === 'delta') value = addDecimal(value, amount);\n          else value = amount;\n        }\n      }\n    }\n    if (policy.delete_wins === true && retained.some((item: any) => item.event.kind === 'delete')) deleted = true;\n    entities.push({tenant: group.tenant, entity: group.entity, value: formatDecimal(value, policy.precision), deleted: deleted, earliest: group.earliest});\n  }\n  if (policy.order === 'key') {\n    entities.sort((a: any, b: any) => {\n      const at: string = String(a.tenant);\n      const bt: string = String(b.tenant);\n      if (at < bt) return -1;\n      if (at > bt) return 1;\n      const ae: string = String(a.entity);\n      const be: string = String(b.entity);\n      if (ae < be) return -1;\n      if (ae > be) return 1;\n      return 0;\n    });\n  } else {\n    entities.sort((a: any, b: any) => a.earliest - b.earliest);\n  }\n  const outputEntities: any[] = entities.map((item: any) => ({tenant: item.tenant, entity: item.entity, value: item.value, deleted: item.deleted}));\n  return {entities: outputEntities, duplicates: duplicates};\n}\n", "policy.ts": "export const POLICY: any = {\n  \"allow_resurrection\": true,\n  \"amount_mode\": \"absolute\",\n  \"audit_duplicate\": true,\n  \"delete_wins\": true,\n  \"gap_policy\": \"accept\",\n  \"inclusive_age\": true,\n  \"latest_by\": \"sequence\",\n  \"max_age\": 60,\n  \"order\": \"key\",\n  \"precision\": 0,\n  \"scope_key\": \"tenant\",\n  \"tie_policy\": \"first\"\n};\n", "helpers.ts": "export type Decimal = {units: bigint, scale: number};\nexport function zeroDecimal(): Decimal { return {units: 0n, scale: 0}; }\nexport function parseDecimal(raw: any): Decimal {\n  const s: string = String(raw).trim();\n  let sign: bigint = 1n;\n  let body: string = s;\n  if (body.startsWith('-')) { sign = -1n; body = body.slice(1); }\n  else if (body.startsWith('+')) { body = body.slice(1); }\n  const dot: number = body.indexOf('.');\n  let intPart: string = dot === -1 ? body : body.slice(0, dot);\n  let fracPart: string = dot === -1 ? '' : body.slice(dot + 1);\n  if (intPart === '') intPart = '0';\n  const digits: string = intPart + fracPart;\n  const units: bigint = digits === '' ? 0n : BigInt(digits);\n  return {units: sign * units, scale: fracPart.length};\n}\nexport function addDecimal(a: Decimal, b: Decimal): Decimal {\n  const scale: number = Math.max(a.scale, b.scale);\n  const av: bigint = a.units * (10n ** BigInt(scale - a.scale));\n  const bv: bigint = b.units * (10n ** BigInt(scale - b.scale));\n  return {units: av + bv, scale: scale};\n}\nexport function formatDecimal(value: Decimal, precision: number): string {\n  const p: number = Number(precision);\n  let units: bigint = value.units;\n  const negative: boolean = units < 0n;\n  if (negative) units = -units;\n  if (value.scale > p) {\n    const divisor: bigint = 10n ** BigInt(value.scale - p);\n    const quotient: bigint = units / divisor;\n    const remainder: bigint = units % divisor;\n    units = remainder * 2n >= divisor ? quotient + 1n : quotient;\n  } else if (value.scale < p) {\n    units = units * (10n ** BigInt(p - value.scale));\n  }\n  const sign: string = negative && units !== 0n ? '-' : '';\n  if (p === 0) return sign + units.toString();\n  const digits: string = units.toString().padStart(p + 1, '0');\n  const intPart: string = digits.slice(0, digits.length - p);\n  const fracPart: string = digits.slice(digits.length - p);\n  return sign + intPart + '.' + fracPart;\n}\n"}}