b122de518c
File path = route. JSON/JSON5, dynamic .ts handlers + faker, templating, stateful CRUD (pagination/sort/persist), __variants, sequence, chaos, rate-limit, auth, validation, per-route + global proxy/record, splat routes, X-Scenario overlays, SSE. Visual docs (Scalar) at /__docs from a live OpenAPI 3 spec. OpenAPI import script. Zero-build via tsx. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
27 lines
941 B
TypeScript
27 lines
941 B
TypeScript
// Per-route runtime state: sequence cursors + rate-limit windows.
|
|
// Keyed by route file; reset on server restart.
|
|
|
|
const seqCursor = new Map<string, number>()
|
|
|
|
/** Return the next index in a sequence of length n, advancing the cursor. */
|
|
export function nextSeqIndex(key: string, n: number): number {
|
|
const i = seqCursor.get(key) ?? 0
|
|
seqCursor.set(key, i + 1)
|
|
return i % n
|
|
}
|
|
|
|
const rlHits = new Map<string, number[]>()
|
|
|
|
/** Sliding-window rate limit. Returns remaining + whether this call is allowed. */
|
|
export function rateCheck(key: string, max: number, windowMs: number, now: number) {
|
|
const hits = (rlHits.get(key) ?? []).filter((t) => now - t < windowMs)
|
|
if (hits.length >= max) {
|
|
rlHits.set(key, hits)
|
|
const retryMs = windowMs - (now - hits[0])
|
|
return { allowed: false, remaining: 0, retryMs }
|
|
}
|
|
hits.push(now)
|
|
rlHits.set(key, hits)
|
|
return { allowed: true, remaining: max - hits.length, retryMs: 0 }
|
|
}
|