import type { PostgresJsDatabase } from "drizzle-orm/postgres-js"; /** * A key part for {@link aggregateSnapshotKey}. `null`/`undefined` parts * are skipped; objects are stable-stringified (sorted keys) so the same * params always produce the same key regardless of property order. */ export type AggregateSnapshotKeyPart = string | number | boolean | null | undefined | Record; export interface ReadThroughAggregateSnapshotOptions { /** * Semantic cache key, e.g. `finance:aggregates:`. MUST * include every parameter that changes the computed result — build it * with {@link aggregateSnapshotKey}. */ key: string; /** Freshness window. A row older than this is recomputed in place. */ ttlSeconds: number; /** The (expensive) aggregate computation. Result must be JSON-serializable. */ compute: () => Promise; /** Clock override for tests. Defaults to `() => new Date()`. */ now?: () => Date; } export interface AggregateSnapshotResult { data: T; computedAt: Date; /** `true` when served from a fresh stored snapshot, `false` when computed live. */ fromSnapshot: boolean; } /** * Builds a snapshot key by joining parts with `:`. `null`/`undefined` * parts are skipped; object parts are stable-stringified (sorted keys, * `undefined` values dropped) so `{ from, to }` and `{ to, from }` hit * the same snapshot. Any rendered part longer than 48 chars is replaced * by its FNV-1a 64-bit hex digest so keys stay short and index-friendly. * * Example: `aggregateSnapshotKey("finance", "aggregates", query)` → * `finance:aggregates:{"from":"2026-01-01"}` (or * `finance:aggregates:` for long param sets). */ export declare function aggregateSnapshotKey(...parts: AggregateSnapshotKeyPart[]): string; /** * Read-through TTL cache over the `aggregate_snapshots` table. * * Reads the row for `key`; if it is still fresh (`stale_after > now`) * the stored payload is returned without running `compute`. Otherwise * `compute()` runs and the result is upserted in a single * `INSERT ... ON CONFLICT (key) DO UPDATE` statement (works on * neon-http — no transaction required). * * The cache is strictly best-effort: * - a failed snapshot read (e.g. table not yet migrated) is treated as * a miss — the endpoint still computes and responds; * - a failed upsert is swallowed — the freshly computed data is * returned regardless. * * Concurrency: two concurrent cold/stale requests may both run * `compute()` and both upsert — that is acceptable by design (last * write wins, the results are equivalent fresh aggregates). There is * deliberately no locking or stampede protection. * * Serialization caveat: the payload round-trips through `jsonb`, so a * snapshot hit returns the JSON shape of `T` (e.g. `Date` fields come * back as ISO strings). For HTTP handlers that immediately * `c.json(...)` the result, the response bytes are identical either way. */ export declare function readThroughAggregateSnapshot(db: PostgresJsDatabase, options: ReadThroughAggregateSnapshotOptions): Promise>; //# sourceMappingURL=aggregate-snapshots.d.ts.map