You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Core: Disable component manifest by default - #34408, thanks @yannbf!
[!NOTE] Version >=0.5.0 of @​storybook/addon-mcp enables component manifests again. If you're upgrading Storybook from version >= 10.3.0 to >= 10.3.5 and are using the MCP addon, you should also upgrade @​storybook/addon-mcp to keep the docs toolset in the MCP server.
React-Docgen: Add tsconfig fallback chain and warning for monorepos - #34353, thanks @viditkbhatnagar!
React-Docgen: Try .tsx fallback when resolving .js ESM imports in docgen resolvers - #34393, thanks @mixelburg!
UI: Fix mobile navigation when renderLabel returns a React node - #34262, thanks @Nathan54Villaume!
Vite: Use vite hook filter for performance improvements - #34022, thanks @huang-julien!
Commit history:
993996 Merge pull request #35109 from storybookjs/split/docgen-ui-consumption
Docs: Route ArgTypes and Controls through docgen service when flag enabled
3e9e4d refactor(docs): extract ControlsTables component from render helper
Replace renderControlsTables with a ControlsTables FC so table rendering
follows the same component pattern as the rest of the Controls block.
Co-authored-by: Cursor <cursoragent@cursor.com>
35ea56 Merge pull request #35140 from storybookjs/kasper/telemetry-global-state-guard
Telemetry: Preserve state machine when the module loads more than once
6ed38f refactor(telemetry): document load-bearing 'in' guard, type PAYLOAD_ERROR_HANDLER global
The bare PayloadErrorHandler name in typings.d.ts never resolved (declaration
files are skipLibCheck'd), so the global was silently typed any. Export the
type and reference it the same way as the other telemetry globals.
2a8f81 Merge pull request #35147 from storybookjs/kasper/ai-cli-bundled
CLI: Bundle the ai command in core so it never downloads @​storybook/cli
176296 Merge branch 'next' into split/docgen-ui-consumption
616e24 fix(docgen-ui): preserve newlines in service-derived descriptions
Component comments parsed via the docgen service were collapsed onto a
single line because comment-parser defaults to compact spacing, so
multi-paragraph Markdown rendered as one heading. Parse the block
description with preserve spacing (matching react-docgen's legacy __docgenInfo) while keeping a separate compact parse for tag values,
and let the Description block fall back to the service description.
Co-authored-by: Cursor <cursoragent@cursor.com>
356d6d docs(agents): require running fmt:write after edits
Hand-written formatting frequently diverges from oxfmt, so make running yarn fmt:write from code/ a mandatory post-edit step.
Co-authored-by: Cursor <cursoragent@cursor.com>
131e18 Merge pull request #35144 from storybookjs/jeppe-cursor/module-graph-internal-ops-df57
Open Service: Mark module-graph engine commands as internal
5f7ce8 ci: retrigger after onboarding-flow viewport flake
6bc246 Merge branch 'next' into kasper/ai-cli-bundled
c7c43a fix(cli): anchor workingDir to the project root for --config-dir .
dirname(join(process.cwd(), configDir)) collapsed --config-dir . to the
project's parent directory, mispointing story-glob resolution and the
tsconfig/jsconfig lookup in detectLanguage. Taking dirname before resolving
keeps root-level config dirs anchored to the project root. Pre-existing on
next, surfaced by the move (CodeRabbit review on #35147).
65d501 Merge branch 'next' into split/docgen-ui-consumption
ec3c2b Merge pull request #35138 from storybookjs/kasper/ai-cli-telemetry
CLI: Add telemetry for the storybook ai <command> passthrough
1de1a3 Merge remote-tracking branch 'origin/kasper/ai-cli-telemetry' into kasper/ai-cli-bundled
19a3df fix(controls): wait for prepared story and docgen before service table
The service ControlsPanel could render docgen rows before STORY_PREPARED updated
custom argTypes/parameters, causing a brief full-table flicker before include or
exclude filters arrived. Keep the ArgsTable skeleton visible until both the
story is prepared and the docgen query has settled.
Co-authored-by: Cursor <cursoragent@cursor.com>
5a9480 fix(controls): preserve rich object mode after delayed data
When an object control first mounted without data it initialized raw mode and
stayed there after args/docgen arrived. Switch back to rich mode for the
automatic no-data to data transition, and cover the delayed-data case.
Co-authored-by: Cursor <cursoragent@cursor.com>
e5d078 ci: retrigger after auto-cancelled workflow race
3e8cde ci: retrigger after auto-cancelled workflow race
6dd01f refactor(cli): carry tool error text as McpToolResultError cause, document MCP-spec session flow
Review feedback on #35138:
McpToolResultError accepts ErrorOptions and the call site attaches the
tool's error text as cause. The message stays constant so telemetry
error hashes remain aggregatable; the cause is uploaded path-sanitized
and only with crash-reports consent (the standard sanitized error path).
Document that the Mcp-Session-Id flow is MCP Streamable HTTP spec
behavior rather than a tmcp implementation detail, so swapping the
server library in addon-mcp cannot break the CLI handshake.
ef5759 fix(controls): keep delayed object controls in rich mode
ObjectControl initialized raw mode when it first mounted without data and never
switched back after args/docgen arrived. Track the no-data to data transition and
return to rich mode unless the user explicitly requested raw editing via Set
object.
Co-authored-by: Cursor <cursoragent@cursor.com>
208891 fix(docgen-ui): address service controls review feedback
Make service-query snapshots pure for React, remove the server snapshot argument,
and ensure service ControlsPanel still renders prepared custom argTypes when no
service docgen payload exists. Also tidy ArgTypes docs/imports from review.
Co-authored-by: Cursor <cursoragent@cursor.com>
b8fc9e fix(docgen-ui): resolve CI lint, snapshot, and story test failures
Use play-context canvas type in ArgTypes.stories instead of value-importing within
Assert service controls via rendered radio input, not arg name label text
Update StoryStore inline snapshots after argTypes fixture additions
Co-authored-by: Cursor <cursoragent@cursor.com>
20c456 chore: disable experimentalDocgenServer in internal Storybook
The docgen service does not yet work in production builds, so keep the flag off
for now. With it disabled, the docs blocks and controls panel route through the
legacy extraction path and presets keep injecting __docgenInfo, so the normal
non-experimental case is not regressed.
Co-authored-by: Cursor <cursoragent@cursor.com>
93b8b7 Merge remote-tracking branch 'origin/next' into split/docgen-ui-consumption
987c1c Merge pull request #35108 from storybookjs/split/docgen-service-plumbing
Docgen: Register service runtime, payload argTypes, and bundler guardrails
ba60da ci: retrigger after auto-cancelled workflow race
98c9bf test(cli): lock in workingDir-scoped language detection; tidy util re-export
Second thermo-nuclear review pass: detectLanguage's workingDir scoping is
an intended fix (config files are found next to the target Storybook, not
the invoking cwd) — covered with memfs tests; the re-export in util.ts
moves below the import block.
4294bd refactor(cli): single canonical getStorybookData, explicit workingDir in detectLanguage
Addresses the codex thermo-nuclear review:
getStorybookData moves to storybook/internal/cli as the one canonical
project-metadata collector; automigrate/doctor/add delegate to it via a
re-export instead of keeping a near-duplicate.
detectLanguage takes an explicit workingDir for its js/tsconfig lookup
(defaulting to process.cwd() for init), and ai setup passes the target
Storybook's working dir instead of relying on the process cwd.
601178 chore: address codex review — fix stale eval README paths, drop dead import
Core: Disable component manifest by default - #34408, thanks @yannbf!
[!NOTE] Version >=0.5.0 of @​storybook/addon-mcp enables component manifests again. If you're upgrading Storybook from version >= 10.3.0 to >= 10.3.5 and are using the MCP addon, you should also upgrade @​storybook/addon-mcp to keep the docs toolset in the MCP server.
React-Docgen: Add tsconfig fallback chain and warning for monorepos - #34353, thanks @viditkbhatnagar!
React-Docgen: Try .tsx fallback when resolving .js ESM imports in docgen resolvers - #34393, thanks @mixelburg!
UI: Fix mobile navigation when renderLabel returns a React node - #34262, thanks @Nathan54Villaume!
Vite: Use vite hook filter for performance improvements - #34022, thanks @huang-julien!
Commit history:
993996 Merge pull request #35109 from storybookjs/split/docgen-ui-consumption
Docs: Route ArgTypes and Controls through docgen service when flag enabled
3e9e4d refactor(docs): extract ControlsTables component from render helper
Replace renderControlsTables with a ControlsTables FC so table rendering
follows the same component pattern as the rest of the Controls block.
Co-authored-by: Cursor <cursoragent@cursor.com>
35ea56 Merge pull request #35140 from storybookjs/kasper/telemetry-global-state-guard
Telemetry: Preserve state machine when the module loads more than once
6ed38f refactor(telemetry): document load-bearing 'in' guard, type PAYLOAD_ERROR_HANDLER global
The bare PayloadErrorHandler name in typings.d.ts never resolved (declaration
files are skipLibCheck'd), so the global was silently typed any. Export the
type and reference it the same way as the other telemetry globals.
2a8f81 Merge pull request #35147 from storybookjs/kasper/ai-cli-bundled
CLI: Bundle the ai command in core so it never downloads @​storybook/cli
176296 Merge branch 'next' into split/docgen-ui-consumption
616e24 fix(docgen-ui): preserve newlines in service-derived descriptions
Component comments parsed via the docgen service were collapsed onto a
single line because comment-parser defaults to compact spacing, so
multi-paragraph Markdown rendered as one heading. Parse the block
description with preserve spacing (matching react-docgen's legacy __docgenInfo) while keeping a separate compact parse for tag values,
and let the Description block fall back to the service description.
Co-authored-by: Cursor <cursoragent@cursor.com>
356d6d docs(agents): require running fmt:write after edits
Hand-written formatting frequently diverges from oxfmt, so make running yarn fmt:write from code/ a mandatory post-edit step.
Co-authored-by: Cursor <cursoragent@cursor.com>
131e18 Merge pull request #35144 from storybookjs/jeppe-cursor/module-graph-internal-ops-df57
Open Service: Mark module-graph engine commands as internal
5f7ce8 ci: retrigger after onboarding-flow viewport flake
6bc246 Merge branch 'next' into kasper/ai-cli-bundled
c7c43a fix(cli): anchor workingDir to the project root for --config-dir .
dirname(join(process.cwd(), configDir)) collapsed --config-dir . to the
project's parent directory, mispointing story-glob resolution and the
tsconfig/jsconfig lookup in detectLanguage. Taking dirname before resolving
keeps root-level config dirs anchored to the project root. Pre-existing on
next, surfaced by the move (CodeRabbit review on #35147).
65d501 Merge branch 'next' into split/docgen-ui-consumption
ec3c2b Merge pull request #35138 from storybookjs/kasper/ai-cli-telemetry
CLI: Add telemetry for the storybook ai <command> passthrough
1de1a3 Merge remote-tracking branch 'origin/kasper/ai-cli-telemetry' into kasper/ai-cli-bundled
19a3df fix(controls): wait for prepared story and docgen before service table
The service ControlsPanel could render docgen rows before STORY_PREPARED updated
custom argTypes/parameters, causing a brief full-table flicker before include or
exclude filters arrived. Keep the ArgsTable skeleton visible until both the
story is prepared and the docgen query has settled.
Co-authored-by: Cursor <cursoragent@cursor.com>
5a9480 fix(controls): preserve rich object mode after delayed data
When an object control first mounted without data it initialized raw mode and
stayed there after args/docgen arrived. Switch back to rich mode for the
automatic no-data to data transition, and cover the delayed-data case.
Co-authored-by: Cursor <cursoragent@cursor.com>
e5d078 ci: retrigger after auto-cancelled workflow race
3e8cde ci: retrigger after auto-cancelled workflow race
6dd01f refactor(cli): carry tool error text as McpToolResultError cause, document MCP-spec session flow
Review feedback on #35138:
McpToolResultError accepts ErrorOptions and the call site attaches the
tool's error text as cause. The message stays constant so telemetry
error hashes remain aggregatable; the cause is uploaded path-sanitized
and only with crash-reports consent (the standard sanitized error path).
Document that the Mcp-Session-Id flow is MCP Streamable HTTP spec
behavior rather than a tmcp implementation detail, so swapping the
server library in addon-mcp cannot break the CLI handshake.
ef5759 fix(controls): keep delayed object controls in rich mode
ObjectControl initialized raw mode when it first mounted without data and never
switched back after args/docgen arrived. Track the no-data to data transition and
return to rich mode unless the user explicitly requested raw editing via Set
object.
Co-authored-by: Cursor <cursoragent@cursor.com>
208891 fix(docgen-ui): address service controls review feedback
Make service-query snapshots pure for React, remove the server snapshot argument,
and ensure service ControlsPanel still renders prepared custom argTypes when no
service docgen payload exists. Also tidy ArgTypes docs/imports from review.
Co-authored-by: Cursor <cursoragent@cursor.com>
b8fc9e fix(docgen-ui): resolve CI lint, snapshot, and story test failures
Use play-context canvas type in ArgTypes.stories instead of value-importing within
Assert service controls via rendered radio input, not arg name label text
Update StoryStore inline snapshots after argTypes fixture additions
Co-authored-by: Cursor <cursoragent@cursor.com>
20c456 chore: disable experimentalDocgenServer in internal Storybook
The docgen service does not yet work in production builds, so keep the flag off
for now. With it disabled, the docs blocks and controls panel route through the
legacy extraction path and presets keep injecting __docgenInfo, so the normal
non-experimental case is not regressed.
Co-authored-by: Cursor <cursoragent@cursor.com>
93b8b7 Merge remote-tracking branch 'origin/next' into split/docgen-ui-consumption
987c1c Merge pull request #35108 from storybookjs/split/docgen-service-plumbing
Docgen: Register service runtime, payload argTypes, and bundler guardrails
ba60da ci: retrigger after auto-cancelled workflow race
98c9bf test(cli): lock in workingDir-scoped language detection; tidy util re-export
Second thermo-nuclear review pass: detectLanguage's workingDir scoping is
an intended fix (config files are found next to the target Storybook, not
the invoking cwd) — covered with memfs tests; the re-export in util.ts
moves below the import block.
4294bd refactor(cli): single canonical getStorybookData, explicit workingDir in detectLanguage
Addresses the codex thermo-nuclear review:
getStorybookData moves to storybook/internal/cli as the one canonical
project-metadata collector; automigrate/doctor/add delegate to it via a
re-export instead of keeping a near-duplicate.
detectLanguage takes an explicit workingDir for its js/tsconfig lookup
(defaulting to process.cwd() for init), and ai setup passes the target
Storybook's working dir instead of relying on the process cwd.
601178 chore: address codex review — fix stale eval README paths, drop dead import
Core: Disable component manifest by default - #34408, thanks @yannbf!
[!NOTE] Version >=0.5.0 of @​storybook/addon-mcp enables component manifests again. If you're upgrading Storybook from version >= 10.3.0 to >= 10.3.5 and are using the MCP addon, you should also upgrade @​storybook/addon-mcp to keep the docs toolset in the MCP server.
React-Docgen: Add tsconfig fallback chain and warning for monorepos - #34353, thanks @viditkbhatnagar!
React-Docgen: Try .tsx fallback when resolving .js ESM imports in docgen resolvers - #34393, thanks @mixelburg!
UI: Fix mobile navigation when renderLabel returns a React node - #34262, thanks @Nathan54Villaume!
Vite: Use vite hook filter for performance improvements - #34022, thanks @huang-julien!
Commit history:
993996 Merge pull request #35109 from storybookjs/split/docgen-ui-consumption
Docs: Route ArgTypes and Controls through docgen service when flag enabled
3e9e4d refactor(docs): extract ControlsTables component from render helper
Replace renderControlsTables with a ControlsTables FC so table rendering
follows the same component pattern as the rest of the Controls block.
Co-authored-by: Cursor <cursoragent@cursor.com>
35ea56 Merge pull request #35140 from storybookjs/kasper/telemetry-global-state-guard
Telemetry: Preserve state machine when the module loads more than once
6ed38f refactor(telemetry): document load-bearing 'in' guard, type PAYLOAD_ERROR_HANDLER global
The bare PayloadErrorHandler name in typings.d.ts never resolved (declaration
files are skipLibCheck'd), so the global was silently typed any. Export the
type and reference it the same way as the other telemetry globals.
2a8f81 Merge pull request #35147 from storybookjs/kasper/ai-cli-bundled
CLI: Bundle the ai command in core so it never downloads @​storybook/cli
176296 Merge branch 'next' into split/docgen-ui-consumption
616e24 fix(docgen-ui): preserve newlines in service-derived descriptions
Component comments parsed via the docgen service were collapsed onto a
single line because comment-parser defaults to compact spacing, so
multi-paragraph Markdown rendered as one heading. Parse the block
description with preserve spacing (matching react-docgen's legacy __docgenInfo) while keeping a separate compact parse for tag values,
and let the Description block fall back to the service description.
Co-authored-by: Cursor <cursoragent@cursor.com>
356d6d docs(agents): require running fmt:write after edits
Hand-written formatting frequently diverges from oxfmt, so make running yarn fmt:write from code/ a mandatory post-edit step.
Co-authored-by: Cursor <cursoragent@cursor.com>
131e18 Merge pull request #35144 from storybookjs/jeppe-cursor/module-graph-internal-ops-df57
Open Service: Mark module-graph engine commands as internal
5f7ce8 ci: retrigger after onboarding-flow viewport flake
6bc246 Merge branch 'next' into kasper/ai-cli-bundled
c7c43a fix(cli): anchor workingDir to the project root for --config-dir .
dirname(join(process.cwd(), configDir)) collapsed --config-dir . to the
project's parent directory, mispointing story-glob resolution and the
tsconfig/jsconfig lookup in detectLanguage. Taking dirname before resolving
keeps root-level config dirs anchored to the project root. Pre-existing on
next, surfaced by the move (CodeRabbit review on #35147).
65d501 Merge branch 'next' into split/docgen-ui-consumption
ec3c2b Merge pull request #35138 from storybookjs/kasper/ai-cli-telemetry
CLI: Add telemetry for the storybook ai <command> passthrough
1de1a3 Merge remote-tracking branch 'origin/kasper/ai-cli-telemetry' into kasper/ai-cli-bundled
19a3df fix(controls): wait for prepared story and docgen before service table
The service ControlsPanel could render docgen rows before STORY_PREPARED updated
custom argTypes/parameters, causing a brief full-table flicker before include or
exclude filters arrived. Keep the ArgsTable skeleton visible until both the
story is prepared and the docgen query has settled.
Co-authored-by: Cursor <cursoragent@cursor.com>
5a9480 fix(controls): preserve rich object mode after delayed data
When an object control first mounted without data it initialized raw mode and
stayed there after args/docgen arrived. Switch back to rich mode for the
automatic no-data to data transition, and cover the delayed-data case.
Co-authored-by: Cursor <cursoragent@cursor.com>
e5d078 ci: retrigger after auto-cancelled workflow race
3e8cde ci: retrigger after auto-cancelled workflow race
6dd01f refactor(cli): carry tool error text as McpToolResultError cause, document MCP-spec session flow
Review feedback on #35138:
McpToolResultError accepts ErrorOptions and the call site attaches the
tool's error text as cause. The message stays constant so telemetry
error hashes remain aggregatable; the cause is uploaded path-sanitized
and only with crash-reports consent (the standard sanitized error path).
Document that the Mcp-Session-Id flow is MCP Streamable HTTP spec
behavior rather than a tmcp implementation detail, so swapping the
server library in addon-mcp cannot break the CLI handshake.
ef5759 fix(controls): keep delayed object controls in rich mode
ObjectControl initialized raw mode when it first mounted without data and never
switched back after args/docgen arrived. Track the no-data to data transition and
return to rich mode unless the user explicitly requested raw editing via Set
object.
Co-authored-by: Cursor <cursoragent@cursor.com>
208891 fix(docgen-ui): address service controls review feedback
Make service-query snapshots pure for React, remove the server snapshot argument,
and ensure service ControlsPanel still renders prepared custom argTypes when no
service docgen payload exists. Also tidy ArgTypes docs/imports from review.
Co-authored-by: Cursor <cursoragent@cursor.com>
b8fc9e fix(docgen-ui): resolve CI lint, snapshot, and story test failures
Use play-context canvas type in ArgTypes.stories instead of value-importing within
Assert service controls via rendered radio input, not arg name label text
Update StoryStore inline snapshots after argTypes fixture additions
Co-authored-by: Cursor <cursoragent@cursor.com>
20c456 chore: disable experimentalDocgenServer in internal Storybook
The docgen service does not yet work in production builds, so keep the flag off
for now. With it disabled, the docs blocks and controls panel route through the
legacy extraction path and presets keep injecting __docgenInfo, so the normal
non-experimental case is not regressed.
Co-authored-by: Cursor <cursoragent@cursor.com>
93b8b7 Merge remote-tracking branch 'origin/next' into split/docgen-ui-consumption
987c1c Merge pull request #35108 from storybookjs/split/docgen-service-plumbing
Docgen: Register service runtime, payload argTypes, and bundler guardrails
ba60da ci: retrigger after auto-cancelled workflow race
98c9bf test(cli): lock in workingDir-scoped language detection; tidy util re-export
Second thermo-nuclear review pass: detectLanguage's workingDir scoping is
an intended fix (config files are found next to the target Storybook, not
the invoking cwd) — covered with memfs tests; the re-export in util.ts
moves below the import block.
4294bd refactor(cli): single canonical getStorybookData, explicit workingDir in detectLanguage
Addresses the codex thermo-nuclear review:
getStorybookData moves to storybook/internal/cli as the one canonical
project-metadata collector; automigrate/doctor/add delegate to it via a
re-export instead of keeping a near-duplicate.
detectLanguage takes an explicit workingDir for its js/tsconfig lookup
(defaulting to process.cwd() for init), and ai setup passes the target
Storybook's working dir instead of relying on the process cwd.
601178 chore: address codex review — fix stale eval README paths, drop dead import
Core: Disable component manifest by default - #34408, thanks @yannbf!
[!NOTE] Version >=0.5.0 of @​storybook/addon-mcp enables component manifests again. If you're upgrading Storybook from version >= 10.3.0 to >= 10.3.5 and are using the MCP addon, you should also upgrade @​storybook/addon-mcp to keep the docs toolset in the MCP server.
React-Docgen: Add tsconfig fallback chain and warning for monorepos - #34353, thanks @viditkbhatnagar!
React-Docgen: Try .tsx fallback when resolving .js ESM imports in docgen resolvers - #34393, thanks @mixelburg!
UI: Fix mobile navigation when renderLabel returns a React node - #34262, thanks @Nathan54Villaume!
Vite: Use vite hook filter for performance improvements - #34022, thanks @huang-julien!
Commit history:
993996 Merge pull request #35109 from storybookjs/split/docgen-ui-consumption
Docs: Route ArgTypes and Controls through docgen service when flag enabled
3e9e4d refactor(docs): extract ControlsTables component from render helper
Replace renderControlsTables with a ControlsTables FC so table rendering
follows the same component pattern as the rest of the Controls block.
Co-authored-by: Cursor <cursoragent@cursor.com>
35ea56 Merge pull request #35140 from storybookjs/kasper/telemetry-global-state-guard
Telemetry: Preserve state machine when the module loads more than once
6ed38f refactor(telemetry): document load-bearing 'in' guard, type PAYLOAD_ERROR_HANDLER global
The bare PayloadErrorHandler name in typings.d.ts never resolved (declaration
files are skipLibCheck'd), so the global was silently typed any. Export the
type and reference it the same way as the other telemetry globals.
2a8f81 Merge pull request #35147 from storybookjs/kasper/ai-cli-bundled
CLI: Bundle the ai command in core so it never downloads @​storybook/cli
176296 Merge branch 'next' into split/docgen-ui-consumption
616e24 fix(docgen-ui): preserve newlines in service-derived descriptions
Component comments parsed via the docgen service were collapsed onto a
single line because comment-parser defaults to compact spacing, so
multi-paragraph Markdown rendered as one heading. Parse the block
description with preserve spacing (matching react-docgen's legacy __docgenInfo) while keeping a separate compact parse for tag values,
and let the Description block fall back to the service description.
Co-authored-by: Cursor <cursoragent@cursor.com>
356d6d docs(agents): require running fmt:write after edits
Hand-written formatting frequently diverges from oxfmt, so make running yarn fmt:write from code/ a mandatory post-edit step.
Co-authored-by: Cursor <cursoragent@cursor.com>
131e18 Merge pull request #35144 from storybookjs/jeppe-cursor/module-graph-internal-ops-df57
Open Service: Mark module-graph engine commands as internal
5f7ce8 ci: retrigger after onboarding-flow viewport flake
6bc246 Merge branch 'next' into kasper/ai-cli-bundled
c7c43a fix(cli): anchor workingDir to the project root for --config-dir .
dirname(join(process.cwd(), configDir)) collapsed --config-dir . to the
project's parent directory, mispointing story-glob resolution and the
tsconfig/jsconfig lookup in detectLanguage. Taking dirname before resolving
keeps root-level config dirs anchored to the project root. Pre-existing on
next, surfaced by the move (CodeRabbit review on #35147).
65d501 Merge branch 'next' into split/docgen-ui-consumption
ec3c2b Merge pull request #35138 from storybookjs/kasper/ai-cli-telemetry
CLI: Add telemetry for the storybook ai <command> passthrough
1de1a3 Merge remote-tracking branch 'origin/kasper/ai-cli-telemetry' into kasper/ai-cli-bundled
19a3df fix(controls): wait for prepared story and docgen before service table
The service ControlsPanel could render docgen rows before STORY_PREPARED updated
custom argTypes/parameters, causing a brief full-table flicker before include or
exclude filters arrived. Keep the ArgsTable skeleton visible until both the
story is prepared and the docgen query has settled.
Co-authored-by: Cursor <cursoragent@cursor.com>
5a9480 fix(controls): preserve rich object mode after delayed data
When an object control first mounted without data it initialized raw mode and
stayed there after args/docgen arrived. Switch back to rich mode for the
automatic no-data to data transition, and cover the delayed-data case.
Co-authored-by: Cursor <cursoragent@cursor.com>
e5d078 ci: retrigger after auto-cancelled workflow race
3e8cde ci: retrigger after auto-cancelled workflow race
6dd01f refactor(cli): carry tool error text as McpToolResultError cause, document MCP-spec session flow
Review feedback on #35138:
McpToolResultError accepts ErrorOptions and the call site attaches the
tool's error text as cause. The message stays constant so telemetry
error hashes remain aggregatable; the cause is uploaded path-sanitized
and only with crash-reports consent (the standard sanitized error path).
Document that the Mcp-Session-Id flow is MCP Streamable HTTP spec
behavior rather than a tmcp implementation detail, so swapping the
server library in addon-mcp cannot break the CLI handshake.
ef5759 fix(controls): keep delayed object controls in rich mode
ObjectControl initialized raw mode when it first mounted without data and never
switched back after args/docgen arrived. Track the no-data to data transition and
return to rich mode unless the user explicitly requested raw editing via Set
object.
Co-authored-by: Cursor <cursoragent@cursor.com>
208891 fix(docgen-ui): address service controls review feedback
Make service-query snapshots pure for React, remove the server snapshot argument,
and ensure service ControlsPanel still renders prepared custom argTypes when no
service docgen payload exists. Also tidy ArgTypes docs/imports from review.
Co-authored-by: Cursor <cursoragent@cursor.com>
b8fc9e fix(docgen-ui): resolve CI lint, snapshot, and story test failures
Use play-context canvas type in ArgTypes.stories instead of value-importing within
Assert service controls via rendered radio input, not arg name label text
Update StoryStore inline snapshots after argTypes fixture additions
Co-authored-by: Cursor <cursoragent@cursor.com>
20c456 chore: disable experimentalDocgenServer in internal Storybook
The docgen service does not yet work in production builds, so keep the flag off
for now. With it disabled, the docs blocks and controls panel route through the
legacy extraction path and presets keep injecting __docgenInfo, so the normal
non-experimental case is not regressed.
Co-authored-by: Cursor <cursoragent@cursor.com>
93b8b7 Merge remote-tracking branch 'origin/next' into split/docgen-ui-consumption
987c1c Merge pull request #35108 from storybookjs/split/docgen-service-plumbing
Docgen: Register service runtime, payload argTypes, and bundler guardrails
ba60da ci: retrigger after auto-cancelled workflow race
98c9bf test(cli): lock in workingDir-scoped language detection; tidy util re-export
Second thermo-nuclear review pass: detectLanguage's workingDir scoping is
an intended fix (config files are found next to the target Storybook, not
the invoking cwd) — covered with memfs tests; the re-export in util.ts
moves below the import block.
4294bd refactor(cli): single canonical getStorybookData, explicit workingDir in detectLanguage
Addresses the codex thermo-nuclear review:
getStorybookData moves to storybook/internal/cli as the one canonical
project-metadata collector; automigrate/doctor/add delegate to it via a
re-export instead of keeping a near-duplicate.
detectLanguage takes an explicit workingDir for its js/tsconfig lookup
(defaulting to process.cwd() for init), and ai setup passes the target
Storybook's working dir instead of relying on the process cwd.
601178 chore: address codex review — fix stale eval README paths, drop dead import
Core: Disable component manifest by default - #34408, thanks @yannbf!
[!NOTE] Version >=0.5.0 of @​storybook/addon-mcp enables component manifests again. If you're upgrading Storybook from version >= 10.3.0 to >= 10.3.5 and are using the MCP addon, you should also upgrade @​storybook/addon-mcp to keep the docs toolset in the MCP server.
React-Docgen: Add tsconfig fallback chain and warning for monorepos - #34353, thanks @viditkbhatnagar!
React-Docgen: Try .tsx fallback when resolving .js ESM imports in docgen resolvers - #34393, thanks @mixelburg!
UI: Fix mobile navigation when renderLabel returns a React node - #34262, thanks @Nathan54Villaume!
Vite: Use vite hook filter for performance improvements - #34022, thanks @huang-julien!
Commit history:
993996 Merge pull request #35109 from storybookjs/split/docgen-ui-consumption
Docs: Route ArgTypes and Controls through docgen service when flag enabled
3e9e4d refactor(docs): extract ControlsTables component from render helper
Replace renderControlsTables with a ControlsTables FC so table rendering
follows the same component pattern as the rest of the Controls block.
Co-authored-by: Cursor <cursoragent@cursor.com>
35ea56 Merge pull request #35140 from storybookjs/kasper/telemetry-global-state-guard
Telemetry: Preserve state machine when the module loads more than once
6ed38f refactor(telemetry): document load-bearing 'in' guard, type PAYLOAD_ERROR_HANDLER global
The bare PayloadErrorHandler name in typings.d.ts never resolved (declaration
files are skipLibCheck'd), so the global was silently typed any. Export the
type and reference it the same way as the other telemetry globals.
2a8f81 Merge pull request #35147 from storybookjs/kasper/ai-cli-bundled
CLI: Bundle the ai command in core so it never downloads @​storybook/cli
176296 Merge branch 'next' into split/docgen-ui-consumption
616e24 fix(docgen-ui): preserve newlines in service-derived descriptions
Component comments parsed via the docgen service were collapsed onto a
single line because comment-parser defaults to compact spacing, so
multi-paragraph Markdown rendered as one heading. Parse the block
description with preserve spacing (matching react-docgen's legacy __docgenInfo) while keeping a separate compact parse for tag values,
and let the Description block fall back to the service description.
Co-authored-by: Cursor <cursoragent@cursor.com>
356d6d docs(agents): require running fmt:write after edits
Hand-written formatting frequently diverges from oxfmt, so make running yarn fmt:write from code/ a mandatory post-edit step.
Co-authored-by: Cursor <cursoragent@cursor.com>
131e18 Merge pull request #35144 from storybookjs/jeppe-cursor/module-graph-internal-ops-df57
Open Service: Mark module-graph engine commands as internal
5f7ce8 ci: retrigger after onboarding-flow viewport flake
6bc246 Merge branch 'next' into kasper/ai-cli-bundled
c7c43a fix(cli): anchor workingDir to the project root for --config-dir .
dirname(join(process.cwd(), configDir)) collapsed --config-dir . to the
project's parent directory, mispointing story-glob resolution and the
tsconfig/jsconfig lookup in detectLanguage. Taking dirname before resolving
keeps root-level config dirs anchored to the project root. Pre-existing on
next, surfaced by the move (CodeRabbit review on #35147).
65d501 Merge branch 'next' into split/docgen-ui-consumption
ec3c2b Merge pull request #35138 from storybookjs/kasper/ai-cli-telemetry
CLI: Add telemetry for the storybook ai <command> passthrough
1de1a3 Merge remote-tracking branch 'origin/kasper/ai-cli-telemetry' into kasper/ai-cli-bundled
19a3df fix(controls): wait for prepared story and docgen before service table
The service ControlsPanel could render docgen rows before STORY_PREPARED updated
custom argTypes/parameters, causing a brief full-table flicker before include or
exclude filters arrived. Keep the ArgsTable skeleton visible until both the
story is prepared and the docgen query has settled.
Co-authored-by: Cursor <cursoragent@cursor.com>
5a9480 fix(controls): preserve rich object mode after delayed data
When an object control first mounted without data it initialized raw mode and
stayed there after args/docgen arrived. Switch back to rich mode for the
automatic no-data to data transition, and cover the delayed-data case.
Co-authored-by: Cursor <cursoragent@cursor.com>
e5d078 ci: retrigger after auto-cancelled workflow race
3e8cde ci: retrigger after auto-cancelled workflow race
6dd01f refactor(cli): carry tool error text as McpToolResultError cause, document MCP-spec session flow
Review feedback on #35138:
McpToolResultError accepts ErrorOptions and the call site attaches the
tool's error text as cause. The message stays constant so telemetry
error hashes remain aggregatable; the cause is uploaded path-sanitized
and only with crash-reports consent (the standard sanitized error path).
Document that the Mcp-Session-Id flow is MCP Streamable HTTP spec
behavior rather than a tmcp implementation detail, so swapping the
server library in addon-mcp cannot break the CLI handshake.
ef5759 fix(controls): keep delayed object controls in rich mode
ObjectControl initialized raw mode when it first mounted without data and never
switched back after args/docgen arrived. Track the no-data to data transition and
return to rich mode unless the user explicitly requested raw editing via Set
object.
Co-authored-by: Cursor <cursoragent@cursor.com>
208891 fix(docgen-ui): address service controls review feedback
Make service-query snapshots pure for React, remove the server snapshot argument,
and ensure service ControlsPanel still renders prepared custom argTypes when no
service docgen payload exists. Also tidy ArgTypes docs/imports from review.
Co-authored-by: Cursor <cursoragent@cursor.com>
b8fc9e fix(docgen-ui): resolve CI lint, snapshot, and story test failures
Use play-context canvas type in ArgTypes.stories instead of value-importing within
Assert service controls via rendered radio input, not arg name label text
Update StoryStore inline snapshots after argTypes fixture additions
Co-authored-by: Cursor <cursoragent@cursor.com>
20c456 chore: disable experimentalDocgenServer in internal Storybook
The docgen service does not yet work in production builds, so keep the flag off
for now. With it disabled, the docs blocks and controls panel route through the
legacy extraction path and presets keep injecting __docgenInfo, so the normal
non-experimental case is not regressed.
Co-authored-by: Cursor <cursoragent@cursor.com>
93b8b7 Merge remote-tracking branch 'origin/next' into split/docgen-ui-consumption
987c1c Merge pull request #35108 from storybookjs/split/docgen-service-plumbing
Docgen: Register service runtime, payload argTypes, and bundler guardrails
ba60da ci: retrigger after auto-cancelled workflow race
98c9bf test(cli): lock in workingDir-scoped language detection; tidy util re-export
Second thermo-nuclear review pass: detectLanguage's workingDir scoping is
an intended fix (config files are found next to the target Storybook, not
the invoking cwd) — covered with memfs tests; the re-export in util.ts
moves below the import block.
4294bd refactor(cli): single canonical getStorybookData, explicit workingDir in detectLanguage
Addresses the codex thermo-nuclear review:
getStorybookData moves to storybook/internal/cli as the one canonical
project-metadata collector; automigrate/doctor/add delegate to it via a
re-export instead of keeping a near-duplicate.
detectLanguage takes an explicit workingDir for its js/tsconfig lookup
(defaulting to process.cwd() for init), and ai setup passes the target
Storybook's working dir instead of relying on the process cwd.
601178 chore: address codex review — fix stale eval README paths, drop dead import
Core: Disable component manifest by default - #34408, thanks @yannbf!
[!NOTE] Version >=0.5.0 of @​storybook/addon-mcp enables component manifests again. If you're upgrading Storybook from version >= 10.3.0 to >= 10.3.5 and are using the MCP addon, you should also upgrade @​storybook/addon-mcp to keep the docs toolset in the MCP server.
React-Docgen: Add tsconfig fallback chain and warning for monorepos - #34353, thanks @viditkbhatnagar!
React-Docgen: Try .tsx fallback when resolving .js ESM imports in docgen resolvers - #34393, thanks @mixelburg!
UI: Fix mobile navigation when renderLabel returns a React node - #34262, thanks @Nathan54Villaume!
Vite: Use vite hook filter for performance improvements - #34022, thanks @huang-julien!
Commit history:
993996 Merge pull request #35109 from storybookjs/split/docgen-ui-consumption
Docs: Route ArgTypes and Controls through docgen service when flag enabled
3e9e4d refactor(docs): extract ControlsTables component from render helper
Replace renderControlsTables with a ControlsTables FC so table rendering
follows the same component pattern as the rest of the Controls block.
Co-authored-by: Cursor <cursoragent@cursor.com>
35ea56 Merge pull request #35140 from storybookjs/kasper/telemetry-global-state-guard
Telemetry: Preserve state machine when the module loads more than once
6ed38f refactor(telemetry): document load-bearing 'in' guard, type PAYLOAD_ERROR_HANDLER global
The bare PayloadErrorHandler name in typings.d.ts never resolved (declaration
files are skipLibCheck'd), so the global was silently typed any. Export the
type and reference it the same way as the other telemetry globals.
2a8f81 Merge pull request #35147 from storybookjs/kasper/ai-cli-bundled
CLI: Bundle the ai command in core so it never downloads @​storybook/cli
176296 Merge branch 'next' into split/docgen-ui-consumption
616e24 fix(docgen-ui): preserve newlines in service-derived descriptions
Component comments parsed via the docgen service were collapsed onto a
single line because comment-parser defaults to compact spacing, so
multi-paragraph Markdown rendered as one heading. Parse the block
description with preserve spacing (matching react-docgen's legacy __docgenInfo) while keeping a separate compact parse for tag values,
and let the Description block fall back to the service description.
Co-authored-by: Cursor <cursoragent@cursor.com>
356d6d docs(agents): require running fmt:write after edits
Hand-written formatting frequently diverges from oxfmt, so make running yarn fmt:write from code/ a mandatory post-edit step.
Co-authored-by: Cursor <cursoragent@cursor.com>
131e18 Merge pull request #35144 from storybookjs/jeppe-cursor/module-graph-internal-ops-df57
Open Service: Mark module-graph engine commands as internal
5f7ce8 ci: retrigger after onboarding-flow viewport flake
6bc246 Merge branch 'next' into kasper/ai-cli-bundled
c7c43a fix(cli): anchor workingDir to the project root for --config-dir .
dirname(join(process.cwd(), configDir)) collapsed --config-dir . to the
project's parent directory, mispointing story-glob resolution and the
tsconfig/jsconfig lookup in detectLanguage. Taking dirname before resolving
keeps root-level config dirs anchored to the project root. Pre-existing on
next, surfaced by the move (CodeRabbit review on #35147).
65d501 Merge branch 'next' into split/docgen-ui-consumption
ec3c2b Merge pull request #35138 from storybookjs/kasper/ai-cli-telemetry
CLI: Add telemetry for the storybook ai <command> passthrough
1de1a3 Merge remote-tracking branch 'origin/kasper/ai-cli-telemetry' into kasper/ai-cli-bundled
19a3df fix(controls): wait for prepared story and docgen before service table
The service ControlsPanel could render docgen rows before STORY_PREPARED updated
custom argTypes/parameters, causing a brief full-table flicker before include or
exclude filters arrived. Keep the ArgsTable skeleton visible until both the
story is prepared and the docgen query has settled.
Co-authored-by: Cursor <cursoragent@cursor.com>
5a9480 fix(controls): preserve rich object mode after delayed data
When an object control first mounted without data it initialized raw mode and
stayed there after args/docgen arrived. Switch back to rich mode for the
automatic no-data to data transition, and cover the delayed-data case.
Co-authored-by: Cursor <cursoragent@cursor.com>
e5d078 ci: retrigger after auto-cancelled workflow race
3e8cde ci: retrigger after auto-cancelled workflow race
6dd01f refactor(cli): carry tool error text as McpToolResultError cause, document MCP-spec session flow
Review feedback on #35138:
McpToolResultError accepts ErrorOptions and the call site attaches the
tool's error text as cause. The message stays constant so telemetry
error hashes remain aggregatable; the cause is uploaded path-sanitized
and only with crash-reports consent (the standard sanitized error path).
Document that the Mcp-Session-Id flow is MCP Streamable HTTP spec
behavior rather than a tmcp implementation detail, so swapping the
server library in addon-mcp cannot break the CLI handshake.
ef5759 fix(controls): keep delayed object controls in rich mode
ObjectControl initialized raw mode when it first mounted without data and never
switched back after args/docgen arrived. Track the no-data to data transition and
return to rich mode unless the user explicitly requested raw editing via Set
object.
Co-authored-by: Cursor <cursoragent@cursor.com>
208891 fix(docgen-ui): address service controls review feedback
Make service-query snapshots pure for React, remove the server snapshot argument,
and ensure service ControlsPanel still renders prepared custom argTypes when no
service docgen payload exists. Also tidy ArgTypes docs/imports from review.
Co-authored-by: Cursor <cursoragent@cursor.com>
b8fc9e fix(docgen-ui): resolve CI lint, snapshot, and story test failures
Use play-context canvas type in ArgTypes.stories instead of value-importing within
Assert service controls via rendered radio input, not arg name label text
Update StoryStore inline snapshots after argTypes fixture additions
Co-authored-by: Cursor <cursoragent@cursor.com>
20c456 chore: disable experimentalDocgenServer in internal Storybook
The docgen service does not yet work in production builds, so keep the flag off
for now. With it disabled, the docs blocks and controls panel route through the
legacy extraction path and presets keep injecting __docgenInfo, so the normal
non-experimental case is not regressed.
Co-authored-by: Cursor <cursoragent@cursor.com>
93b8b7 Merge remote-tracking branch 'origin/next' into split/docgen-ui-consumption
987c1c Merge pull request #35108 from storybookjs/split/docgen-service-plumbing
Docgen: Register service runtime, payload argTypes, and bundler guardrails
ba60da ci: retrigger after auto-cancelled workflow race
98c9bf test(cli): lock in workingDir-scoped language detection; tidy util re-export
Second thermo-nuclear review pass: detectLanguage's workingDir scoping is
an intended fix (config files are found next to the target Storybook, not
the invoking cwd) — covered with memfs tests; the re-export in util.ts
moves below the import block.
4294bd refactor(cli): single canonical getStorybookData, explicit workingDir in detectLanguage
Addresses the codex thermo-nuclear review:
getStorybookData moves to storybook/internal/cli as the one canonical
project-metadata collector; automigrate/doctor/add delegate to it via a
re-export instead of keeping a near-duplicate.
detectLanguage takes an explicit workingDir for its js/tsconfig lookup
(defaulting to process.cwd() for init), and ai setup passes the target
Storybook's working dir instead of relying on the process cwd.
601178 chore: address codex review — fix stale eval README paths, drop dead import
7daa6e tls: apply the server's default rejectUnauthorized to incoming connections (#31322)
`tls.Server` normalizes `rejectUnauthorized` to its documented
default on the server object, but the per-connection socket copied the
raw, un-defaulted option, so the post-handshake check never enforced the
default. This defaults the per-connection value from the server's
normalized one, gates the peer-certificate verification result on
`requestCert` (matching Node's `onServerSocketSecure`, since the
native verifier reports a non-OK code whenever no peer certificate
exists), and drops the error argument from the reject-path `destroy()`
so the teardown does not surface as an uncaught exception on a
listener-less socket.
Adds two tests to `test/js/node/tls/node-tls-cert.test.ts`: one
asserting a server with `requestCert: true` and no explicit
`rejectUnauthorized` tears down a connection whose client certificate
cannot be verified (and still serves a subsequent verifiable client),
and one asserting an explicit `rejectUnauthorized: false` still admits
the connection with `authorized === false`.
4d79cf sql: throw validation errors for degenerate helper inputs instead of raw TypeErrors and engine errors (#32156)
Fixes #32155
Repro
constsql=newSQL("sqlite://:memory:");awaitsql`SELECT * FROM t WHERE id IN ${sql([null],"id")}`;// TypeError: null is not an object (evaluating 'strings')awaitsql`update t set ${sql({name: undefined})} where id = 1`;// SQLiteError: near "where": syntax errorawaitsql`update t SET ${sql({name: undefined})} where id = 1`;// SyntaxError: Update needs to have at least one column
Both reproduce on 1.4.0 and main, on all three adapters for the first
case.
Cause
The keyed WHERE IN branch of the shared normalizeQuery
(src/js/internal/sql/shared.ts) only guards items against undefined
before reading value[columns[0]], so a null item throws a raw
TypeError. The same property-access-on-null pattern exists in the INSERT
item scan (buildDefinedColumnsAndQuery) and in the keyed UPDATE
branch, so sql([{...}, null]) for INSERT and sql(null, "col") / sql([undefined], "col") for UPDATE hit the same TypeError.
The sqlite adapter's throwIfUpdateEmpty hook detects an empty
update helper with query.endsWith("SET "), a case-sensitive check
against the user's original spelling. Command detection is
case-insensitive, so lowercase set takes the helper path but misses
the check and the malformed SQL reaches the engine. postgres and mysql
use the BaseSQLAdapter default, which gates on the hasValues flag
instead.
Fix
All in src/js/internal/sql/shared.ts plus the sqlite hook (after
#32145 consolidated normalizeQuery, these are single sites covering
all three adapters):
Keyed WHERE IN: a null item now throws SyntaxError: Cannot use null as an item in WHERE IN helper with a column. undefined items keep
binding NULL (existing behavior), as do null items in the non-keyed
form sql([null]) and null values under the key (sql([{ id: null }], "id")).
INSERT (buildDefinedColumnsAndQuery): null/undefined items throw SyntaxError: Cannot use null or undefined as an item in INSERT helper.
Previously both were raw TypeErrors.
Keyed UPDATE: a null/undefined item throws SyntaxError: Cannot use null or undefined as an item in UPDATE helper.
sqlite empty update: SQLiteAdapter.throwIfUpdateEmpty now checks the hasValues flag like the BaseSQLAdapter default (sqlite implements
the adapter interface standalone, so the override stays but with the
correct body), making the empty-helper error spelling-independent.
One deliberate sqlite behavior change beyond the lowercase fix, raised
in review: a literal assignment followed by an all-undefined helper, UPDATE t SET updated_at = CURRENT_TIMESTAMP, ${sql({ name: undefined })}, previously executed on sqlite (the trailing comma was stripped and
the suffix check missed). The old behavior was order-dependent (the
helper-first form already threw), and postgres/mysql throw for both
orders, so sqlite now throws for both as well. A helper with defined
values mixed with literal assignments keeps working; both are pinned by
tests.
The mysql ON DUPLICATE KEY UPDATE suffix check (isUpsertUpdate) has
a similar case-sensitivity problem, tracked separately in #32035 (fix in
#32040), so it is not touched here.
Tests
test/js/sql/sql-helpers-validation.test.ts (new): the validation
contract is identical across the three adapters, so it is tested as one describe.each matrix (null item in keyed WHERE IN, null/undefined
items in INSERT and keyed UPDATE, empty update helper for lowercase set, uppercase SET, the helper-emitted SET form, and the empty
helper alongside a literal assignment in both orders), plus sqlite-only
tests for the behaviors that must keep working (lowercase set with
defined values updates, a helper with defined values alongside a literal
assignment updates, undefined items / null column values / non-keyed
null still bind NULL). Normalization runs when a query is first awaited,
before any connection is attempted, so the postgres and mysql rows run
without a server; sqlite uses :memory:.
On the unfixed build, 11 of the 18 tests fail (raw TypeError: null is not an object instead of the validation errors, and the empty-helper
updates reaching the engine); the other 7 are behavior-preservation and
cross-adapter parity guards. All 18 pass with the fix.
Rebase note
Originally the guards were duplicated across sqlite.ts/postgres.ts/mysql.ts; after #32145 landed (consolidating normalizeQuery into shared.ts), the branch was rebased and the same
fix re-applied at the now-shared sites, shrinking the diff to shared.ts + the sqlite throwIfUpdateEmpty hook.
4d0752 test: give container-awaiting beforeAll hooks a 120s timeout (#32211)
What this does
describeWithContainer (and the valkey/autobahn tests' direct ensure() callers) await compose up --wait-timeout 60 plus a compose build step inside beforeAll, but the hook had bun:test's default 5s
timeout. On a cold or busy machine the hook fired first and the
runner's auto-killer SIGTERMed the in-flight compose subprocess;
compose then exited non-zero with stderr at whatever progress line it
was on, which doUp() reported as Failed to start service X: Container ... Creating.
This bumps the four beforeAll hooks that await ensure() to 120s —
matching the existing pattern at websocket-proxy.test.ts:602. test/docker/index.ts is unchanged.
Why not the alternatives
Retry compose up once (earlier version of this PR): wrong layer
— the failure is bun:test killing compose, not compose actually failing.
Retrying papers over the symptom.
Serialize doUp() across services: not needed. 4 services brought
up concurrently from cold all return exit=0. The coordinator (#32033)
solved a cross-process race (warmup-ci.ts vs the first test process);
within one bun bd test process the per-service in-flight dedup at test/docker/index.ts:200-217 already covers same-service calls, and
cross-service concurrency works.
Spawn a coordinator for local dev: heavyweight when one process
already owns all compose calls.
Verification
2× docker compose down then bun bd test sql-mysql.test.ts sql-onconnect-onclose-throw.test.ts sql-mysql.auth.test.ts from cold: 0
hook timeouts, 0 'Failed to start service' across 5 services starting
concurrently.
Separate pre-existing flake (out of scope)
sql-mysql.test.ts:325 'should not timeout in long results' (10K
inserts + 3 selects, 10s test timeout) can fail on a freshly-cold MySQL
and pass warm. Unchanged by this PR; different timeout class (per-test
10s, not the 5s hook timeout fixed here).
eb98fd sql: guard pool connection scans against unassigned slots during pool start (#32201)
First item of #32198. The second item there (the close({ timeout })
validation mismatch) is already addressed by the open #32100, which
bounds the timeout at setTimeout's real millisecond limit and rewords
the message, so it is deliberately not touched here.
Repro
No database server needed:
constsql=newBun.SQL({adapter: "postgres",hostname: "127.0.0.1",port: 5432,username: "u",database: "d",max: 2,password: ()=>{sql.flush();// TypeError: undefined is not an object (evaluating 'this.closed')return"";},});sql.connect().catch(()=>{});
Cause
BaseSQLAdapter.connections is allocated as new Array(max) and filled
one slot at a time in connect()'s pool-start loop. createPooledConnection can synchronously run user code: a
function-valued password option is invoked synchronously by createPooledConnectionHandle. If that user code re-enters a pool
method that scans this.connections, the scan hits slots that are still
unassigned holes.
hasConnectionsAvailable() already guards for this (if (connection && ...)) and documents the scenario, but the equivalent loops in isConnected(), flush(), #close(), and the retry scan in connect() read connection.state unguarded and throw a raw TypeError: undefined is not an object (evaluating 'connection.state').
(In the observed errors JSC quotes a nearby expression, this.closed / this.readyConnections, due to line skew between the embedded and
on-disk bundle source; the stack points at the connection.state
reads.)
Fix
Optional-chain the state reads in those four loops
(connection?.state), matching the guard hasConnectionsAvailable()
already has, and document the hole invariant where the array is created.
In the retry scan a hole counts as a connection still being created
(all_closed = false), so the caller is queued and served when the pool
finishes connecting. #close() does not appear reachable with holes
today (pool start always queues a waiter first, so close defers), but it
gets the same guard for consistency, which also covers the connections[i] = null slots it writes.
Verification
New test in test/js/sql/sql-close-pending-connection.test.ts re-enters sql.flush() and sql.connect() from a function-valued password
during pool start, against a local TCP server that never answers (no
database needed). On the unfixed build it fails with the TypeErrors
above captured from both re-entry points; with the fix the whole file
passes (bun bd test test/js/sql/sql-close-pending-connection.test.ts,
5 pass).
14bcc6 docs: fix dead discordjs.guide links in Discord.js bot guide (#32238)
What
Fixes two dead links to discordjs.guide in the Discord.js ecosystem
guide introduced by #32237.
discordjs.guide was rebuilt and moved into the discord.js monorepo at apps/guide,
served via fumadocs with a different route layout. The v14 material now
lives under /legacy/ and several pages were renamed
(setting-up-a-bot-application → app-setup, adding-your-bot-to-servers → adding-your-app).
The replacement pages contain the same content and the same screenshots
the guide text references: app-setup.mdx walks through creating the
application and copying the token (images create-app.png, created-bot.png), and adding-your-app.mdx walks through the OAuth2
invite flow (images bot-auth-page.png, bot-authorized.png, bot-in-memberlist.png).
The other external links in the guide
(https://discord.com/developers/applications and https://discord.js.org/docs, which redirects to the latest package
docs via middleware)
are unchanged and still valid.
Verification
Confirmed the new paths exist in the discord.js repo tree:
fumadocs baseUrl is /
and the docs dir is content/docs, so these map to /legacy/preparations/app-setup and /legacy/preparations/adding-your-app on the live site. The guide's own
index also links to ./legacy/preparations/app-setup.
Docs-only change.
719be6 docs: rewrite Discord.js bot guide around a working slash command (#32237)
What
Rewrites the Discord.js guide so it ends with a bot that actually does
something: a /ping slash command that replies Pong!. The previous
version stopped at a bot that logged in and then sat idle.
The guide now follows the structure discord.js's own guide recommends:
deploy-commands.ts: a standalone script that registers /ping with
Discord using the REST route (Routes.applicationGuildCommands), run
once.
bot.ts: the runtime, with the interactionCreate handler that
replies.
It also spells out the steps a first-timer needs: inviting the bot with
the applications.commands scope, copying the application ID and server
ID, and storing DISCORD_TOKEN, DISCORD_CLIENT_ID, and DISCORD_GUILD_ID in .env.local. The existing "no build step"
deployment note and the systemd/PM2 links are kept.
Why
Slash commands are how bots are built on Discord today, and they need no
privileged intents, so the bot runs with just GatewayIntentBits.Guilds. The old guide's ready-only example left
readers with a connected bot and no way to make it respond.
Command registration lives in a separate REST script rather than running
on every ready event, which is what the official guide recommends
(commands only need registering when their definition changes, and there
is a daily command-creation limit). Registration is guild-scoped so the
command appears instantly while developing; the deploy section explains
switching to the global route for production.
This also points the API reference link at https://discord.js.org/docs, the same correction made in #31187 (that
PR can be closed once this lands). An earlier revision carried a "global
commands can take up to an hour to update" note; that was removed
because Discord dropped the global command cache in 2022 and the current
official guide no longer claims it.
Verification
Both deploy-commands.ts and bot.ts shown in the guide were checked
against discord.js@​14.26.4 (latest):
tsc --noEmit under strict passes for both files.
Both run on Bun and issue the real API calls; with invalid credentials
they fail with the expected DiscordAPIError, confirming imports, REST
registration, client construction, and login all work.
Events.ClientReady resolves to 'clientReady' and Events.InteractionCreate to 'interactionCreate' in 14.26.
new SlashCommandBuilder().setName("ping").setDescription(...).toJSON()
produces a valid command payload, and Routes.applicationGuildCommands(clientId, guildId) builds the correct
REST path.
Live behavior (registering against real Discord and receiving a /ping
interaction) was not exercised: it needs a real bot token and test
server.
Docs-only change, no native code touched.
Fixes #31186 (the stale discordjs.guide API link).
6f67e0 docs: add production deployment note to Discord.js bot guide (#32236)
Summary
Reviewed the Discord.js
guide for accuracy
after users asked whether a bundling/build step is needed to run a bot
in production. The guide stops once the bot logs in and never answers
that, so people assume bundling is required. It is not: Bun runs the bot.ts entrypoint and everything it imports directly, so the source
deploys as-is.
Changes
docs/guides/ecosystem/discordjs.mdx:
Add a closing section stating no build step is needed and linking the
existing systemd and PM2 guides for keeping the bot process alive
and restarting it after a crash or reboot.
Fix a grammar error: "It may take a several seconds" -> "several
seconds".
Review notes (verified current, left unchanged)
The sample code uses the Events.ClientReady enum, which resolves to 'clientReady' in discord.js 14.26.4. Only the legacy 'ready' string
event is deprecated, so the snippet runs clean with no deprecation
warning.
The .env.local auto-loading claim is accurate (Bun loads it except
when NODE_ENV=test).
The stale "official discord.js docs" link on line 80 is already handled
separately in #31187, so this PR deliberately leaves that line alone.
Both touch this file but on different lines and concerns; if #31187
merges first this branch takes a trivial rebase.
Verification
Ran the guide's bot.ts setup on discord.js 14.26.4 + Bun 1.4.0:
imports resolve, client constructs, no deprecation warning.
Both new <Card> hrefs (/guides/ecosystem/systemd, /guides/ecosystem/pm2) match their entries in docs/docs.json.
prettier reports the file formatted.
a0e221 sql_jsc: drop dead to_js helpers, Signature::hash, and unused MySQLValue Decimal/timestamp (#32209)
What this does
Pure dead-code deletion extracted from #31664 (closed). Net −251 lines
across 12 files in src/sql_jsc/. Every removed item verified
zero-caller via grep across all of src/ on current main.
4 deleted files in src/sql_jsc/postgres/types/: PostgresString.rs (ToJsWithType trait + 3 impls), bool.rs (already
empty), bytea.rs (ByteaToJs), json.rs (JsonToJs). The mod
declarations in postgres.rs go too.
Signature::hash() in both postgres/Signature.rs and mysql/protocol/Signature.rs — callers use bun_wyhash::hash(&sig.name) directly.
mysql/MySQLValue.rs: Time::to_unix_timestamp(), Decimal
struct + impl (only references were two commented-out lines, also
removed).
jsc.rs: the sql_jsc-local JSHostFnZig alias (the live one is
in bun_jsc) and the HostFnRaw marker (never inferred — all 6
registered host fns use the safe-Rust signature).
Verification
cargo check -p bun_sql_jsc and cargo clippy --no-deps: clean.
bun bd test postgres-binary-numeric sql-mysql-mediumint: 14 pass / 0
fail.
Left in #31664 for separate PRs
The SQLDataCell tag-constructor refactor (~94 sites), the do_run
error-cleanup extraction with its 3 OOM-path ref-leak fixes, the two TODO(port) Postgres error-shape fixes, and the field-table/TLS-verify
dedup — all behavior-adjacent rather than pure deletion.
80d92d test(sql): bound spawned fixture lifetime so a test timeout cannot leave orphans (#32207)
When a test that does await using proc = Bun.spawn(...) times out
before the using-scope exits, the dispose never runs and the subprocess
survives indefinitely. The sql fixture subprocesses connect to a (mock
or real) database and await the pool draining; under a regression that
hangs the pool (e.g. while iterating on #32145), the fixture never
exits, the test times out, and the fixture is orphaned.
Locally observed 16 such orphans pinning the machine at load ~100 for
hours after a bun bd test test/js/sql/ run that exercised a buggy
intermediate commit.
Adding timeout to the Bun.spawn options gives the child its own hard
deadline independent of the test runner, so an abandoned fixture
self-kills within a minute even when the parent test has already moved
on.
sql.test.ts already had timeout on all three of its spawns; this
brings the other 8 files in line.
ac312f ci: bump darwin test timeout 40→45 min (#32194)
What
darwin test timeout 40 → 45 min (matching windows/asan).
Why
The Tart fleet's larger shard (~2175 tests) takes ~43 min on some hosts
— a healthy run gets killed ~2 minutes from the finish line.
This is temporary debt: when more darwin slots come online (hardware
order pending), parallelism increases → shards shrink → this comes
back down. Same rationale as the prior 30→40 bump.
f2821a test/docker: BUN_TEST_SERVICE_ env override for ensure() (#32139)
What
Lets DB-dependent tests run in environments that have the services
reachable but no docker CLI, without changing any test file. The fix is
entirely in test/docker/index.ts and test/harness.ts.
dockerCompose.ensure(service) now resolves in order:
BUN_TEST_SERVICE_<service> env var
BUN_DOCKER_COORDINATOR socket (existing)
docker compose (existing)
describeWithContainer no longer bails on !isDockerEnabled() before
the service is resolvable; it describe.todos only when none of the
three paths are available. isDockerEnabled() itself is unchanged.
Env var format
Single-port: BUN_TEST_SERVICE_postgres_plain=127.0.0.1:5432 (or just 127.0.0.1 to use the default port)
The per-service ports/tls/users that were inlined in ensure()'s switch
move to a serviceMeta table so the env path produces a ServiceInfo
shaped identically to the compose path; existing callers
(describeWithContainer's port lookup, valkey/test-utils.ts:redisInfo.ports[6380], s3.test.ts:minioInfo.ports[9000]) consume it unchanged.
Behavior
Docker available, no env: identical to before.
Env var set: takes precedence over docker; down() is a no-op
since compose never started.
Neither: clean describe.todo.
Tests using raw if (isDockerEnabled()) { ... } (not describeWithContainer) keep skipping in dockerless envs as before;
this PR doesn't touch them.
5d8b29 sql: consolidate JS pool/connection plumbing into BasePooledConnection in shared.ts (#32145)
What this does
Final slice from #31994 (closed as too big), on top of #32128, #32135,
#32141. Consolidates the JS adapters' duplicated pool/connection/query
plumbing from src/js/internal/sql/{postgres,mysql,sqlite}.ts into src/js/internal/sql/shared.ts as a BasePooledConnection class
hierarchy. Net −995 lines across 4 files.
Two commits:
Cherry-pick from #31994 (1aa3dbb045) — checks out src/js/internal/sql/ from origin/claude/split/sql as-is. This
intentionally clobbers two pool-lifecycle fixes that landed on main
since #31994's merge-base: #32041 (throwing onconnect/onclose corrupting
the pool) and #32097 (forced close() resolving mid-handshake).
Re-integrate #32041 + #32097 into the new structure (6e6fd4fcca) —
re-hosts both fixes in BasePooledConnection:
#32041: try/finally around the user onconnect/onclose calls in handleConnected and #finishClose so a throwing callback never skips
pool bookkeeping; the createPooledConnectionHandle catch always defers
via process.nextTick (drops the per-driver deferSyncCloseError flag
— both drivers now match).
#32097: startConnection() is abstract Promise<void> and both
subclasses await + assign this.connection at creation; #beginConnecting awaits it and closes the handle if onFinish was set
(pool force-closed) in the microtask window before it materialized.
Behavioral equivalence
The original BasePooledConnection extraction in #31994 carried a
per-file behavioral-equivalence audit against its merge-base
(placeholders, escaping, helper commands, error message text,
pool/transaction semantics all preserved) and was green on full CI build
61383. This PR re-applies that diff and adds the two missing fixes; the
load-bearing verification is below.
Verification
bun bd: builds clean.
The three lifecycle test files (sql-onconnect-onclose-throw.test.ts, sql-close-pending-connection.test.ts, sql-connect-error-reporting.test.ts): 26 pass / 0 fail.
New tests in this PR: sql-onconnect-onclose-throw.test.ts gains
a forced-close variant per driver (a throwing onclose while the pool
is force-closed mid-handshake, against a fake net server). The two
re-hosted fixes meet in BasePooledConnection's close handler, and that
interaction had no coverage. Note these pass against origin/main's src/js/internal/sql too (verified): both underlying fixes already live
on main per-driver, so versus main this PR is equivalence-preserving by
design and no test can fail on one side only. The HEAD~1 stash test
below is the proof that the re-integration commit is load-bearing.
Load-bearing stash test: built and ran the same three files at
HEAD~1 (the cherry-pick before re-integration) — 6 fail (mysql: forced close() resolves when called before the native handle is stored
timeout; postgres: pool calls from onclose are safe when connecting fails synchronously → reentry threw: TypeError; both drivers' throwing onclose still rejects pending queries on connect refused
timeouts). With the re-integration: 26 pass. The diff is required for
both fixes.
Full test/js/sql/ against live postgres_plain/mysql_plain/*_tls containers: 1526 pass / 7 fail /
6 errors locally. Every failure reproduces on origin/main in the same
environment — see Notes below.
Notes on local-only test failures (none introduced by this PR)
sql-mysql-query-string-leak.test.ts fails locally on a debug+ASAN
build for both origin/main (314.6 MiB) and this branch (317.2 MiB),
within noise of each other; both exceed the 256 MiB ASAN threshold. A heapStats() probe of the same workload shows MySQLQuery 2→1 after GC
— wrappers are finalized correctly. The test passes on main's CI
(release build); the threshold is tuned for release RSS overhead, not
local debug.
sql.test.ts > query string memory leak test (postgres): same
category.
sqlite-sql.test.ts > Query Normalization Fuzzing Tests > handles exotic but valid SQL patterns: 5.5s timeout on debug builds —
pre-existing per #31994's original verification notes.
describeWithContainer cold-start races: docker compose up -d --wait returns non-zero on an already-healthy container
(test/docker/index.ts:157), causing beforeEach hook timeouts in sql-mysql.test.ts (TLS), sql-mysql-bind-oob, sql-mysql.helpers, sql-mysql.auth. Pre-existing harness flake; clears on warm rerun.
The Rust-side changes from #32097 (src/sql_jsc/) were already on main before this PR — only the JS hooks needed re-integrating.
885c44 Fix panic on lazy-export modules in bun build --format=internal_bake_dev (#31948)
Fixes #31943
Repro
echo'{"value":1}'> data.json
echo'import data from "./data.json"; console.log(data.value)'> index.ts
bun build --format=internal_bake_dev index.ts
panic: index out of bounds: the len is 0 but the index is 0
Crashed while printing /tmp/repro/data.json
panic: called `Option::unwrap()` on a `None` value
Crashed while printing /tmp/repro/data.json
Every lazy-export loader hits this in the internal_bake_dev format:
JSON/JSONC/TOML imports, require() of JSON, a JSON entry point, CSS
module imports, and empty .cjs/.mjs files.
Cause
The standalone CLI path runs the full link(), which calls generate_code_for_lazy_export for every module with a lazy-export AST.
Both arms of that transform destroy the SLazyExport stmt: the CJS arm
rewrites it into a module.exports = ... assignment, and the ESM arm
empties the stmt list while synthesizing export parts. ast.has_lazy_export stays set, and print_dev_server_module
unconditionally did body_stmts[0].data.s_lazy_export().unwrap()
whenever that flag was set, so it panicked on either arm's output (index
out of bounds for the ESM arm, unwrap on None for the CJS arm).
The dev server itself never hits this because it calls LinkerContext::load without the full link, so the SLazyExport stmt
reaches the printer intact.
Fix
generateCodeForLazyExport.rs: when the output format is internal_bake_dev, force exports_kind to CommonJS so the transform
always generates the module.exports = ... form. The HMR runtime
evaluates lazy-export modules as CJS (the dev server prints them as hmr.cjs.exports = ...), and the ESM form would synthesize export parts
the dev-server module printer cannot represent.
js_printer/lib.rs: apply the lazy-export special case in print_dev_server_module only while the SLazyExport stmt is actually
intact (the dev server path, unchanged output), and fall through to the
regular CommonJS printing for the rewritten form.
A JSON module now prints as a plain CJS dev module, matching how the
format already prints .cjs files:
CSS module imports no longer panic; like the dev server, the JS chunk
omits the CSS stub (CSS is delivered out of band) and the CSS chunk is
emitted correctly.
Verification
New tests in test/bundler/bundler_loader.test.ts cover JSON
default/named/star imports, require() of JSON, a JSON entry point,
TOML, and CSS modules under format: "internal_bake_dev". They crash
the bundler without this change and pass with it. Dev server output is
unchanged: test/bake/dev/bundle.test.ts, esm.test.ts, and css.test.ts pass, and a manual Bun.serve HMR probe still prints hmr.cjs.exports = { value: 1 }; // bun .s_lazy_export for JSON
modules. bundler_loader.test.ts, bundler_edgecase.test.ts, and css/css-modules.test.ts all pass.
541ea3 node:fs: implement lchown on Windows (#32052)
The Windows arm of NodeFS::lchown in src/runtime/node/node_fs.rs was
a Maybe::todo() stub, so every fs.lchown/fs.lchownSync/fs.promises.lchown call that passed
argument validation threw Unknown Error, TODO on Windows (and Error::todo() panics outright in debug builds). Node succeeds: libuv
implements the whole chown family on Windows (fs__chown, fs__fchown, fs__lchown in libuv's src/win/fs.c) as no-ops that report success,
and Bun's fs.chown/fs.fchown already route through uv_fs_chown/uv_fs_fchown via sys_uv. Only lchown was missing: on
Windows Syscall aliases bun_sys::sys_uv, which had no lchown.
Fix
src/sys/sys_uv.rs: add lchown wrapping uv_fs_lchown, mirroring
the existing chown (the FFI declaration in src/libuv_sys/libuv.rs
already existed).
src/sys/lib.rs: route the windows_impl facade's lchown through sys_uv::lchown like its chown sibling, instead of a hardcoded Ok(()).
src/runtime/node/node_fs.rs: drop the cfg(windows) stub so every
platform uses Syscall::lchown. This is a deliberate deviation from the .zig reference, which stubbed Windows.
lchmod directly above has the same stub shape but is intentionally
left alone: Node only implements fs.lchmod on macOS, so erroring there
matches Node.
Verification
New test in test/js/node/fs/fs.test.ts exercises lchownSync,
callback lchown, and promises.lchown on a regular file and on a
dangling symlink (which distinguishes lchown from chown: it must not
follow the link). The bug is Windows-only, so the test passes on POSIX
with or without the fix; on Windows it fails before this change (the
stub throws on every call) and passes after. Cross-compilation checked
for x86_64-pc-windows-msvc and aarch64-pc-windows-msvc; bun bd test test/js/node/fs/fs.test.ts passes on Linux.
fc7957 dev server: fix require binding when a module flips from ESM to CJS (#31947)
Fixes #31942
Problem
In src/runtime/bake/hmr-module.ts, the ESM->CJS flip arms of loadModuleSync and loadModuleAsync built the replacement CJS module
object with:
require: mod.require.bind(this),
this inside those plain exported functions is not the HMRModule: it
is undefined in strict mode, globalThis in sloppy mode. Either way,
once a module flips from ESM to CJS across a hot update, calls through
its require pass a bogus importer into the module loader:
strict (<script type="module"> in a real browser): loadModuleSync(id, true, undefined), so mod.importers.add(importer)
never runs and the importer edge is silently dropped. The next edit to
that dependency finds importers.size === 0 on a non-accepting module
and forces a full page reload instead of a hot update.
sloppy (chunks evaluated via indirect eval, e.g. the bake test
harness): window is truthy, so it gets added to importers, and the
next boundary walk crashes with TypeError: Cannot convert undefined or null to object at Object.keys(mod.data) in replaceModules, which
then triggers a full reload.
The first-construction path in the HMRModule constructor (and the toCommonJS path) correctly use this.require.bind(this); only the two
flip arms were affected.
Note the trigger requires a require call that is not statically
rewritten: direct require('./x') (and even module.require('./x')) in
a CJS module is rewritten by the parser to hmr.require("x.ts"), a
method call with correct this. Indirect access (const m = module; m.require(...)) keeps the runtime-bound function and hits the bug.
Fix
Bind require to mod in both flip arms, matching the constructor.
Test
test/bake/dev/esm.test.ts: "importer tracking survives flipping a
module from ESM to CJS". A self-accepting root imports dep; dep is
edited from ESM to a CJS module that requires leaf through an
indirectly-accessed module.require; then leaf is edited. With the
fix the edit propagates as a hot update to the accepting root. Without
the fix the client crashes with the TypeError above and attempts a
full reload, which the harness rejects:
TypeError: Cannot convert undefined or null to object
at Object.keys (<anonymous>)
at replaceModules (http://localhost:33453/_bun/client/index-00000000dc9372ec.js:2452:29)
Fails on unfixed main (bun bd test with src/ stashed) and on USE_SYSTEM_BUN=1; passes with the fix. Full test/bake/dev/esm.test.ts (17/17) and test/bake/dev/hot.test.ts
(9/9) pass.
A related but separate defect (stale CJS exports when a hot update
propagates through an already-loaded CJS module without re-executing it)
is intentionally not addressed here; the test asserts importer tracking
rather than the re-executed value so it stays valid when that lands.
6b001f Surface posix_spawn file action errors on macOS instead of spawning with closed stdio (#32068)
Fixes #32067.
Repro
On macOS, with more than OPEN_MAX (10240) fds open:
Non-PTY spawns on macOS go through the system posix_spawn. Darwin's posix_spawn_file_actions_adddup2 (and the other file-action
registration calls) return EBADF for any fd >= OPEN_MAX (10240,
hardcoded in sys/syslimits.h), independent of RLIMIT_NOFILE. In spawn_z (src/spawn_sys/posix_spawn.rs) every registration error was
debug-logged and dropped, then posix_spawn ran anyway. Because Bun
sets POSIX_SPAWN_CLOEXEC_DEFAULT, the silently missing dup2 meant the
child executed with its stdio fds closed: the pipe's parent end hit
immediate EOF, so the child looked like a successful run that produced
no output. libuv checks these return values, which is why node reports
EBADF.
Fix
The PosixSpawnActions / PosixSpawnAttr wrappers now return sys::Result carrying the real errno, and spawn_z propagates any
registration failure as a spawn error (same channel as an ENOENT from posix_spawn itself). The load-bearing change is the new convert_spawn_objects helper replacing the swallow-and-continue loop;
the rest is the signature change from bun_core::Error to bun_sys::Error so the errno survives. The now-unused errno constants, unexpected_errno, and the byte-slice chdir wrapper are deleted.
After the fix, spawnSync returns error.code === "EBADF" and async spawn throws EBADF synchronously, both matching node (EBADF is not in
node's delayed-error list, so node also throws rather than emitting error).
The issue also mentions that bun test filter mode holds repo-scan
directory fds during test execution, which is what pushed pipe fds above
10240 in the first place. That retention is the resolver's deliberate fd
cache (FileSystem::need_to_close_files), and this PR does not change
it; with this fix the condition now surfaces as a clear EBADF instead of
silent empty output.
Verification
Two tests in test/js/node/child_process/child_process.test.ts:
All POSIX platforms: spawnSync/spawn with a numeric stdio fd of
Darwin's posix_spawn_file_actions_adddup2 rejects any fd number
= OPEN_MAX at registration time, before checking whether the fd is
open, so this hits the exact swallow site deterministically with no fd
exhaustion. Expectation is uniform (error.code === "EBADF" for sync,
synchronous EBADF throw for async, matching node on both platforms; on
Linux the child-side dup2 of a not-open fd produces the same EBADF). On
an unfixed macOS build this "succeeds" silently, so it fails before the
fix there.
Linux only: the issue's original fd-pressure scenario (open ~11000
fds, then spawn with piped stdio) asserting spawns keep working. It is
Linux-only because default macOS installs cap RLIMIT_NOFILE at kern.maxfilesperproc = 10240, which makes it impossible to exceed
OPEN_MAX open fds there at all (the first CI run confirmed this: the
child hit EMFILE after 10236 fds on the darwin lane). On machines with a
raised sysctl, like the reporter's, the condition arises organically and
is covered by the mechanism test above.
Note for the fail-before check: the buggy code is inside
`#[cfg(target_os =
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Updated Packages