Validate a mapping before you run it
A mapping that arrives from a file, a request, or another team may not be a valid mapping. Check it first; evaluate only what validates.
Check a mapping’s form
Section titled “Check a mapping’s form”The named export checks well-formedness; no instance needed. Wrap pairings
under mapping: (the same shape the registry stores; validate does not
wrap bare pairing maps the way map does):
import { validate } from '@christiansmith/mapper-js'
const report = validate(mapping)mapping: /year: { source: /published, as: integer }valid: falseerrors: - rule: KW-pipeline-1 message: as must be string, number, boolean, or json pointer: /mapping/~1year/as descriptor: integerwarnings: []The pointer walks into the mapping document you passed (~1 is an
escaped /), rule names the violated requirement, and every problem reports at once.
Fix the list, not one error per run.
Check it against your instance
Section titled “Check it against your instance”The method form adds reachability: do the names the mapping uses exist on this instance?
const report = mapper.validate(mapping)mapping: /slug: { source: /title, transform: sluggify }against an instance registering a slugify transformer:
valid: falseerrors: - rule: KW-transform-1 message: sluggify matches no registered transformer pointer: /mapping/~1slug/transform descriptor: sluggifywarnings: []Validate against the instance you will evaluate on: reachability is a fact about that instance, not about the mapping.
Gate evaluation on the report
Section titled “Gate evaluation on the report”The idiom is two lines, and it turns silent degradation into a refusal:
const report = mapper.validate(mapping)if (!report.valid) throw new Error(JSON.stringify(report.errors))
const { valid, errors, ...result } = await mapper.map(mapping, input)Validation checks the mapping; evaluation still checks the input. Both
valids matter, and they answer different questions; see the error
model.
Treat warnings as review items
Section titled “Treat warnings as review items”warnings never block. They flag the legal-but-suspect, most usefully a
descriptor key that names no keyword and no installed extension, which
evaluation would silently ignore. Only the instance level can know what is
installed, so this warning comes from mapper.validate:
mapping: /book: { source: /id, requets: {} }valid: trueerrors: []warnings: - rule: KW-2 message: requets matches no keyword or registered extension pointer: /mapping/~1book/requets descriptor: {}A misspelled plugin name is exactly this shape. Print warnings in development; page nobody over them in production.
Validate in CI
Section titled “Validate in CI”Mapping documents in a repository can be validated on every commit, no input, no network:
import { parse } from '@std/yaml'import { validate } from '@christiansmith/mapper-js'
for await (const entry of Deno.readDir('./mappings')) { Deno.test(entry.name, async () => { const mapping = parse(await Deno.readTextFile(`./mappings/${entry.name}`)) const report = validate(mapping) if (!report.valid) throw new Error(JSON.stringify(report.errors, null, 2)) })}Swap validate for an instance’s mapper.validate, constructed with the
extensions the deployment really installs, and the gate covers reachability
too. To offer the same gate over the network, see Validate over
HTTP.