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:
Peter Meier
2026-06-23 12:44:31 +02:00
commit b122de518c
53 changed files with 2563 additions and 0 deletions
+26
View File
@@ -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 }
}