feat: mockly-hono — file-based mock API server (Hono)
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>
This commit is contained in:
@@ -0,0 +1,26 @@
|
||||
// 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 }
|
||||
}
|
||||
Reference in New Issue
Block a user