@dreamlake/json-layer
v0.2.0
Published
Layered JSON configuration: JSON Lines of static overwrites and operators, resolved in order, with the source line of every value.
Readme
@dreamlake/json-layer
Layer JSON changes on top of JSON. One change per line.
npm install @dreamlake/json-layerimport { 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 commentsL:1: not JSON["merge", "a", {
"b": 1}]L:1: not JSON{"tags": "none"}["insert", "tags", "x"]L:1: insert needs an array at tagsCLI
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.jsonlReference
| 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
