x402-smartperps / tree / src/snapshot-do.js ยท raw (this view has old GitHub links rewritten for the mirror; raw is byte-exact)
// SQLite Durable Object holding the latest positions snapshot, the cohort and a compact per-coin history (26 h) for
// flows. Written by the cron refresh (worker.js scheduled) and read by paid requests. No KV anywhere.
import { DurableObject } from 'cloudflare:workers';

const HIST_KEEP_MS = 26 * 3600_000;
const TARGETS = [3600_000, 4 * 3600_000, 24 * 3600_000];

export class SnapStore extends DurableObject {
  constructor(ctx, env) {
    super(ctx, env);
    this.sql = ctx.storage.sql;
    this.sql.exec('CREATE TABLE IF NOT EXISTS kv (k TEXT PRIMARY KEY, v TEXT NOT NULL, at INTEGER NOT NULL)');
    this.sql.exec('CREATE TABLE IF NOT EXISTS hist (at INTEGER PRIMARY KEY, coins TEXT NOT NULL)');
  }
  get(k) { const r = this.sql.exec('SELECT v, at FROM kv WHERE k = ?', k).toArray()[0]; return r ? { v: JSON.parse(r.v), at: r.at } : null; }
  set(k, v, at) { this.sql.exec('INSERT INTO kv (k, v, at) VALUES (?, ?, ?) ON CONFLICT (k) DO UPDATE SET v = excluded.v, at = excluded.at', k, JSON.stringify(v), at); }
  /** Store one refresh result. cohortRecord is only passed when it changed. */
  put(snapshot, cohortRecord) {
    if (!snapshot || !Number.isFinite(snapshot.at)) return false;
    this.set('snapshot', snapshot, snapshot.at);
    if (cohortRecord) this.set('cohort', cohortRecord, cohortRecord.at);
    const compact = Object.fromEntries(Object.entries(snapshot.coins || {}).map(([c, a]) => [c, { net: a.net, ln: a.ln, sn: a.sn, tl: a.tl, ts: a.ts }]));
    this.sql.exec('INSERT OR REPLACE INTO hist (at, coins) VALUES (?, ?)', snapshot.at, JSON.stringify(compact));
    this.sql.exec('DELETE FROM hist WHERE at < ?', snapshot.at - HIST_KEEP_MS);
    this.sql.exec("DELETE FROM kv WHERE k = 'lock'");
    return true;
  }
  /** Refresh lock (cron and lazy refresh never overlap). True = caller may refresh. */
  lock(now, ttlMs = 120_000) {
    const l = this.get('lock');
    if (l && now - l.at < ttlMs) return false;
    this.set('lock', 1, now); return true;
  }
  unlock() { this.sql.exec("DELETE FROM kv WHERE k = 'lock'"); return true; }
  cohort() { return this.get('cohort')?.v || null; }
  /** Latest snapshot, cohort list and the history samples closest to 1 h, 4 h and 24 h before it. */
  read() {
    const snap = this.get('snapshot')?.v || null;
    const cohort = this.get('cohort')?.v || null;
    const hist = [];
    if (snap) {
      for (const ago of TARGETS) {
        const t = snap.at - ago;
        const r = this.sql.exec('SELECT at, coins FROM hist WHERE at < ? ORDER BY ABS(at - ?) LIMIT 1', snap.at, t).toArray()[0];
        if (r && !hist.some((h) => h.at === r.at)) hist.push({ at: r.at, coins: JSON.parse(r.coins) });
      }
    }
    return { snapshot: snap, cohort: cohort?.cohort || [], hist };
  }
}

agentgate-source: code mirror on Cloudflare Pages. All repos