Skip to content

js_parser: treat "async as T" / "async satisfies T" as a cast, not an arrow - #34246

Merged
dylan-conway merged 8 commits into
mainfrom
farm/a6bf168e/async-as-satisfies-ts
Jul 16, 2026
Merged

dylan-conway merged 8 commits into
mainfrom
farm/a6bf168e/async-as-satisfies-ts

Conversation

@robobun

@robobun robobun commented Jul 15, 2026 •

Copy link
Copy Markdown
Collaborator

Problem

A variable or parameter literally named async cannot be followed by a TypeScript as/satisfies cast:

function f(async?) { return g(async as boolean) }
$ bun build repro.ts --no-bundle
error: Expected "=>" but found "boolean"

Both tsc and esbuild accept this and emit g(async).

Cause

parse_async_prefix_expr commits to async <ident> => ... as soon as it sees any identifier after async, then fails in parse_arrow_body when => is not the next token. TypeScript's parser resolves this ambiguity with a two-token lookahead (isUnParenthesizedAsyncArrowFunctionWorker, microsoft/TypeScript#8444), and esbuild ported the same lookahead in evanw/esbuild@df815ac for evanw/esbuild#4027.

Fix

In TypeScript mode, before committing to async ident => ..., snapshot the lexer, advance one token, and check whether it is =>. If it is, parse the async arrow as before. If it is not, fall through and treat async as a plain identifier so the suffix parser can handle as/satisfies/in/etc. A new next_token_matches helper owns the snapshot/advance/restore sequence.

Because the lookahead removes the accidental rejection of for (async of [7]), t_for now enforces the [lookahead != async of] grammar restriction directly via a bad_async_range guard next to the existing bad_let_range, keyed on the literal async of token sequence. That guard runs in both JS and TS mode, so for (async\nof x); is now rejected with For loop initializers cannot start with "async of" in JS mode too (it was previously accepted there); this matches V8 and the spec. for await (async of ...), for (async of => {};;), for (async.x of ...), for ((async) of ...), for (\u0061sync of ...), for (async as T of ...) and for (async! of ...) are all still accepted.

Verification

New cases in test/bundler/transpiler/transpiler.test.js cover async as T, async satisfies T, async in x, async as => ... (still an arrow with parameter as), statement-level and export default positions, a real async x => ..., the for (async of rejection in both loaders (single-line and with a newline), and the for-of edge cases listed above. The new test block fails on the current release (Expected "=>" but found "boolean") and passes with this change; the rest of transpiler.test.js (172 pass) is unaffected.


[review] gate passed · iteration 2 · 3 files touched

fails on main (without fix)
ASAN without fix: 2 failed, 22 skipped
$ BUN_DEBUG_QUIET_LOGS=1 bun scripts/build.ts --profile=debug --quiet test "--reporter=junit" "--reporter-outfile=/tmp/mechgate.xml" test/bundler/transpiler/transpiler.test.js
info: syncing channel updates for nightly-2026-05-06-x86_64-unknown-linux-gnu
info: latest update on 2026-05-06 for version 1.97.0-nightly (e95e73209 2026-05-05)
info: component rust-src is up to date
info: checking for self-update (current version: 1.29.0)
bun test v1.4.0 (985542c44)

test/bundler/transpiler/transpiler.test.js:
(pass) Bun.Transpiler > handles errors when parsing macros [4.91ms]
(pass) Bun.Transpiler > normalizes \r\n [6.75ms]
1
(pass) Bun.Transpiler > doesn't hang indefinitely #2746 [4.16ms]
(pass) Bun.Transpiler > property access inlining > bails out with spread [6.57ms]
(pass) Bun.Transpiler > property access inlining > bails out with multiple items [2.61ms]
(pass) Bun.Transpiler > property access inlining > works [2.40ms]
(pass) Bun.Transpiler > property access inlining > works nested [2.11ms]
(pass) Bun.Transpiler > TypeScript > import Foo = Baz.Bar [2.82ms]
(pass) Bun.Transpiler > TypeScript > ternary should parse correctly when parsing typescript fails [2.39ms]
(pass) B
... (truncated)

release without fix: 22 skipped
bun test v1.4.0-canary.1 (3638fd1a6)

test/bundler/transpiler/transpiler.test.js:
(pass) Bun.Transpiler > handles errors when parsing macros [4.33ms]
(pass) Bun.Transpiler > normalizes \r\n [0.29ms]
1
(pass) Bun.Transpiler > doesn't hang indefinitely #2746 [1.21ms]
(pass) Bun.Transpiler > property access inlining > bails out with spread [0.17ms]
(pass) Bun.Transpiler > property access inlining > bails out with multiple items [0.03ms]
(pass) Bun.Transpiler > property access inlining > works [0.03ms]
(pass) Bun.Transpiler > property access inlining > works nested [0.02ms]
(pass) Bun.Transpiler > TypeScript > import Foo = Baz.Bar [0.04ms]
(pass) Bun.Transpiler > TypeScript > ternary should parse correctly when parsing typescript fails [0.04ms]
(pass) Bun.Transpiler > TypeScript > contextual keywords used as plain identifiers keep their statements [0.27ms]
(pass) Bun.Transpiler > TypeScript > does not crash when export default abstract is an expression followed by a class [0.24ms]
(pass) Bun.Transpiler > TypeScript > scope tracking stays balanced when a contextual keyword starts a larger expression [0.21ms]
(pass) Bun.Transpiler > TypeScript > scope tracking stays balan
... (truncated)
passes on PR (with fix)
ASAN with fix: 22 skipped
$ BUN_DEBUG_QUIET_LOGS=1 bun scripts/build.ts --profile=debug --quiet test "--reporter=junit" "--reporter-outfile=/tmp/mechgate.xml" test/bundler/transpiler/transpiler.test.js
info: syncing channel updates for nightly-2026-05-06-x86_64-unknown-linux-gnu
info: latest update on 2026-05-06 for version 1.97.0-nightly (e95e73209 2026-05-05)
info: component rust-src is up to date
info: checking for self-update (current version: 1.29.0)
bun test v1.4.0 (985542c44)

test/bundler/transpiler/transpiler.test.js:
(pass) Bun.Transpiler > handles errors when parsing macros [5.11ms]
(pass) Bun.Transpiler > normalizes \r\n [6.46ms]
1
(pass) Bun.Transpiler > doesn't hang indefinitely #2746 [4.36ms]
(pass) Bun.Transpiler > property access inlining > bails out with spread [6.60ms]
(pass) Bun.Transpiler > property access inlining > bails out with multiple items [2.59ms]
(pass) Bun.Transpiler > property access inlining > works [2.46ms]
(pass) Bun.Transpiler > property access inlining > works nested [2.10ms]
(pass) Bun.Transpiler > TypeScript > import Foo = Baz.Bar [2.77ms]
(pass) Bun.Transpiler > TypeScript > ternary should parse correctly when parsing typescript fails [2.27ms]
(pass) B
... (truncated)

release with fix: 22 skipped
$ bun scripts/build.ts --profile=release
info: syncing channel updates for nightly-2026-05-06-x86_64-unknown-linux-gnu
info: latest update on 2026-05-06 for version 1.97.0-nightly (e95e73209 2026-05-05)
info: component rust-src is up to date
info: checking for self-update (current version: 1.29.0)
[configured] bun-profile → bun (stripped) in 794ms (unchanged)
ninja: Entering directory `/workspace/bun/build/release'
[1/6] gen generated_host_exports.rs
generated_host_exports.rs: 91 exports (host=3, lazy=10, generic=78, rust=0); 243 extern-C blocks audited
[1/6] cargo bun_bin → libbun_rust.a (--target x86_64-unknown-linux-gnu)
info: syncing channel updates for nightly-2026-05-06-x86_64-unknown-linux-gnu
info: latest update on 2026-05-06 for version 1.97.0-nightly (e95e73209 2026-05-05)
info: component rust-src is up to date
info: component rust-std is up to date

  nightly-2026-05-06-x86_64-unknown-linux-gnu unchanged - rustc 1.97.0-nightly (e95e73209 2026-05-05)

info: checking for self-update (current version: 1.29.0)
�[1m�[92m   Compiling�[0m bun_core v0.0.0 (/workspace/bun/src/bun_core)
�[1m�[92m   Compiling�[0m bun_errno v0.0.0 (/workspace/bun/src/errno)
�[1m�
... (truncated)
diff hotspot
src/js_parser/parse/mod.rs                 | 86 +++++++++++++++++++++---------
 src/js_parser/parse/parse_stmt.rs          | 28 ++++++++++
 test/bundler/transpiler/transpiler.test.js | 41 ++++++++++++++
 3 files changed, 129 insertions(+), 26 deletions(-)

gate history · 4 passed · 0 rejected · iteration 2

evidence per changed file
file                                        reads  edits  tests
src/js_parser/parse/mod.rs                     13     10      0
src/js_parser/parse/parse_stmt.rs               7      8      0
test/bundler/transpiler/transpiler.test.js      6     10      0

@robobun

robobun commented Jul 15, 2026 •

Copy link
Copy Markdown
Collaborator Author

Reproduced with bun build on a file containing function f(async?) { return g(async as boolean) }, which currently fails with Expected "=>" but found "boolean". Fix and tests are in this PR.

c05d57f adds a bad_async_range guard in t_for so for (async of [7]); stays rejected in TypeScript mode after the lookahead stops the arrow commit; caafbca tightens that guard to the literal async of token sequence so for (async as T of x) / for (async! of x) are not caught. 3638fd1 collapses the two lookahead helpers into one next_token_matches(pred) and locks in the for (async\nof x); rejection in both JS and TS loaders (matching V8 and the spec). b7f57b1 reverts the single-scan optimisation since the double-lexed token is => (cheap) and the simpler snapshot/restore code reads better.

CI on 3638fd1 (build 73378): transpiler.test.js green on every lane; remaining red lanes are all failures also present on main or infrastructure flake (JSC getOwnPropertyDescriptor assertion, bake/deinitialization, net-connect-memleak, docker daemon on one alpine agent, require-cache RSS threshold on darwin). Ready for review.

@coderabbitai

coderabbitai Bot commented Jul 15, 2026 •

Copy link
Copy Markdown
Contributor

Warning

Review limit reached

@robobun, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 14 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 7d7e8ce2-fae2-4608-b9f7-88c94dc2501c

📥 Commits

Reviewing files that changed from the base of the PR and between be77b65 and 3dddc6f.

📒 Files selected for processing (3)
  • src/js_parser/parse/mod.rs
  • src/js_parser/parse/parse_stmt.rs
  • test/bundler/transpiler/transpiler.test.js

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

@robobun

robobun commented Jul 15, 2026 •

Copy link
Copy Markdown
Collaborator Author
Updated 5:46 PM PT - Jul 15th, 2026

@robobun, your commit b7f57b1 is building: #73522

@github-actions

Copy link
Copy Markdown
Contributor

Found 1 issue this PR may fix:

  1. Parsing issue when compiling binaryen with cjs #17561 - Bundler emits Expected "=>" but found "," when parsing binaryen's minified CJS, where async is used as a plain identifier — same async-arrow misparse this PR fixes

If this is helpful, copy the block below into the PR description to auto-close this issue on merge.

Fixes #17561

🤖 Generated with Claude Code

@robobun

robobun commented Jul 15, 2026

Copy link
Copy Markdown
Collaborator Author

Checked #17561: it does not reproduce as Expected "=>" any more. On current main the binaryen --format=cjs build fails with "await" can only be used inside an "async" function because binaryen/index.js has a top-level await that CJS output rejects, and this PR does not change that. The fix here is TypeScript-only (it gates on IS_TYPESCRIPT_ENABLED), so it does not close #17561.

Comment thread src/js_parser/parse/mod.rs
The two-token lookahead that lets "async as T" parse as a cast also stops the
arrow commit on "for (async of [7])", so add the missing [lookahead != async of]
restriction in t_for instead of relying on the arrow path to reject it. Matches
esbuild's diagnostic; "for await (async of ...)" and "for (async of => {};;)"
stay accepted.
Comment thread src/js_parser/parse/mod.rs Outdated
Comment thread src/js_parser/parse/parse_stmt.rs
Only set bad_async_range when the next token is literally "of". Casts and the
non-null "!" are erased by parse_suffix without wrapping the expression, so the
previous "bare EIdentifier" check incorrectly rejected "for (async as any of x)"
and "for (async! of x)".

@claude claude Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

All three earlier review rounds have been addressed (c05d57f added the for (async of guard, 3dddc6f trimmed the comments, caafbca tightened the guard to the literal async of token sequence). I didn't find anything further, but this is parser grammar code that took a couple of iterations to get right, so worth a quick human pass.

What was reviewed:

  • Traced the new lookahead through parse_async_prefix_expr for async as/satisfies/in/!, async ident =>, and export default async as T — all resolve correctly and JS-mode behavior is unchanged.
  • Verified bad_async_range now keys on is_contextual_keyword(b"async") && next-token-is-"of" (raw-text match, so escaped spellings and async.x/async!/async as T are not caught) and is cleared when the init becomes an arrow (async of => {}).
  • Checked for await (async of stays accepted, for (async\nof stays rejected, and the snapshot/restore + is_log_disabled pattern matches the existing lexer backtracking helpers.
Extended reasoning...

Overview

The PR fixes a TypeScript-mode parse failure where an identifier literally named async followed by as/satisfies/in was misparsed as the start of an async arrow. The fix adds a one-token lookahead in parse_async_prefix_expr (mod.rs) so async <ident> only commits to the arrow path when the token after <ident> is =>, matching tsc's isUnParenthesizedAsyncArrowFunctionWorker and esbuild's port of it. Because that lookahead removed the accidental rejection of for (async of ...), a dedicated bad_async_range guard was added in t_for (parse_stmt.rs) alongside the existing bad_let_range, and after a follow-up round it now keys on the literal two-token sequence async of via check_for_of_after_the_current_token. Tests in transpiler.test.js cover the positive cases, the arrow-still-works case, the for-of rejection, and the for-of edge cases (for await, async.x, async as T, async!, async of => {}).

Security risks

None. This is a syntactic disambiguation in the TS parser; no I/O, allocation-size, or trust-boundary changes. The lexer snapshot/restore pattern used for lookahead is the same one already used elsewhere for TS backtracking, and is_log_disabled is saved/restored so a failing speculative next() doesn't leak diagnostics.

Level of scrutiny

Moderate-to-high. js_parser is core, hot-path, ported-from-esbuild code where subtle grammar interactions are easy to miss — this PR itself needed two follow-up commits after review to close the for (async of hole and then to stop over-rejecting for (async as T of / for (async! of. The final shape diverges structurally from esbuild (which threads an exprFlagForLoopInit into parseAsyncPrefixExpr and checks ident == "of" there) in favor of a bad_let_range-style guard in t_for; after caafbca the two are functionally equivalent, but a maintainer may have a preference on which pattern to keep for future esbuild-diff maintainability.

Other factors

All three of my prior inline threads are resolved and the fixes match what was requested. The bug hunter found nothing on the current head. Test coverage is good and includes the negative case (ts.expectParseError("for (async of [7]);", ...)), the still-an-arrow case (async as => ...), and the for-await exemption. I traced the remaining edge I could think of — for await (async of outside an async context, where is_for_await is reset to false before the guard — and it produces a cascading second error on already-invalid input, which is harmless. Given the change is narrow and now matches reference behavior, this is close to approvable, but parser changes that required multiple correction rounds are worth a human sanity check before merge.

next_token_matches(pred) owns the snapshot/advance/restore sequence; restore()
already resets is_log_disabled so drop the redundant manual save/restore.

Also lock in "for (async\nof x);" being rejected in both JS and TS mode: the
guard is not TS-gated, which matches V8 and the [lookahead != async of] spec
restriction (esbuild accepts because its check sits inside the no-newline
block).

@claude claude Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The final revision addresses all three earlier findings and I found nothing new, but I'd like a human to sign off given this touches the core JS parser and intentionally changes JS-mode behavior (for (async\nof x) now rejected — spec-correct, but user-visible).

Checked: the next_token_matches snapshot/restore round-trips is_log_disabled; is_contextual_keyword compares raw() so the escaped-\u0061sync and for await carve-outs hold; bad_async_range is keyed on the literal async of token pair and cleared for the async of => {} arrow case; JS-mode for (async of x) still errors via the old arrow-commit path.

Extended reasoning...

Overview

The PR fixes a TypeScript-mode parse failure where async used as a plain identifier followed by as T / satisfies T was misparsed as the start of an async arrow. It touches src/js_parser/parse/mod.rs (adds next_token_matches lookahead helper and gates the T::TIdentifier arm of parse_async_prefix_expr on it in TS mode), src/js_parser/parse/parse_stmt.rs (adds a bad_async_range guard in t_for mirroring the existing bad_let_range pattern), and adds ~20 test cases in test/bundler/transpiler/transpiler.test.js.

This is the fourth review pass. The prior three rounds surfaced (1) a spec-compliance regression where for (async of x) became accepted in TS mode, (2) a comment-length nit, and (3) the bad_async_range guard over-matching async as T / async! in for-of position. All three are resolved in the current diff (commits c05d57f, 3dddc6f, caafbca, 3638fd1) and each has dedicated test coverage.

Security risks

None. This is grammar-level disambiguation in the parser with no I/O, no allocation-size arithmetic on untrusted data, and no auth/crypto involvement. The lookahead uses the existing snapshot()/restore() lexer machinery, which is Copy-based and already used for other backtracking paths.

Level of scrutiny

High — the JS parser runs on every file Bun bundles or executes, and grammar edge cases here have a track record of subtle interactions (this PR itself needed three rounds to get the for-of guard right). The change is small and matches both reference implementations (tsc's isUnParenthesizedAsyncArrowFunctionWorker and esbuild's df815ac), which reduces risk considerably, but it also introduces an intentional behavior change in JS mode: for (async\nof x); was previously accepted (the newline skipped the arrow commit) and is now rejected with the new diagnostic. That is spec-correct and matches V8, but it is a user-visible tightening outside the TS-only scope of the headline fix, and worth a maintainer glance.

Other factors

  • Test coverage is thorough: as/satisfies/in, arrow-still-works (async as => ...), statement-level and export default positions, and the full for-of matrix (for await, async.x, (async), async as T, async satisfies T, async!, async of => {}, newline, escaped spelling) in both loaders.
  • The bad_async_range guard now keys on the literal token pair via is_contextual_keyword(b"async") && next_token_matches(is_contextual_keyword(b"of")), which matches the spec's two-token lookahead restriction exactly and avoids the parsed-shape false positives from the earlier revision.
  • I verified LexerSnapshot captures is_log_disabled, so the helper's is_log_disabled = true is unconditionally restored; and is_contextual_keyword compares raw(), so \u0061sync correctly bypasses the guard.
  • In JS mode without a newline, for (async of x) still errors via the pre-existing arrow-commit path (Expected "=>") before the new guard is reached, so the existing .toThrow() test at transpiler.test.js:2272 remains satisfied for the same reason as before.

@dylan-conway

Copy link
Copy Markdown
Member

@robobun is the token lookahead smart so it remembers the future token for the next scan() and doesn't scan it twice (if it goes down that codepath)?

@robobun

robobun commented Jul 15, 2026

Copy link
Copy Markdown
Collaborator Author

No, it is not smart in that sense. next_token_matches snapshots, calls lexer.next(), checks the predicate, then unconditionally restores, so whichever branch the parser takes next re-scans that token. For async x => {} in TS mode that means the identifier after async is lexed twice (once for the peek, once when the arrow arm does p.lexer.next()?).

This is the same trade-off esbuild makes: its checkForArrowAfterTheCurrentToken also does oldLexer := p.lexer; p.lexer.Next(); ...; p.lexer = oldLexer with no token cache. It also matches the existing backtracking helpers in this crate (is_ts_arrow_fn_jsx, look_ahead_next_token_is_open_paren_or_less_than_or_dot, lexer_backtracker_bool/_result), which all snapshot/restore and re-scan.

The restore cannot be skipped on the arrow path because the arm immediately reads p.lexer.identifier and p.lexer.loc() for the parameter before calling next(); without the restore those would point at =>. Avoiding the double scan entirely would need a one-token pushback slot on the lexer, which it does not have today. The second lookahead site (async of in a for header) only fires when the init literally starts with the token async, so it is essentially cold. Happy to wire up a pushback if you think the async x => hit is worth it, otherwise this keeps the existing pattern.

@dylan-conway

Copy link
Copy Markdown
Member

yeah it would be best if we avoid lexing that identifier twice, code like that is common. can you investigate and determine if this is feasible?

Capture the identifier and its loc before advancing, then advance once. In TS
mode the advance is speculative (log disabled, snapshot restored when the next
token is not "=>"), so the common "async x => ..." path now scans "=>" once
instead of twice. JS mode is unchanged: it advances once and commits, exactly
as before the lookahead was introduced.
@robobun

robobun commented Jul 15, 2026

Copy link
Copy Markdown
Collaborator Author

Investigated and it is feasible without a lexer pushback slot. Pushed in 985542c.

What was getting scanned twice was the token after the identifier (so => in the common case), not the identifier itself. The fix is to capture lexer.identifier/loc() before advancing, then advance once and branch on where we land:

  • TypeScript, async x => ... (the common case): snapshot, is_log_disabled = true, next() lands on =>, keep the advance, restore only is_log_disabled. => is lexed once. Net cost vs. main: one LexerSnapshot copy; no second next() and no restore().
  • TypeScript, async as T etc.: same speculative advance lands on T/boolean/etc., restore the snapshot so async falls through as a plain identifier. That following token still gets lexed again by parse_suffix, but this is the uncommon path.
  • JavaScript: no snapshot at all, just the single p.lexer.next()? that was already there, so byte-for-byte unchanged vs. main.

is_log_disabled is set during the TS speculative advance and reset from the snapshot either way, which also avoids a duplicate "Invalid identifier" diagnostic when the following token is something like an invalid escaped identifier.

The remaining next_token_matches helper is only used for the for (async of guard in t_for, which fires only when a for-loop init literally starts with the token async, so the double-scan there is cold.

@dylan-conway

Copy link
Copy Markdown
Member

@robobun if the token lexed twice is the token after the identifier then we should probably continue to do this because it's not expensive and it's most likely simpler code. please update it if this is true

@robobun

robobun commented Jul 16, 2026

Copy link
Copy Markdown
Collaborator Author

That is true, reverted in b7f57b1. The token that was being lexed twice is the one after the identifier (so => on the arrow path, or the type name on the as/satisfies path), not the identifier itself. Back to the simpler shape from 3638fd1: the TS-mode lookahead is next_token_matches(|p| p.lexer.token == T::TEqualsGreaterThan) via check_for_arrow_after_the_current_token, which snapshots, advances, checks, and restores unconditionally, and the arrow arm then advances again with its existing p.lexer.next()?. Same behavior as the reverted commit, just the one extra next() on =>.

@dylan-conway
dylan-conway merged commit f6f1523 into main Jul 16, 2026
10 of 39 checks passed
@dylan-conway
dylan-conway deleted the farm/a6bf168e/async-as-satisfies-ts branch July 16, 2026 00:51
hughescr added a commit to hughescr/bun that referenced this pull request Jul 16, 2026
* upstream/main: (57 commits)
  node:http/https/http2: raise Node v26.3.0 compat to ~94%, sync the upstream suites, and fix the Windows/macOS transport-layer teardown bugs they exposed (oven-sh#32488)
  expect: fix panic in toBeArrayOfSize/toHaveBeenCalledTimes with length > i32 max (oven-sh#32266)
  lexer: fix TOKEN_TO_STRING[TColon] showing " =" instead of ":" (oven-sh#34253)
  Bun.Terminal: write() returns bytes accepted, fire drain on POSIX (oven-sh#34289)
  test(serve-body-leak): give release-asan the same 60s per-test timeout as debug (oven-sh#34297)
  worker: mark the context terminating before the final concurrent-queue drain (oven-sh#34278)
  buffer: wrap negative ucs2 indexOf offset against raw byte length for Buffer needles (oven-sh#34273)
  fs.promises.watch: yield events with a null prototype (oven-sh#34279)
  child_process: latch stdin write EPIPE as 'error' + destroy, fail later writes with ERR_STREAM_DESTROYED (oven-sh#34268)
  Fix asString assertion when passing String objects as signals (oven-sh#34265)
  Buffer: carry size_t through toString/write so length 2^32 doesn't wrap to 0 (oven-sh#34274)
  test: use tempDir in log-test.test.ts instead of hardcoded /tmp path (oven-sh#34294)
  tty: track raw mode per handle instead of per process (oven-sh#33527)
  test: expect the bumped mimalloc SHA in process.versions
  Return freed memory to the OS on a background thread instead of the JS thread (oven-sh#34181)
  Move WTFTimer out of the shared timer heap to fix a cross-thread race (oven-sh#33131)
  test: update block-scoped enum lowering expectations to let (oven-sh#34287)
  Error.captureStackTrace: install .stack as non-enumerable (oven-sh#34259)
  js_parser: treat "async as T" / "async satisfies T" as a cast, not an arrow (oven-sh#34246)
  js_parser: accept `!`, `#name`, and `export @dec` in standard decorator grammar (oven-sh#34245)
  ...
hughescr added a commit to hughescr/bun that referenced this pull request Jul 16, 2026
* upstream/main: (70 commits)
  node:http/https/http2: raise Node v26.3.0 compat to ~94%, sync the upstream suites, and fix the Windows/macOS transport-layer teardown bugs they exposed (oven-sh#32488)
  expect: fix panic in toBeArrayOfSize/toHaveBeenCalledTimes with length > i32 max (oven-sh#32266)
  lexer: fix TOKEN_TO_STRING[TColon] showing " =" instead of ":" (oven-sh#34253)
  Bun.Terminal: write() returns bytes accepted, fire drain on POSIX (oven-sh#34289)
  test(serve-body-leak): give release-asan the same 60s per-test timeout as debug (oven-sh#34297)
  worker: mark the context terminating before the final concurrent-queue drain (oven-sh#34278)
  buffer: wrap negative ucs2 indexOf offset against raw byte length for Buffer needles (oven-sh#34273)
  fs.promises.watch: yield events with a null prototype (oven-sh#34279)
  child_process: latch stdin write EPIPE as 'error' + destroy, fail later writes with ERR_STREAM_DESTROYED (oven-sh#34268)
  Fix asString assertion when passing String objects as signals (oven-sh#34265)
  Buffer: carry size_t through toString/write so length 2^32 doesn't wrap to 0 (oven-sh#34274)
  test: use tempDir in log-test.test.ts instead of hardcoded /tmp path (oven-sh#34294)
  tty: track raw mode per handle instead of per process (oven-sh#33527)
  test: expect the bumped mimalloc SHA in process.versions
  Return freed memory to the OS on a background thread instead of the JS thread (oven-sh#34181)
  Move WTFTimer out of the shared timer heap to fix a cross-thread race (oven-sh#33131)
  test: update block-scoped enum lowering expectations to let (oven-sh#34287)
  Error.captureStackTrace: install .stack as non-enumerable (oven-sh#34259)
  js_parser: treat "async as T" / "async satisfies T" as a cast, not an arrow (oven-sh#34246)
  js_parser: accept `!`, `#name`, and `export @dec` in standard decorator grammar (oven-sh#34245)
  ...
hughescr added a commit to hughescr/bun that referenced this pull request Jul 16, 2026
* upstream/main: (52 commits)
  node:http/https/http2: raise Node v26.3.0 compat to ~94%, sync the upstream suites, and fix the Windows/macOS transport-layer teardown bugs they exposed (oven-sh#32488)
  expect: fix panic in toBeArrayOfSize/toHaveBeenCalledTimes with length > i32 max (oven-sh#32266)
  lexer: fix TOKEN_TO_STRING[TColon] showing " =" instead of ":" (oven-sh#34253)
  Bun.Terminal: write() returns bytes accepted, fire drain on POSIX (oven-sh#34289)
  test(serve-body-leak): give release-asan the same 60s per-test timeout as debug (oven-sh#34297)
  worker: mark the context terminating before the final concurrent-queue drain (oven-sh#34278)
  buffer: wrap negative ucs2 indexOf offset against raw byte length for Buffer needles (oven-sh#34273)
  fs.promises.watch: yield events with a null prototype (oven-sh#34279)
  child_process: latch stdin write EPIPE as 'error' + destroy, fail later writes with ERR_STREAM_DESTROYED (oven-sh#34268)
  Fix asString assertion when passing String objects as signals (oven-sh#34265)
  Buffer: carry size_t through toString/write so length 2^32 doesn't wrap to 0 (oven-sh#34274)
  test: use tempDir in log-test.test.ts instead of hardcoded /tmp path (oven-sh#34294)
  tty: track raw mode per handle instead of per process (oven-sh#33527)
  test: expect the bumped mimalloc SHA in process.versions
  Return freed memory to the OS on a background thread instead of the JS thread (oven-sh#34181)
  Move WTFTimer out of the shared timer heap to fix a cross-thread race (oven-sh#33131)
  test: update block-scoped enum lowering expectations to let (oven-sh#34287)
  Error.captureStackTrace: install .stack as non-enumerable (oven-sh#34259)
  js_parser: treat "async as T" / "async satisfies T" as a cast, not an arrow (oven-sh#34246)
  js_parser: accept `!`, `#name`, and `export @dec` in standard decorator grammar (oven-sh#34245)
  ...
hughescr added a commit to hughescr/bun that referenced this pull request Jul 16, 2026
* upstream/main: (52 commits)
  node:http/https/http2: raise Node v26.3.0 compat to ~94%, sync the upstream suites, and fix the Windows/macOS transport-layer teardown bugs they exposed (oven-sh#32488)
  expect: fix panic in toBeArrayOfSize/toHaveBeenCalledTimes with length > i32 max (oven-sh#32266)
  lexer: fix TOKEN_TO_STRING[TColon] showing " =" instead of ":" (oven-sh#34253)
  Bun.Terminal: write() returns bytes accepted, fire drain on POSIX (oven-sh#34289)
  test(serve-body-leak): give release-asan the same 60s per-test timeout as debug (oven-sh#34297)
  worker: mark the context terminating before the final concurrent-queue drain (oven-sh#34278)
  buffer: wrap negative ucs2 indexOf offset against raw byte length for Buffer needles (oven-sh#34273)
  fs.promises.watch: yield events with a null prototype (oven-sh#34279)
  child_process: latch stdin write EPIPE as 'error' + destroy, fail later writes with ERR_STREAM_DESTROYED (oven-sh#34268)
  Fix asString assertion when passing String objects as signals (oven-sh#34265)
  Buffer: carry size_t through toString/write so length 2^32 doesn't wrap to 0 (oven-sh#34274)
  test: use tempDir in log-test.test.ts instead of hardcoded /tmp path (oven-sh#34294)
  tty: track raw mode per handle instead of per process (oven-sh#33527)
  test: expect the bumped mimalloc SHA in process.versions
  Return freed memory to the OS on a background thread instead of the JS thread (oven-sh#34181)
  Move WTFTimer out of the shared timer heap to fix a cross-thread race (oven-sh#33131)
  test: update block-scoped enum lowering expectations to let (oven-sh#34287)
  Error.captureStackTrace: install .stack as non-enumerable (oven-sh#34259)
  js_parser: treat "async as T" / "async satisfies T" as a cast, not an arrow (oven-sh#34246)
  js_parser: accept `!`, `#name`, and `export @dec` in standard decorator grammar (oven-sh#34245)
  ...

# Conflicts:
#	test/js/bun/websocket/websocket-server.test.ts
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants