Skip to content

Validate app.bundlerOptions types in Bun.serve#30423

Closed
robobun wants to merge 2 commits into
mainfrom
farm/6768fec8/bake-bundler-options-validation
Closed

Validate app.bundlerOptions types in Bun.serve#30423
robobun wants to merge 2 commits into
mainfrom
farm/6768fec8/bake-bundler-options-validation

Conversation

@robobun

@robobun robobun commented May 9, 2026

Copy link
Copy Markdown
Collaborator

Passing a non-object value for app.bundlerOptions, or for its server/client/ssr sub-objects, or a non-boolean non-object for their minify property, would hit a debug assertion in JSValue.get() (which requires its receiver to be an object).

Additionally, minify: false would fall through the isBoolean() and asBoolean() check to the object code path and hit the same assertion.

Throw proper TypeErrors for these cases instead.

Bun.serve({
  app: {
    bundlerOptions: { ssr: 1225 },
  },
});
// Before: panic: reached unreachable code
// After:  TypeError: 'bundlerOptions.ssr' must be an object

Found by Fuzzilli.

Passing a non-object value for app.bundlerOptions, or for its server/
client/ssr sub-objects, or a non-boolean non-object for their minify
property, would hit a debug assertion in JSValue.get(). Additionally,
minify: false would fall through to the object path and hit the same
assertion. Throw proper TypeErrors for these cases instead.
@github-actions github-actions Bot added the claude label May 9, 2026
@robobun

robobun commented May 9, 2026

Copy link
Copy Markdown
Collaborator Author
Updated 11:27 PM PT - May 8th, 2026

@robobun, your commit e09316e has 1 failures in Build #52986 (All Failures):


🧪   To try this PR locally:

bunx bun-pr 30423

That installs a local version of the PR into your bun-30423 executable, so you can run:

bun-30423 --bun

@coderabbitai

coderabbitai Bot commented May 9, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Walkthrough

The PR enhances bundler options validation error messages by introducing a comptime property_name parameter to BuildConfigSubset.fromJS. This parameter flows from UserOptions.fromJS call sites through the validation pipeline, enabling fully qualified error paths like bundlerOptions.server.minify instead of generic messages. The test suite validates rejection of non-object types and confirms valid minify configurations.

Changes

Bundler Options Validation Error Messages

Layer / File(s) Summary
Public API Contract
src/bake/bake.zig
BuildConfigSubset.fromJS signature now requires a comptime property_name: []const u8 parameter to enable qualified error message paths.
Error Handling Implementation
src/bake/bake.zig
Minify field validation errors now include the fully qualified path bundlerOptions.<property_name>.minify when neither boolean nor object.
Call Site Integration
src/bake/bake.zig
UserOptions.fromJS passes property name literals "server", "client", and "ssr" when parsing each bundlerOptions sub-object.
Validation Tests
test/bake/bundler-options-validation.test.ts
Test suite validates bundlerOptions object shape, checks that each key (server, client, ssr) must be an object, confirms minify accepts boolean or object, and rejects invalid types with expected error messages.
🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the main change: adding validation for app.bundlerOptions types in Bun.serve, which is the primary focus of the changeset.
Description check ✅ Passed The description provides a complete explanation of the problem, the solution, a concrete example showing before/after behavior, and mentions the discovery source, meeting all key requirements for a comprehensive PR description.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


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

