Files
Peter Meier b122de518c 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>
2026-06-23 12:44:31 +02:00

59 lines
1.9 KiB
TypeScript

import type { MockContext } from './respond.ts'
/**
* Replace `{{ path }}` placeholders inside a JSON value.
* {{params.id}} {{query.q}} {{headers.x-foo}}
* {{faker.person.fullName}} {{faker.number.int}} (zero-arg faker methods)
* If a string is exactly one placeholder, the resolved (typed) value is used —
* so {{faker.number.int}} yields a number, not "123".
*/
const RE = /\{\{\s*([\w.$-]+)\s*\}\}/g
function resolve(path: string, ctx: MockContext): unknown {
const parts = path.split('.')
let cur: any =
parts[0] === 'faker'
? ctx.faker
: parts[0] === 'params'
? ctx.params
: parts[0] === 'query'
? ctx.query
: parts[0] === 'headers'
? ctx.headers
: undefined
if (cur === undefined) return undefined
for (let i = 1; i < parts.length; i++) {
cur = cur?.[parts[i]]
if (cur === undefined) return undefined
}
return typeof cur === 'function' ? cur.call(parts[0] === 'faker' ? undefined : cur) : cur
}
export function hasTemplate(v: unknown): boolean {
if (typeof v === 'string') return RE.test(v)
if (Array.isArray(v)) return v.some(hasTemplate)
if (v && typeof v === 'object') return Object.values(v).some(hasTemplate)
return false
}
export function applyTemplate(value: unknown, ctx: MockContext): unknown {
if (typeof value === 'string') {
const whole = value.match(/^\{\{\s*([\w.$-]+)\s*\}\}$/)
if (whole) {
const r = resolve(whole[1], ctx)
return r === undefined ? value : r
}
return value.replace(RE, (m, p) => {
const r = resolve(p, ctx)
return r === undefined ? m : String(r)
})
}
if (Array.isArray(value)) return value.map((v) => applyTemplate(v, ctx))
if (value && typeof value === 'object') {
const out: Record<string, unknown> = {}
for (const [k, v] of Object.entries(value)) out[k] = applyTemplate(v, ctx)
return out
}
return value
}