Write lenses in the lens DSL
The lens DSL describes schema-level steps and value-level field transforms in Nickel, JSON, or YAML. Exactly one body variant, such as steps, rules, compose, auto, from_diff, or symmetric, may be present.
Write a document
This JSON document renames one property key:
{
"id": "dev.example.user-v1-to-v2",
"description": "Rename the user name property",
"source": "dev.example.user.v1",
"target": "dev.example.user.v2",
"steps": [
{
"rename_field": {
"old": "name",
"new": "display_name"
}
}
]
}
Each step is a single-key object. --body-vertex supplies the parent vertex for field-level steps; its default is record:body.
Compile and apply in TypeScript
The TypeScript handle retains both the structural chain and any value transforms:
using chain = p.compileLensDocument(
document,
'record:body',
'json',
);
using lens = chain.instantiate(sourceSchema);
const { view, complement } = lens.getJson(
inputRecord,
'record:body',
);
const restored = lens.putJson(view, complement, 'record:body');
compileLensDocument accepts a JavaScript object, text, or UTF-8 bytes. JSON is the default format; pass 'yaml' for YAML. Nickel is not accepted by this WASM entry point because its imports require filesystem resolution.
Use chain.fieldTransforms() to confirm that apply_expr or compute_field steps survived compilation. Those transforms are stored beside the chain and do not appear in chain.toJson().
Compile Nickel or files in Rust
panproto-lens-dsl resolves the bundled Nickel contract and filesystem imports:
#![allow(unused)]
fn main() {
use std::path::Path;
let compiled = panproto_core::lens_dsl::load_and_compile(
Path::new("lenses/user-v1-to-v2.ncl"),
"record:body",
)?;
println!("{} structural steps", compiled.chain.steps.len());
println!("{} transform anchors", compiled.field_transforms.len());
Ok::<(), panproto_core::lens_dsl::LensDslError>(())
}
load_and_compile supports .ncl, .json, .yaml, and .yml. Named references in a compose body resolve against sibling documents in the same directory. auto and from_diff require the schema-aware compile_with_schemas entry point.
Compile and apply with the CLI
schema lens compile lenses/user-v1-to-v2.json \
--body-vertex record:body \
--out compiled.json
schema lens apply compiled.json user-v1.json \
--protocol atproto \
--schema user-v1.schema.json
schema lens compile writes a panproto-compiled-lens-v1 artifact. The file contains the structural chain, the value transforms, and an ordered stages array. The stages determine whether a value expression runs before or after a structural rename. schema lens apply reads this artifact directly and instantiates each stage against the schema produced by the preceding stage.
Raw ProtolensChain JSON remains accepted by schema lens apply, as does a target schema used for automatic lens generation. A file written by schema lens generate --save is a raw structural chain rather than a compiled-lens artifact. It can be applied, but it does not store value transforms derived during automatic generation.
Verification
Instantiate the document against the intended source schema, run get and put on representative instances, and use LensHandle.checkLaws or panproto_lens::laws::check_laws. Compilation validates document shape and expression syntax; it does not show that a value transform is total on production data.
See also
- Apply field transforms for
apply_exprandcompute_field. - Use lenses for runtime operations.
- Lens DSL: denotational semantics for the document model.