@dreamlake/json-layer
Layer JSON changes on top of JSON. One change per line.
npm install @dreamlake/json-layer
import { resolve, explain } from '@dreamlake/json-layer';
const { value, touches } = resolve(base, [
{ id: 'provider', text: providerLayer },
{ id: 'team', text: teamLayer },
]);
explain(touches, 'network.subnets.b'); // { layer: 'team', line: 3, path: [...] }
Every example below is a base, a layer, and the result. They run as tests.
Overwrite
An object line merges objects and replaces everything else.
{"type": "t3.large", "tags": {"team": "a", "cost": "x"}, "groups": ["sg-1"], "disk": 100}
{"type": "g5.xlarge", "tags": {"team": "b"}, "groups": ["sg-2"], "disk": null}
{"type": "g5.xlarge", "tags": {"team": "b", "cost": "x"}, "groups": ["sg-2"], "disk": null}
Lines apply in order.
{}
{"a": 1}
{"a": 2, "b": 3}
{"a": 2, "b": 3}
Operators
[operator, path, ...values]
insert
{"setup": ["apt update"]}
["insert", "setup", "pip install -e .", "make"]
["insert", "env.paths", "/opt/bin"]
{"setup": ["apt update", "pip install -e .", "make"], "env": {"paths": ["/opt/bin"]}}
prepend
{"setup": ["make"]}
["prepend", "setup", "apt update"]
{"setup": ["apt update", "make"]}
union
{"groups": ["sg-1", "sg-2"]}
["union", "groups", "sg-2", "sg-3"]
{"groups": ["sg-1", "sg-2", "sg-3"]}
remove
{"groups": ["sg-1", "sg-2", "sg-1"], "mounts": [{"src": "/a", "dst": "/b"}, {"src": "/c", "dst": "/d"}]}
["remove", "groups", "sg-1"]
["remove", "mounts", {"dst": "/b", "src": "/a"}]
{"groups": ["sg-2"], "mounts": [{"src": "/c", "dst": "/d"}]}
delete
{"tags": {"team": "a", "scratch": "yes"}}
["delete", "tags.scratch"]
["delete", "tags.missing"]
{"tags": {"team": "a"}}
replace
{"tags": {"team": "a", "cost": "x"}}
["replace", "tags", {"team": "b"}]
{"tags": {"team": "b"}}
merge
{"network": {"vpc": "vpc-1", "subnets": {"a": "subnet-a", "b": "subnet-b"}}}
["merge", "network.subnets", {"b": "subnet-b2", "c": {"id": "subnet-c"}}]
{"network": {"vpc": "vpc-1", "subnets": {"a": "subnet-a", "b": "subnet-b2", "c": {"id": "subnet-c"}}}}
whole value
{"a": 1}
["replace", "", {"b": 2}]
["merge", "", {"c": 3}]
{"b": 2, "c": 3}
Paths
{}
["replace", "a.b.c", 1]
["replace", ["x.y", "z"], 2]
{"p.q": 3}
{"a": {"b": {"c": 1}}, "x.y": {"z": 2}, "p.q": 3}
Comments
{"groups": []}
// whole lines only
// indented is fine
["insert", "groups", "sg-1"]
{"url": "https://example.com//kept"}
{"groups": ["sg-1"], "url": "https://example.com//kept"}
Nothing is reserved
{}
{"$insert": ["x"], "note": "[\"delete\", \"a\"]"}
{"$insert": ["x"], "note": "[\"delete\", \"a\"]"}
Errors
{"a": 1}
["insrt", "a", 1]
L:2: unknown operator "insrt"
{"a": 1} // no trailing comments
L:1: not JSON
["merge", "a", {
"b": 1}]
L:1: not JSON
{"tags": "none"}
["insert", "tags", "x"]
L:1: insert needs an array at tags
CLI
json-layer resolve base.json provider.jsonl team.jsonl
json-layer resolve base.json provider.jsonl team.jsonl --explain network.subnets.b
# network.subnets.b team.jsonl:3 (network.subnets.b)
json-layer check team.jsonl
Reference
| Line | Effect |
|---|---|
{...} | merge objects, replace the rest |
["merge", p, {...}] | the same, at p |
["replace", p, v] | set p to v |
["delete", p] | remove p if present |
["insert", p, v...] | append to array |
["prepend", p, v...] | add to array front |
["union", p, v...] | append values not present |
["remove", p, v...] | remove equal values |
// ... | comment, whole line |
p:"a.b.c", or["a.b", "c"]when a key has a dot;""is the whole value.- Missing parents are created, except by
deleteandremove. - Arrays match by value, never index.
- One document per line.
- Comment lines make a layer non-strict JSON Lines.
License
MIT