惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Google DeepMind News
Google DeepMind News
博客园_首页
H
Help Net Security
T
Tailwind CSS Blog
S
SegmentFault 最新的问题
GbyAI
GbyAI
Scott Helme
Scott Helme
D
Docker
Hacker News: Ask HN
Hacker News: Ask HN
P
Privacy & Cybersecurity Law Blog
Jina AI
Jina AI
雷峰网
雷峰网
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
Spread Privacy
Spread Privacy
G
GRAHAM CLULEY
C
Cisco Blogs
The Hacker News
The Hacker News
F
Full Disclosure
Y
Y Combinator Blog
Blog — PlanetScale
Blog — PlanetScale
Recent Announcements
Recent Announcements
G
Google Developers Blog
量子位
K
Kaspersky official blog
Cisco Talos Blog
Cisco Talos Blog
The Cloudflare Blog
A
About on SuperTechFans
C
Cybersecurity and Infrastructure Security Agency CISA
Last Week in AI
Last Week in AI
博客园 - 三生石上(FineUI控件)
Microsoft Security Blog
Microsoft Security Blog
Martin Fowler
Martin Fowler
T
Tenable Blog
P
Palo Alto Networks Blog
H
Heimdal Security Blog
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
W
WeLiveSecurity
Schneier on Security
Schneier on Security
The Register - Security
The Register - Security
F
Fortinet All Blogs
Stack Overflow Blog
Stack Overflow Blog
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
T
The Blog of Author Tim Ferriss
N
News and Events Feed by Topic
Hugging Face - Blog
Hugging Face - Blog
小众软件
小众软件
V
V2EX
爱范儿
爱范儿

DEV Community

Authentication Security Deep Dive: From Brute Force to Salted Hashing (With Java Examples) Why AI Systems Don’t Fail — They Drift Spilling beans for how i learn for exam😁"Reinforcement Learning Cheat Sheet" I Replaced Chrome with Safari for AI Browser Automation. Here's What Broke (and What Finally Worked) How Python Borrows Other People's Work The $40 Architecture: Processing 1 Billion API Requests with 99.99% Uptime Vibe Coding: A Workflow Guide (From Zero to SaaS) Most webhook security guides protect the wrong side. The scary part is delivery. Headless CMS for TanStack Start: Build a Blog with Cosmic EU Age Verification App "Hacked in 2 Minutes" — What Actually Happened Comfy Cloud’s delete function does not actually remove files Running AI Models on GPU Cloud Servers: A Beginner Guide Event-driven media intelligence with AWS Step Functions and Bedrock I scored 500 AI prompts across 8 quality dimensions — here's what broke How to Call Google Gemini API from Next.js (Free Tier, No Backend Needed) The Portal Protocol: Reclaiming Human Connection in the Age of AI How to Fix Your Team's Scattered Knowledge Problem With a Self-Hosted Forum Intro to tc Cloud Functors: A Graph-First Mental Model for the Modern Cloud Designing Multi-Tenant Backends With Both Ownership and Team Access I Built a Neumorphic CSS Library with 77+ Components — Here's What I Learned PostgreSQL Performance Optimization: Why Connection Pooling Is Critical at Scale Cómo construí un SaaS multi-rubro para gestionar expensas en Argentina con FastAPI + Vue 3 🚀 I Built an Ethical Hacking Scanner Tool – Open Source Project I Replaced /usage and /context in Claude Code With a Single Statusline A Pythonic Way to Handle Emails (IMAP/SMTP) with Auto-Discovery and AI-Ready Design I Collected 8.9 Million Polymarket Price Points — Here's What I Found About How Markets Really Move EcoTrack AI — Carbon Footprint Tracker & Dashboard Everyone's Using AI. No One Agrees How. 5 self-hosted ebook managers worth trying in 2026 Building Your First AI Agent with LangChain: From Chatbot to Autonomous Assistant Common SOC 2 Failures (Real World) Stop Vibe-Checking Your AI App: A Practical Guide to Evals How to Use SonarQube and SonarScanner Locally to Level Up Your Code Quality Your Next To-Do App Is Dead — I Replaced Mine with an OpenClaw AI Sign a Nostr event in 60 lines of Python using coincurve — no nostr-sdk, no nbxplorer, no rust toolchain ITGC Audit Explained Like You’re in Big 4 Patch Tuesday abril 2026: Microsoft parcha 163 vulnerabilidades y un zero-day en SharePoint Stop scraping everything: a better way to track competitor price changes Listing on MCPize + the Official MCP Registry while routing payments OUTSIDE the marketplace — how I kept 100% of my x402 revenue Building an AI-Powered Risk Intelligence System Using Serverless Architecture Why We Ripped Function Overloading Out of Our AI Toolchain Testing AI-Generated Code: How to Actually Know If It Works SaaS Churn Is Killing Your Business. Here Is What to Do About It (Without a Support Team) The Speed of AI Is No Longer Linear - And Self-Improving Models Are Why How to Implement RBAC for MCP Tools: A Practical Guide for Engineering Teams From Standard Quote to Persuasive Proposal: AI Automation for Arborists I built a CLI that scaffolds complete multi-tenant SaaS apps Axios CVE-2025–62718: The Silent SSRF Bug That Could Be Hiding in Your Node.js App Right Now The dashboard that ended our friendship Data Pipelines Explained Simply (and How to Build Them with Python) The Hidden Cost of AI Systems Nobody Talks About. undefined vs undeclared, and how typeof behaves Switching from file-based jobs to NATS/Kafka in Rust without changing code io_uring Adventures: Rust Servers That Love Syscalls Why Agentic AI is Killing the Traditional Database The POUR principles of web accessibility for developers and designers Quantum Neural Network 3D — A Deep Dive into Interactive WebGL Visualization How To Install Caveman In Codex On macOS And Windows Automation Pipeline Reliability: Why Your Workflow Breaks When Nobody Is Watching I Built an 'Open World' AI Coding Agent — It Works From ANY Folder From Freelancing to Product: A Tech Service Company's SaaS Transformation China's AI Giants: Adding Tencent Hunyuan & ByteDance Doubao to AI University (74 Providers) On the Vibe Coders and Their Lies clerk: Auto-Summarize Your Claude Code Sessions AI Weekly — 2026/04/10–04/17 | The Model Lockdown Is Here, but the Toolchain Is the Real Battleground AI 週報 — 2026/04/10–2026/04/17 模型封鎖潮來了,但工具鏈才是真戰場 Maybe this is how Open-Source apps are born... 🚀 Fine-Tune LLMs with LoRA and QLoRA: 2026 Guide tRPC v11 + Next.js App Router: End-to-End Type Safety Without the Boilerplate ShadCN UI in 2026: Why I Stopped Installing Component Libraries and Started Owning My Components SaaS Billing in React Server Components: Stripe + Supabase Without a Single `useEffect` Join our DEV Weekend Challenge — $1,000 in Prizes Across TEN winners! Submissions Due April 20 at 6:59 AM UTC. Implementing FSRS Spaced Repetition in Flutter + Supabase — Adding Memory Science to an AI Learning App "I Texted My Localhost From the Train — Claude Code Fixed the Bug Before I Got Home" I Built a Sales Prep AI and It Went Deeper Than Expected Design to Code #2: One JSON, Eleven Outputs Solving the 100M-Row Problem: A Summary Table Pattern for High-Volume Push Notification Logs Flutter Web With Wasm: What Actually Changes For Developers I Built 50 Royalty-Free Soundtracks for My Side Project in a Weekend Using AI Music Generation The Vibe Coding Security Checklist: 7 Things to Check Before You Ship Stop Letting Googlebot Guess Fix Your React App's SEO Right Desconstruindo o Streaming do LinkedIn: Como Criar um Engine de Extração de Vídeo de Alta Performance com HLS e FFmpeg (EDA Part-1) EDA (Exploratory Data Analysis) Explained With Real Life — Why Looking at Your Data Is the Most Important Step in Machine Learning Brand Relationship Management at Scale: Our 4-Touch Outreach System for 200+ Brands Why String.fromEnvironment() Might Return an Empty String in Dart JGuardrails 1.0.0 — Hardening Java LLM Apps Against Jailbreaks, Toxicity, and Prompt Injection Plan and Schedule a Full Week of Threads Content From One Claude Conversation Coding Cat Oran Ep3, Five Tables Changed Everything Updated: BFF Pattern I'm done watching freelancers get buried by 200 proposals. So I'm building the alternative. This is my first post BFS Algorithm in Java Step by Step Tutorial with Examples Tracking LLM Pricing Monthly: An Open Dataset for 22 AI Models How We Measure Content ROI on a Comparison Site: Revenue Attribution Without Perfect Data Introducing Nova AI Ops: The AI-Native Operating System for SRE Teams I built a free desktop video downloader for Windows — Grabbit How Talkie OCR Helps Vision-Impaired & Dyslexic Users Read the World Around Them VRCFaceTracking安装和iPhone面捕配置教程,有bug Even CrowdStrike Can't See Your Agents The Automation Gold Rush: What n8n Workflows and Claude Are Opening Up for Developers Right Now
nesdit: Targeted In-Place Edits for JSON, YAML, and TOML with jq-Style Queries
sdkks · 2026-04-29 · via DEV Community

You know what you want to do. You want to set one value in a config file. The problem is finding it — three levels deep in a YAML you didn't write, in a key named something slightly different from what you expected, in a file that's 400 lines long and growing. So you open it in an editor, search, scroll, edit, save. It works. Then you need to do it again in CI, on a different file, with a value that comes from an environment variable. Now it's a shell script. Now it's fragile.

The deeper frustration isn't the individual file. It's that the moment you want to go slightly further — preview what would change before writing, confirm a file is already at the right value, apply the same change across a stream of Kubernetes manifests — you hit the ceiling of whatever tool you're using. jq is JSON-only and has no in-place flag. yq handles YAML but can't write TOML and gives you no way to detect drift. You end up gluing three tools together with shell, and the glue breaks in ways that are hard to test and harder to debug.

nesdit takes a different approach: one binary, one query syntax (jq-style, so you already know it), three formats. You describe what the document should look like — not how to get there. nesdit applies it, shows you a diff if you want one, tells you via exit code whether anything actually changed, and writes atomically if you say so. The same command that edits one file in your terminal gates a CI step, validates a stream of manifests, or updates a batch of configs — without any shell glue in between.


Install

# Go
go install github.com/sdkks/nesdit/cmd/nesdit@latest

# Homebrew
brew tap sdkks/tap && brew install sdkks/tap/nesdit

Enter fullscreen mode Exit fullscreen mode

Pre-built binaries (Linux amd64/arm64, macOS arm64) are on the releases page.


The Basics

nesdit reads a file (or stdin), applies a jq-style query, and writes the result. Format is auto-detected from the file extension.

# Print to stdout — file untouched
nesdit config.json --query '.env = "production"'

# Edit in-place (atomic write via temp file + rename)
nesdit -i config.json --query '.env = "production"'

# Preview changes without writing (unified diff to stdout)
nesdit -n config.json --query '.env = "production"'

Enter fullscreen mode Exit fullscreen mode

The same flags work for YAML and TOML — no format-specific commands to remember.


JSON: Key Order Preserved, Compact Output

$ echo '{"env":"staging","replicas":1,"debug":false}' | nesdit --query '.env = "production" | .replicas = 3'
{"env":"production","replicas":3,"debug":false}

Enter fullscreen mode Exit fullscreen mode

Key order is preserved — env stays first, debug stays last. nesdit always outputs compact JSON (no newlines between keys). If you hand it pretty-printed JSON it comes back compact — that's the current behaviour, worth knowing before you run it on a formatted file.

Compare with jq, which always pretty-prints by default:

$ echo '{"env":"staging","replicas":1}' | jq '.env = "production"'
{
  "env": "production",
  "replicas": 1
}

Enter fullscreen mode Exit fullscreen mode

And jq has no -i flag — in-place editing requires the workaround:

jq '.env = "production"' config.json > tmp.json && mv tmp.json config.json
# If jq fails, tmp.json is empty and the original is gone.

Enter fullscreen mode Exit fullscreen mode

nesdit's -i writes atomically:

nesdit -i config.json --query '.env = "production"'
# Atomic: temp file in same dir → rename over target.
# On failure: original is untouched.

Enter fullscreen mode Exit fullscreen mode


YAML: Key Order Preserved, Merge Keys Work

# values.yaml
zebra: z-value
apple: a-value
mango: m-value

Enter fullscreen mode Exit fullscreen mode

$ nesdit values.yaml --query '.apple = "updated"'
zebra: z-value
apple: updated
mango: m-value

Enter fullscreen mode Exit fullscreen mode

Key order is preserved exactly. This matters when you're diffing manifests and don't want spurious reordering in the git log.

Anchor merge keys (<<:) are applied at decode time

As of v0.3.2, nesdit correctly applies YAML merge keys. This is particularly useful for Helm values files and Kubernetes configs that use anchors for shared defaults:

# config.yaml
defaults: &defaults
  replicas: 1
  image: nginx

web:
  <<: *defaults
  replicas: 3

Enter fullscreen mode Exit fullscreen mode

$ nesdit config.yaml --query '.web.image = "nginx:1.25"'
defaults:
  replicas: 1
  image: nginx
web:
  replicas: 3
  image: nginx:1.25

Enter fullscreen mode Exit fullscreen mode

The <<: merge is resolved at decode — web.image is accessible and writable. The <<: key does not appear in the output.

What nesdit doesn't preserve in YAML

Comments and quoted strings are both stripped during the parse-serialize cycle:

# input
name: myapp
replicas: 1   # scale this
cpu: "100m"

Enter fullscreen mode Exit fullscreen mode

$ nesdit input.yaml --query '.replicas = 3'
name: myapp
replicas: 3
cpu: 100m

Enter fullscreen mode Exit fullscreen mode

The comment is gone. The quotes on cpu are gone. This is consistent with how most YAML parsers behave — the YAML spec treats 100m and "100m" as the same string value. If your workflow depends on comment preservation, nesdit is not the right tool for that file.

For files without comments — which is the majority of machine-managed config and Kubernetes manifests — nesdit's output is clean and minimal:

# deploy.yaml
name: myapp
replicas: 1
image: nginx:1.24

Enter fullscreen mode Exit fullscreen mode

$ nesdit -i deploy.yaml --query '.replicas = 3 | .image = "nginx:1.25"'

Enter fullscreen mode Exit fullscreen mode

# deploy.yaml after
name: myapp
replicas: 3
image: nginx:1.25

Enter fullscreen mode Exit fullscreen mode

Only the touched values changed.


TOML: Write Support (Flat Keys Work Best)

This is where the ecosystem gap is most obvious. yq (mikefarah) can read TOML but explicitly cannot write it back. kislyuk/yq's tomlq drops comments. dasel strips explicit string quotes.

nesdit can write TOML. For flat (no-section) TOML, it preserves key order and only changes the targeted value:

# config.toml
version = "1.0"
name = "project"
debug = false

Enter fullscreen mode Exit fullscreen mode

$ nesdit config.toml --query '.version = "2.0"'
version = "2.0"
name = "project"
debug = false

Enter fullscreen mode Exit fullscreen mode

For sectioned TOML ([package] style), nesdit currently rewrites sections to inline-table syntax — this is a known trade-off documented in the project:

# input: Cargo.toml
[package]
name = "my-crate"
version = "0.1.0"

[dependencies]
tokio = "1.0"

Enter fullscreen mode Exit fullscreen mode

$ nesdit Cargo.toml --query '.package.version = "0.2.0"'
package = {name = "my-crate", version = "0.2.0"}
dependencies = {tokio = "1.0"}

Enter fullscreen mode Exit fullscreen mode

Semantically correct, structurally different. The inline-table form guarantees stable round-trips regardless of key order — but it is not idiomatic for Cargo.toml or pyproject.toml. For flat TOML configs (version = "x" style) it works cleanly. For sectioned TOML, wait for a future release or file a request.

Human-readable TOML output with --pretty

The --pretty flag emits TOML with blank lines between top-level entries and multi-line arrays:

$ printf '{"name":"myapp","tags":["go","toml","cli"]}\n' | nesdit --format json --output-format toml --pretty
name = "myapp"

tags = [
  "go",
  "toml",
  "cli",
]

Enter fullscreen mode Exit fullscreen mode

--pretty is silently ignored for JSON and YAML output.


Dry-Run and Drift Check: The CI/CD Workflow

This is where nesdit earns its place in a pipeline.

--dry-run (-n): preview before writing

$ nesdit -n helm/values.yaml --query '.image.tag = "v2.0.0"'
--- helm/values.yaml
+++ helm/values.yaml
@@ -1,6 +1,6 @@
 image:
   repository: nginx
-  tag: v1.9.0
+  tag: v2.0.0
   pullPolicy: IfNotPresent
 service:
   port: 80

Enter fullscreen mode Exit fullscreen mode

Exits 0 whether or not there's a diff. Use this in CI to log what a release would change before actually changing it.

--check: gate on drift via exit code

nesdit --check config/values.yaml --query '.image.tag = "v2.0.0"'
# Exit 0 — already up to date
# Exit 1 — error (parse failure, bad query, etc.)
# Exit 2 — would change (drift detected)

Enter fullscreen mode Exit fullscreen mode

Exit code 2 is reserved exclusively for drift, making it safe to branch on in scripts:

nesdit --check helm/values.yaml --query '.image.tag = "v2.0.0"'
case $? in
  0) echo "Already at v2.0.0, nothing to do" ;;
  2) echo "Drift — applying update" && nesdit -i helm/values.yaml --query '.image.tag = "v2.0.0"' ;;
  *) echo "Error" && exit 1 ;;
esac

Enter fullscreen mode Exit fullscreen mode

Neither yq nor jq provides this — you'd have to extract the value, compare it in shell, and then run the edit as a separate step.

Pre-commit hook

# .pre-commit-hooks.yaml
- id: nesdit-check
  name: Detect config drift
  language: system
  entry: nesdit --check --query '.'
  files: ^config/.*\.(yaml|json|toml)$
  pass_filenames: true

Enter fullscreen mode Exit fullscreen mode

Run the identity query (.) against committed config files. If someone hand-edits a file in a way that would change its canonical form, the hook catches it before the commit lands.


Multi-Document YAML via Stdin

Kubernetes manifests are often multi-document YAML — multiple resources in one file, separated by ---. nesdit handles multi-doc YAML in stdin stream mode:

cat manifests.yaml | nesdit --format yaml \
  --where '.kind == "Deployment"' \
  --query '.spec.replicas = 3'

Enter fullscreen mode Exit fullscreen mode

The --where flag applies your query only to documents matching the predicate. Non-matching documents (like Services) pass through unchanged.

Input (manifests.yaml):

---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: frontend
spec:
  replicas: 1
  template:
    spec:
      containers:
        - name: app
          image: frontend:v1.0
---
apiVersion: v1
kind: Service
metadata:
  name: frontend
spec:
  port: 80
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: backend
spec:
  replicas: 2
  template:
    spec:
      containers:
        - name: app
          image: backend:v1.0

Enter fullscreen mode Exit fullscreen mode

Command:

cat manifests.yaml | nesdit --format yaml \
  --where '.kind == "Deployment" and .metadata.name == "frontend"' \
  --query '.spec.template.spec.containers[0].image = "frontend:v2.0"'

Enter fullscreen mode Exit fullscreen mode

Output:

---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: frontend
spec:
  replicas: 1
  template:
    spec:
      containers:
        - name: app
          image: frontend:v2.0
---
apiVersion: v1
kind: Service
metadata:
  name: frontend
spec:
  port: 80
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: backend
spec:
  replicas: 2
  template:
    spec:
      containers:
        - name: app
          image: backend:v1.0

Enter fullscreen mode Exit fullscreen mode

Only the frontend Deployment was touched. The Service and backend Deployment are byte-for-byte identical.

Note: nesdit -i manifests.yaml only supports single-document YAML — multi-doc requires stdin. To write multi-doc edits back to a file, redirect:

cat manifests.yaml | nesdit --format yaml \
  --where '.kind == "Deployment"' \
  --query '.spec.replicas = 3' > manifests.yaml

Enter fullscreen mode Exit fullscreen mode


Shell Variable Injection

--arg: string values

VERSION="v2.0.0"
nesdit -i values.yaml --arg version="$VERSION" --query '.image.tag = $version'

Enter fullscreen mode Exit fullscreen mode

--arg always binds as a string. Numbers passed this way stay strings.

--argjson: typed values

nesdit -i deploy.yaml --argjson replicas=5 --query '.spec.replicas = $replicas'
# .spec.replicas becomes the number 5, not the string "5"

nesdit -i config.yaml --argjson enabled=true --query '.feature.enabled = $enabled'
# boolean

nesdit -i config.yaml --argjson tags='["web","api"]' \
  --create-missing --query '.service.tags = $tags'
# array

Enter fullscreen mode Exit fullscreen mode

--argjson parses the value as JSON — numbers, booleans, arrays, and objects all work. If the value isn't valid JSON, nesdit exits with a clear error:

$ nesdit config.json --argjson v='not-json' --query '.v = $v'
nesdit: error: arg.decode: --argjson v: expected JSON, got "not-json"

Enter fullscreen mode Exit fullscreen mode


Batch Updates

Pass multiple files to update them all in one run:

nesdit -i config/a.yaml config/b.yaml config/c.yaml --query '.replicas = 5'
# nesdit: info: batch.summary: 3 changed, 0 unchanged, 0 errored

Enter fullscreen mode Exit fullscreen mode

By default (--strict), if any file fails, nothing is written. This is the safe default for pipelines where partial updates are worse than no update.

Use --keep-going to process all files and report errors at the end:

nesdit -i config/*.yaml --query '.replicas = 5' --keep-going
# nesdit: error: config/broken.yaml: query.runtime: ...
# nesdit: info: batch.summary: 4 changed, 0 unchanged, 1 errored
# Exit code: 1

Enter fullscreen mode Exit fullscreen mode


A Complete CI/CD Example

# .github/workflows/release.yml
on:
  push:
    tags: ['v*']

jobs:
  bump-chart:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Install nesdit
        run: go install github.com/sdkks/nesdit/cmd/nesdit@latest

      - name: Preview change
        run: |
          nesdit helm/values.yaml -n \
            --arg tag="${{ github.ref_name }}" \
            --query '.image.tag = $tag'

      - name: Check if already current
        id: drift
        run: |
          nesdit helm/values.yaml --check \
            --arg tag="${{ github.ref_name }}" \
            --query '.image.tag = $tag'
          echo "exit=$?" >> $GITHUB_OUTPUT
        continue-on-error: true

      - name: Apply and commit
        if: steps.drift.outputs.exit == '2'
        run: |
          nesdit -i helm/values.yaml \
            --arg tag="${{ github.ref_name }}" \
            --query '.image.tag = $tag'
          git config user.name "release-bot"
          git config user.email "bot@example.com"
          git add helm/values.yaml
          git commit -m "chore: bump image tag to ${{ github.ref_name }}"
          git push

Enter fullscreen mode Exit fullscreen mode

Three steps: preview (log what changes), check (skip commit if already current), apply (one clean diff). The --arg injection avoids shell-quoting issues around the tag value.


Format Transcoding

nesdit can read one format and write another with --output-format:

# JSON → YAML
nesdit config.json --output-format yaml > config.yaml

# YAML → JSON
nesdit values.yaml --output-format json
# {"name":"alice","version":1}

# YAML → TOML
nesdit values.yaml --output-format toml
# name = "alice"
# version = 1

# TOML → JSON
nesdit config.toml --output-format json --query '.package'

Enter fullscreen mode Exit fullscreen mode

When transcoding to TOML with multiple input documents (from JSONL or multi-doc YAML stdin), nesdit separates output documents with +++:

$ printf '{"a":1}\n{"b":2}\n' | nesdit --format jsonl --output-format toml
a = 1
+++
b = 2

Enter fullscreen mode Exit fullscreen mode

Note: --output-format and -i are mutually exclusive — writing a different format back into the same file would silently corrupt it. Pipe to a new file instead.


How It Compares

Feature nesdit jq yq (mikefarah) dasel
JSON
YAML
TOML write
Key order preserved ⚠️
Comment preservation N/A
YAML merge keys (<<:) N/A
Atomic in-place write ❌ (workaround)
Dry-run diff
Drift check (exit code)
Multi-doc YAML + --where ✅ (stdin)
jq query syntax mostly different
Single binary

Where to use each tool:

  • jq: JSON transformation and data extraction pipelines. Nothing beats it for raw JSON power.
  • yq: YAML-heavy workflows, especially when you need XML, CSV, or properties format support, or full multi-doc file editing.
  • dasel: Unified selector syntax if you prefer one query language across all formats.
  • nesdit: Targeted field edits in CI/CD pipelines, especially when you want --check drift detection or --dry-run preview before writing. Also the only option for TOML write support with a jq query syntax.

Safety Under Adversarial Inputs

nesdit ships with per-run resource caps by default:

Limit Default Override
Input size 10 MiB --max-bytes 0
Document nesting depth 1000 --max-depth 0
YAML alias expansion 100,000 nodes --max-yaml-nodes 0
Query file size 1 MiB --max-query-bytes 0

The YAML alias cap mitigates billion-laughs attacks — a crafted YAML file that expands exponentially during parsing. All limits are enforced before the query runs.


Where to Find It

The project is in active development. If you hit an edge case — especially around TOML section formatting, comment handling, or a query behaviour that differs from jq — opening an issue with a minimal reproduction is the most useful contribution. Feature requests and PRs welcome.