Skip to content

bun-types: correct the JSDoc for spawn lazy and JSON5.stringify quotes - #42257

Open
robobun wants to merge 2 commits into
mainfrom
robobun/54d64a53/docs-spawn-lazy-json5-quotes
Open

robobun wants to merge 2 commits into
mainfrom
robobun/54d64a53/docs-spawn-lazy-json5-quotes

Conversation

@robobun

@robobun robobun commented Sep 11, 2026 •

Copy link
Copy Markdown
Collaborator

Problem

  • SpawnOptions.lazy (packages/bun-types/bun.d.ts:7793) says "Reading begins only when you access the stdout or stderr properties." Since child_process: apply kernel backpressure to stdout/stderr pipes #34971 a lazy pipe reader starts paused, and the first read from JS starts it. p.stdout alone starts nothing.
  • JSON5.stringify (bun.d.ts:2089, example at :2107) says "strings use double quotes" and shows {a:1,b:"two"}. Bun prints {a:1,b:'two'}. append_quoted_string (src/runtime/api/JSON5Object.rs:365) always writes single quotes.

Fix

  • lazy: reading begins when you first read from the stream, for example .text() or .getReader(). Access to the property alone does not start it. A new sentence says that, until then, a child that fills the pipe buffer blocks.
  • JSON5.stringify: the prose says "single quotes" and the example output is {a:1,b:'two'}.
  • Comments only. No declaration changes.
  • Verified: new test/integration/bun-types/jsdoc-examples.test.ts. It runs the JSON5.stringify example and compares stdout with the example's comments (fails with the old JSDoc). A second test covers the lazy text. Both pass on Linux x64 (debug build) and Windows x64.

Background

  • node:child_process passes lazy: true to Bun.spawn. child_process: apply kernel backpressure to stdout/stderr pipes #34971 made lazy defer poll registration (SubprocessPipeReader.rs:192, :158 on Windows). The kernel pipe buffer then applies backpressure and the child blocks, as in Node.
  • The ReadableStream over the pipe starts its native source when a consumer attaches. getReader(), .text(), for await, tee() and pipeTo() start the read. p.stdout and new Response(p.stdout) do not.
  • The reference json5 package also prints {a:1,b:'two'}. It uses double quotes only for a string with more ' than ". Bun always writes single quotes and escapes '.
Notes

The new test file sits next to bun-types.test.ts and not inside it, because that file packs and installs bun-types in beforeAll and only type-checks. The lazy test passes with and without this diff, because the runtime does not change. It fails when the spawn uses lazy: false: the child exits within 16 ms.

lazy probe. The child writes 4 MB to stdout with fs.writeSync. maxBuffer: 1000 shows when the reader runs, because the kill fires only after the reader counts bytes. The output is the same on Bun 1.4.3 on Linux x64 (4ff9193) and Windows x64 (canary e5f9986):

eager, nothing                  -> killed, SIGTERM, exited 143 (within 500 ms)
lazy, nothing                   -> not killed, not exited
lazy, nothing, no maxBuffer     -> not exited (child blocks on a full pipe)
lazy, p.stdout (property only)  -> not killed, not exited
lazy, p.stdout.getReader()      -> killed, SIGTERM, exited 143
lazy, p.stdout.text()           -> killed, SIGTERM, exited 143
lazy, new Response(p.stdout)    -> not killed, not exited

On Linux the same probe also covered .bytes(), for await, tee(), pipeTo() and new Response(p.stdout).text() (each starts the read), p.stdout.locked (does not), and stderr (same as stdout).

JSON5.stringify on 1.4.3:

JSON5.stringify({ a: 1, b: "two" })   // {a:1,b:'two'}
JSON5.stringify({ a: "it's" })        // {a:'it\'s'}     (json5 2.2.3: {a:"it's"})
JSON5.stringify({ a: 'say "hi"' })    // {a:'say "hi"'}
JSON5.stringify({ "not-ident": 1 })   // {'not-ident':1}

Before #34971 the poll was registered at spawn with or without lazy, and lazy only skipped the first synchronous read. #34971 gave lazy its current meaning and did not touch bun.d.ts. docs/runtime/child-process.mdx does not mention lazy, so it needs no edit. docs/runtime/json5.mdx already shows single quotes.

Two open PRs touch the same JSDoc blocks. #39925 (the replacer feature) carries the same two JSON5 lines with the same text. #42256 (enforce maxBuffer with lazy) adds a paragraph further down in the lazy block. Neither conflicts with this diff.

test/integration/bun-types/bun-types.test.ts gives identical output with and without this diff. The TypeScript types check fails on every PR that touches packages/bun-types/** since 2026-09-09, because the npm latest tag of @types/node moved to 22.20.2. #42230 has the cause and the fix. prettier --check passes.


[human-review] gate passed · iteration 1 · 2 files touched

fails on main (without fix)
ASAN without fix: 1 FAILED
$ BUN_DEBUG_QUIET_LOGS=1 bun scripts/build.ts --profile=debug --quiet test "--reporter=junit" "--reporter-outfile=/tmp/pr_gate.xml" test/integration/bun-types/jsdoc-examples.test.ts
bun test v1.4.3 (4ff919377)

test/integration/bun-types/jsdoc-examples.test.ts:
45 |     stdout: "pipe",
46 |     stderr: "inherit",
47 |   });
48 |   const [stdout, exitCode] = await Promise.all([proc.stdout.text(), proc.exited]);
49 | 
50 |   expect(stdout).toBe(documented);
                      ^
error: expect(received).toBe(expected)

- "{a:1,b:"two"}
+ "{a:1,b:'two'}
  {
    a: 1,
    b: 2,
  }
  "

- Expected  - 1
+ Received  + 1

      at <anonymous> (/workspace/bun/test/integration/bun-types/jsdoc-examples.test.ts:50:18)
(fail) the JSON5.stringify @example prints what its comments say [430.27ms]
(pass) SpawnOptions.lazy: reading begins at the first read, not at property access [1246.00ms]

 1 pass
 1 fail
 5 expect() calls
Ran 2 tests across 1 file. [3.48s]
error: script "bd" exited with code 1
__F:1:S:0

release without fix: 1 FAILED
bun test v1.4.3-canary.1 (4ff919377)

test/integration/bun-types/jsdoc-examples.test.ts:
45 |     stdout: "pipe",
46 |     stderr: "inherit",
47 |   });
48 |   const [stdout, exitCode] = await Promise.all([proc.stdout.text(), proc.exited]);
49 | 
50 |   expect(stdout).toBe(documented);
                      ^
error: expect(received).toBe(expected)

- "{a:1,b:"two"}
+ "{a:1,b:'two'}
  {
    a: 1,
    b: 2,
  }
  "

- Expected  - 1
+ Received  + 1

      at <anonymous> (/workspace/bun/test/integration/bun-types/jsdoc-examples.test.ts:50:18)
(fail) the JSON5.stringify @example prints what its comments say [10.35ms]
(pass) SpawnOptions.lazy: reading begins at the first read, not at property access [523.24ms]

 1 pass
 1 fail
 5 expect() calls
Ran 2 tests across 1 file. [626.00ms]
__F:1:S:0
passes on PR (with fix)
ASAN with fix: all passed
$ BUN_DEBUG_QUIET_LOGS=1 bun scripts/build.ts --profile=debug --quiet test "--reporter=junit" "--reporter-outfile=/tmp/pr_gate.xml" test/integration/bun-types/jsdoc-examples.test.ts
bun test v1.4.3 (4ff919377)

test/integration/bun-types/jsdoc-examples.test.ts:
(pass) the JSON5.stringify @example prints what its comments say [327.00ms]
(pass) SpawnOptions.lazy: reading begins at the first read, not at property access [986.39ms]

 2 pass
 0 fail
 6 expect() calls
Ran 2 tests across 1 file. [3.05s]
__F:0:S:0

release with fix: all passed
$ bun scripts/build.ts --profile=release
[configured] bun-profile → bun (stripped) in 705ms (unchanged)
ninja: Entering directory `/workspace/bun/build/release'
[1/26] gen generated_host_exports.rs
generated_host_exports.rs: 122 exports (host=5, lazy=10, generic=107, rust=0); 243 extern-C blocks audited
[2/26] gen cpp.rs (cppbind)
[3/26] gen JS modules (bundle-modules)
Preprocess modules (8061ms)
Bundle modules (52ms)
Postprocesss modules (196ms)
Bundle Functions (448ms)
Generate Code (48ms)

[8.81s] Bundled "src/js" for production
  2594 kb
  197 internal modules
  13 native modules
  50 internal functions across 16 files
[3/15] cargo bun_runtime → libbun_runtime.a
�[1m�[92m   Compiling�[0m bun_react_compiler v0.0.0 (/workspace/bun/src/react_compiler)
�[1m�[92m   Compiling�[0m bun_js_printer v0.0.0 (/workspace/bun/src/js_printer)
�[1m�[92m   Compiling�[0m bun_js_parser v0.0.0 (/workspace/bun/src/js_parser)
�[1m�[92m   Compiling�[0m bun_resolver v0.0.0 (/workspace/bun/src/resolver)
�[1m�[92m   Compiling�[0m bun_ini v0.0.0 (/workspace/bun/src/ini)
�[1m�[92m   Compiling�[0m bun_bundler v0.0.0 (/workspace/bun/src/bundler)
�[1m�[92m   Compiling�[0m bun_router v0.0.0
... (truncated)
diff hotspot
packages/bun-types/bun.d.ts                       | 14 ++--
 test/integration/bun-types/jsdoc-examples.test.ts | 99 +++++++++++++++++++++++
 2 files changed, 108 insertions(+), 5 deletions(-)

gate history · 1 passed · 1 rejected · iteration 1

evidence per changed file
file                                               reads  edits  tests
packages/bun-types/bun.d.ts                            1      2     11
test/integration/bun-types/jsdoc-examples.test.ts      1      1     10

`lazy: true` starts the stdout/stderr pipe readers paused. Reading begins
on the first read from the stream, not on property access. Say so, and
say that a child that fills the pipe buffer blocks until then.

JSON5.stringify writes single-quoted strings. The JSDoc prose and its
example said double quotes.
@coderabbitai

coderabbitai Bot commented Sep 11, 2026 •

Copy link
Copy Markdown
Contributor

Warning

Review limit reached

  • Run on-demand review

On-demand reviews are free for the next 10 days. After that, they cost $0.25 per reviewed file.

Or wait 58 seconds for your next included review.

Check out review usage here.

View limit details

Limit details: You’ve used all 10 included reviews currently available.

Learn how review limits work.

Review configuration:

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Essentials

Run ID: 73068688-3569-45ab-9d34-3da84b43158f

📥 Commits

Reviewing files that changed from the base of the PR and between c2bf9b4 and affb29c.

📒 Files selected for processing (2)
  • packages/bun-types/bun.d.ts
  • test/integration/bun-types/jsdoc-examples.test.ts

Comment @coderabbitai help to get the list of available commands.

@claude claude Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I reviewed this PR and didn't find any bugs — both JSDoc corrections check out against the implementation. Leaving final sign-off to the packages/bun-types/ CODEOWNER.

What was reviewed:

  • JSON5.stringify single-quote claim — append_quoted_string in src/runtime/api/JSON5Object.rs:365 writes ' unconditionally; the example output now matches runtime behavior.
  • lazy semantics — SubprocessPipeReader.rs (posix line ~192, windows ~158) defers poll registration "until JS first pulls" and notes the kernel pipe buffer provides backpressure; the new prose mirrors those source comments.
  • No */ introduced inside the JSDoc blocks; no declaration signatures touched.
Extended reasoning...

Overview

This PR edits JSDoc comments only, in a single file: packages/bun-types/bun.d.ts. Two spots change: the JSON5.stringify description and example are corrected from double quotes to single quotes, and the SpawnOptions.lazy description is clarified to say reading starts on the first stream read (not on property access), with a note about pipe-buffer backpressure. No type signatures, overloads, or exported symbols change.

Security risks

None. This is documentation prose inside .d.ts comments; it does not affect compiled output, runtime behavior, or the type-check surface.

Level of scrutiny

Per the repo's docs/types review guidance, .d.ts JSDoc must mirror the runtime exactly and every claim should be verified against the implementation. I checked both:

  • src/runtime/api/JSON5Object.rs:364-365 — append_quoted_string opens with a literal ', confirming single quotes are always used and the new example {a:1,b:'two'} is what Bun prints.
  • src/runtime/api/bun/subprocess/SubprocessPipeReader.rs — on both the Windows (~158) and POSIX (~192) branches, lazy leaves the reader paused so poll/uv_read_start is "deferred until JS first pulls", and the in-source comment explicitly states "the kernel pipe buffer then provides backpressure and the child blocks." The new JSDoc restates exactly this, and the added backpressure sentence is load-bearing (it explains the observable consequence, not just the option name).

I also confirmed the edited JSDoc blocks contain no stray */ that would break the .d.ts parse.

Other factors

.github/CODEOWNERS assigns /packages/bun-types/ to a specific owner, so per the approval guidelines I'm deferring rather than approving outright even though the change is small and verifiably correct. The bug hunt ran to a dry streak with zero findings.

…nst the runtime

jsdoc-examples.test.ts runs the @example of JSON5.stringify from bun.d.ts
and compares stdout with the comment lines under each console.log call.
A second test covers what the lazy JSDoc states: access to the stdout
property does not start the pipe reader, the first read does.

@claude claude Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code review found no issues

No high-confidence issues detected in this change.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants