{"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 {compareId, effectiveDue} from './helpers.ts';\nexport function run(payload: any, policy: any): any {\n  const now: number = payload && typeof payload.now === 'number' ? payload.now : 0;\n  const jobs: any[] = payload && Array.isArray(payload.jobs) ? payload.jobs : [];\n  const retryableCodes: any[] = Array.isArray(policy.retryable_codes) ? policy.retryable_codes : [];\n  const dedupMap = new Map<any, any>();\n  for (let i = 0; i < jobs.length; i++) {\n    const job = jobs[i];\n    const dedupValue = job ? job[policy.dedup_key] : undefined;\n    dedupMap.set(dedupValue, {job: job, index: i});\n  }\n  const selected: any[] = Array.from(dedupMap.values()).sort((a: any, b: any) => a.index - b.index);\n  const readyItems: any[] = [];\n  const waitingItems: any[] = [];\n  const deadItems: any[] = [];\n  const cancelledItems: any[] = [];\n  for (const entry of selected) {\n    const job = entry.job;\n    const index: number = entry.index;\n    const id = job.id;\n    const lastCode: number = job.last_code;\n    const retryable: boolean = retryableCodes.indexOf(lastCode) !== -1;\n    if (job.cancelled && (policy.cancel_wins || !retryable)) {\n      cancelledItems.push({id: id, index: index});\n      continue;\n    }\n    const age: number = now - job.created;\n    const stale: boolean = policy.stale_inclusive ? age >= policy.stale_after : age > policy.stale_after;\n    const nonRetryableFailure: boolean = lastCode !== 0 && !retryable;\n    if (stale || job.attempt >= policy.max_attempts || nonRetryableFailure) {\n      deadItems.push({id: id, index: index});\n      continue;\n    }\n    const due: number = effectiveDue(job, policy, retryable);\n    if (due > now) {\n      waitingItems.push({id: id, at: due, index: index});\n    } else {\n      readyItems.push({id: id, index: index, priority: job.priority, created: job.created});\n    }\n  }\n  readyItems.sort((a: any, b: any) => {\n    const priorityCmp: number = policy.priority_high_first ? b.priority - a.priority : a.priority - b.priority;\n    if (priorityCmp !== 0) return priorityCmp;\n    const createdCmp: number = policy.fifo_ties ? a.created - b.created : b.created - a.created;\n    if (createdCmp !== 0) return createdCmp;\n    return a.index - b.index;\n  });\n  waitingItems.sort((a: any, b: any) => a.index - b.index);\n  cancelledItems.sort((a: any, b: any) => a.index - b.index);\n  if (policy.dead_order === 'id') {\n    deadItems.sort((a: any, b: any) => {\n      const c: number = compareId(a.id, b.id);\n      return c !== 0 ? c : a.index - b.index;\n    });\n  } else {\n    deadItems.sort((a: any, b: any) => a.index - b.index);\n  }\n  return {\n    ready: readyItems.map((x: any) => x.id),\n    waiting: waitingItems.map((x: any) => ({id: x.id, at: x.at})),\n    dead: deadItems.map((x: any) => x.id),\n    cancelled: cancelledItems.map((x: any) => x.id)\n  };\n}\n", "policy.ts": "export const POLICY: any = {\n  \"base_delay\": 100,\n  \"exponential\": true,\n  \"max_attempts\": 3,\n  \"retryable_codes\": [429, 500],\n  \"cancel_wins\": true,\n  \"dedup_key\": \"id\",\n  \"fifo_ties\": true,\n  \"priority_high_first\": true,\n  \"dead_order\": \"id\",\n  \"delay_cap\": 150,\n  \"stale_after\": 1000,\n  \"stale_inclusive\": true\n};\n", "helpers.ts": "export function compareId(a: any, b: any): number {\n  if (typeof a === 'number' && typeof b === 'number') return a - b;\n  if (a < b) return -1;\n  if (a > b) return 1;\n  return 0;\n}\nexport function effectiveDue(job: any, policy: any, retryable: boolean): number {\n  if (!retryable || job.last_code === 0) return job.due;\n  const exponent: number = Math.max(0, job.attempt - 1);\n  const factor: number = policy.exponential ? Math.pow(2, exponent) : 1;\n  const delay: number = Math.min(policy.base_delay * factor, policy.delay_cap);\n  const retryAt: number = job.failed_at + delay;\n  return Math.max(job.due, retryAt);\n}\n"}}