Releases: google/zx
8.8.5 — Temporary Reservoir
This release fixes the issue, when zx flushes external node_modules on linking #1348 #1349 #1355
Also globby@15.0.0 arrives here.
8.8.4 — Flange Coupling
It's time. This release updates zx internals to make the ps API and related methods ProcessPromise.kill(), kill() work on Windows systems without wmic.
#1344 webpod/ps#15
- WMIC will be missing in Windows 11 25H2 (kernel >= 26000)
- The windows-latest label in GitHub Actions will migrate from Windows Server 2022 to Windows Server 2025 beginning September 2, 2025 and finishing by September 30, 2025.
8.8.3 — Sealing Gasket
Continues #1339 to prevent injections via Proxy input or custom toString() manipulations.
8.8.2 — Leaking Valve
8.8.1 — Turbo Flush
We keep improving the projects internal infra to bring more stability, safety and performance for artifacts.
Featfixes
- Applied flags filtration for CLI-driven deps install #1308
- Added
kill()event logging #1312 - Set
SIGTERMaskill()fallback signal #1313 - Allowed
stdio()arg be an array #1311
const p = $({halt: true})`cmd`
p.stdio([stream, 'ignore', 'pipe'])Enhancements
- Added check for zx@lite pkg contents #1317 #1316
- Simplified
ProcessPromise[asyncIterator]inners #1307 - Updated deps: chalk 5.6.0, fs-extra 11.3.1, yaml 2.8.1 #1309 #1323 #1326
- Added TS@next to the test matrix #1310
- Optimized internal
shellsetters #1314 - Refactored build-publish pipelines and scripts #1319 #1320 #1321 #1322 #1324 #1325 #1327
8.8.0 — Pressure Tested
This release enhances the coherence between the ProcessPromise and the Streams API, eliminating the need for certain script-level workarounds.
✨ New Features
unpipe() — Selectively stop piping
You can now call .unpipe() to stop data transfer from a source to a destination without closing any of the pair. #1302
const p1 = $`echo foo && sleep 0.1 && echo bar && sleep 0.1 && echo baz && sleep 0.1 && echo qux`
const p2 = $`echo 1 && sleep 0.15 && echo 2 && sleep 0.1 && echo 3`
const p3 = $`cat`
p1.pipe(p3)
p2.pipe(p3)
setTimeout(() => p1.unpipe(p3), 150)
const { stdout } = await p3
// 'foo\n1\nbar\n2\n3\n'Many-to-one piping
Multiple sources can now stream into a single destination. All sources complete before the destination closes. #1300
const $h = $({ halt: true })
const p1 = $`echo foo`
const p2 = $h`echo a && sleep 0.1 && echo c && sleep 0.2 && echo e`
const p3 = $h`sleep 0.05 && echo b && sleep 0.1 && echo d`
const p4 = $`sleep 0.4 && echo bar`
const p5 = $h`cat`
await p1
p1.pipe(p5)
p2.pipe(p5)
p3.pipe(p5)
p4.pipe(p5)
const { stdout } = await p5.run()
// 'foo\na\nb\nc\nd\ne\nbar\n'Piping from rejected processes
Processes that exit with errors can now still pipe their output. The internal recorder retains their stream, status, and exit code. #1296
const p1 = $({ nothrow: true })`echo foo && exit 1`
await p1
const p2 = p1.pipe($({ nothrow: true })`cat`)
await p2
p1.output.toString() // 'foo\n'
p1.output.ok // false
p1.output.exitCode // 1
p2.output.toString() // 'foo\n'
p2.output.ok // false
p2.output.exitCode // 1Components versions
Since zx bundles third-party libraries without their package.jsons, their versions weren’t previously visible. You can now access them via the versions static map — including zx itself. #1298 #1295
import { versions } from 'zx'
versions.zx // 8.7.2
versions.chalk // 5.4.18.7.2 — Copper Crafter
Stability and customizability improvements
- Handle
nothrowoption onProcessPromiseinit stage #1288
const o = await $({ nothrow: true })`\033`
o.ok // false
o.cause // Error- Handle
_snapshot.killSignalvalue onkill()#1287
const p = $({killSignal: 'SIGKILL'})`sleep 10`
await p.kill()
p.signal // 'SIGKILL'- Introduced
Failclass #1285
import { Fail } from 'zx'
Fail.EXIT_CODES['2'] = 'Custom error message'
Fail.formatErrorMessage = (err: Error, from: string): string =>
`${err.message} (${from})`- Expose
$as type #1283
import type { $, Options } from 'zx'
const custom$: $ = (pieces: TemplateStringsArray | Partial<Options>, ...args: any[]) => {
// ... custom implementation
}-
Internal tweak ups #1276 #1277 #1278 #1279 #1280 #1281 #1282 #1286 #1289
-
Described the zx architecture basics. This section helps to better understand the zx concepts and internal logic, and will be useful for those who want to become a project contributor, make tools based on it, or create something similar from scratch. #1290 #1291 #1292
8.7.1 — Pipe Whisperer
Continues v8.7.0: handles new ps() corner case and improves $.kill mechanics on Windows #1266 #1267 #1269 webpod/ps#14
8.7.0 — Solder Savior
Important fixes for annoying flaky bugs
kill() 🐞
We've found an interesting case #1262
const p = $`sleep 1000`
const {pid} = p // 12345
await p.kill()If we kill the process again, the result might be unexpected:
await ps({pid}) // {pid: 12345, ppid: 67890, command: 'another command', ...}
p.kill()This happens because the pid may be reused by the system for another process, so we've added extra assertions to prevent indeterminacy:
p.kill() // Error: Too late to kill the process.
p.abort() // Error: Too late to abort the process.ps() 🐛
ps()uses wmic internally on Windows, it relies on fragile heuristics to parse the output. We have improved this logic to handle more format variants, but over time (in v9 maybe) we're planning to change the approach.
#1256 #1263 webpod/ps#12 webpod/ingrid#6
const [root] = await ps.lookup({ pid: process.pid })
assert.equal(root.pid, process.pid)