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>
59 lines
1.9 KiB
TypeScript
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
|
|
}
|