{"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 {deduplicate, isRetryable, isDead, computeEffectiveDue} from './helpers.ts';\nexport function run(payload: any, policy: any): any {\n  const now: number = payload.now;\n  const jobs: any[] = Array.isArray(payload.jobs) ? payload.jobs : [];\n  const selected: any[] = deduplicate(jobs, policy.dedup_key);\n  const ready: any[] = [];\n  const waiting: any[] = [];\n  const dead: any[] = [];\n  const cancelled: any[] = [];\n  for (const entry of selected) {\n    const job: any = entry.job;\n    const index: number = entry.index;\n    const retryable: boolean = isRetryable(job, policy);\n    if (job.cancelled && (policy.cancel_wins || !retryable)) {\n      cancelled.push({id: job.id, index: index});\n      continue;\n    }\n    if (isDead(job, policy, now, retryable)) {\n      dead.push({id: job.id, index: index});\n      continue;\n    }\n    const due: number = computeEffectiveDue(job, policy, retryable);\n    if (due > now) {\n      waiting.push({id: job.id, at: due, index: index});\n    } else {\n      ready.push({id: job.id, index: index, priority: job.priority, created: job.created});\n    }\n  }\n  ready.sort((a: any, b: any) => {\n    const p: number = policy.priority_high_first ? (b.priority - a.priority) : (a.priority - b.priority);\n    if (p !== 0) return p;\n    const c: number = policy.fifo_ties ? (a.created - b.created) : (b.created - a.created);\n    if (c !== 0) return c;\n    return a.index - b.index;\n  });\n  if (policy.dead_order === 'id') {\n    dead.sort((a: any, b: any) => {\n      if (a.id < b.id) return -1;\n      if (a.id > b.id) return 1;\n      return a.index - b.index;\n    });\n  } else {\n    dead.sort((a: any, b: any) => a.index - b.index);\n  }\n  waiting.sort((a: any, b: any) => a.index - b.index);\n  cancelled.sort((a: any, b: any) => a.index - b.index);\n  return {ready: ready.map((e: any) => e.id), waiting: waiting.map((e: any) => ({id: e.id, at: e.at})), dead: dead.map((e: any) => e.id), cancelled: cancelled.map((e: any) => e.id)};\n}\n", "policy.ts": "export const POLICY: any = {\"base_delay\": 100, \"cancel_wins\": true, \"dead_order\": \"id\", \"dedup_key\": \"id\", \"delay_cap\": 150, \"exponential\": true, \"fifo_ties\": true, \"max_attempts\": 3, \"priority_high_first\": true, \"retryable_codes\": [429, 500], \"stale_after\": 1000, \"stale_inclusive\": true};\n", "helpers.ts": "export function deduplicate(jobs: any[], dedupKey: string): any[] {\n  const map: Map<any, any> = new Map();\n  jobs.forEach((job: any, index: number) => {\n    map.set(job[dedupKey], {job: job, index: index});\n  });\n  return Array.from(map.values());\n}\nexport function isRetryable(job: any, policy: any): boolean {\n  return Array.isArray(policy.retryable_codes) && policy.retryable_codes.includes(job.last_code);\n}\nexport function isDead(job: any, policy: any, now: number, retryable: boolean): boolean {\n  const diff: number = now - job.created;\n  const stale: boolean = policy.stale_inclusive ? (diff >= policy.stale_after) : (diff > policy.stale_after);\n  if (stale) return true;\n  if (job.attempt >= policy.max_attempts) return true;\n  if (job.last_code !== 0 && !retryable) return true;\n  return false;\n}\nexport function computeEffectiveDue(job: any, policy: any, retryable: boolean): number {\n  if (!retryable) return job.due;\n  const exponent: number = Math.max(0, job.attempt - 1);\n  const multiplier: number = policy.exponential ? Math.pow(2, exponent) : 1;\n  const delay: number = Math.min(policy.base_delay * multiplier, policy.delay_cap);\n  return Math.max(job.due, job.failed_at + delay);\n}\n"}}