@coderabbitai coderabbitai 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.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@test/bake/bundler-options-validation.test.ts`:
- Around line 13-42: Replace the manual for-loops with Jest's parameterized
helpers: use describe.each([...]) for the outer matrix over
["server","client","ssr"] to create a describe block per key, and inside each
describe use either test.each([...]) or another describe.each for the minify
variants ([true, false, { whitespace: true }]) to produce individual tests for
non-object cases and accepted values; update the test titles to interpolate the
current key/minify (matching existing strings like `non-object
bundlerOptions.${key} throws`, `non-object non-boolean
bundlerOptions.${key}.minify throws`, and `bundlerOptions.${key}.minify =
${JSON.stringify(minify)} is accepted`) and remove the loop variables, keeping
the same Bun.serve assertions.
- Around line 33-40: The test currently uses .not.toThrow(/must be/) which can
pass for unrelated errors; instead assert for the specific downstream error that
occurs only when the bundlerOptions shape is accepted. Update the assertion in
the test around Bun.serve(...) (the block creating app: { bundlerOptions: {
[key]: { minify } } }) to expect the known runtime/startup error message
produced when the bundlerOptions branch is accepted (e.g., replace
.not.toThrow(/must be/) with .toThrow(/<actual downstream error message>/)); run
the test once to capture the real downstream message and use that exact regex so
the test proves this validation path was exercised.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 958e89b7-7c61-4a21-9d6c-a73c6b0d1b7d

📥 Commits

Reviewing files that changed from the base of the PR and between 6d0d86b and d39cbe0.

📒 Files selected for processing (2)
  • src/bake/bake.zig
  • test/bake/bundler-options-validation.test.ts

Comment on lines +13 to +42
for (const key of ["server", "client", "ssr"] as const) {
test(`non-object bundlerOptions.${key} throws`, () => {
expect(() =>
Bun.serve({
// @ts-expect-error
app: { bundlerOptions: { [key]: 1225 } },
}),
).toThrow(`'bundlerOptions.${key}' must be an object`);
});

test(`non-object non-boolean bundlerOptions.${key}.minify throws`, () => {
expect(() =>
Bun.serve({
// @ts-expect-error
app: { bundlerOptions: { [key]: { minify: 1225 } } },
}),
).toThrow(`'bundlerOptions.${key}.minify' must be a boolean or an object`);
});

for (const minify of [true, false, { whitespace: true }]) {
test(`bundlerOptions.${key}.minify = ${JSON.stringify(minify)} is accepted`, () => {
expect(() =>
Bun.serve({
// @ts-expect-error
app: { bundlerOptions: { [key]: { minify } } },
}),
).not.toThrow(/must be/);
});
}
}

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.

🧹 Nitpick | 🔵 Trivial | ⚡ Quick win

Use describe.each() for parameterized cases.

The loop-based matrix works, but this suite should use describe.each() per repo test conventions for parameterized coverage and reporting clarity.

Refactor sketch
-  for (const key of ["server", "client", "ssr"] as const) {
-    test(`non-object bundlerOptions.${key} throws`, () => {
+  describe.each(["server", "client", "ssr"] as const)("bundlerOptions.%s", key => {
+    test("non-object throws", () => {
       ...
-    test(`non-object non-boolean bundlerOptions.${key}.minify throws`, () => {
+    test("non-object non-boolean minify throws", () => {
       ...
-    for (const minify of [true, false, { whitespace: true }]) {
-      test(`bundlerOptions.${key}.minify = ${JSON.stringify(minify)} is accepted`, () => {
+    describe.each([true, false, { whitespace: true }] as const)("minify=%j", minify => {
+      test("is accepted", () => {
         ...
       });
-    }
-  }
+    });
+  });

As per coding guidelines: "Use describe.each() for parameterized tests".

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
for (const key of ["server", "client", "ssr"] as const) {
test(`non-object bundlerOptions.${key} throws`, () => {
expect(() =>
Bun.serve({
// @ts-expect-error
app: { bundlerOptions: { [key]: 1225 } },
}),
).toThrow(`'bundlerOptions.${key}' must be an object`);
});
test(`non-object non-boolean bundlerOptions.${key}.minify throws`, () => {
expect(() =>
Bun.serve({
// @ts-expect-error
app: { bundlerOptions: { [key]: { minify: 1225 } } },
}),
).toThrow(`'bundlerOptions.${key}.minify' must be a boolean or an object`);
});
for (const minify of [true, false, { whitespace: true }]) {
test(`bundlerOptions.${key}.minify = ${JSON.stringify(minify)} is accepted`, () => {
expect(() =>
Bun.serve({
// @ts-expect-error
app: { bundlerOptions: { [key]: { minify } } },
}),
).not.toThrow(/must be/);
});
}
}
describe.each(["server", "client", "ssr"] as const)("bundlerOptions.%s", key => {
test("non-object throws", () => {
expect(() =>
Bun.serve({
// `@ts-expect-error`
app: { bundlerOptions: { [key]: 1225 } },
}),
).toThrow(`'bundlerOptions.${key}' must be an object`);
});
test("non-object non-boolean minify throws", () => {
expect(() =>
Bun.serve({
// `@ts-expect-error`
app: { bundlerOptions: { [key]: { minify: 1225 } } },
}),
).toThrow(`'bundlerOptions.${key}.minify' must be a boolean or an object`);
});
describe.each([true, false, { whitespace: true }] as const)("minify=%j", minify => {
test("is accepted", () => {
expect(() =>
Bun.serve({
// `@ts-expect-error`
app: { bundlerOptions: { [key]: { minify } } },
}),
).not.toThrow(/must be/);
});
});
});
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test/bake/bundler-options-validation.test.ts` around lines 13 - 42, Replace
the manual for-loops with Jest's parameterized helpers: use describe.each([...])
for the outer matrix over ["server","client","ssr"] to create a describe block
per key, and inside each describe use either test.each([...]) or another
describe.each for the minify variants ([true, false, { whitespace: true }]) to
produce individual tests for non-object cases and accepted values; update the
test titles to interpolate the current key/minify (matching existing strings
like `non-object bundlerOptions.${key} throws`, `non-object non-boolean
bundlerOptions.${key}.minify throws`, and `bundlerOptions.${key}.minify =
${JSON.stringify(minify)} is accepted`) and remove the loop variables, keeping
the same Bun.serve assertions.

Comment on lines +33 to +40
test(`bundlerOptions.${key}.minify = ${JSON.stringify(minify)} is accepted`, () => {
expect(() =>
Bun.serve({
// @ts-expect-error
app: { bundlerOptions: { [key]: { minify } } },
}),
).not.toThrow(/must be/);
});

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.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Strengthen accepted-shape assertions to avoid false positives.

At Line 39, .not.toThrow(/must be/) still passes if Bun.serve() throws a different error for an unrelated reason. Assert a specific downstream error to prove this validation branch was actually accepted.

Proposed fix
-        ).not.toThrow(/must be/);
+        ).toThrow("'app' is missing 'framework'");
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
test(`bundlerOptions.${key}.minify = ${JSON.stringify(minify)} is accepted`, () => {
expect(() =>
Bun.serve({
// @ts-expect-error
app: { bundlerOptions: { [key]: { minify } } },
}),
).not.toThrow(/must be/);
});
test(`bundlerOptions.${key}.minify = ${JSON.stringify(minify)} is accepted`, () => {
expect(() =>
Bun.serve({
// `@ts-expect-error`
app: { bundlerOptions: { [key]: { minify } } },
}),
).toThrow("'app' is missing 'framework'");
});
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test/bake/bundler-options-validation.test.ts` around lines 33 - 40, The test
currently uses .not.toThrow(/must be/) which can pass for unrelated errors;
instead assert for the specific downstream error that occurs only when the
bundlerOptions shape is accepted. Update the assertion in the test around
Bun.serve(...) (the block creating app: { bundlerOptions: { [key]: { minify } }
}) to expect the known runtime/startup error message produced when the
bundlerOptions branch is accepted (e.g., replace .not.toThrow(/must be/) with
.toThrow(/<actual downstream error message>/)); run the test once to capture the
real downstream message and use that exact regex so the test proves this
validation path was exercised.

@github-actions

github-actions Bot commented May 9, 2026

Copy link
Copy Markdown
Contributor

This PR may be a duplicate of:

  1. Validate app.bundlerOptions types in Bun.serve #30402 - Same fix: validates bundlerOptions types in Bun.serve and fixes minify: false fallthrough in BuildConfigSubset.fromJS
  2. bake: validate bundlerOptions values are objects before property access #30125 - Same fix: guards against non-object bundlerOptions and sub-properties in bake.zig, with identical test file name

🤖 Generated with Claude Code

@robobun

robobun commented May 9, 2026

Copy link
Copy Markdown
Collaborator Author

Duplicate of #30125 (and #30402), which fix the same bundlerOptions validation crash with the same approach. Closing in favor of the earlier PR.

@robobun robobun closed this May 9, 2026
@robobun robobun deleted the farm/6768fec8/bake-bundler-options-validation branch May 9, 2026 06:27
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.

1 participant