Compare commits

...

94 Commits

Author SHA1 Message Date
Claude
b4e5646b36 Fix HTTP/2 client: socket handler, memory management, and frame processing
- Fix socket handler registration to route data to HTTP2Client instead of HTTPClient
- Implement transferToHTTP2Client() for proper protocol upgrade handoff
- Fix double-free bug in frame processing by removing problematic defer
- Fix infinite loop in parseFrames with proper buffer management
- Add safety checks and error handling for malformed frames
- Make ActiveSocket public for proper socket context updates
- Reuse existing h2_frame_parser types instead of duplicating them

The HTTP/2 client now successfully:
- Receives responses in the correct handler
- Processes frames without memory crashes
- Handles errors gracefully without crashing

Co-authored-by: Claude <claude@anthropic.com>
2025-08-08 00:39:57 +02:00
Claude
2c195c6774 Extract HTTP/2 frame buffering into FrameBuffer struct
Added proper frame buffering to handle partial HTTP/2 frames that arrive
across multiple TCP packets. This replaces the previous approach which
only logged buffering was needed but didn't actually buffer.

- Added FrameBuffer struct with proper ByteList integration
- Implemented appendData, hasCompleteFrame, extractCompleteFrame methods
- Added frame_buffer field to HTTP2Client and proper cleanup in deinit
- Updated parseFrames to use buffering instead of breaking on partial frames
- Maintains memory safety with proper allocation/deallocation

The HTTP/2 client now handles fragmented frame reception correctly.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-07 03:47:04 +02:00
Claude
ff50446253 Extract HTTP/2 frame header parsing into pure function
Split frame header parsing logic into a standalone parseFrameHeader() function
with a FrameHeaderInfo struct. This reduces code duplication and makes the
parsing logic more testable and reusable.

- Added FrameHeaderInfo struct to hold parsed header data
- Created parseFrameHeader() function that takes data slice and returns parsed header
- Updated parseFrames() to use the new function instead of inline parsing
- No functional changes, just better code organization

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-07 03:40:57 +02:00
Claude
ac74fdd74c Work on HTTP/2 implementation
Added partial HTTP/2 support to fetch(). The implementation sends requests successfully but responses don't complete properly yet.

Changes:
- Added callback invocation when HTTP/2 streams end
- Fixed state transitions to set all required fields to .done
- Added CONTINUATION frame support for multi-frame headers
- Enhanced HPACK error reporting with specific error types
- Fixed header block accumulation logic

Current issues:
- HPACK decoder fails with certain header encodings from Node.js servers
- No flow control (WINDOW_UPDATE frames) yet
- No decompression support for HTTP/2 streams

The code compiles but HTTP/2 requests still don't work end-to-end. More work needed on HPACK compatibility.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-07 01:02:26 +02:00
Claude
0f99389d8b Merge remote-tracking branch 'origin/main' into claude/add-http2-client-support 2025-08-06 07:45:06 +02:00
Claude
026eea57cd a 2025-08-06 07:44:27 +02:00
Jarred Sumner
ccbd3f3575 Update BuildMimalloc.cmake 2025-08-05 22:27:01 -07:00
pfg
a72d74e09a Split JS parser into multiple files (#20880)
Splits up js_parser.zig into multiple files. Also changes visitExprInOut
to use function calls rather than switch

Not ready:

- [ ] P.zig is ~70,000 tokens, still needs to get smaller
- [x] ~~measure zig build time before & after (is it slower?)~~ no
significant impact

---------

Co-authored-by: pfgithub <6010774+pfgithub@users.noreply.github.com>
2025-08-05 20:52:16 -07:00
Claude
40bd143072 feat: Add ALPN support for HTTP/2 negotiation
- Extended uSockets to support ALPN protocol configuration
- Added ALPN fields to BunSocketContextOptions structures
- Created protocol-specific SSL contexts for h1/h2/both
- ALPN negotiation now works but HTTP/2 protocol not implemented

Note: HTTP/2 requests will fail as only ALPN negotiation is implemented,
not the actual HTTP/2 protocol. This is foundational work only.

🤖 Generated with Claude Code

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-06 05:49:54 +02:00
Alistair Smith
04883a8bdc revert fe28e00d53.
This reverts commit fe28e00d53.
2025-08-05 16:10:29 -07:00
Alistair Smith
fe28e00d53 feat: add Bun.SQL API with initial SQLite support 2025-08-05 16:04:11 -07:00
robobun
da856dd347 docs: update node:vm compatibility status (#21634) 2025-08-05 13:50:06 -07:00
robobun
25d490fb65 docs: document Atomics global support (#21625)
Co-authored-by: Claude Bot <claude-bot@bun.sh>
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2025-08-05 11:48:38 -07:00
Michael H
806d6c156f add catalog support to bun (outdated|update -i) and --filter to bun update -i (#21482) 2025-08-05 05:12:22 -07:00
Jarred Sumner
198d7c3b19 internal: add lldb inline comment tool 2025-08-05 03:23:16 -07:00
Claude
71826732e3 slop 2025-08-05 12:01:27 +02:00
Jarred Sumner
dfe1a1848a Fix 2025-08-04 23:33:29 -07:00
Jarred Sumner
0612f459a4 Tweak crash handler for linux
Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-04 23:30:46 -07:00
Jarred Sumner
e3b2017e20 Tweak crash handler for linux
Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-04 23:30:34 -07:00
Jarred Sumner
1cbdb4c443 Set it up
Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-04 22:16:47 -07:00
Jarred Sumner
20bc928346 init
Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-04 21:58:16 -07:00
pfg
408fda7ad2 Continue emitting 'readable' events after pausing stdin (#17690)
Fixes #21189

`.pause()` should unref but it should still continue to emit `readable`
events (although it should not send `data` events)

also stdin.unref() should not pause input, it should only prevent stdin
from keeping the process alive.

DRAFT:

- [x] ~~this causes a bug where `process.stdin.on("readable", () => {});
process.stdin.pause()` will allow the process to exit when it
shouldn't.~~ fixed

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2025-08-04 21:04:08 -07:00
Jarred Sumner
7ad3049e70 Update CLAUDE.md 2025-08-04 20:37:15 -07:00
Ciro Spaciari
ed6f099e5e fix(tls) fix ciphers (#21545)
### What does this PR do?
Uses same ciphers than node.js for compatibility and do the same error
checking on empty ciphers
Fixes https://github.com/oven-sh/bun/issues/9425
Fixes https://github.com/oven-sh/bun/issues/21518
Fixes https://github.com/oven-sh/bun/issues/19859
Fixes https://github.com/oven-sh/bun/issues/18980

You can see more about redis ciphers here
https://redis.io/docs/latest/operate/rs/security/encryption/tls/ciphers/
this should fix redis related ciphers issues
### How did you verify your code works?
Tests

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2025-08-04 19:42:40 -07:00
Ciro Spaciari
258a2a2e3a fix(postgres) memory fix when connection fails sync (#21616)
### What does this PR do?
We should not call .deinit() after .toJS otherwise hasPendingActivity
will access invalid memory

### How did you verify your code works?
Test run it with debug build on macos or asan on and will catch it

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2025-08-04 19:41:20 -07:00
Jarred Sumner
4568258960 -t should not run {before,after}{Each,All} for scopes with no tests (#21602)
### What does this PR do?

Before:
```js
❯ bun test /Users/jarred/Code/bun/test/cli/test/test-filter-lifecycle.js -t "should run test"
bun test v1.2.20-canary.135 (1ac2391b)

test/cli/test/test-filter-lifecycle.js:

# Unhandled error between tests
-------------------------------
1 | // This test is intended to be able to run in Vitest and Jest.
2 | describe("top-level sibling", () => {
3 |   beforeAll(() => {
4 |     throw new Error("FAIL");
                              ^
error: FAIL
      at <anonymous> (test-filter-lifecycle.js:4:27)
      at test-filter-lifecycle.js:2:1
      at loadAndEvaluateModule (2:1)
-------------------------------

✗ top-level sibling > test
<parent beforeAll>
<beforeAll>

# Unhandled error between tests
-------------------------------
65 |     beforeEach(() => {
66 |       throw new Error("FAIL");
67 |     });
68 | 
69 |     afterEach(() => {
70 |       throw new Error("FAIL");
                                 ^
error: FAIL
      at <anonymous> (test-filter-lifecycle.js:70:29)
-------------------------------

<afterEach>
<parent afterEach>
<parent beforeEach>
<beforeEach>
<test 1>
✓ parent > should run > test [0.02ms]
<afterEach>
<parent afterEach>
<parent beforeEach>
<beforeEach>
<test 2>
✓ parent > should run > test 2 [0.02ms]

# Unhandled error between tests
-------------------------------
106 |       console.log("<beforeEach>");
107 |     });
108 | 
109 |     afterEach(() => {
110 |       if (++ran.afterEach > 2) {
111 |         throw new Error("FAIL 2");
                                      ^
error: FAIL 2
      at <anonymous> (test-filter-lifecycle.js:111:33)
-------------------------------


# Unhandled error between tests
-------------------------------
106 |       console.log("<beforeEach>");
107 |     });
108 | 
109 |     afterEach(() => {
110 |       if (++ran.afterEach > 2) {
111 |         throw new Error("FAIL 2");
                                      ^
error: FAIL 2
      at <anonymous> (test-filter-lifecycle.js:111:33)
-------------------------------

<afterAll>
<parent afterAll>

 2 pass
 3 filtered out
 1 fail
 4 errors
Ran 3 tests across 1 file. [93.00ms]
```

After:
```js
bun test <version> (<revision>)
<parent beforeAll>
<beforeAll>
<parent beforeEach>
<beforeEach>
<test 1>
<afterEach>
<parent afterEach>
<parent beforeEach>
<beforeEach>
<test 2>
<afterEach>
<parent afterEach>
<afterAll>
<parent afterAll>

test/cli/test/test-filter-lifecycle.js:
(pass) parent > should run > test
(pass) parent > should run > test 2

2 pass
4 filtered out
0 fail
Ran 2 tests across 1 file.
```

### How did you verify your code works?

There is a test

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2025-08-04 19:30:09 -07:00
Zack Radisic
dd27ad7716 Add edge deletion safety checks to DevServer and fix cases where it was caught (#21551)
### What does this PR do?

The DevSever's `IncrementalGraph` uses a data-oriented design memory
management style, storing data in lists and using indices instead of
pointers.

In conventional memory management, when we free a pointer and
accidentally use it will trip up asan. Obviously this doesn't apply when
using lists and indices, so this PR adds a check in debug & asan builds.
Everytime we free an `Edge` we better make sure that there are no more
dangling references to that spot.

This caught a case where we weren't setting `g.first_import[file_index]
= .none` when deleting a file's imports, causing a dangling reference
and out of bounds access.

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2025-08-04 19:21:28 -07:00
Jarred Sumner
d3d08eeb2d CI: disable --icf=safe in debug builds and asan builds 2025-08-04 18:34:47 -07:00
Jarred Sumner
47727bdbe3 CI: Add ENABLE_ZIG_ASAN option to enable/disable asan for zig specifically with a value that inherits from ENABLE_ASAN 2025-08-04 18:27:15 -07:00
Alistair Smith
be5c69df79 fix: main is not readonly in @types/node (#21612)
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2025-08-04 13:07:42 -07:00
Jarred Sumner
9785e37e10 Deflake some CI things (#21600) 2025-08-04 07:03:40 -07:00
Jarred Sumner
4494353abf Split up some of sys.zig into more files (#21603) 2025-08-04 07:02:06 -07:00
Jarred Sumner
fa1ad54257 Update CLAUDE.md 2025-08-04 04:07:25 -07:00
Jarred Sumner
a0687c06f8 Update CLAUDE.md 2025-08-04 04:06:10 -07:00
Jarred Sumner
15578df7fc Update CLAUDE.md 2025-08-04 04:01:24 -07:00
Jarred Sumner
b6d3768038 Use stopIfNecessary() instead of heap.{acquireAccess,releaseAccess} (#21598)
### What does this PR do?

dropAllLocks causes Thread::yield several times which means more system
calls which means more thread switches which means slower

### How did you verify your code works?
2025-08-04 00:45:33 -07:00
Jarred Sumner
1ac2391b20 Reduce idle CPU usage in long-running processes (#21579)
### What does this PR do?

Releasing heap access causes all the heap helper threads to wake up and
lock and then unlock futexes, but it's important to do that to ensure
finalizers run quickly.

That means releasing heap access is a balance between:
 1. CPU usage
 2. Memory usage

Not releasing heap access causes benchmarks like
https://github.com/oven-sh/bun/pull/14885 to regress due to finalizers
not being called quickly enough.

Releasing heap access too often causes high idle CPU usage.

 For the following code:
 ```
 setTimeout(() => {}, 10 * 1000)
 ```

command time -v when with defaultRemainingRunsUntilSkipReleaseAccess =
0:
>
>   Involuntary context switches: 605
>

command time -v when with defaultRemainingRunsUntilSkipReleaseAccess =
5:
>
>   Involuntary context switches: 350
>

command time -v when with defaultRemainingRunsUntilSkipReleaseAccess =
10:
>
>  Involuntary context switches: 241
>

 Also comapre the #14885 benchmark with different values.

 The idea here is if you entered JS "recently", running any
 finalizers that might've been waiting to be run is a good idea.
 But if you haven't, like if the process is just waiting on I/O
 then don't bother.

### How did you verify your code works?
2025-08-03 18:14:40 -07:00
github-actions[bot]
276eee74eb deps: update hdrhistogram to 0.11.8 (#21575)
## What does this PR do?

Updates hdrhistogram to version 0.11.8

Compare:
652d51bcc3...8dcce8f685

Auto-updated by [this
workflow](https://github.com/oven-sh/bun/actions/workflows/update-hdrhistogram.yml)

Co-authored-by: Jarred-Sumner <Jarred-Sumner@users.noreply.github.com>
2025-08-03 18:13:53 -07:00
github-actions[bot]
deaef1882b deps: update sqlite to 3.50.400 (#21577)
## What does this PR do?

Updates SQLite to version 3.50.400

Compare: https://sqlite.org/src/vdiff?from=3.50.3&to=3.50.400

Auto-updated by [this
workflow](https://github.com/oven-sh/bun/actions/workflows/update-sqlite3.yml)

Co-authored-by: Jarred-Sumner <Jarred-Sumner@users.noreply.github.com>
2025-08-03 18:12:54 -07:00
github-actions[bot]
711de8a667 deps: update libarchive to v3.8.1 (#21574)
## What does this PR do?

Updates libarchive to version v3.8.1

Compare:
7118f97c26...9525f90ca4

Auto-updated by [this
workflow](https://github.com/oven-sh/bun/actions/workflows/update-libarchive.yml)

Co-authored-by: RiskyMH <RiskyMH@users.noreply.github.com>
2025-08-02 23:30:07 -07:00
Jarred Sumner
a5af485354 Refactor h2_frame_parser to use GC-visited fields (#21573)
### What does this PR do?

Instead of holding a strong for the options object passed with the
handlers, we make each of the callbacks kept alive by the handlers and
it detaches once the detachFromJS function is called.

This should fix #21570, which looks like it was caused by wrapper
functions for AsyncLocalStorage getting collected prematurely.

fixes #21254
fixes #21553
fixes #21422

### How did you verify your code works?

Ran test/js/node/http2/node-http2.test.js
2025-08-02 20:38:49 -07:00
Michael H
73e737be56 fix .github/workflows/packages-ci.yml (#21563)
### What does this PR do?

use local bun-types instead of a canary one to ensure more up to date
data

### How did you verify your code works?
2025-08-02 01:18:03 -07:00
Jarred Sumner
68d322f05f Fix mimalloc memory usage regression (#21550)
### What does this PR do?

### How did you verify your code works?

---------

Co-authored-by: taylor.fish <contact@taylor.fish>
2025-08-01 23:38:34 -07:00
Jarred Sumner
39eccf89a8 Deflake sql.test.ts 2025-08-01 22:41:05 -07:00
Ciro Spaciari
a729a046bd update(roots_certs) update root certificates to NSS 3.114 (#21557)
### What does this PR do?
This is the certdata.txt[0] from NSS 3.114, released on 2025-07-22.

This is the version of NSS that will ship in Firefox 141.0 on
2025-07-22.

[0]
https://hg.mozilla.org/projects/nss/raw-file/NSS_3_114_RTM/lib/ckfw/builtins/certdata.txt
9e39b6d8-c531-4737-af1f-9c29fb93917b

### How did you verify your code works?
Compiles
2025-08-01 21:01:55 -07:00
robobun
9bb4a6af19 Optimize uSockets sweep timer to only run when connections exist (#21456)
## Summary

This PR optimizes the uSockets sweep timer to only run when there are
active connections that need timeout checking, rather than running
continuously even when no connections exist.

**Problem**: The sweep timer was running every 4 seconds
(LIBUS_TIMEOUT_GRANULARITY) regardless of whether there were any active
connections, causing unnecessary CPU usage when Bun applications are
idle.

**Solution**: Implement reference counting for active sockets so the
timer is only enabled when needed.

## Changes

- **Add sweep_timer_count field** to both C and Zig loop data structures
- **Implement helper functions** `us_internal_enable_sweep_timer()` and
`us_internal_disable_sweep_timer()`
- **Timer lifecycle management**:
  - Timer starts disabled when loop is initialized
  - Timer enables when first socket is linked (count 0→1)
  - Timer disables when last socket is unlinked (count 1→0)
- **Socket coverage**: Applied to both regular sockets and connecting
sockets that need timeout sweeping
- **Listen socket exclusion**: Listen sockets don't increment the
counter as they don't need timeout checking

## Files Modified

- `packages/bun-usockets/src/internal/loop_data.h` - Added
sweep_timer_count field
- `src/deps/uws/InternalLoopData.zig` - Updated Zig struct to match C
struct
- `packages/bun-usockets/src/loop.c` - Helper functions and
initialization
- `packages/bun-usockets/src/internal/internal.h` - Function
declarations
- `packages/bun-usockets/src/context.c` - Socket link/unlink
modifications

## Test Results

Verified the optimization works correctly:

1. ** No timer during idle**: 5-second idle test showed no sweep timer
activity
2. ** Timer activates with connections**: Timer enables when server
starts listening
3. ** Timer runs periodically**: Sweep timer callbacks occur every ~4
seconds when connections are active
4. ** Timer deactivates**: Timer disables when connections are closed

## Performance Impact

This change significantly reduces CPU usage for idle Bun applications
with no active HTTP connections by eliminating unnecessary timer
callbacks. The optimization maintains all existing timeout functionality
while only running the sweep when actually needed.

## Test plan

- [x] Verify no timer activity during idle periods
- [x] Verify timer enables when connections are created
- [x] Verify timer runs at expected intervals when active
- [x] Verify timer disables when connections are closed
- [x] Test with HTTP server scenarios
- [ ] Run existing HTTP server test suite to ensure no regressions

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-authored-by: Claude Bot <claude-bot@bun.sh>
Co-authored-by: Claude <noreply@anthropic.com>
2025-08-01 21:01:30 -07:00
Jarred Sumner
07ffde8a69 Add missing check for .write() on a data-backed blob (#21552)
### What does this PR do?

Add missing check for .write() on a data-backed blob

### How did you verify your code works?

There is a test

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: Alistair Smith <hi@alistair.sh>
2025-08-01 20:04:16 -07:00
pfg
bb67f2b345 Add clearAllMocks to mock. (#21555)
Fixes #21437, Fixes #18820
2025-08-01 19:30:51 -07:00
pfg
7c4c360431 Make getIfPropertyValueExistsImpl accept a slice (#21554)
Previously it accepted `property: anytype` but now it's `[]const u8`
because that was the only allowed value, so it makes it easier to see
what type it accepts in autocomplete.

Also updates the doc comment, switches it to use ZIG_EXPORT, and updates
the cppbind doc comment
2025-08-01 19:26:55 -07:00
Alistair Smith
4b39a9b07d off by one 2025-08-01 16:11:48 -07:00
Alistair Smith
d0edcc69ae Support TypeScript 5.9 (#21539)
### What does this PR do?

Fixes #21535

### How did you verify your code works?
2025-08-01 16:09:44 -07:00
pfg
0cf2b71ff1 expect.toHaveReturnedWith/toHaveLastReturnedWith/toHaveNthReturnedWith (#21363)
Fixes #10380

DRAFT: not reviewed

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2025-08-01 15:09:03 -07:00
pfg
40bff9fea8 Support functions in cppbind (#21439)
Fixes #21434: Makes sure 'bun install' is executed before running
2025-08-01 15:07:51 -07:00
Jarred Sumner
7726e5c670 Update node-zlib-brotli.mjs 2025-08-01 14:35:04 -07:00
pfg
7a31108019 Implement expectTypeOf (#21513)
Fixes #7569 

This adds expectTypeOf, but not the experimental `--typecheck` flag from
vitest. To use it, you need to typecheck manually with `bunx tsc
--noEmit` in addition to `bun test`

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2025-08-01 12:11:03 -07:00
Alistair Smith
dd68364630 Remove outdated examples (#21540)
### What does this PR do?

### How did you verify your code works?
2025-08-01 11:11:47 -07:00
Jack W.
7d4f6efe7a This does NOT return undefined (#21542)
You updated the types but not the comment in Bun 1.1.14

### What does this PR do?

Removes the sentence "This returns `undefined`." in the SQLite
Statement.run function

### How did you verify your code works?

It says this on the website

<img width="787" height="90" alt="Screenshot 2025-08-01 at 2 05 09 PM"
src="https://github.com/user-attachments/assets/63259ab3-b5fd-4392-bf69-8e297f4922f2"
/>
2025-08-01 11:11:37 -07:00
robobun
7cdcd34f58 Add Blob support for WebSocket binaryType (#21471) 2025-08-01 02:05:56 -07:00
Meghan Denny
2a6d018d73 node-fallbacks:buffer: fix numberIsNaN ReferenceError (#21527)
fixes https://github.com/oven-sh/bun/issues/21522
2025-07-31 22:07:17 -07:00
Alistair Smith
8efe7945eb More strictly type bun:sqlite transaction functions (#21495)
### What does this PR do?

Fixes #21479

### How did you verify your code works?

bun-types test updated
2025-07-31 22:06:55 -07:00
robobun
5bdcf339d7 Document static routes vs file routes in HTTP server (#21506)
## Summary
- Add comprehensive documentation distinguishing static routes from file
routes in `Bun.serve()`
- Document caching behavior differences: ETag vs Last-Modified
- Explain performance characteristics and error handling differences
- Provide clear use case recommendations

## Changes
- **File Responses vs Static Responses** section explaining the two
approaches
- **HTTP Caching Behavior** section detailing ETag and Last-Modified
support
- **Status Code Handling** section covering automatic 204/304 responses
- Code examples showing both patterns with clear explanations

## Key Documentation Points
- Static routes (`new Response(await file.bytes())`) buffer content in
memory at startup
- File routes (`new Response(Bun.file(path))`) read from filesystem per
request
- Static routes use ETags for caching, file routes use Last-Modified
headers
- File routes handle 404s automatically, static routes cause startup
errors for missing files
- Both support HTTP caching standards but with different validation
strategies

🤖 Generated with [Claude Code](https://claude.ai/code)

---------

Co-authored-by: Claude Bot <claude-bot@bun.sh>
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2025-07-31 22:06:35 -07:00
Ciro Spaciari
03afe6ef28 fix(postgres) regression (#21466)
### What does this PR do?
Fix: https://github.com/oven-sh/bun/issues/21351

Relevant changes:
Fix advance to properly cleanup success and failed queries that could be
still be in the queue
Always ref before executing
Use stronger atomics for ref/deref and hasPendingActivity
Fallback when thisValue is freed/null/zero and check if vm is being
shutdown
The bug in --hot in `resolveRopeIfNeeded` Issue is not meant to be fixed
in this PR this is a fix for the postgres regression
Added assertions so this bug is easier to catch on CI
### How did you verify your code works?
Test added

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2025-07-31 16:26:35 -07:00
pfg
ce5152dd7a Readablestreamdefaultcontroller oxlint fix (#21525) 2025-07-31 15:05:42 -07:00
Jarred Sumner
5c65c18e72 Delete incorrect assertion in ComptimeStringMap (#21504)
### What does this PR do?

Resolves
```js
Bun v1.2.13 ([64ed68c](64ed68c9e0)) on windows x86_64 [TestCommand]

panic: ComptimeStringMap.fromJS: input is not a string

[comptime_string_map.zig:268](64ed68c9e0/src/comptime_string_map.zig (L268)): getWithEql
[Response.zig:682](64ed68c9e0/src/bun.js/webcore/Response.zig (L682)): init
[Request.zig:679](64ed68c9e0/src/bun.js/webcore/Request.zig (L679)): constructInto
[ZigGeneratedClasses.cpp:37976](64ed68c9e0/C:/buildkite-agent/builds/EC2AMAZ-Q4V5GV4/bun/bun/build/release/codegen/ZigGeneratedClasses.cpp#L37976): WebCore::JSRequestConstructor::construct
2 unknown/js code
llint_entry

Features: tsconfig, Bun.stdout, dotenv, jsc
```

### How did you verify your code works?

There is a test.
2025-07-31 00:56:50 -07:00
pfg
100ab8c503 Fix "test failing but passed" arrow pointing to the wrong test (#21502)
Before:

```
      failing-test-passes.fixture.ts:
        ^ this test is marked as failing but it passed. Remove \`.failing\` if tested behavior now works
      (fail) This should fail but it doesnt [0.24ms]
        ^ this test is marked as failing but it passed. Remove \`.failing\` if tested behavior now works
      (fail) This should fail but it doesnt (async) [0.23ms]
```

After:

```
      failing-test-passes.fixture.ts:
      (fail) This should fail but it doesnt [0.24ms]
        ^ this test is marked as failing but it passed. Remove \`.failing\` if tested behavior now works
      (fail) This should fail but it doesnt (async) [0.23ms]
        ^ this test is marked as failing but it passed. Remove \`.failing\` if tested behavior now works
```

Adds a snapshot test

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2025-07-30 23:50:06 -07:00
Jarred Sumner
a51af710c0 Fixes "Stream is already ended" error when cancelling a request (#21481)
### What does this PR do?

if you spam the refresh button in `next dev`, we print this error:
```
 ⨯ Error: Stream is already ended
    at writeHead (null)
    at <anonymous> (null)
    at <anonymous> (null)
    at <anonymous> (null)
    at <anonymous> (null)
    at <anonymous> (null)
    at <anonymous> (null)
    at <anonymous> (null)
    at processTicksAndRejections (null) {
  digest: '2259044225',
  code: 'ERR_STREAM_ALREADY_FINISHED',
  toString: [Function: toString]
}
 ⨯ Error: failed to pipe response
    at processTicksAndRejections (unknown:7:39) {
  [cause]: Error: Stream is already ended
      at writeHead (null)
      at <anonymous> (null)
      at <anonymous> (null)
      at <anonymous> (null)
      at <anonymous> (null)
      at <anonymous> (null)
      at <anonymous> (null)
      at <anonymous> (null)
      at processTicksAndRejections (null) {
    digest: '2259044225',
    code: 'ERR_STREAM_ALREADY_FINISHED',
    toString: [Function: toString]
  }
}
 ⨯ Error: failed to pipe response
    at processTicksAndRejections (unknown:7:39) {
  page: '/',
  [cause]: Error: Stream is already ended
      at writeHead (null)
      at <anonymous> (null)
      at <anonymous> (null)
      at <anonymous> (null)
      at <anonymous> (null)
      at <anonymous> (null)
      at <anonymous> (null)
      at <anonymous> (null)
      at processTicksAndRejections (null) {
    digest: '2259044225',
    code: 'ERR_STREAM_ALREADY_FINISHED',
    toString: [Function: toString]
  }
}
```

If the socket is already closed when writeHead is called, we're supposed
to silently ignore it instead of throwing an error . The close event is
supposed to be emitted on the next tick. Now, I think there are also
cases where we do not emit the close event which is similarly bad.

### How did you verify your code works?

Need to go through the node http server tests and see if any new ones
pass. Also maybe some will fail on this PR, let's see.
2025-07-30 23:49:42 -07:00
Zack Radisic
5ca1580427 Fix assertion failure on Windows in resolver (#21510)
### What does this PR do?

We had `bun.strings.assertIsValidWindowsPath(...)` in the resolver, but
we can't do this because the path may come from the user. Instead, let
our error handling code handle it.

Also fixes #21065

---------

Co-authored-by: Jarred Sumner <jarred@jarredsumner.com>
2025-07-30 23:33:09 -07:00
Meghan Denny
b34bab745b test: handle docker exiting with a signal (#21512) 2025-07-30 23:11:17 -07:00
fuyou
6034c2f94b fix(mock): add support for rejected values in JSMockFunction (#21489) 2025-07-30 21:45:38 -07:00
Zack Radisic
2b5a59cae1 Fix lldb pretty printing for bun.collections.MultiArrayList(...) (#21499)
### What does this PR do?

We have our own `MultiArrayList(...)` in
`src/collections/multi_array_list.zig` (is this really necessary?) and
this does not work with the existing lldb pretty printing functions
because they are under a different symbol name:
`collections.multi_array_list.MultiArrayList*` instead of
`multi_array_list.MultiArrayList*`
2025-07-30 16:37:21 -07:00
taylor.fish
3bcf93ddd6 Resync MultiArrayList with Zig standard library (#21500)
(For internal tracking: fixes STAB-912)
2025-07-30 16:37:07 -07:00
Dylan Conway
53b24ace79 sync webkit (#21436)
### What does this PR do?

<!-- **Please explain what your changes do**, example: -->

<!--

This adds a new flag --bail to bun test. When set, it will stop running
tests after the first failure. This is useful for CI environments where
you want to fail fast.

-->

### How did you verify your code works?
ran fuzzy-wuzzy.test.ts
<!-- **For code changes, please include automated tests**. Feel free to
uncomment the line below -->

<!-- I wrote automated tests -->

<!-- If JavaScript/TypeScript modules or builtins changed:

- [ ] I included a test for the new code, or existing tests cover it
- [ ] I ran my tests locally and they pass (`bun-debug test
test-file-name.test`)

-->

<!-- If Zig files changed:

- [ ] I checked the lifetime of memory allocated to verify it's (1)
freed and (2) only freed when it should be
- [ ] I included a test for the new code, or an existing test covers it
- [ ] JSValue used outside of the stack is either wrapped in a
JSC.Strong or is JSValueProtect'ed
- [ ] I wrote TypeScript/JavaScript tests and they pass locally
(`bun-debug test test-file-name.test`)
-->

<!-- If new methods, getters, or setters were added to a publicly
exposed class:

- [ ] I added TypeScript types for the new methods, getters, or setters
-->

<!-- If dependencies in tests changed:

- [ ] I made sure that specific versions of dependencies are used
instead of ranged or tagged versions
-->

<!-- If a new builtin ESM/CJS module was added:

- [ ] I updated Aliases in `module_loader.zig` to include the new module
- [ ] I added a test that imports the module
- [ ] I added a test that require() the module
-->
2025-07-30 15:49:15 -07:00
taylor.fish
a1f44caa87 Simplify mimalloc alignment check (#21497)
This slightly reduces memory use.

Maximum memory use of `bun test html-rewriter`, averaged across 100
iterations:

* 101975 kB without this change
* 101634 kB with this change

I also tried changing the code to always use the aligned allocation
functions, but this slightly increased memory use, to 102160 kB.

(For internal tracking: fixes ENG-19866)
2025-07-30 14:30:47 -07:00
taylor.fish
3de884f2c9 Add helper type to detect unsynchronized concurrent accesses of shared data (#21476)
Add a helper type to help detect race conditions. There's no performance
or memory use penalty in release builds.

Actually adding the type to various places will be left for future PRs.

(For internal tracking: fixes STAB-852)
2025-07-30 00:46:42 -07:00
Kai Tamkun
a6162295c5 Replace allocator isNull() check with an assertion in String.toThreadSafeSlice (#21474)
### What does this PR do?

Replaces an if statement with an assertion that the condition is false.
The allocator in question should never be null.

### How did you verify your code works?
2025-07-30 00:10:03 -07:00
Jarred Sumner
80c46b1607 Disable findSourceMap when --enable-source-maps is not passed (#21478)
### What does this PR do?

Fixes the error printed:
```js
❯ bun --bun dev
$ next dev --turbopack
   ▲ Next.js 15.4.5 (Turbopack)
   - Local:        http://localhost:3000
   - Network:      http://192.168.1.250:3000

 ✓ Starting...
 ✓ Ready in 637ms
 ○ Compiling / ...
 ✓ Compiled / in 1280ms
/private/tmp/empty/my-app/.next/server/chunks/ssr/[root-of-the-server]__012ba519._.js: Invalid source map. Only conformant source maps can be used to filter stack frames. Cause: TypeError: payload is not an Object. (evaluating '"sections" in payload')
/private/tmp/empty/my-app/.next/server/chunks/ssr/[root-of-the-server]__93bf7db5._.js: Invalid source map. Only conformant source maps can be used to filter stack frames. Cause: TypeError: payload is not an Object. (evaluating '"sections" in payload')
 GET / 200 in 1416ms
^C^[[A
```

### How did you verify your code works?
2025-07-30 00:09:42 -07:00
Meghan Denny
26cbcd21c1 test: split napi tests into separate files (#21475)
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2025-07-29 22:33:19 -07:00
Zack Radisic
3d6dda6901 Add asan checks to HiveArray (#21449) 2025-07-29 19:35:46 -07:00
Jarred Sumner
93f92658b3 Try mimalloc v3 (#17378)
(For internal tracking: fixes ENG-19852)

---------

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: Jarred-Sumner <709451+Jarred-Sumner@users.noreply.github.com>
Co-authored-by: Kai Tamkun <kai@tamkun.io>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: taylor.fish <contact@taylor.fish>
2025-07-29 18:07:15 -07:00
pfg
f8c2dac836 Fix docs in test.md (#21472) 2025-07-29 17:42:11 -07:00
Ciro Spaciari
4bbe32fff8 fix(net/http2) fix socket internal timeout and owner_symbol check, fix padding support in http2 (#21263)
### What does this PR do?

<!-- **Please explain what your changes do**, example: -->

<!--

This adds a new flag --bail to bun test. When set, it will stop running
tests after the first failure. This is useful for CI environments where
you want to fail fast.

-->

- [ ] Documentation or TypeScript types (it's okay to leave the rest
blank in this case)
- [x] Code changes

### How did you verify your code works?
Tests added for padding support
Timeout of socket is being fired earlier due to backpressure or lack of
precision in usockets timers (now matchs node.js behavior).
Added check for owner_symbol so the error showed in
https://github.com/oven-sh/bun/issues/21055 is handled

<!-- **For code changes, please include automated tests**. Feel free to
uncomment the line below -->

<!-- I wrote automated tests -->

<!-- If JavaScript/TypeScript modules or builtins changed:

- [ ] I included a test for the new code, or existing tests cover it
- [ ] I ran my tests locally and they pass (`bun-debug test
test-file-name.test`)

-->

<!-- If Zig files changed:

- [ ] I checked the lifetime of memory allocated to verify it's (1)
freed and (2) only freed when it should be
- [ ] I included a test for the new code, or an existing test covers it
- [ ] JSValue used outside of the stack is either wrapped in a
JSC.Strong or is JSValueProtect'ed
- [ ] I wrote TypeScript/JavaScript tests and they pass locally
(`bun-debug test test-file-name.test`)
-->

<!-- If new methods, getters, or setters were added to a publicly
exposed class:

- [ ] I added TypeScript types for the new methods, getters, or setters
-->

<!-- If dependencies in tests changed:

- [ ] I made sure that specific versions of dependencies are used
instead of ranged or tagged versions
-->

<!-- If a new builtin ESM/CJS module was added:

- [ ] I updated Aliases in `module_loader.zig` to include the new module
- [ ] I added a test that imports the module
- [ ] I added a test that require() the module
-->

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2025-07-29 17:20:16 -07:00
Jarred Sumner
60c735a11d Ensure we handle aborted requests correctly in Rendering API (#21398)
### What does this PR do?

We have to use the existing code for handling aborted requests instead
of immediately calling deinit.

Also made the underlying uws.Response an optional pointer to mark when
the request has already been aborted to make it clear it's no longer
accessible.

### How did you verify your code works?

This needs a test

---------

Co-authored-by: Zack Radisic <56137411+zackradisic@users.noreply.github.com>
2025-07-29 13:29:29 -07:00
Jarred Sumner
003d13ec27 Introduce yarn.lock -> bun.lock{b} migrator (#16166)
### What does this PR do?

fixes #6409

This PR implements `bun install` automatic migration from yarn.lock
files to bun.lock, preserving versions exactly. The migration happens
automatically when:

1. A project has a `yarn.lock` file
2. No `bun.lock` or `bun.lockb` file exists  
3. User runs `bun install`

### Current Status:  Complete and Working

The yarn.lock migration feature is **fully functional and
comprehensively tested**. All dependency types are supported:

-  Regular npm dependencies (`package@^1.0.0`)
-  Git dependencies (`git+https://github.com/user/repo.git`,
`github:user/repo`)
-  NPM alias dependencies (`alias@npm:package@version`)
-  File dependencies (`file:./path`)
-  Remote tarball URLs (`https://registry.npmjs.org/package.tgz`)
-  Local tarball files (`file:package.tgz`)

### Test Results

```bash
$ bun bd test test/cli/install/migration/yarn-lock-migration.test.ts
 4 pass, 0 fail
- yarn-lock-mkdirp (basic npm dependency)
- yarn-lock-mkdirp-no-resolved (npm dependency without resolved field)
- yarn-lock-mkdirp-file-dep (file dependency)
- yarn-stuff (all complex dependency types: git, npm aliases, file, remote tarballs)
```

### How did you verify your code works?

1. **Comprehensive test suite**: Added 4 test cases covering all
dependency types
2. **Version preservation**: Verified that package versions are
preserved exactly during migration
3. **Real-world scenarios**: Tested with complex yarn.lock files
containing git deps, npm aliases, file deps, and remote tarballs
4. **Migration logging**: Confirms migration with log message `[X.XXms]
migrated lockfile from yarn.lock`

### Key Implementation Details

- **Core parser**: `src/install/yarn.zig` handles all yarn.lock parsing
and dependency type resolution
- **Integration**: Migration is built into existing lockfile loading
infrastructure
- **Performance**: Migration typically completes in ~1ms for most
projects
- **Compatibility**: Preserves exact dependency versions and resolution
behavior

The implementation correctly handles edge cases like npm aliases, git
dependencies with commits, file dependencies with transitive deps, and
remote tarballs.

---------

Co-authored-by: Jarred-Sumner <709451+Jarred-Sumner@users.noreply.github.com>
Co-authored-by: Claude Bot <claude-bot@bun.sh>
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: RiskyMH <git@riskymh.dev>
Co-authored-by: RiskyMH <56214343+RiskyMH@users.noreply.github.com>
2025-07-29 13:07:47 -07:00
Jarred Sumner
245abb92fb Cleanup some code from recent PRs (#21451)
### What does this PR do?

Remove some duplicate code

### How did you verify your code works?

Ran the tests

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2025-07-28 23:35:46 -07:00
robobun
066a25ac40 Fix crash in Response.redirect with invalid arguments (#21440)
## Summary
- Fix crash in `Response.redirect()` when called with invalid arguments
like `Response.redirect(400, "a")`
- Add proper status code validation per Web API specification (301, 302,
303, 307, 308)
- Add comprehensive tests to prevent regression and ensure spec
compliance

## Issue
When `Response.redirect()` is called with invalid arguments (e.g.,
`Response.redirect(400, "a")`), the code crashes with a panic due to an
assertion failure in `fastGet()`. The second argument is passed to
`Response.Init.init()` which attempts to call `fastGet()` on non-object
values, triggering `bun.assert(this.isObject())` to fail.

Additionally, the original implementation didn't properly validate
redirect status codes according to the Web API specification.

## Fix
Enhanced the `constructRedirect()` function with:

1. **Proper status code validation**: Only allows valid redirect status
codes (301, 302, 303, 307, 308) as specified by the MDN Web API
documentation
2. **Crash prevention**: Only processes object init values to prevent
`fastGet()` crashes with non-object values
3. **Consistent behavior**: Throws `RangeError` for invalid status codes
in both number and object forms

## Changes
- **`src/bun.js/webcore/Response.zig`**: Enhanced `constructRedirect()`
with validation logic
- **`test/js/web/fetch/response.test.ts`**: Added comprehensive tests
for crash prevention and status validation
- **`test/js/web/fetch/fetch.test.ts`**: Updated existing test to use
valid redirect status (307 instead of 408)

## Test Plan
- [x] Added test that reproduces the original crash scenario - now
passes without crashing
- [x] Added tests for proper status code validation (valid codes pass,
invalid codes throw RangeError)
- [x] Verified existing Response.redirect tests still pass
- [x] Confirmed Web API compliance with MDN specification
- [x] Tested various edge cases: `Response.redirect(400, "a")`,
`Response.redirect("url", 400)`, etc.

## Behavior Changes
- **Invalid status codes now throw RangeError** (spec compliant
behavior)
- **Non-object init values are safely ignored** (no more crashes)
- **Maintains backward compatibility** for valid use cases

Per [MDN Web API
specification](https://developer.mozilla.org/en-US/docs/Web/API/Response/redirect_static),
Response.redirect() should only accept status codes 301, 302, 303, 307,
or 308.

Fixes https://github.com/oven-sh/bun/issues/18414

🤖 Generated with [Claude Code](https://claude.ai/code)

---------

Co-authored-by: Claude Bot <claude-bot@bun.sh>
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: Jarred Sumner <jarred@jarredsumner.com>
2025-07-28 21:16:47 -07:00
its-me-mhd
ab88317846 fix(install): Fix PATH mangling in Windows install.ps1 (#21446)
The install script was incorrectly setting $env:PATH by assigning an
array directly, which PowerShell converts to a space-separated string
instead of the required semicolon-separated format.

This caused the Windows PATH environment variable to be malformed,
making installed programs inaccessible.

Fixes #16811

### What does this PR do?

Fixes a bug in the Windows PowerShell install script where `$env:PATH`
was being set incorrectly, causing the PATH environment variable to be
malformed.

**The Problem:**
- The script assigns an array directly to `$env:PATH`
- PowerShell converts this to a space-separated string instead of
semicolon-separated
- This breaks the Windows PATH, making installed programs inaccessible

**The Fix:**
- Changed `$env:PATH = $Path;` to `$env:PATH = $Path -join ';'`
- Now properly creates semicolon-separated PATH entries as required by
Windows

### How did you verify your code works?

 **Tested the bug reproduction:**
```powershell
$Path = @('C:\Windows', 'C:\Windows\System32', 'C:\test')
$env:PATH = $Path  # WRONG: Results in "C:\Windows C:\Windows\System32 C:\test"
2025-07-28 20:23:34 -07:00
Jarred Sumner
e7373bbf32 when CI scripts/build.mjs is killed, also kill the processes it started (#21445)
### What does this PR do?

reduce number of zombie build processes that can happen in CI or when
building locally

### How did you verify your code works?
2025-07-28 19:54:14 -07:00
Meghan Denny
4687cc4f5e ci: only run the remap server when in CI (#21444) 2025-07-28 19:24:26 -07:00
Jarred Sumner
a5ff729665 Delete agent.mjs 2025-07-28 18:37:07 -07:00
Jarred Sumner
62e8a7fb01 Update pull_request_template.md 2025-07-28 18:28:54 -07:00
robobun
220807f3dc Add If-None-Match support to StaticRoute with automatic ETag generation (#21424)
## Summary

Fixes https://github.com/oven-sh/bun/issues/19198

This implements RFC 9110 Section 13.1.2 If-None-Match conditional
request support for static routes in Bun.serve().

**Key Features:**
- Automatic ETag generation for static content based on content hash
- If-None-Match header evaluation with weak entity tag comparison
- 304 Not Modified responses for cache efficiency
- Standards-compliant handling of wildcards (*), multiple ETags, and
weak ETags (W/)
- Method-specific application (GET/HEAD only) with proper 405 responses
for other methods

## Implementation Details

- ETags are generated using `bun.hash()` and formatted as strong ETags
(e.g., "abc123")
- Preserves existing ETag headers from Response objects
- Uses weak comparison semantics as defined in RFC 9110 Section 8.8.3.2
- Handles comma-separated ETag lists and malformed headers gracefully
- Only applies to GET/HEAD requests with 200 status codes

## Files Changed

- `src/bun.js/api/server/StaticRoute.zig` - Core implementation (~100
lines)
- `test/js/bun/http/serve-if-none-match.test.ts` - Comprehensive test
suite (17 tests)

## Test Results

-  All 17 new If-None-Match tests pass
-  All 34 existing static route tests pass (no regressions)
-  Debug build compiles successfully

## Test plan

- [ ] Run existing HTTP server tests to ensure no regressions
- [ ] Test ETag generation for various content types
- [ ] Verify 304 responses reduce bandwidth in real scenarios
- [ ] Test edge cases like malformed If-None-Match headers

🤖 Generated with [Claude Code](https://claude.ai/code)

---------

Co-authored-by: Claude Bot <claude-bot@bun.sh>
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2025-07-28 16:22:21 -07:00
robobun
562f82d3f8 Fix Windows watcher index out of bounds crash when events exceed buffer size (#21400)
## Summary
Fixes a crash in the Windows file watcher that occurred when the number
of file system events exceeded the fixed `watch_events` buffer size
(128).

## Problem
The crash manifested as:
```
index out of bounds: index 128, len 128
```

This happened when:
1. More than 128 file system events were generated in a single watch
cycle
2. The code tried to access `this.watch_events[128]` on an array of
length 128 (valid indices: 0-127)
3. Later, `std.sort.pdq()` would operate on an invalid array slice

## Solution
Implemented a hybrid approach that preserves the original behavior while
handling overflow gracefully:

- **Fixed array for common case**: Uses the existing 128-element array
when possible for optimal performance
- **Dynamic allocation for overflow**: Switches to `ArrayList` only when
needed
- **Single-batch processing**: All events are still processed together
in one batch, preserving event coalescing
- **Graceful fallback**: Handles allocation failures with appropriate
fallbacks

## Benefits
-  **Fixes the crash** while maintaining existing performance
characteristics
-  **Preserves event coalescing** - events for the same file still get
properly merged
-  **Single consolidated callback** instead of multiple partial updates
-  **Memory efficient** - no overhead for normal cases (≤128 events)
-  **Backward compatible** - no API changes

## Test Plan
- [x] Compiles successfully with `bun run zig:check-windows`
- [x] Preserves existing behavior for common case (≤128 events)
- [x] Handles overflow case gracefully with dynamic allocation

🤖 Generated with [Claude Code](https://claude.ai/code)

---------

Co-authored-by: Claude Bot <claude-bot@bun.sh>
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: Jarred Sumner <jarred@bun.sh>
Co-authored-by: Jarred Sumner <jarred@jarredsumner.com>
2025-07-28 15:43:54 -07:00
Dylan Conway
4580e11fc3 [PKG-517] fix(install): --linker=isolated should spawn scripts on the main thread (#21425)
### What does this PR do?
Fixes thread safety issues due to file poll code being not thread safe.
<!-- **Please explain what your changes do**, example: -->

<!--

This adds a new flag --bail to bun test. When set, it will stop running
tests after the first failure. This is useful for CI environments where
you want to fail fast.

-->

### How did you verify your code works?
Added tests for lifecycle scripts. The tests are unlikely to reproduce
the bug, but we'll know if it actually fixes the issue if
`test/package.json` doesn't show in flaky tests anymore.
<!-- **For code changes, please include automated tests**. Feel free to
uncomment the line below -->

<!-- I wrote automated tests -->

<!-- If JavaScript/TypeScript modules or builtins changed:

- [ ] I included a test for the new code, or existing tests cover it
- [ ] I ran my tests locally and they pass (`bun-debug test
test-file-name.test`)

-->

<!-- If Zig files changed:

- [ ] I checked the lifetime of memory allocated to verify it's (1)
freed and (2) only freed when it should be
- [ ] I included a test for the new code, or an existing test covers it
- [ ] JSValue used outside of the stack is either wrapped in a
JSC.Strong or is JSValueProtect'ed
- [ ] I wrote TypeScript/JavaScript tests and they pass locally
(`bun-debug test test-file-name.test`)
-->

<!-- If new methods, getters, or setters were added to a publicly
exposed class:

- [ ] I added TypeScript types for the new methods, getters, or setters
-->

<!-- If dependencies in tests changed:

- [ ] I made sure that specific versions of dependencies are used
instead of ranged or tagged versions
-->

<!-- If a new builtin ESM/CJS module was added:

- [ ] I updated Aliases in `module_loader.zig` to include the new module
- [ ] I added a test that imports the module
- [ ] I added a test that require() the module
-->

---------

Co-authored-by: taylor.fish <contact@taylor.fish>
2025-07-28 12:29:47 -07:00
CountBleck
2956281845 Support WebAssembly.{instantiate,compile}Streaming() (#20503)
### What does this PR do?

<!-- **Please explain what your changes do** -->

This PR should fix #14219 and implement
`WebAssembly.instantiateStreaming()` and
`WebAssembly.compileStreaming()`.

This is a mixture of WebKit's implementation (using a helper,
`handleResponseOnStreamingAction`, also containing a fast-path for
blobs) and some of Node.js's validation (error messages) and its
builtin-based strategy to consume chunks from streams.

`src/bun.js/bindings/GlobalObject.zig` has a helper function
(`getBodyStreamOrBytesForWasmStreaming`), called by C++, to validate the
response (like
[Node.js](214e4db60e/lib/internal/wasm_web_api.js)
does) and to extract the data from the response, either as a slice/span
(if we can get the data synchronously), or as a `ReadableStream` body
(if the data is still pending or if it is a file/S3 `Blob`).

In C++, `handleResponseOnStreamingAction` is called by
`compileStreaming` and `instantiateStreaming` on the
`JSC::GlobalObjectMethodTable`, just like in
[WebKit](97ee3c598a/Source/WebCore/bindings/js/JSDOMGlobalObject.cpp (L517)).
It calls the aforementioned Zig helper for validation and getting the
response data. The data is then fed into `JSC::Wasm::StreamingCompiler`.

If the data is received as a `ReadableStream`, then we call a JS builtin
in `WasmStreaming.ts` to iterate over each chunk of the stream, like
[Node.js](214e4db60e/lib/internal/wasm_web_api.js (L50-L52))
does. The `JSC::Wasm::StreamingCompiler` is passed into JS through a new
wrapper object, `WebCore::WasmStreamingCompiler`, like
[Node.js](214e4db60e/src/node_wasm_web_api.h)
does. It has `addBytes`, `finalize`, `error`, and (unused) `cancel`
methods to mirror the underlying JSC class.

(If there's a simpler way to do this, please let me know...that would be
very much appreciated)

- [x] Code changes

### How did you verify your code works?

<!-- **For code changes, please include automated tests**. Feel free to
uncomment the line below -->

I wrote automated tests (`test/js/web/fetch/wasm-streaming.test`).

<!-- If JavaScript/TypeScript modules or builtins changed: -->

- [x] I included a test for the new code, or existing tests cover it
- [x] I ran my tests locally and they pass (`bun-debug test
test/js/web/fetch/wasm-streaming.test`)

<!-- If Zig files changed: -->

- [x] I checked the lifetime of memory allocated to verify it's (1)
freed and (2) only freed when it should be (NOTE: consumed `AnyBlob`
bodies are freed, and all other allocations are in C++ and either GCed
or ref-counted)
- [x] I included a test for the new code, or an existing test covers it
(NOTE: via JS/TS unit test)
- [x] JSValue used outside of the stack is either wrapped in a
JSC.Strong or is JSValueProtect'ed (NOTE: N/A, JSValue never used
outside the stack)
- [x] I wrote TypeScript/JavaScript tests and they pass locally
(`bun-debug test test/js/web/fetch/wasm-streaming.test`)

---------

Co-authored-by: graphite-app[bot] <96075541+graphite-app[bot]@users.noreply.github.com>
2025-07-28 11:59:45 -07:00
494 changed files with 67101 additions and 44659 deletions

View File

@@ -1,78 +0,0 @@
import { spawnSync } from "node:child_process";
import { readFileSync, existsSync } from "node:fs";
import { parseArgs } from "node:util";
const { positionals, values } = parseArgs({
allowPositionals: true,
options: {
help: {
type: "boolean",
short: "h",
default: false,
},
interactive: {
type: "boolean",
short: "i",
default: false,
},
},
});
if (values.help || positionals.length === 0) {
console.log("Usage: node agent.mjs <prompt_name> [extra_args...]");
console.log("Example: node agent.mjs triage fix bug in authentication");
console.log("Options:");
console.log(" -h, --help Show this help message");
console.log(" -i, --interactive Run in interactive mode");
process.exit(0);
}
const promptName = positionals[0].toUpperCase();
const promptFile = `.agent/${promptName}.md`;
const extraArgs = positionals.slice(1);
if (!existsSync(promptFile)) {
console.error(`Error: Prompt file "${promptFile}" not found`);
console.error(`Available prompts should be named like: .agent/triage.md, .agent/debug.md, etc.`);
process.exit(1);
}
try {
let prompt = readFileSync(promptFile, "utf-8");
const githubEnvs = Object.entries(process.env)
.filter(([key]) => key.startsWith("GITHUB_"))
.sort(([a], [b]) => a.localeCompare(b));
if (githubEnvs.length > 0) {
const githubContext = `## GitHub Environment\n\n${githubEnvs
.map(([key, value]) => `**${key}**: \`${value}\``)
.join("\n")}\n\n---\n\n`;
prompt = githubContext + prompt;
}
if (extraArgs.length > 0) {
const extraArgsContext = `\n\n## Additional Arguments\n\n${extraArgs.join(" ")}\n\n---\n\n`;
prompt = prompt + extraArgsContext;
}
const claudeArgs = [prompt, "--allowedTools=Edit,Write,Replace,Search", "--output-format=json"];
if (!values.interactive) {
claudeArgs.unshift("--print");
}
const { status, error } = spawnSync("claude", claudeArgs, {
stdio: "inherit",
encoding: "utf-8",
});
if (error) {
console.error("Error running claude:", error);
process.exit(1);
}
process.exit(status || 0);
} catch (error) {
console.error(`Error reading prompt file "${promptFile}":`, error);
process.exit(1);
}

View File

@@ -1,50 +1,3 @@
### What does this PR do?
<!-- **Please explain what your changes do**, example: -->
<!--
This adds a new flag --bail to bun test. When set, it will stop running tests after the first failure. This is useful for CI environments where you want to fail fast.
-->
- [ ] Documentation or TypeScript types (it's okay to leave the rest blank in this case)
- [ ] Code changes
### How did you verify your code works?
<!-- **For code changes, please include automated tests**. Feel free to uncomment the line below -->
<!-- I wrote automated tests -->
<!-- If JavaScript/TypeScript modules or builtins changed:
- [ ] I included a test for the new code, or existing tests cover it
- [ ] I ran my tests locally and they pass (`bun-debug test test-file-name.test`)
-->
<!-- If Zig files changed:
- [ ] I checked the lifetime of memory allocated to verify it's (1) freed and (2) only freed when it should be
- [ ] I included a test for the new code, or an existing test covers it
- [ ] JSValue used outside of the stack is either wrapped in a JSC.Strong or is JSValueProtect'ed
- [ ] I wrote TypeScript/JavaScript tests and they pass locally (`bun-debug test test-file-name.test`)
-->
<!-- If new methods, getters, or setters were added to a publicly exposed class:
- [ ] I added TypeScript types for the new methods, getters, or setters
-->
<!-- If dependencies in tests changed:
- [ ] I made sure that specific versions of dependencies are used instead of ranged or tagged versions
-->
<!-- If a new builtin ESM/CJS module was added:
- [ ] I updated Aliases in `module_loader.zig` to include the new module
- [ ] I added a test that imports the module
- [ ] I added a test that require() the module
-->

4
.gitignore vendored
View File

@@ -184,4 +184,6 @@ codegen-for-zig-team.tar.gz
*.sock
scratch*.{js,ts,tsx,cjs,mjs}
*.bun-build
*.bun-build
scripts/lldb-inline

View File

@@ -168,5 +168,5 @@
"WebKit/WebInspectorUI": true,
},
"git.detectSubmodules": false,
// "bun.test.customScript": "./build/debug/bun-debug test"
"bun.test.customScript": "./build/debug/bun-debug test"
}

View File

@@ -6,7 +6,7 @@ This is the Bun repository - an all-in-one JavaScript runtime & toolkit designed
- **Build debug version**: `bun bd`
- Creates a debug build at `./build/debug/bun-debug`
- Compilation takes ~5 minutes. Don't timeout, be patient.
- **CRITICAL**: DO NOT set a build timeout. Compilation takes ~5 minutes. Be patient.
- **Run tests with your debug build**: `bun bd test <test-file>`
- **CRITICAL**: Never use `bun test` directly - it won't include your changes
- **Run any command with debug build**: `bun bd <command>`
@@ -59,8 +59,8 @@ test("my feature", async () => {
});
const [stdout, stderr, exitCode] = await Promise.all([
new Response(proc.stdout).text(),
new Response(proc.stderr).text(),
proc.stdout.text(),
proc.stderr.text(),
proc.exited,
]);
@@ -69,6 +69,8 @@ test("my feature", async () => {
});
```
- Always use `port: 0`. Do not hardcode ports. Do not use your own random port number function.
## Code Architecture
### Language Structure
@@ -133,7 +135,6 @@ test("my feature", async () => {
When implementing JavaScript classes in C++:
1. Create three classes if there's a public constructor:
- `class Foo : public JSC::JSDestructibleObject` (if has C++ fields)
- `class FooPrototype : public JSC::JSNonFinalObject`
- `class FooConstructor : public JSC::InternalFunction`
@@ -193,7 +194,6 @@ Built-in JavaScript modules use special syntax and are organized as:
```
3. **Debug helpers**:
- `$debug()` - Like console.log but stripped in release builds
- `$assert()` - Assertions stripped in release builds
- `if($debug) {}` - Check if debug env var is set
@@ -221,15 +221,16 @@ bun ci
## Important Development Notes
1. **Never use `bun test` or `bun <file>` directly** - always use `bun bd test` or `bun bd <command>`. `bun bd` compiles & runs the debug build.
2. **Use `await using`** for proper resource cleanup with Bun APIs (Bun.spawn, Bun.serve, Bun.connect, etc.)
3. **Follow existing code style** - check neighboring files for patterns
4. **Create regression tests** in `test/regression/issue/` when fixing bugs
5. **Use absolute paths** - Always use absolute paths in file operations
6. **Avoid shell commands** - Don't use `find` or `grep` in tests; use Bun's Glob and built-in tools
7. **Memory management** - In Zig code, be careful with allocators and use defer for cleanup
8. **Cross-platform** - Test on macOS, Linux, and Windows when making platform-specific changes
9. **Debug builds** - Use `BUN_DEBUG_QUIET_LOGS=1` to disable debug logging, or `BUN_DEBUG_<scope>=1` to enable specific scopes
10. **Transpiled source** - Find transpiled files in `/tmp/bun-debug-src/` for debugging
2. **All changes must be tested** - if you're not testing your changes, you're not done.
3. **Get your tests to pass**. If you didn't run the tests, your code does not work.
4. **Follow existing code style** - check neighboring files for patterns
5. **Create tests in the right folder** in `test/` and the test must end in `.test.ts` or `.test.tsx`
6. **Use absolute paths** - Always use absolute paths in file operations
7. **Avoid shell commands** - Don't use `find` or `grep` in tests; use Bun's Glob and built-in tools
8. **Memory management** - In Zig code, be careful with allocators and use defer for cleanup
9. **Cross-platform** - Run `bun run zig:check-all` to compile the Zig code on all platforms when making platform-specific changes
10. **Debug builds** - Use `BUN_DEBUG_QUIET_LOGS=1` to disable debug logging, or `BUN_DEBUG_<scope>=1` to enable specific scopes
11. **Be humble & honest** - NEVER overstate what you got done or what actually works in commits, PRs or in messages to the user.
## Key APIs and Features

309
PLAN.md Normal file
View File

@@ -0,0 +1,309 @@
# HTTP/2 Implementation Fix Plan
## Progress Status
-**Phase 1: Fix Response Completion** - COMPLETED
-**CONTINUATION Frame Support** - COMPLETED
-**Enhanced Error Reporting** - COMPLETED
- 🔧 **HPACK/lshpack Compatibility** - IN PROGRESS (Node.js interop issue)
-**Phase 2: Streaming Decompression** - PENDING
-**Phase 3: Flow Control** - PENDING
-**Phase 4: Protocol Compliance** - PENDING
-**Phase 5: Testing & Validation** - PENDING
## Current State Summary
### Completed Fixes ✅
1. **Response callback invocation** - HTTP/2 responses now properly call the result callback when complete
2. **State management** - All state fields (`stage`, `response_stage`, `request_stage`) properly transition to `.done`
3. **CONTINUATION frame support** - Added full support for header blocks spanning multiple frames
4. **Enhanced error reporting** - HPACK errors now provide specific error codes instead of generic failures
5. **Header block accumulation** - Properly accumulates and processes header data across frames
### Remaining Issues 🔧
1. **HPACK/lshpack compatibility** - The lshpack library is failing to decode headers from Node.js HTTP/2 servers (error at offset 10)
2. **No decompression support** - Compressed responses aren't handled correctly with HTTP/2's frame-based protocol
3. **No flow control** - Missing WINDOW_UPDATE frames for flow control
4. **Limited testing** - Need comprehensive tests beyond Node.js interop
## Root Cause Analysis
### Primary Issue: Missing Callback
When an HTTP/2 response completes (END_STREAM flag received), the code:
- Sets `state.response_stage = .done`
- Calls `handleResponseMetadata()` which returns a status
- **Never calls the result callback** to notify fetch that the response is complete
### Secondary Issues
1. **State Management**: `state.stage` is never set to `.done`, only `response_stage`
2. **Body Assembly**: Response body is processed but not properly assembled for the callback
3. **Decompression**: Each DATA frame is processed independently, breaking decompression state
## Implementation Phases
### Phase 1: Fix Response Completion [COMPLETED ✅]
**Goal**: Make basic HTTP/2 GET requests work end-to-end
#### 1.1 Fix Callback Invocation ✅
- **Status**: COMPLETED
- **Location**: `src/http.zig` lines 790-811 and 952-972
- **Implementation**: Added callback invocation when END_STREAM flag is received:
```zig
// Set all state flags
this.state.response_stage = .done;
this.state.request_stage = .done;
this.state.stage = .done;
// Invoke the callback
const callback = this.result_callback;
const result = this.toResult();
callback.run(@fieldParentPtr("client", this), result);
```
#### 1.2 Fix Response Body Assembly ✅
- **Status**: COMPLETED
- **Implementation**: Response body is properly assembled via `toResult()` method
#### 1.3 Fix State Transitions ✅
- **Status**: COMPLETED
- **Implementation**: Both `stage` and `response_stage` are now properly set to `.done`
- All three state fields are updated: `response_stage`, `request_stage`, and `stage`
### HPACK Decoder & CONTINUATION Frames [COMPLETED ✅]
**Discovery**: During Phase 1 testing, found critical issues with HPACK decoder
#### Issues Fixed
1. **Missing CONTINUATION Frame Support** ✅
- **Root Cause**: HTTP/2 implementation was missing support for CONTINUATION frames
- **Impact**: Headers spanning multiple frames couldn't be processed
- **Fix**: Added full CONTINUATION frame support with header block accumulation
2. **Poor Error Reporting** ✅
- **Issue**: Generic `UnableToDecode` errors made debugging difficult
- **Fix**: Added specific error types: `BadHPACKData`, `HPACKHeaderTooLarge`, `HPACKNeedMoreBuffer`
3. **Improper Header Block Processing** ✅
- **Issue**: Headers were decoded immediately instead of waiting for complete block
- **Fix**: Now accumulates header data until END_HEADERS flag is received
#### Implementation Details
- **New State Fields**: Added `http2_header_block_buffer`, `http2_header_block_len`, `http2_expecting_continuation`
- **New Methods**: `accumulateHeaderBlock()`, `getCompleteHeaderBlock()`, `clearHeaderBlock()`
- **Frame Type 0x09**: Full CONTINUATION frame handling implemented
- **Files Modified**:
- `src/http.zig` - Added CONTINUATION support and header accumulation
- `src/bun.js/api/bun/lshpack.zig` - Enhanced error reporting
- `src/bun.js/bindings/c-bindings.cpp` - Fixed error code handling
### Phase 2: Fix Streaming Decompression [HIGH PRIORITY]
**Goal**: Support gzip, deflate, brotli, and zstd with HTTP/2
#### 2.1 Initialize Decompression State
```zig
// In HEADERS frame processing
if (strings.eqlComptime(header.name, "content-encoding")) {
if (strings.eqlComptime(header.value, "gzip")) {
this.state.encoding = .gzip;
// Initialize decompressor for streaming
this.state.decompressor = .{ .gzip = ... };
} else if (strings.eqlComptime(header.value, "deflate")) {
// ... similar for other encodings
}
}
```
#### 2.2 Accumulate Compressed Data
```zig
// In DATA frame processing
if (this.state.encoding.isCompressed()) {
// Accumulate compressed data across frames
try this.state.compressed_body.appendSlice(data_payload);
const should_decompress = end_stream or
this.state.compressed_body.list.items.len > 32 * 1024; // 32KB threshold
if (should_decompress) {
try this.state.decompressBytes(
this.state.compressed_body.list.items,
this.state.body_out_str.?,
end_stream // is_final_chunk
);
if (!end_stream) {
this.state.compressed_body.reset();
}
}
} else {
// Uncompressed - append directly
try this.state.body_out_str.?.appendSlice(data_payload);
}
```
#### 2.3 Maintain Decompressor State
- Keep decompressor alive between DATA frames
- Only reset decompressor when stream ends
- Handle partial decompression for streaming responses
### Phase 3: Add Flow Control [MEDIUM PRIORITY]
**Goal**: Handle large responses and backpressure
#### 3.1 Track Flow Control Windows
```zig
// Add to HTTPClient
http2_stream_window: i32 = 65535, // Per-stream window
http2_connection_window: i32 = 65535, // Connection window
```
#### 3.2 Send WINDOW_UPDATE Frames
```zig
// After consuming DATA frame
fn sendWindowUpdate(this: *HTTPClient, stream_id: u32, increment: u32) !void {
var frame: [13]u8 = undefined;
// Frame header (9 bytes)
frame[0..3] = @bitCast([3]u8, @as(u24, 4)); // Length = 4
frame[3] = 0x08; // Type = WINDOW_UPDATE
frame[4] = 0x00; // Flags = 0
frame[5..9] = @bitCast([4]u8, stream_id);
// Window increment (4 bytes)
frame[9..13] = @bitCast([4]u8, increment);
_ = try socket.write(frame);
}
// In DATA frame processing
if (data_payload.len > 0) {
// Send WINDOW_UPDATE for consumed bytes
try this.sendWindowUpdate(stream_id, @intCast(data_payload.len));
try this.sendWindowUpdate(0, @intCast(data_payload.len)); // Connection window
}
```
#### 3.3 Handle Flow Control Errors
- Check if DATA frame exceeds window
- Send FLOW_CONTROL_ERROR if violated
- Block sending if window exhausted
### Phase 4: Protocol Compliance [LOW PRIORITY]
**Goal**: Full HTTP/2 specification compliance
#### 4.1 PING/PONG Frames
```zig
0x06 => { // PING frame
if ((frame_flags & 0x01) == 0) { // Not ACK
// Send PONG with same payload
try this.sendPong(frame_payload);
}
}
```
#### 4.2 GOAWAY Frame
```zig
0x07 => { // GOAWAY frame
const last_stream_id = @bitCast(u32, frame_payload[0..4].*);
const error_code = @bitCast(u32, frame_payload[4..8].*);
log("Received GOAWAY: last_stream={}, error={}", .{last_stream_id, error_code});
this.closeAndFail(error.HTTP2GoAway, is_ssl, socket);
}
```
#### 4.3 RST_STREAM Frame
```zig
0x03 => { // RST_STREAM frame
const error_code = @bitCast(u32, frame_payload[0..4].*);
log("Stream {} reset with error {}", .{stream_id, error_code});
this.state.response_stage = .fail;
this.closeAndFail(error.HTTP2StreamReset, is_ssl, socket);
}
```
### Phase 5: Testing & Validation
**Goal**: Ensure reliability and correctness
#### 5.1 Basic Tests
- GET request to HTTP/2 server
- POST request with body
- Large response (>1MB)
- Compressed response (gzip, brotli)
#### 5.2 Error Tests
- Server sends RST_STREAM
- Server sends GOAWAY
- Invalid HEADERS
- Flow control violation
#### 5.3 Performance Tests
- Concurrent streams
- Large file downloads
- Streaming responses
## Success Criteria
1. **Basic Functionality**: `fetch()` with HTTP/2 returns response without hanging
2. **Decompression**: Compressed responses are properly decompressed
3. **Flow Control**: Large responses (>64KB) work correctly
4. **Error Handling**: Protocol errors are properly reported
5. **Tests Pass**: All HTTP/2 tests in test suite pass
## Implementation Order
1. ✅ **Fix callback invocation** (Phase 1.1) - COMPLETED
2. ✅ **Fix body assembly** (Phase 1.2) - COMPLETED
3. ✅ **Fix state management** (Phase 1.3) - COMPLETED
4. ✅ **Add CONTINUATION frame support** - COMPLETED
5. 🔧 **Fix HPACK/lshpack compatibility** - IN PROGRESS
6. ⏳ **Add decompression** (Phase 2) - PENDING
7. ⏳ **Add flow control** (Phase 3) - PENDING
8. ⏳ **Add protocol compliance** (Phase 4) - PENDING
## Files Modified
### Already Changed ✅
1. **src/http.zig**
- ✅ Added callback invocation in `handleHTTP2Data()` for END_STREAM
- ✅ Fixed state transitions to set all state fields to `.done`
- ✅ Added CONTINUATION frame support (frame type 0x09)
- ✅ Added header block accumulation methods
- ✅ Enhanced debug logging for HTTP/2 frames
2. **src/bun.js/api/bun/lshpack.zig**
- ✅ Added specific error types for HPACK failures
- ✅ Enhanced error reporting from C++ wrapper
3. **src/bun.js/bindings/c-bindings.cpp**
- ✅ Modified to return specific error codes for HPACK failures
### Still Need Changes 🔧
1. **src/http.zig**
- Fix HPACK/lshpack compatibility issue
- Add `sendWindowUpdate()` for flow control
- Add decompression state management for HTTP/2
2. **src/http/InternalState.zig**
- Add HTTP/2-specific decompression handling
- Fix decompression for frame-based protocol
## Risks and Mitigations
1. **Risk**: Breaking HTTP/1.1 functionality
- **Mitigation**: Keep HTTP/2 code paths separate, test HTTP/1.1 thoroughly
2. **Risk**: Memory leaks from incomplete streams
- **Mitigation**: Proper cleanup in error paths, test with valgrind
3. **Risk**: Decompression state corruption
- **Mitigation**: Reset decompressor on stream errors, validate compressed data
## Timeline Estimate
- Phase 1: 2-3 hours (critical path)
- Phase 2: 3-4 hours (decompression complexity)
- Phase 3: 2-3 hours (flow control)
- Phase 4: 2-3 hours (protocol details)
- Phase 5: 2-3 hours (testing)
**Total: 11-16 hours of focused development**
## Next Steps
1. Start with Phase 1.1 - Fix callback invocation
2. Test with simple HTTP/2 server
3. Iterate through phases based on test results
4. Create regression tests for each fix

38
STATUS.md Normal file
View File

@@ -0,0 +1,38 @@
# HTTP/2 Implementation Status
## Current State: Not Working
Attempted to implement HTTP/2 support in fetch(). The implementation does not work. Fetch calls using `httpVersion: 2` hang indefinitely.
### What Actually Happens
1. HTTP/2 is negotiated via ALPN
2. Connection preface is sent
3. SETTINGS frames are exchanged
4. Request HEADERS frame is sent
5. Server receives the request
6. Server sends response
7. **Fetch never completes - hangs forever**
### What Was Implemented
- Basic HTTP/2 frame parsing (DATA, HEADERS, SETTINGS, etc.)
- HPACK header encoding/decoding integration
- HTTP/2 request sending with pseudo-headers
- ALPN negotiation (this part already existed)
### Major Problems
- Response data arrives but isn't processed correctly
- The fetch promise never resolves
- No proper stream state management
- No flow control implementation
- No error handling for protocol violations
- Likely has memory safety issues
### Code Changes
- `src/http.zig` - Added ~500 lines of HTTP/2 code
- `src/http/InternalState.zig` - Added HTTP/2 flags
- Test file created but tests fail/timeout
### Bottom Line
This is an incomplete implementation that doesn't work. HTTP/2 is complex. What exists is a partial attempt that successfully sends requests but fails to handle responses. The code compiles but is not functional for actual use.
HTTP/1.1 continues to work normally.

View File

@@ -28,10 +28,4 @@ bench("brotli compress stream", async () => {
await pipeline(source, compress);
});
bench("brotli decompress stream", async () => {
const source = Readable.from([compressed]);
const decompress = createBrotliDecompress();
await pipeline(source, decompress);
});
await run();

View File

@@ -15,7 +15,7 @@
"react": "^18.3.1",
"react-dom": "^18.3.1",
"source-map-js": "^1.2.0",
"typescript": "^5.7.2",
"typescript": "5.9.2",
},
},
"packages/@types/bun": {
@@ -32,7 +32,6 @@
},
"devDependencies": {
"@types/react": "^19",
"typescript": "^5.0.2",
},
"peerDependencies": {
"@types/react": "^19",
@@ -308,7 +307,7 @@
"tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
"typescript": ["typescript@5.8.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ=="],
"typescript": ["typescript@5.9.2", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A=="],
"uglify-js": ["uglify-js@3.19.3", "", { "bin": { "uglifyjs": "bin/uglifyjs" } }, "sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ=="],
@@ -334,6 +333,8 @@
"@octokit/webhooks/@octokit/webhooks-methods": ["@octokit/webhooks-methods@4.1.0", "", {}, "sha512-zoQyKw8h9STNPqtm28UGOYFE7O6D4Il8VJwhAtMHFt2C4L0VQT1qGKLeefUOqHNs1mNRYSadVv7x0z8U2yyeWQ=="],
"bun-tracestrings/typescript": ["typescript@5.8.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ=="],
"camel-case/no-case": ["no-case@2.3.2", "", { "dependencies": { "lower-case": "^1.1.1" } }, "sha512-rmTZ9kz+f3rCvK2TD1Ue/oZlns7OGoIWP4fc3llxxRXlOkHKoWPPWJOfFYpITabSow43QJbRIoHQXtt10VldyQ=="],
"change-case/camel-case": ["camel-case@4.1.2", "", { "dependencies": { "pascal-case": "^3.1.2", "tslib": "^2.0.3" } }, "sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw=="],

View File

@@ -102,6 +102,11 @@ else()
endif()
optionx(ENABLE_ASAN BOOL "If ASAN support should be enabled" DEFAULT ${DEFAULT_ASAN})
optionx(ENABLE_ZIG_ASAN BOOL "If Zig ASAN support should be enabled" DEFAULT ${ENABLE_ASAN})
if (NOT ENABLE_ASAN)
set(ENABLE_ZIG_ASAN OFF)
endif()
if(RELEASE AND LINUX AND CI AND NOT ENABLE_ASSERTIONS AND NOT ENABLE_ASAN)
set(DEFAULT_LTO ON)

View File

@@ -350,6 +350,7 @@ src/bun.js/bindings/webcore/JSTextEncoderStream.cpp
src/bun.js/bindings/webcore/JSTransformStream.cpp
src/bun.js/bindings/webcore/JSTransformStreamDefaultController.cpp
src/bun.js/bindings/webcore/JSURLSearchParams.cpp
src/bun.js/bindings/webcore/JSWasmStreamingCompiler.cpp
src/bun.js/bindings/webcore/JSWebSocket.cpp
src/bun.js/bindings/webcore/JSWorker.cpp
src/bun.js/bindings/webcore/JSWorkerOptions.cpp

View File

@@ -29,6 +29,7 @@ src/js/builtins/TransformStream.ts
src/js/builtins/TransformStreamDefaultController.ts
src/js/builtins/TransformStreamInternals.ts
src/js/builtins/UtilInspect.ts
src/js/builtins/WasmStreaming.ts
src/js/builtins/WritableStreamDefaultController.ts
src/js/builtins/WritableStreamDefaultWriter.ts
src/js/builtins/WritableStreamInternals.ts

View File

@@ -10,6 +10,7 @@ src/allocators/NullableAllocator.zig
src/analytics.zig
src/analytics/schema.zig
src/api/schema.zig
src/asan.zig
src/ast.zig
src/ast/Ast.zig
src/ast/ASTMemoryAllocator.zig
@@ -18,19 +19,43 @@ src/ast/base.zig
src/ast/Binding.zig
src/ast/BundledAst.zig
src/ast/CharFreq.zig
src/ast/ConvertESMExportsForHmr.zig
src/ast/E.zig
src/ast/Expr.zig
src/ast/foldStringAddition.zig
src/ast/G.zig
src/ast/ImportScanner.zig
src/ast/KnownGlobal.zig
src/ast/Macro.zig
src/ast/maybe.zig
src/ast/NewStore.zig
src/ast/Op.zig
src/ast/P.zig
src/ast/parse.zig
src/ast/parseFn.zig
src/ast/parseImportExport.zig
src/ast/parseJSXElement.zig
src/ast/parsePrefix.zig
src/ast/parseProperty.zig
src/ast/Parser.zig
src/ast/parseStmt.zig
src/ast/parseSuffix.zig
src/ast/parseTypescript.zig
src/ast/S.zig
src/ast/Scope.zig
src/ast/ServerComponentBoundary.zig
src/ast/SideEffects.zig
src/ast/skipTypescript.zig
src/ast/Stmt.zig
src/ast/Symbol.zig
src/ast/symbols.zig
src/ast/TS.zig
src/ast/TypeScript.zig
src/ast/UseDirective.zig
src/ast/visit.zig
src/ast/visitBinaryExpression.zig
src/ast/visitExpr.zig
src/ast/visitStmt.zig
src/async/posix_event_loop.zig
src/async/stub_event_loop.zig
src/async/windows_event_loop.zig
@@ -377,7 +402,6 @@ src/collections/baby_list.zig
src/collections/bit_set.zig
src/collections/hive_array.zig
src/collections/multi_array_list.zig
src/collections/safety.zig
src/compile_target.zig
src/comptime_string_map.zig
src/copy_file.zig
@@ -546,9 +570,15 @@ src/http/AsyncHTTP.zig
src/http/CertificateInfo.zig
src/http/Decompressor.zig
src/http/Encoding.zig
src/http/ETag.zig
src/http/FetchRedirect.zig
src/http/HeaderBuilder.zig
src/http/Headers.zig
src/http/http2/Connection.zig
src/http/http2/FrameParser.zig
src/http/http2/Stream.zig
src/http/HTTP2Client.zig
src/http/HTTP2Integration.zig
src/http/HTTPCertError.zig
src/http/HTTPContext.zig
src/http/HTTPRequestBody.zig
@@ -633,6 +663,7 @@ src/install/resolvers/folder_resolver.zig
src/install/versioned_url.zig
src/install/windows-shim/BinLinkingShim.zig
src/install/windows-shim/bun_shim_impl.zig
src/install/yarn.zig
src/interchange.zig
src/interchange/json.zig
src/interchange/toml.zig
@@ -703,6 +734,9 @@ src/s3/multipart_options.zig
src/s3/multipart.zig
src/s3/simple_request.zig
src/s3/storage_class.zig
src/safety.zig
src/safety/alloc_ptr.zig
src/safety/CriticalSection.zig
src/semver.zig
src/semver/ExternalString.zig
src/semver/SemverObject.zig
@@ -853,6 +887,10 @@ src/string/StringJoiner.zig
src/string/WTFStringImpl.zig
src/sys_uv.zig
src/sys.zig
src/sys/coreutils_error_map.zig
src/sys/Error.zig
src/sys/File.zig
src/sys/libuv_error_map.zig
src/system_timer.zig
src/test/fixtures.zig
src/test/recover.zig

View File

@@ -618,7 +618,7 @@ register_command(
-Doptimize=${ZIG_OPTIMIZE}
-Dcpu=${ZIG_CPU}
-Denable_logs=$<IF:$<BOOL:${ENABLE_LOGS}>,true,false>
-Denable_asan=$<IF:$<BOOL:${ENABLE_ASAN}>,true,false>
-Denable_asan=$<IF:$<BOOL:${ENABLE_ZIG_ASAN}>,true,false>
-Dversion=${VERSION}
-Dreported_nodejs_version=${NODEJS_VERSION}
-Dcanary=${CANARY_REVISION}
@@ -1034,7 +1034,6 @@ if(LINUX)
--ld-path=${LLD_PROGRAM}
-fno-pic
-Wl,-no-pie
-Wl,-icf=safe
-Wl,--as-needed
-Wl,-z,stack-size=12800000
-Wl,--compress-debug-sections=zlib
@@ -1060,6 +1059,13 @@ if(LINUX)
-Wl,--gc-sections
)
endif()
if (NOT DEBUG AND NOT ENABLE_ASAN)
target_link_options(${bun} PUBLIC
-Wl,-icf=safe
)
endif()
endif()
# --- Symbols list ---

View File

@@ -4,7 +4,7 @@ register_repository(
REPOSITORY
HdrHistogram/HdrHistogram_c
COMMIT
652d51bcc36744fd1a6debfeb1a8a5f58b14022c
8dcce8f68512fca460b171bccc3a5afce0048779
)
register_cmake_command(

View File

@@ -4,7 +4,7 @@ register_repository(
REPOSITORY
libarchive/libarchive
COMMIT
7118f97c26bf0b2f426728b482f86508efc81d02
9525f90ca4bd14c7b335e2f8c84a4607b0af6bdf
)
register_cmake_command(

View File

@@ -4,7 +4,7 @@ register_repository(
REPOSITORY
oven-sh/mimalloc
COMMIT
1beadf9651a7bfdec6b5367c380ecc3fe1c40d1a
1cef3e8f4167733818f1883b2f3a9dd4754224cf
)
set(MIMALLOC_CMAKE_ARGS
@@ -13,14 +13,47 @@ set(MIMALLOC_CMAKE_ARGS
-DMI_BUILD_SHARED=OFF
-DMI_BUILD_TESTS=OFF
-DMI_USE_CXX=ON
-DMI_OVERRIDE=OFF
-DMI_OSX_ZONE=OFF
-DMI_OSX_INTERPOSE=OFF
-DMI_SKIP_COLLECT_ON_EXIT=ON
# ```
# mimalloc_allow_large_os_pages=0 BUN_PORT=3004 mem bun http-hello.js
# Started development server: http://localhost:3004
#
# Peak memory usage: 52 MB
#
# mimalloc_allow_large_os_pages=1 BUN_PORT=3004 mem bun http-hello.js
# Started development server: http://localhost:3004
#
# Peak memory usage: 74 MB
# ```
#
# ```
# mimalloc_allow_large_os_pages=1 mem bun --eval 1
#
# Peak memory usage: 52 MB
#
# mimalloc_allow_large_os_pages=0 mem bun --eval 1
#
# Peak memory usage: 30 MB
# ```
-DMI_NO_THP=1
)
if(ENABLE_ASAN)
list(APPEND MIMALLOC_CMAKE_ARGS -DMI_TRACK_ASAN=ON)
list(APPEND MIMALLOC_CMAKE_ARGS -DMI_OVERRIDE=OFF)
list(APPEND MIMALLOC_CMAKE_ARGS -DMI_OSX_ZONE=OFF)
list(APPEND MIMALLOC_CMAKE_ARGS -DMI_OSX_INTERPOSE=OFF)
elseif(APPLE OR LINUX)
# Enable static override when ASAN is not enabled
list(APPEND MIMALLOC_CMAKE_ARGS -DMI_OVERRIDE=ON)
if(APPLE)
list(APPEND MIMALLOC_CMAKE_ARGS -DMI_OSX_ZONE=ON)
list(APPEND MIMALLOC_CMAKE_ARGS -DMI_OSX_INTERPOSE=ON)
else()
list(APPEND MIMALLOC_CMAKE_ARGS -DMI_OSX_ZONE=OFF)
list(APPEND MIMALLOC_CMAKE_ARGS -DMI_OSX_INTERPOSE=OFF)
endif()
endif()
if(DEBUG)
@@ -31,13 +64,7 @@ if(ENABLE_VALGRIND)
list(APPEND MIMALLOC_CMAKE_ARGS -DMI_VALGRIND=ON)
endif()
if(WIN32)
if(DEBUG)
set(MIMALLOC_LIBRARY mimalloc-static-debug)
else()
set(MIMALLOC_LIBRARY mimalloc-static)
endif()
elseif(DEBUG)
if(DEBUG)
if (ENABLE_ASAN)
set(MIMALLOC_LIBRARY mimalloc-asan-debug)
else()
@@ -53,6 +80,7 @@ if(APPLE OR (LINUX AND NOT DEBUG))
set(MIMALLOC_LIBRARY CMakeFiles/mimalloc-obj.dir/src/static.c.o)
endif()
register_cmake_command(
TARGET
mimalloc

View File

@@ -2,7 +2,7 @@ option(WEBKIT_VERSION "The version of WebKit to use")
option(WEBKIT_LOCAL "If a local version of WebKit should be used instead of downloading")
if(NOT WEBKIT_VERSION)
set(WEBKIT_VERSION 1098cc50652ab1eab171f58f7669e19ca6c276ae)
set(WEBKIT_VERSION 642e2252f6298387edb6d2f991a0408fd0320466)
endif()
string(SUBSTRING ${WEBKIT_VERSION} 0 16 WEBKIT_VERSION_PREFIX)

View File

@@ -164,6 +164,70 @@ Static responses do not allocate additional memory after initialization. You can
Static route responses are cached for the lifetime of the server object. To reload static routes, call `server.reload(options)`.
### File Responses vs Static Responses
When serving files in routes, there are two distinct behaviors depending on whether you buffer the file content or serve it directly:
```ts
Bun.serve({
routes: {
// Static route - content is buffered in memory at startup
"/logo.png": new Response(await Bun.file("./logo.png").bytes()),
// File route - content is read from filesystem on each request
"/download.zip": new Response(Bun.file("./download.zip")),
},
});
```
**Static routes** (`new Response(await file.bytes())`) buffer content in memory at startup:
- **Zero filesystem I/O** during requests - content served entirely from memory
- **ETag support** - Automatically generates and validates ETags for caching
- **If-None-Match** - Returns `304 Not Modified` when client ETag matches
- **No 404 handling** - Missing files cause startup errors, not runtime 404s
- **Memory usage** - Full file content stored in RAM
- **Best for**: Small static assets, API responses, frequently accessed files
**File routes** (`new Response(Bun.file(path))`) read from filesystem per request:
- **Filesystem reads** on each request - checks file existence and reads content
- **Built-in 404 handling** - Returns `404 Not Found` if file doesn't exist or becomes inaccessible
- **Last-Modified support** - Uses file modification time for `If-Modified-Since` headers
- **If-Modified-Since** - Returns `304 Not Modified` when file hasn't changed since client's cached version
- **Range request support** - Automatically handles partial content requests with `Content-Range` headers
- **Streaming transfers** - Uses buffered reader with backpressure handling for efficient memory usage
- **Memory efficient** - Only buffers small chunks during transfer, not entire file
- **Best for**: Large files, dynamic content, user uploads, files that change frequently
### HTTP Caching Behavior
Both route types implement HTTP caching standards but with different strategies:
#### Static Routes Caching
- **ETag generation**: Automatically computes ETag hash from content at startup
- **If-None-Match**: Validates client ETag against server ETag
- **304 responses**: Returns `304 Not Modified` with empty body when ETags match
- **Cache headers**: Inherits any `Cache-Control` headers you provide in the Response
- **Consistency**: ETag remains constant until server restart or route reload
#### File Routes Caching
- **Last-Modified**: Uses file's `mtime` for `Last-Modified` header
- **If-Modified-Since**: Compares client date with file modification time
- **304 responses**: Returns `304 Not Modified` when file unchanged since client's cached version
- **Content-Length**: Automatically set based on current file size
- **Dynamic validation**: Checks file modification time on each request
#### Status Code Handling
Both route types automatically adjust status codes:
- **200 → 204**: Empty files (0 bytes) return `204 No Content` instead of `200 OK`
- **200 → 304**: Successful cache validation returns `304 Not Modified`
- **File routes only**: Missing or inaccessible files return `404 Not Found`
```ts
const server = Bun.serve({
static: {

View File

@@ -208,8 +208,8 @@ export class ArrayBufferSink {
*
* This API might change later to separate Uint8ArraySink and ArrayBufferSink
*/
flush(): number | Uint8Array | ArrayBuffer;
end(): ArrayBuffer | Uint8Array;
flush(): number | Uint8Array<ArrayBuffer> | ArrayBuffer;
end(): ArrayBuffer | Uint8Array<ArrayBuffer>;
}
```

View File

@@ -158,7 +158,7 @@ See [Test > Lifecycle](https://bun.com/docs/test/lifecycle) for complete documen
## Mocks
Create mock functions with the `mock` function. Mocks are automatically reset between tests.
Create mock functions with the `mock` function.
```ts
import { test, expect, mock } from "bun:test";

View File

@@ -14,7 +14,7 @@ if (typeof Bun !== "undefined") {
---
In TypeScript environments, the previous approach will result in a type error unless `bun-types` is globally installed. To avoid this, you can check `process.versions` instead.
In TypeScript environments, the previous approach will result in a type error unless `@types/bun` is installed. To avoid this, you can check `process.versions` instead.
```ts
if (process.versions.bun) {

View File

@@ -148,7 +148,7 @@ This page is updated regularly to reflect compatibility status of the latest ver
### [`node:vm`](https://nodejs.org/api/vm.html)
🟡 Core functionality works, but experimental VM ES modules are not implemented, including `vm.Module`, `vm.SourceTextModule`, `vm.SyntheticModule`,`importModuleDynamically`, and `vm.measureMemory`. Options like `timeout`, `breakOnSigint`, `cachedData` are not implemented yet.
🟡 Core functionality and ES modules are implemented, including `vm.Script`, `vm.createContext`, `vm.runInContext`, `vm.runInNewContext`, `vm.runInThisContext`, `vm.compileFunction`, `vm.isContext`, `vm.Module`, `vm.SourceTextModule`, `vm.SyntheticModule`, and `importModuleDynamically` support. Options like `timeout` and `breakOnSigint` are fully supported. Missing `vm.measureMemory` and some `cachedData` functionality.
### [`node:wasi`](https://nodejs.org/api/wasi.html)
@@ -214,6 +214,10 @@ The table below lists all globals implemented by Node.js and Bun's current compa
🟢 Fully implemented.
### [`Atomics`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Atomics)
🟢 Fully implemented.
### [`BroadcastChannel`](https://developer.mozilla.org/en-US/docs/Web/API/BroadcastChannel)
🟢 Fully implemented.

View File

@@ -426,6 +426,54 @@ test("exactly two assertions", () => {
This helps ensure all your assertions run, especially in complex async code with multiple code paths.
## Type Testing
Bun includes `expectTypeOf` for testing typescript types, compatible with Vitest.
### expectTypeOf
{% callout %}
**Note** — These functions are no-ops at runtime - you need to run TypeScript separately to verify the type checks.
{% endcallout %}
The `expectTypeOf` function provides type-level assertions that are checked by TypeScript's type checker. **Important**:
To test your types:
1. Write your type assertions using `expectTypeOf`
2. Run `bunx tsc --noEmit` to check that your types are correct
```ts
import { expectTypeOf } from "bun:test";
// Basic type assertions
expectTypeOf<string>().toEqualTypeOf<string>();
expectTypeOf(123).toBeNumber();
expectTypeOf("hello").toBeString();
// Object type matching
expectTypeOf({ a: 1, b: "hello" }).toMatchObjectType<{ a: number }>();
// Function types
function greet(name: string): string {
return `Hello ${name}`;
}
expectTypeOf(greet).toBeFunction();
expectTypeOf(greet).parameters.toEqualTypeOf<[string]>();
expectTypeOf(greet).returns.toEqualTypeOf<string>();
// Array types
expectTypeOf([1, 2, 3]).items.toBeNumber();
// Promise types
expectTypeOf(Promise.resolve(42)).resolves.toBeNumber();
```
For full documentation on expectTypeOf matchers, see the [API Reference](/reference/bun/test/expectTypeOf)
## Matchers
Bun implements the following matchers. Full Jest compatibility is on the roadmap; track progress [here](https://github.com/oven-sh/bun/issues/1825).
@@ -629,17 +677,17 @@ Bun implements the following matchers. Full Jest compatibility is on the roadmap
---
-
-
- [`.toHaveReturnedWith()`](https://jestjs.io/docs/expect#tohavereturnedwithvalue)
---
-
-
- [`.toHaveLastReturnedWith()`](https://jestjs.io/docs/expect#tohavelastreturnedwithvalue)
---
-
-
- [`.toHaveNthReturnedWith()`](https://jestjs.io/docs/expect#tohaventhreturnedwithnthcall-value)
---

1
examples/.gitignore vendored
View File

@@ -1 +0,0 @@
bun-examples-all

View File

@@ -1,7 +0,0 @@
#[no_mangle]
pub extern "C" fn add(a: i32, b: i32) -> i32 {
a + b
}
// to compile:
// rustc --crate-type cdylib add.rs

View File

@@ -1,12 +0,0 @@
import { dlopen, suffix } from "bun:ffi";
const {
symbols: { add },
} = dlopen(`./libadd.${suffix}`, {
add: {
args: ["i32", "i32"],
returns: "i32",
},
});
console.log(add(1, 2));

View File

@@ -1,6 +0,0 @@
pub export fn add(a: i32, b: i32) i32 {
return a + b;
}
// to compile:
// zig build-lib -OReleaseFast ./add.zig -dynamic --name add

View File

@@ -1,89 +0,0 @@
// To run this example:
//
// bun --hot bun-hot-websockets.js
//
const css = ([inner]) => {
return inner;
};
const styles = css`
#bun {
margin: 0 auto;
margin-top: 200px;
object-fit: cover;
}
html,
body {
margin: 0;
padding: 0;
}
body {
background: #f1239f;
font-family: "Inter", sans-serif;
display: flex;
align-items: center;
justify-content: center;
align-content: center;
color: white;
}
h1 {
padding: 0;
text-align: center;
font-size: 3rem;
-webkit-text-stroke: 2px black;
}
* {
box-sizing: border-box;
}
`;
Bun.serve({
websocket: {
message(ws, msg) {
ws.send(styles);
},
},
fetch(req, server) {
if (req.url.endsWith("/hot")) {
if (server.upgrade(req))
return new Response("", {
status: 101,
});
}
return new Response(
`
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>WebSockets</title>
</head>
<body>
<style></style>
<script>
const ws = new WebSocket("ws://localhost:3000/hot");
const style = document.querySelector("style");
ws.onmessage = (e) => {
style.innerHTML = e.data;
};
setInterval(() => {
ws.send("ping");
}, 8);
</script>
<div id="app">
<img src="https://bun.com/logo.svg" alt="Bun" id='bun' />
<h1>bun --hot websockets</h1>
</div>
</body>
`,
{
headers: {
"Content-Type": "text/html; charset=utf-8",
},
},
);
},
});

View File

@@ -1,9 +0,0 @@
import { resolve } from "path";
const { write, stdout, file } = Bun;
import { argv } from "process";
const path = resolve(argv.at(-1)!);
await write(stdout, file(path));
Bun.stdout;
process.stdout;

View File

@@ -1,11 +0,0 @@
const sequence = [1, 2, 3];
sequence.toReversed(); // => [3, 2, 1]
sequence; // => [1, 2, 3]
const outOfOrder = new Uint8Array([3, 1, 2]);
outOfOrder.toSorted(); // => Uint8Array [1, 2, 3]
outOfOrder; // => Uint8Array [3, 1, 2]
const correctionNeeded = [1, 1, 3];
correctionNeeded.with(1, 2); // => [1, 2, 3]
correctionNeeded; // => [1, 1, 3]

View File

@@ -1,23 +0,0 @@
// Accepts a string, TypedArray, or Blob (file blob support is not implemented but planned)
const input = "hello world".repeat(400);
// Bun.hash() defaults to Wyhash because it's fast
console.log(Bun.hash(input));
console.log(Bun.hash.wyhash(input));
// and returns a bigint
// all of these hashing functions return number if 32-bit or bigint if 64-bit, not typed arrays.
console.log(Bun.hash.adler32(input)); // number
console.log(Bun.hash.crc32(input)); // number
console.log(Bun.hash.cityHash32(input)); // number
console.log(Bun.hash.cityHash64(input)); // bigint
console.log(Bun.hash.xxHash32(input)); // number
console.log(Bun.hash.xxHash64(input)); // bigint
console.log(Bun.hash.xxHash3(input)); // bigint
console.log(Bun.hash.murmur32v3(input)); // number
console.log(Bun.hash.murmur32v2(input)); // number
console.log(Bun.hash.murmur64v2(input)); // bigint
console.log(Bun.hash.rapidhash(input)); // bigint
// Second argument accepts a seed where relevant
console.log(Bun.hash(input, 12345));

View File

@@ -1,37 +0,0 @@
// Start a fast HTTP server from a function
Bun.serve({
async fetch(req) {
const { pathname } = new URL(req.url);
if (!(pathname.startsWith("/https://") || pathname.startsWith("/http://"))) {
return new Response("Enter a path that starts with https:// or http://\n", {
status: 400,
});
}
const response = await fetch(req.url.substring("http://localhost:3000/".length), req.clone());
return new HTMLRewriter()
.on("a[href]", {
element(element) {
element.setAttribute("href", "https://www.youtube.com/watch?v=dQw4w9WgXcQ");
},
})
.transform(response);
},
// this is called when fetch() throws or rejects
// error(err: Error) {
// },
// this boolean enables the bun's default error handler
// sometime after the initial release, it will auto reload as well
development: process.env.NODE_ENV !== "production",
// note: this isn't node, but for compatibility bun supports process.env + more stuff in process
// SSL is enabled if these two are set
// certFile: './cert.pem',
// keyFile: './key.pem',
port: 3000, // number or string
});

View File

@@ -1,76 +0,0 @@
import { file, serve } from "bun";
import { existsSync, statSync } from "fs";
serve({
fetch(req: Request) {
let pathname = new URL(req.url).pathname.substring(1);
if (pathname == "") {
pathname = import.meta.url.replace("file://", "");
}
if (!existsSync(pathname)) {
return new Response(null, { status: 404 });
}
const stats = statSync(pathname);
// https://github.com/gornostay25/svelte-adapter-bun/blob/master/src/sirv.js
const headers = new Headers({
"Content-Length": "" + stats.size,
"Last-Modified": stats.mtime.toUTCString(),
ETag: `W/"${stats.size}-${stats.mtime.getTime()}"`,
});
if (req.headers.get("if-none-match") === headers.get("ETag")) {
return new Response(null, { status: 304 });
}
const opts = { code: 200, start: 0, end: Infinity, range: false };
if (req.headers.has("range")) {
opts.code = 206;
let [x, y] = req.headers.get("range")!.replace("bytes=", "").split("-");
let end = (opts.end = parseInt(y, 10) || stats.size - 1);
let start = (opts.start = parseInt(x, 10) || 0);
if (start >= stats.size || end >= stats.size) {
headers.set("Content-Range", `bytes */${stats.size}`);
return new Response(null, {
headers: headers,
status: 416,
});
}
headers.set("Content-Range", `bytes ${start}-${end}/${stats.size}`);
headers.set("Content-Length", "" + (end - start + 1));
headers.set("Accept-Ranges", "bytes");
opts.range = true;
}
if (opts.range) {
return new Response(file(pathname).slice(opts.start, opts.end), {
headers,
status: opts.code,
});
}
return new Response(file(pathname), { headers, status: opts.code });
},
// this is called when fetch() throws or rejects
// error(err: Error) {
// return new Response("uh oh! :(" + String(err.toString()), { status: 500 });
// },
// this boolean enables the bun's default error handler
// sometime after the initial release, it will auto reload as well
development: process.env.NODE_ENV !== "production",
// note: this isn't node, but for compatibility bun supports process.env + more stuff in process
// SSL is enabled if these two are set
// certFile: './cert.pem',
// keyFile: './key.pem',
port: 3000, // number or string
hostname: "localhost", // defaults to 0.0.0.0
});

View File

@@ -1,31 +0,0 @@
import { file, serve } from "bun";
serve({
fetch(req: Request) {
const pathname = new URL(req.url).pathname.substring(1);
// If the URL is empty, display this file.
if (pathname === "") {
return new Response(file(import.meta.url.replace("file://", "")));
}
return new Response(file(pathname));
},
// this is called when fetch() throws or rejects
// error(err: Error) {
// return new Response("uh oh! :(" + String(err.toString()), { status: 500 });
// },
// this boolean enables the bun's default error handler
// sometime after the initial release, it will auto reload as well
development: process.env.NODE_ENV !== "production",
// note: this isn't node, but for compatibility bun supports process.env + more stuff in process
// SSL is enabled if these two are set
// certFile: './cert.pem',
// keyFile: './key.pem',
port: 3000, // number or string
hostname: "localhost", // defaults to 0.0.0.0
});

View File

@@ -1,17 +0,0 @@
import { serve } from "bun";
serve({
async fetch(req) {
// body is a ReadableStream
const body = req.body;
const writer = Bun.file(`upload.${Date.now()}.txt`).writer();
for await (const chunk of body!) {
writer.write(chunk);
}
const wrote = await writer.end();
// @ts-ignore
return Response.json({ wrote, type: req.headers.get("Content-Type") });
},
});

View File

@@ -1,12 +0,0 @@
import { serve } from "bun";
const server = serve({
fetch(req) {
return new Response(`Pending requests count: ${this.pendingRequests}`);
},
});
// Stop the server after 5 seconds
setTimeout(() => {
server.stop();
}, 5000);

View File

@@ -1,34 +0,0 @@
// Start a fast HTTP server from a function
Bun.serve({
fetch(req: Request) {
return new Response(`Echo: ${req.url}`);
},
// baseURI: "http://localhost:3000",
// this is called when fetch() throws or rejects
// error(err: Error) {
// return new Response("uh oh! :(\n" + err.toString(), { status: 500 });
// },
// this boolean enables bun's default error handler
development: process.env.NODE_ENV !== "production",
// note: this isn't node, but for compatibility bun supports process.env + more stuff in process
// SSL is enabled if these two are set
// certFile: './cert.pem',
// keyFile: './key.pem',
port: 3000, // number or string
});
// Start a fast HTTP server from the main file's export
// export default {
// fetch(req) {
// return new Response(
// `This is another way to start a server!
// if the main file export default's an object
// with 'fetch'. Bun automatically calls Bun.serve`
// );
// },
// // so autocomplete & type checking works
// } as Bun.Serve;

View File

@@ -1,193 +0,0 @@
const { AWS_LAMBDA_RUNTIME_API, LAMBDA_TASK_ROOT, _HANDLER } = process.env;
if (!AWS_LAMBDA_RUNTIME_API || AWS_LAMBDA_RUNTIME_API === "") {
throw new Error("AWS_LAMBDA_RUNTIME_API is not set");
}
const nextURL = `http://${AWS_LAMBDA_RUNTIME_API}/2018-06-01/runtime/invocation/next`;
const sourceDir = LAMBDA_TASK_ROOT;
if (!sourceDir) {
throw new Error("handler is not set");
}
if (!_HANDLER) {
throw new Error("handler is not set");
}
// don't care if this fails
if (process.cwd() !== sourceDir) {
try {
process.chdir(sourceDir);
} catch (e) {}
}
var handlerDot = _HANDLER.lastIndexOf(".");
var sourcefile = handlerDot > 0 ? _HANDLER.substring(0, handlerDot) : _HANDLER;
if (sourcefile.length === 0) {
throw new Error("handler is not set");
}
if (!sourcefile.startsWith("/")) {
sourcefile = `./${sourcefile}`;
}
function noop() {}
const method = (handlerDot > 0 ? _HANDLER.substring(handlerDot) : "") || "GET";
if (typeof process.env.VERBOSE !== "undefined") {
console.time(`Loaded ${sourcefile}`);
}
var Handler;
try {
Handler = await import(sourcefile);
} catch (e: any) {
console.error("Error loading sourcefile:", e);
try {
await fetch(new URL(`http://${AWS_LAMBDA_RUNTIME_API}/2018-06-01/runtime/init/error`).href, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
errorMessage: e.message,
errorType: e.name,
stackTrace: e?.stack?.split("\n") ?? [],
}),
});
} catch (e2) {
console.error("Error sending error to runtime:", e2);
}
process.exit(1);
}
if (typeof process.env.VERBOSE !== "undefined") {
console.timeEnd(`Loaded ${sourcefile}`);
}
const handlerFunction = Handler.default?.fetch;
if (typeof handlerFunction !== "function") {
const e = new Error(`${sourcefile} must export default a function called fetch
Here is an example:
export default {
fetch(req) {
return new Response("Hello World");
}
}
`);
console.error(e);
try {
await fetch(new URL(`http://${AWS_LAMBDA_RUNTIME_API}/2018-06-01/runtime/init/error`).href, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
errorMessage: e.message,
errorType: e.name,
stackTrace: e?.stack?.split("\n") ?? [],
}),
});
} catch (e2) {
console.error("Error sending error to runtime:", e2);
}
process.exit(1);
}
var baseURLString = AWS_LAMBDA_RUNTIME_API;
if ("baseURI" in Handler.default) {
baseURLString = Handler.default.baseURI?.toString();
}
var baseURL;
try {
baseURL = new URL(baseURLString);
} catch (e: any) {
console.error("Error parsing baseURI:", e);
try {
await fetch(new URL(`http://${AWS_LAMBDA_RUNTIME_API}/2018-06-01/runtime/init/error`).href, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
errorMessage: e.message,
errorType: e.name,
stackTrace: e?.stack?.split("\n") || [],
}),
});
} catch (e2) {
console.error("Error sending error to runtime:", e2);
}
process.exit(1);
}
async function runHandler(response: Response) {
const traceID = response.headers.get("Lambda-Runtime-Trace-Id");
const requestID = response.headers.get("Lambda-Runtime-Aws-Request-Id");
var request = new Request(baseURL.href, {
method,
headers: response.headers,
body: parseInt(response.headers.get("Content-Length") || "0", 10) > 0 ? await response.blob() : undefined,
});
// we are done with the Response object here
// allow it to be GC'd
(response as any) = undefined;
var result: Response;
try {
if (typeof process.env.VERBOSE !== "undefined") {
console.time(`[${traceID}] Run ${request.url}`);
}
result = handlerFunction(request, {});
if (result && (result as any).then) {
await result;
}
} catch (e1: any) {
if (typeof process.env.VERBOSE !== "undefined") {
console.error(`[${traceID}] Error running handler:`, e1);
}
fetch(`http://${AWS_LAMBDA_RUNTIME_API}/2018-06-01/runtime/invocation/${requestID}/error`, {
method: "POST",
body: JSON.stringify({
errorMessage: e1.message,
errorType: e1.name,
stackTrace: e1?.stack?.split("\n") ?? [],
}),
}).finally(noop);
return;
} finally {
if (typeof process.env.VERBOSE !== "undefined") {
console.timeEnd(`[${traceID}] Run ${request.url}`);
}
}
if (!result || !("headers" in result)) {
await fetch(`http://${AWS_LAMBDA_RUNTIME_API}/2018-06-01/runtime/invocation/${requestID}/error`, {
method: "POST",
body: JSON.stringify({
errorMessage: "Expected Response object",
errorType: "ExpectedResponseObject",
stackTrace: [],
}),
});
return;
}
await fetch(`http://${AWS_LAMBDA_RUNTIME_API}/2018-06-01/runtime/invocation/${requestID}/response`, {
method: "POST",
headers: result.headers,
body: await result.blob(),
});
(result as any) = undefined;
}
while (true) {
fetch(nextURL).then(runHandler, console.error);
}
export {};

View File

@@ -1,48 +0,0 @@
{
"lockfileVersion": 1,
"workspaces": {
"": {
"name": "macros",
"dependencies": {
"moment": "^2.29.1",
"papaparse": "^5.3.1",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react-refresh": "^0.10.0",
},
"devDependencies": {
"@types/react": "^17.0.24",
"@types/react-dom": "^17.0.9",
},
},
},
"packages": {
"@types/prop-types": ["@types/prop-types@15.7.5", "", {}, "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w=="],
"@types/react": ["@types/react@17.0.53", "", { "dependencies": { "@types/prop-types": "*", "@types/scheduler": "*", "csstype": "^3.0.2" } }, "sha512-1yIpQR2zdYu1Z/dc1OxC+MA6GR240u3gcnP4l6mvj/PJiVaqHsQPmWttsvHsfnhfPbU2FuGmo0wSITPygjBmsw=="],
"@types/react-dom": ["@types/react-dom@17.0.19", "", { "dependencies": { "@types/react": "^17" } }, "sha512-PiYG40pnQRdPHnlf7tZnp0aQ6q9tspYr72vD61saO6zFCybLfMqwUCN0va1/P+86DXn18ZWeW30Bk7xlC5eEAQ=="],
"@types/scheduler": ["@types/scheduler@0.16.2", "", {}, "sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew=="],
"csstype": ["csstype@3.1.1", "", {}, "sha512-DJR/VvkAvSZW9bTouZue2sSxDwdTN92uHjqeKVm+0dAqdfNykRzQ95tay8aXMBAAPpUiq4Qcug2L7neoRh2Egw=="],
"js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="],
"loose-envify": ["loose-envify@1.4.0", "", { "dependencies": { "js-tokens": "^3.0.0 || ^4.0.0" }, "bin": { "loose-envify": "cli.js" } }, "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q=="],
"moment": ["moment@2.29.4", "", {}, "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w=="],
"object-assign": ["object-assign@4.1.1", "", {}, "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg=="],
"papaparse": ["papaparse@5.3.2", "", {}, "sha512-6dNZu0Ki+gyV0eBsFKJhYr+MdQYAzFUGlBMNj3GNrmHxmz1lfRa24CjFObPXtjcetlOv5Ad299MhIK0znp3afw=="],
"react": ["react@17.0.2", "", { "dependencies": { "loose-envify": "^1.1.0", "object-assign": "^4.1.1" } }, "sha512-gnhPt75i/dq/z3/6q/0asP78D0u592D5L1pd7M8P+dck6Fu/jJeL6iVVK23fptSUZj8Vjf++7wXA8UNclGQcbA=="],
"react-dom": ["react-dom@17.0.2", "", { "dependencies": { "loose-envify": "^1.1.0", "object-assign": "^4.1.1", "scheduler": "^0.20.2" }, "peerDependencies": { "react": "17.0.2" } }, "sha512-s4h96KtLDUQlsENhMn1ar8t2bEa+q/YAtj8pPPdIjPDGBDIVNsrD9aXNWqspUe6AzKCIG0C1HZZLqLV7qpOBGA=="],
"react-refresh": ["react-refresh@0.10.0", "", {}, "sha512-PgidR3wST3dDYKr6b4pJoqQFpPGNKDSCDx4cZoshjXipw3LzO7mG1My2pwEzz2JVkF+inx3xRpDeQLFQGH/hsQ=="],
"scheduler": ["scheduler@0.20.2", "", { "dependencies": { "loose-envify": "^1.1.0", "object-assign": "^4.1.1" } }, "sha512-2eWfGgAqqWFGqtdMmcL5zCMK1U8KlXv8SQFGglL3CEtd0aDVDWgeF/YoCmvln55m5zSk3J/20hTaSBeSObsQDQ=="],
}
}

View File

@@ -1,30 +0,0 @@
import { fetchCSV } from "macro:fetchCSV";
export const Covid19 = () => {
const rows = fetchCSV("https://covid19.who.int/WHO-COVID-19-global-data.csv", {
last: 100,
columns: ["New_cases", "Date_reported", "Country"],
});
return (
<div>
<h2>Covid-19</h2>
<h6>last {rows.length} updates from the WHO</h6>
<div className="Table">
<div className="Header">
<div className="Heading">New Cases</div>
<div className="Heading">Date</div>
<div className="Heading">Country</div>
</div>
{rows.map((row, index) => (
<div className="Row" key={index}>
<div className="Column">{row[0]}</div>
<div className="Column">{row[1]}</div>
<div className="Column">{row[2]}</div>
</div>
))}
</div>
</div>
);
};

View File

@@ -1,15 +0,0 @@
// source code
import { matchInFile } from "macro:matchInFile";
export const IPAddresses = () => (
<div>
<h2>recent ip addresses</h2>
<div className="Lines">
{matchInFile("access.log", /^(?:[0-9]{1,3}\.){3}[0-9]{1,3}/).map((ipAddress, index) => (
<div className="Line" key={index}>
{ipAddress}
</div>
))}
</div>
</div>
);

View File

@@ -1,15 +0,0 @@
import * as ReactDOM from "react-dom";
import * as React from "react";
import { IPAddresses } from "./example";
import { Covid19 } from "./covid19";
const Start = function () {
const root = document.createElement("div");
document.body.appendChild(root);
// comment out to switch between examples
// ReactDOM.render(<IPAddresses />, root);
ReactDOM.render(<Covid19 />, root);
};
Start();

View File

@@ -1,4 +0,0 @@
// source code
import { mysteryBox } from "macro:./mystery-box";
export default "You roll! " + mysteryBox(123);

View File

@@ -1,54 +0,0 @@
import Pappa from "papaparse";
// Example usage:
// const rows = fetchCSV(
// "https://covid19.who.int/WHO-COVID-19-global-data.csv",
// {
// last: 100,
// columns: ["New_cases", "Date_reported", "Country"],
// }
// );
export async function fetchCSV(callExpression) {
console.time("fetchCSV Total");
const [
urlNode,
{
properties: { last: limit = 10, columns = [] },
},
] = callExpression.arguments;
const url = urlNode.get();
console.time("Fetch");
const response = await fetch(url);
const csvText = await response.text();
console.timeEnd("Fetch");
console.time("Parse");
let rows = Pappa.parse(csvText, { fastMode: true }).data;
console.timeEnd("Parse");
console.time("Render");
const columnIndices = new Array(columns.length);
for (let i = 0; i < columns.length; i++) {
columnIndices[i] = rows[0].indexOf(columns[i]);
}
rows = rows
.slice(Math.max(limit, rows.length) - limit)
.reverse()
.filter(columns => columns.every(Boolean));
const value = (
<array>
{rows.map(columns => (
<array>
{columnIndices.map(columnIndex => (
<string value={columns[columnIndex]} />
))}
</array>
))}
</array>
);
console.timeEnd("Render");
console.timeEnd("fetchCSV Total");
return value;
}

View File

@@ -1,23 +0,0 @@
// macro code
export async function matchInFile(callExpression: BunAST.CallExpression) {
const [filePathNode, matcherNode] = callExpression.arguments;
let filePath: string;
filePath = filePathNode.get();
let matcher: RegExp;
matcher = matcherNode.get();
const file: string = await Bun.file(Bun.cwd + filePath).text();
return (
<array>
{file
.split("\n")
.map(line => line.match(matcher))
.filter(Boolean)
.reverse()
.map(line => (
<string value={line[0]} />
))}
</array>
);
}

View File

@@ -1,13 +0,0 @@
export function mysteryBox(callExpression) {
console.log(callExpression.log);
// get arguments
const [countNode] = callExpression.arguments;
const countString: string = countNode.get();
const count: number = parseInt(countString, 10);
// validate
if (!(count >= 1 && count <= 1000)) return new Error(`Argument ${countString} is expected to be between 1 and 1000`);
// return a value
return (Math.random() * count) | 0;
}

View File

@@ -1,10 +0,0 @@
import moment from "moment";
export function now(node) {
var fmt = "HH:mm:ss";
const args = node.arguments;
if (args[0] instanceof <string />) {
fmt = args[0].get();
}
const time = moment().format(fmt);
return <string value={time}></string>;
}

View File

@@ -1,17 +0,0 @@
{
"name": "macros",
"version": "1.0.0",
"main": "index.js",
"license": "MIT",
"dependencies": {
"moment": "^2.29.1",
"papaparse": "^5.3.1",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react-refresh": "^0.10.0"
},
"devDependencies": {
"@types/react": "^17.0.24",
"@types/react-dom": "^17.0.9"
}
}

View File

@@ -1,14 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<title>Macro test</title>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="stylesheet" href="/styles.css" type="text/css" />
</head>
<body>
<script async type="module" src="/components/index.tsx"></script>
</body>
</html>

View File

@@ -1,47 +0,0 @@
html {
font-size: 4rem;
margin: 0;
padding: 0;
background-color: black;
color: rgb(0, 255, 0);
font-family: "Courier";
}
body {
margin: 48px auto;
text-align: center;
}
.Line {
font-size: 0.5rem;
font-family: monospace;
}
.Table {
display: grid;
width: fit-content;
}
.Row,
.Header {
display: grid;
grid-template-columns: 2fr 1fr 1fr;
text-align: right;
column-gap: 2rem;
}
.Heading {
text-align: right;
}
.Header {
border-bottom: 1px solid rgb(0, 255, 0);
margin-bottom: 20px;
padding-bottom: 20px;
}
.Heading:nth-of-type(2) {
text-align: left;
}

View File

@@ -1,7 +0,0 @@
{
"compilerOptions": {
"baseUrl": ".",
"paths": {},
"jsx": "preserve"
}
}

View File

@@ -1,11 +0,0 @@
const map = Bun.mmap("./mmap.txt", { shared: true });
const utf8decoder = new TextDecoder("utf-8");
let old = new TextEncoder().encode("12345");
setInterval(() => {
old = old.sort((a, b) => (Math.random() > 0.5 ? -1 : 1));
console.log(`changing mmap to ~> ${utf8decoder.decode(old)}`);
map.set(old);
}, 4);

View File

@@ -1,22 +0,0 @@
const map = Bun.mmap("./mmap.txt");
function buffer_hash(buffer) {
let hash = 0;
for (let i = 0; i < buffer.length; i++) {
hash = (hash << 5) - hash + buffer[i];
hash |= 0; // Convert to 32bit integer
}
return hash;
}
const decoder = new TextDecoder();
let hash = buffer_hash(map);
console.log(decoder.decode(map));
while (true) {
if (buffer_hash(map) !== hash) {
hash = buffer_hash(map);
console.log(`mmap changed to ~> ${decoder.decode(map)}`);
}
}

View File

@@ -1 +0,0 @@
43521

View File

@@ -1,23 +0,0 @@
import { resolve } from "path";
import { parse } from "querystring";
export default {
fetch(req) {
const url = new URL(req.url);
if (url.pathname === "/favicon.ico") return new Response("nooo dont open favicon in editor", { status: 404 });
var pathname = req.url.substring(1);
const q = pathname.indexOf("?");
var { editor } = parse(pathname.substring(q + 1)) || {};
if (q > 0) {
pathname = pathname.substring(0, q);
}
Bun.openInEditor(resolve(pathname), {
editor,
});
return new Response(`Opened ${req.url}`);
},
};

File diff suppressed because it is too large Load Diff

View File

@@ -1,33 +0,0 @@
{
"name": "simple-react",
"version": "1.0.0",
"license": "MIT",
"dependencies": {
"@emotion/css": "^11.1.3",
"@vitejs/plugin-react-refresh": "^1.3.3",
"antd": "^4.16.1",
"left-pad": "^1.3.0",
"next": "^11.0.0",
"parcel": "2.0.0-beta.3",
"react": "^17.0.2",
"react-bootstrap": "^1.6.1",
"react-dom": "^17.0.2",
"react-form": "^4.0.1",
"react-hook-form": "^7.8.3"
},
"parcel": "parceldist/index.js",
"targets": {
"parcel": {
"outputFormat": "esmodule",
"sourceMap": false,
"optimize": false,
"engines": {
"chrome": "last 1 version"
}
}
},
"devDependencies": {
"@snowpack/plugin-react-refresh": "^2.5.0",
"typescript": "^4.3.4"
}
}

View File

@@ -1,15 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<link
rel="stylesheet"
crossorigin="anonymous"
href="https://fonts.googleapis.com/css2?family=IBM+Plex+Sans:wght@400;700&family=Space+Mono:wght@400;700&display=swap"
/>
</head>
<body>
<div id="reactroot"></div>
<link rel="stylesheet" href="./src/index.css" />
<script src="./src/index.tsx" async type="module"></script>
</body>
</html>

File diff suppressed because it is too large Load Diff

View File

@@ -1,14 +0,0 @@
:root {
--timestamp: "0";
--interval: "8";
--progress-bar: 11.83299999999997%;
--spinner-1-muted: rgb(142, 6, 182);
--spinner-1-primary: rgb(177, 8, 227);
--spinner-2-muted: rgb(110, 148, 190);
--spinner-2-primary: rgb(138, 185, 238);
--spinner-3-muted: rgb(75, 45, 64);
--spinner-3-primary: rgb(94, 56, 80);
--spinner-4-muted: rgb(155, 129, 108);
--spinner-4-primary: rgb(194, 161, 135);
--spinner-rotate: 213deg;
}

View File

@@ -1,21 +0,0 @@
import React from "react";
export function RenderCounter({ name, children }) {
const counter = React.useRef(1);
return (
<div className="RenderCounter">
<div className="RenderCounter-meta">
<div className="RenderCounter-title">
{name} rendered <strong>{counter.current++} times</strong>
</div>
<div className="RenderCounter-lastRender">
LAST RENDER:{" "}
{new Intl.DateTimeFormat([], {
timeStyle: "long",
}).format(new Date())}
</div>
</div>
<div className="RenderCounter-children">{children}</div>
</div>
);
}

View File

@@ -1,14 +0,0 @@
import * as React from "react";
import { Button } from "./Button";
import { RenderCounter } from "./RenderCounter";
export function App() {
return (
<RenderCounter name="App">
<div className="AppRoot">
<h1>This is the root element</h1>
<Button>Click</Button>
</div>
</RenderCounter>
);
}

View File

@@ -1,9 +0,0 @@
import { RenderCounter } from "./RenderCounter";
export const Button = ({ children }) => {
return (
<RenderCounter name="Button">
<div className="Button">{children}</div>
</RenderCounter>
);
};

View File

@@ -1 +0,0 @@
@import "https://fonts.googleapis.com/css2?family=IBM+Plex+Sans:wght@400;700&family=Space+Mono:wght@400;700&display=swap";

View File

@@ -1,98 +0,0 @@
@import "./colors.css";
:root {
--heading-font: "Space Mono", system-ui;
--body-font: "IBM Plex Sans", system-ui;
--color-brand: #02ff00;
--color-brand-muted: rgb(2, 150, 0);
--padding-horizontal: 90px;
--page-background: black;
--page-background-alpha: rgba(0, 0, 0, 0.8);
--result__background-color: black;
--result__primary-color: var(--color-brand);
--result__foreground-color: white;
--result__muted-color: rgb(165, 165, 165);
--card-width: 352px;
--page-width: 1152px;
--snippets_container-background-unfocused: #171717;
--snippets_container-background-focused: #0017e9;
--snippets_container-background: var(
--snippets_container-background-unfocused
);
--snippets_container-muted-color: rgb(153, 153, 153);
}
body {
color: white;
margin: 0;
padding: 0;
font-family: var(--body-font);
background-color: var(--page-background);
color: var(--result__muted-color);
display: flex;
flex-direction: column;
height: 100%;
}
#reactroot,
#__next,
body,
html {
height: 100%;
}
.RenderCounter {
border: 10px solid var(--snippets_container-background-focused);
margin: 10px;
padding: 10px;
animation: flash 0.2s linear;
animation-fill-mode: forwards;
}
.RenderCounter-meta {
display: flex;
flex-direction: row;
justify-content: space-between;
margin: -10px;
padding: 10px;
background-color: #111;
}
.RenderCounter-lastRender,
.RenderCounter-title {
white-space: nowrap;
color: rgb(153, 153, 153);
}
@keyframes flash {
from {
border-color: var(--snippets_container-background-focused);
}
to {
border-color: var(--snippets_container-background-unfocused);
}
}
.Button {
display: block;
border: 1px solid rgb(20, 180, 0);
background-color: rgb(2, 150, 0);
color: white;
font-weight: 500;
padding: 10px 12px;
border-radius: 4px;
text-transform: uppercase;
text-align: center;
width: fit-content;
cursor: pointer;
}

View File

@@ -1,15 +0,0 @@
import ReactDOM from "react-dom";
import React from "react";
import { App } from "./components/app";
import classNames from "classnames";
function startReact() {
ReactDOM.render(<App />, document.querySelector("#reactroot"));
}
globalThis.addEventListener("DOMContentLoaded", () => {
startReact();
});
startReact();
export { App };

View File

@@ -1,62 +0,0 @@
import React from "react";
export const Main = ({ productName }) => {
return (
<>
<header>
<div className="Title">CSS HMR Stress Test</div>
<p className="Description">
This page visually tests how quickly a bundler can update CSS over Hot Module Reloading.
</p>
</header>
<main className="main">
<section className="ProgressSection">
<p className="Subtitle">
<span className="Subtitle-part">
Ran:&nbsp;<span className="timer"></span>
</span>
</p>
<div className="ProgressBar-container">
<div className="ProgressBar"></div>
</div>
<div className="SectionLabel">The progress bar should move from left to right smoothly.</div>
</section>
<section>
<div className="Spinners">
<div className="Spinner-container Spinner-1">
<div className="Spinner"></div>
</div>
<div className="Spinner-container Spinner-2">
<div className="Spinner"></div>
</div>
<div className="Spinner-container Spinner-3">
<div className="Spinner"></div>
</div>
<div className="Spinner-container Spinner-4">
<div className="Spinner"></div>
</div>
</div>
<div className="SectionLabel">The spinners should rotate &amp; change color smoothly.</div>
</section>
</main>
<footer>
<div className="SectionLabel FooterLabel">There are no CSS animations on this page.</div>
<div className="Bundler-container">
<div className="Bundler">{productName}</div>
<div className="Bundler-updateRate">
Saving a css file every&nbsp;
<span className="highlight">
<span className="interval"></span>ms
</span>
</div>
</div>
</footer>
</>
);
};

View File

@@ -1,29 +0,0 @@
{
"compilerOptions": {
"target": "esnext",
"lib": [
"dom",
"dom.iterable",
"esnext"
],
"allowJs": true,
"skipLibCheck": true,
"strict": false,
"forceConsistentCasingInFileNames": true,
"noEmit": true,
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve"
},
"include": [
"next-env.d.ts",
"**/*.ts",
"**/*.tsx"
],
"exclude": [
"node_modules"
]
}

View File

@@ -1,20 +0,0 @@
// A simple way to connect FileSystemRouter to Bun#serve
// run with `bun run index.tsx`
import { renderToReadableStream } from "react-dom/server";
import { FileSystemRouter } from "bun";
export default {
port: 3000,
async fetch(request: Request) {
const router = new FileSystemRouter({
dir: process.cwd() + "/pages",
style: "nextjs",
});
const route = router.match(request);
const { default: Root } = await import(route.filePath!);
return new Response(await renderToReadableStream(<Root {...route.params} />));
},
};

View File

@@ -1,14 +0,0 @@
{
"name": "react-routes",
"module": "index.tsx",
"type": "module",
"devDependencies": {
"@types/react": "^18.0.27",
"@types/react-dom": "^18.0.10",
"bun-types": "^0.7.0"
},
"dependencies": {
"react": "^18.2.0",
"react-dom": "^18.2.0"
}
}

View File

@@ -1,17 +0,0 @@
// reachable from http://localhost:3000/
export default () => (
<html>
<head>
<title>index</title>
</head>
<body>
<h1>
<a href="/one">one</a>
</h1>
<h1>
<a href="/two">two</a>
</h1>
</body>
</html>
);

View File

@@ -1,12 +0,0 @@
// reachable from http://localhost:3000/one
export default () => (
<html>
<head>
<title>one</title>
</head>
<body>
<p>one</p>
</body>
</html>
);

View File

@@ -1,12 +0,0 @@
// reachable from http://localhost:3000/two
export default () => (
<html>
<head>
<title>two</title>
</head>
<body>
<p>two</p>
</body>
</html>
);

View File

@@ -1,20 +0,0 @@
{
"compilerOptions": {
"lib": [
"ESNext"
],
"module": "esnext",
"target": "esnext",
"moduleResolution": "nodenext",
"strict": false,
"downlevelIteration": true,
"skipLibCheck": true,
"jsx": "preserve",
"allowSyntheticDefaultImports": true,
"forceConsistentCasingInFileNames": true,
"allowJs": true,
"types": [
"bun-types" // add Bun global
]
}
}

View File

@@ -1,68 +0,0 @@
import { SHA1, SHA256, SHA512, SHA384, SHA512_256, MD5, MD4, RIPEMD160, sha } from "bun";
const input = "Hello World";
const [first, second] = input.split(" ");
const log = (name, ...args) => console.log(`${name}:`.padStart("SHA512_256: ".length), ...args);
console.log("");
// This is SHA512-256:
// This function is shorthand for SHA512_256.hash(input)
log("Bun.sha()", sha(input, "base64"));
log("SHA1", SHA1.hash(input, "hex"));
log("SHA256", SHA256.hash(input, "hex"));
log("SHA384", SHA384.hash(input, "hex"));
log("SHA512", SHA512.hash(input, "hex"));
log("SHA512_256", SHA512_256.hash(input, "hex"));
log("RIPEMD160", RIPEMD160.hash(input, "hex"));
console.log("");
console.log("---- Chunked ----");
console.log("");
// You can also do updates in chunks:
// const hash = new Hash();
for (let Hash of [SHA1, SHA256, SHA384, SHA512, SHA512_256, RIPEMD160]) {
const hash = new Hash();
hash.update(first);
hash.update(" " + second);
log(Hash.name, hash.digest("hex"));
}
console.log("");
console.log("---- Base64 ----");
console.log("");
// base64 or hex
for (let Hash of [SHA1, SHA256, SHA384, SHA512, SHA512_256]) {
const hash = new Hash();
hash.update(first);
hash.update(" " + second);
log(Hash.name, hash.digest("base64"));
}
console.log("");
console.log("---- Uint8Array ----");
console.log("");
// Uint8Array by default
for (let Hash of [SHA1, SHA256, SHA384, SHA512, SHA512_256]) {
const hash = new Hash();
hash.update(first);
hash.update(" " + second);
log(Hash.name, hash.digest());
}
console.log("");
console.log("---- Uint8Array can be updated in-place ----");
console.log("");
var oneBuf = new Uint8Array(1024);
// Update Uint8Array in-place instead of allocating a new one
for (let Hash of [SHA1, SHA256, SHA384, SHA512, SHA512_256]) {
const hash = new Hash();
hash.update(first);
hash.update(" " + second);
log(Hash.name, hash.digest(oneBuf).subarray(0, Hash.byteLength));
}

View File

@@ -1,48 +0,0 @@
import { spawn, which } from "bun";
import { rmSync } from "fs";
import { basename } from "path";
const repo = process.argv.at(3) || "TheoBr/vercel-vite-demo";
const target = basename(repo) + "-main";
console.log("Downloading", repo, "to", "/tmp/" + target);
const archive = await fetch(`https://github.com/${repo}/archive/refs/heads/main.tar.gz`);
// remove the directory if it already exists locally
rmSync("/tmp/" + target, { recursive: true, force: true });
const tar = spawn({
cmd: ["tar", "-xzf", "-"],
stdin: archive.body,
stderr: "inherit",
stdout: "inherit",
cwd: "/tmp",
});
await tar.exited;
// if vercel isn't installed, install it
if (!which("vercel")) {
console.log("Installing vercel...");
const installer = spawn(["bun", "install", "-g", "vercel"], {
stderr: "inherit",
stdout: "inherit",
stdin: "inherit",
});
await installer.exited;
if (!which("vercel")) {
throw new Error("Failed to install Vercel CLI");
}
}
const { exited: deployed } = spawn({
cmd: ["vercel", "deploy", "--yes", "--public", target],
stdio: ["inherit", "inherit", "inherit"],
cwd: "/tmp",
});
await deployed;

View File

@@ -1,15 +0,0 @@
import { resolve } from "path";
import type { ServeOptions } from "bun";
const development = process.env.NODE_ENV !== "production";
export default {
fetch(req: Request) {
return new Response(Bun.file(resolve(req.url.substring(1))));
},
// hostname: "0.0.0.0",
port: process.env.PORT || "443",
keyFile: process.env.SSL_KEY_FILE || "./key.pem",
certFile: process.env.SSL_CERTIFICATE_FILE || "./cert.pem",
development,
} as ServeOptions;

View File

@@ -1,49 +0,0 @@
import { listen, connect } from "bun";
var counter = 0;
const msg = Buffer.from("Hello World!");
const handlers = {
open(socket) {
if (!socket.data?.isServer) {
if (!socket.write(msg)) {
socket.data = { pending: msg };
}
}
},
data(socket, buffer) {
if (!socket.write(buffer)) {
socket.data = { pending: buffer };
return;
}
counter++;
},
drain(socket) {
const pending = socket.data?.pending;
if (!pending) return;
if (socket.write(pending)) {
socket.data = undefined;
counter++;
return;
}
},
};
setInterval(() => {
console.log("Wrote", counter, "messages");
counter = 0;
}, 1000);
const server = listen({
socket: handlers,
hostname: "localhost",
port: 8080,
data: {
isServer: true,
},
});
const connection = await connect({
socket: handlers,
hostname: "localhost",
port: 8080,
});

View File

@@ -686,7 +686,7 @@ def add(debugger, *, category, regex=False, type, identifier=None, synth=False,
if summary: debugger.HandleCommand('type summary add --category %s%s%s "%s"' % (category, ' --inline-children' if inline_children else ''.join((' --expand' if expand else '', ' --python-function %s_SummaryProvider' % prefix if summary == True else ' --summary-string "%s"' % summary)), ' --regex' if regex else '', type))
if synth: debugger.HandleCommand('type synthetic add --category %s%s --python-class %s_SynthProvider "%s"' % (category, ' --regex' if regex else '', prefix, type))
def MultiArrayList_Entry(type): return '^multi_array_list\\.MultiArrayList\\(%s\\)\\.Entry__struct_[1-9][0-9]*$' % type
def MultiArrayList_Entry(type): return 'multi_array_list\\.MultiArrayList\\(%s\\)\\.Entry__struct_[1-9][0-9]*$' % type
def __lldb_init_module(debugger, _=None):
# Initialize Zig Categories
@@ -701,8 +701,8 @@ def __lldb_init_module(debugger, _=None):
# Initialize Zig Standard Library
add(debugger, category='zig.std', type='mem.Allocator', summary='${var.ptr}')
add(debugger, category='zig.std', regex=True, type='^segmented_list\\.SegmentedList\\(.*\\)$', identifier='std_SegmentedList', synth=True, expand=True, summary='len=${var.len}')
add(debugger, category='zig.std', regex=True, type='^multi_array_list\\.MultiArrayList\\(.*\\)$', identifier='std_MultiArrayList', synth=True, expand=True, summary='len=${var.len} capacity=${var.capacity}')
add(debugger, category='zig.std', regex=True, type='^multi_array_list\\.MultiArrayList\\(.*\\)\\.Slice$', identifier='std_MultiArrayList_Slice', synth=True, expand=True, summary='len=${var.len} capacity=${var.capacity}')
add(debugger, category='zig.std', regex=True, type='multi_array_list\\.MultiArrayList\\(.*\\)$', identifier='std_MultiArrayList', synth=True, expand=True, summary='len=${var.len} capacity=${var.capacity}')
add(debugger, category='zig.std', regex=True, type='multi_array_list\\.MultiArrayList\\(.*\\)\\.Slice$', identifier='std_MultiArrayList_Slice', synth=True, expand=True, summary='len=${var.len} capacity=${var.capacity}')
add(debugger, category='zig.std', regex=True, type=MultiArrayList_Entry('.*'), identifier='std_Entry', synth=True, inline_children=True, summary=True)
add(debugger, category='zig.std', regex=True, type='^hash_map\\.HashMapUnmanaged\\(.*\\)$', identifier='std_HashMapUnmanaged', synth=True, expand=True, summary=True)
add(debugger, category='zig.std', regex=True, type='^hash_map\\.HashMapUnmanaged\\(.*\\)\\.Entry$', identifier = 'std_Entry', synth=True, inline_children=True, summary=True)

View File

@@ -18,7 +18,7 @@
"react": "^18.3.1",
"react-dom": "^18.3.1",
"source-map-js": "^1.2.0",
"typescript": "^5.7.2"
"typescript": "5.9.2"
},
"resolutions": {
"bun-types": "workspace:packages/bun-types",

View File

@@ -5,7 +5,7 @@
"name": "bun-plugin-svelte",
"devDependencies": {
"@threlte/core": "8.0.1",
"bun-types": "canary",
"@types/bun": "../bun-types",
"svelte": "^5.20.4",
},
"peerDependencies": {
@@ -28,11 +28,13 @@
"@threlte/core": ["@threlte/core@8.0.1", "", { "dependencies": { "mitt": "^3.0.1" }, "peerDependencies": { "svelte": ">=5", "three": ">=0.155" } }, "sha512-vy1xRQppJFNmfPTeiRQue+KmYFsbPgVhwuYXRTvVrwPeD2oYz43gxUeOpe1FACeGKxrxZykeKJF5ebVvl7gBxw=="],
"@types/bun": ["bun-types@file:../bun-types", { "dependencies": { "@types/node": "*" }, "devDependencies": { "@types/react": "^19" }, "peerDependencies": { "@types/react": "^19" } }],
"@types/estree": ["@types/estree@1.0.6", "", {}, "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw=="],
"@types/node": ["@types/node@22.13.5", "", { "dependencies": { "undici-types": "~6.20.0" } }, "sha512-+lTU0PxZXn0Dr1NBtC7Y8cR21AJr87dLLU953CWA6pMxxv/UDc7jYAY90upcrie1nRcD6XNG5HOYEDtgW5TxAg=="],
"@types/ws": ["@types/ws@8.5.14", "", { "dependencies": { "@types/node": "*" } }, "sha512-bd/YFLW+URhBzMXurx7lWByOu+xzU9+kb3RboOteXYDfW+tr+JZa99OyNmPINEGB/ahzKrEuc8rcv4gnpJmxTw=="],
"@types/react": ["@types/react@19.1.9", "", { "dependencies": { "csstype": "^3.0.2" } }, "sha512-WmdoynAX8Stew/36uTSVMcLJJ1KRh6L3IZRx1PZ7qJtBqT3dYTgyDTx8H1qoRghErydW7xw9mSJ3wS//tCRpFA=="],
"acorn": ["acorn@8.14.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA=="],
@@ -42,10 +44,10 @@
"axobject-query": ["axobject-query@4.1.0", "", {}, "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ=="],
"bun-types": ["bun-types@1.2.4-canary.20250226T140704", "", { "dependencies": { "@types/node": "*", "@types/ws": "~8.5.10" } }, "sha512-P8b2CGLtbvi/kQ4dPHBhU5qkguIjHMYCjNqjWDTKSnodWDTbcv9reBdktZJ7m5SF4m15JLthfFq2PtwKpA9a+w=="],
"clsx": ["clsx@2.1.1", "", {}, "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA=="],
"csstype": ["csstype@3.1.3", "", {}, "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="],
"esm-env": ["esm-env@1.2.2", "", {}, "sha512-Epxrv+Nr/CaL4ZcFGPJIYLWFom+YeV1DqMLHJoEd9SYRxNbaFruBwfEX/kkHUJf55j2+TUbmDcmuilbP1TmXHA=="],
"esrap": ["esrap@1.4.5", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.4.15" } }, "sha512-CjNMjkBWWZeHn+VX+gS8YvFwJ5+NDhg8aWZBSFJPR8qQduDNjbJodA2WcwCm7uQa5Rjqj+nZvVmceg1RbHFB9g=="],

View File

@@ -23,7 +23,7 @@
"build:types": "tsc --emitDeclarationOnly --declaration --declarationDir ./dist"
},
"devDependencies": {
"bun-types": "canary",
"@types/bun": "../bun-types",
"svelte": "^5.20.4",
"@threlte/core": "8.0.1"
},

View File

@@ -21,7 +21,7 @@ declare module "bun" {
| DataView<TArrayBuffer>;
type BufferSource = NodeJS.TypedArray | DataView | ArrayBufferLike;
type StringOrBuffer = string | NodeJS.TypedArray | ArrayBufferLike;
type XMLHttpRequestBodyInit = Blob | BufferSource | string | FormData | Iterable<Uint8Array>;
type XMLHttpRequestBodyInit = Blob | BufferSource | FormData | URLSearchParams | string;
type ReadableStreamController<T> = ReadableStreamDefaultController<T>;
type ReadableStreamDefaultReadResult<T> =
| ReadableStreamDefaultReadValueResult<T>
@@ -826,7 +826,7 @@ declare module "bun" {
buffers: Array<ArrayBufferView | ArrayBufferLike>,
maxLength: number,
asUint8Array: true,
): Uint8Array;
): Uint8Array<ArrayBuffer>;
/**
* Consume all data from a {@link ReadableStream} until it closes or errors.
@@ -843,35 +843,6 @@ declare module "bun" {
stream: ReadableStream<ArrayBufferView | ArrayBufferLike>,
): Promise<ArrayBuffer> | ArrayBuffer;
/**
* Consume all data from a {@link ReadableStream} until it closes or errors.
*
* Concatenate the chunks into a single {@link ArrayBuffer}.
*
* Each chunk must be a TypedArray or an ArrayBuffer. If you need to support
* chunks of different types, consider {@link readableStreamToBlob}
*
* @param stream The stream to consume.
* @returns A promise that resolves with the concatenated chunks or the concatenated chunks as a {@link Uint8Array}.
*
* @deprecated Use {@link ReadableStream.bytes}
*/
function readableStreamToBytes(
stream: ReadableStream<ArrayBufferView | ArrayBufferLike>,
): Promise<Uint8Array> | Uint8Array;
/**
* Consume all data from a {@link ReadableStream} until it closes or errors.
*
* Concatenate the chunks into a single {@link Blob}.
*
* @param stream The stream to consume.
* @returns A promise that resolves with the concatenated chunks as a {@link Blob}.
*
* @deprecated Use {@link ReadableStream.blob}
*/
function readableStreamToBlob(stream: ReadableStream): Promise<Blob>;
/**
* Consume all data from a {@link ReadableStream} until it closes or errors.
*
@@ -904,30 +875,6 @@ declare module "bun" {
multipartBoundaryExcludingDashes?: string | NodeJS.TypedArray | ArrayBufferView,
): Promise<FormData>;
/**
* Consume all data from a {@link ReadableStream} until it closes or errors.
*
* Concatenate the chunks into a single string. Chunks must be a TypedArray or an ArrayBuffer. If you need to support chunks of different types, consider {@link readableStreamToBlob}.
*
* @param stream The stream to consume.
* @returns A promise that resolves with the concatenated chunks as a {@link String}.
*
* @deprecated Use {@link ReadableStream.text}
*/
function readableStreamToText(stream: ReadableStream): Promise<string>;
/**
* Consume all data from a {@link ReadableStream} until it closes or errors.
*
* Concatenate the chunks into a single string and parse as JSON. Chunks must be a TypedArray or an ArrayBuffer. If you need to support chunks of different types, consider {@link readableStreamToBlob}.
*
* @param stream The stream to consume.
* @returns A promise that resolves with the concatenated chunks as a {@link String}.
*
* @deprecated Use {@link ReadableStream.json}
*/
function readableStreamToJSON(stream: ReadableStream): Promise<any>;
/**
* Consume all data from a {@link ReadableStream} until it closes or errors.
*
@@ -1027,8 +974,8 @@ declare module "bun" {
*
* This API might change later to separate Uint8ArraySink and ArrayBufferSink
*/
flush(): number | Uint8Array | ArrayBuffer;
end(): ArrayBuffer | Uint8Array;
flush(): number | Uint8Array<ArrayBuffer> | ArrayBuffer;
end(): ArrayBuffer | Uint8Array<ArrayBuffer>;
}
/** DNS Related APIs */
@@ -1595,13 +1542,18 @@ declare module "bun" {
* Executes a SQL query using template literals
* @example
* ```ts
* const [user] = await sql`select * from users where id = ${1}`;
* const [user] = await sql<Users[]>`select * from users where id = ${1}`;
* ```
*/
<T = any>(strings: TemplateStringsArray, ...values: unknown[]): SQL.Query<T>;
/**
* Execute a SQL query using a string
*
* @example
* ```ts
* const users = await sql<User[]>`SELECT * FROM users WHERE id = ${1}`;
* ```
*/
<T = any>(string: string): SQL.Query<T>;
@@ -1620,6 +1572,23 @@ declare module "bun" {
* const result = await sql`insert into users ${sql(user)} returning *`;
* ```
*/
<T extends { [Key in PropertyKey]: unknown }>(obj: T | T[] | readonly T[]): SQL.Helper<T>;
/**
* Helper function for inserting an object into a query, supporting specific columns
*
* @example
* ```ts
* // Insert an object
* const result = await sql`insert into users ${sql(users)} returning *`;
*
* // Or pick specific columns
* const result = await sql`insert into users ${sql(users, "id", "name")} returning *`;
*
* // Or a single object
* const result = await sql`insert into users ${sql(user)} returning *`;
* ```
*/
<T extends { [Key in PropertyKey]: unknown }, Keys extends keyof T = keyof T>(
obj: T | T[] | readonly T[],
...columns: readonly Keys[]
@@ -3682,7 +3651,7 @@ declare module "bun" {
/**
* If set, the HTTP server will listen on a unix socket instead of a port.
* (Cannot be used with hostname+port)
* (Cannot use unix with port + hostname)
*/
unix?: never;
@@ -3705,9 +3674,21 @@ declare module "bun" {
interface UnixServeOptions extends GenericServeOptions {
/**
* If set, the HTTP server will listen on a unix socket instead of a port.
* (Cannot be used with hostname+port)
*/
unix: string;
/**
* If set, the HTTP server will listen on this port
* (Cannot use port with unix)
*/
port?: never;
/**
* If set, the HTTP server will listen on this hostname
* (Cannot use hostname with unix)
*/
hostname?: never;
/**
* Handle HTTP requests
*
@@ -4635,7 +4616,7 @@ declare module "bun" {
*
* @param path The path to the file as a byte buffer (the buffer is copied) if the path starts with `s3://` it will behave like {@link S3File}
*/
function file(path: ArrayBufferLike | Uint8Array, options?: BlobPropertyBag): BunFile;
function file(path: ArrayBufferLike | Uint8Array<ArrayBuffer>, options?: BlobPropertyBag): BunFile;
/**
* [`Blob`](https://developer.mozilla.org/en-US/docs/Web/API/Blob) powered by the fastest system calls available for operating on files.
@@ -4658,7 +4639,7 @@ declare module "bun" {
*
* This can be 3.5x faster than `new Uint8Array(size)`, but if you send uninitialized memory to your users (even unintentionally), it can potentially leak anything recently in memory.
*/
function allocUnsafe(size: number): Uint8Array;
function allocUnsafe(size: number): Uint8Array<ArrayBuffer>;
/**
* Options for `Bun.inspect`
@@ -4941,7 +4922,7 @@ declare module "bun" {
*
* To close the file, set the array to `null` and it will be garbage collected eventually.
*/
function mmap(path: PathLike, opts?: MMapOptions): Uint8Array;
function mmap(path: PathLike, opts?: MMapOptions): Uint8Array<ArrayBuffer>;
/**
* Write to stdout
@@ -4971,8 +4952,8 @@ declare module "bun" {
| { r: number; g: number; b: number; a?: number }
| [number, number, number]
| [number, number, number, number]
| Uint8Array
| Uint8ClampedArray
| Uint8Array<ArrayBuffer>
| Uint8ClampedArray<ArrayBuffer>
| Float32Array
| Float64Array
| string
@@ -5095,7 +5076,7 @@ declare module "bun" {
*
* **The input buffer must not be garbage collected**. That means you will need to hold on to it for the duration of the string's lifetime.
*/
function arrayBufferToString(buffer: Uint8Array | ArrayBufferLike): string;
function arrayBufferToString(buffer: Uint8Array<ArrayBuffer> | ArrayBufferLike): string;
/**
* Cast bytes to a `String` without copying. This is the fastest way to get a `String` from a `Uint16Array`
@@ -5644,9 +5625,9 @@ declare module "bun" {
* @returns The output buffer with the compressed data
*/
function deflateSync(
data: Uint8Array | string | ArrayBuffer,
data: Uint8Array<ArrayBuffer> | string | ArrayBuffer,
options?: ZlibCompressionOptions | LibdeflateCompressionOptions,
): Uint8Array;
): Uint8Array<ArrayBuffer>;
/**
* Compresses a chunk of data with `zlib` GZIP algorithm.
* @param data The buffer of data to compress
@@ -5654,27 +5635,27 @@ declare module "bun" {
* @returns The output buffer with the compressed data
*/
function gzipSync(
data: Uint8Array | string | ArrayBuffer,
data: Uint8Array<ArrayBuffer> | string | ArrayBuffer,
options?: ZlibCompressionOptions | LibdeflateCompressionOptions,
): Uint8Array;
): Uint8Array<ArrayBuffer>;
/**
* Decompresses a chunk of data with `zlib` INFLATE algorithm.
* @param data The buffer of data to decompress
* @returns The output buffer with the decompressed data
*/
function inflateSync(
data: Uint8Array | string | ArrayBuffer,
data: Uint8Array<ArrayBuffer> | string | ArrayBuffer,
options?: ZlibCompressionOptions | LibdeflateCompressionOptions,
): Uint8Array;
): Uint8Array<ArrayBuffer>;
/**
* Decompresses a chunk of data with `zlib` GUNZIP algorithm.
* @param data The buffer of data to decompress
* @returns The output buffer with the decompressed data
*/
function gunzipSync(
data: Uint8Array | string | ArrayBuffer,
data: Uint8Array<ArrayBuffer> | string | ArrayBuffer,
options?: ZlibCompressionOptions | LibdeflateCompressionOptions,
): Uint8Array;
): Uint8Array<ArrayBuffer>;
/**
* Compresses a chunk of data with the Zstandard (zstd) compression algorithm.
@@ -6651,7 +6632,7 @@ declare module "bun" {
interface BinaryTypeList {
arraybuffer: ArrayBuffer;
buffer: Buffer;
uint8array: Uint8Array;
uint8array: Uint8Array<ArrayBuffer>;
// TODO: DataView
// dataview: DataView;
}
@@ -6829,6 +6810,7 @@ declare module "bun" {
* The unix socket to listen on or connect to
*/
unix: string;
/**
* TLS Configuration with which to create the socket
*/
@@ -7223,7 +7205,7 @@ declare module "bun" {
}
type ReadableToIO<X extends Readable> = X extends "pipe" | undefined
? ReadableStream<Uint8Array>
? ReadableStream<Uint8Array<ArrayBuffer>>
: X extends BunFile | ArrayBufferView | number
? number
: undefined;

View File

@@ -1,4 +1,57 @@
declare module "bun" {
/**
* Consume all data from a {@link ReadableStream} until it closes or errors.
*
* Concatenate the chunks into a single {@link ArrayBuffer}.
*
* Each chunk must be a TypedArray or an ArrayBuffer. If you need to support
* chunks of different types, consider {@link readableStreamToBlob}
*
* @param stream The stream to consume.
* @returns A promise that resolves with the concatenated chunks or the concatenated chunks as a {@link Uint8Array}.
*
* @deprecated Use {@link ReadableStream.bytes}
*/
function readableStreamToBytes(
stream: ReadableStream<ArrayBufferView | ArrayBufferLike>,
): Promise<Uint8Array<ArrayBuffer>> | Uint8Array<ArrayBuffer>;
/**
* Consume all data from a {@link ReadableStream} until it closes or errors.
*
* Concatenate the chunks into a single {@link Blob}.
*
* @param stream The stream to consume.
* @returns A promise that resolves with the concatenated chunks as a {@link Blob}.
*
* @deprecated Use {@link ReadableStream.blob}
*/
function readableStreamToBlob(stream: ReadableStream): Promise<Blob>;
/**
* Consume all data from a {@link ReadableStream} until it closes or errors.
*
* Concatenate the chunks into a single string. Chunks must be a TypedArray or an ArrayBuffer. If you need to support chunks of different types, consider {@link readableStreamToBlob}.
*
* @param stream The stream to consume.
* @returns A promise that resolves with the concatenated chunks as a {@link String}.
*
* @deprecated Use {@link ReadableStream.text}
*/
function readableStreamToText(stream: ReadableStream): Promise<string>;
/**
* Consume all data from a {@link ReadableStream} until it closes or errors.
*
* Concatenate the chunks into a single string and parse as JSON. Chunks must be a TypedArray or an ArrayBuffer. If you need to support chunks of different types, consider {@link readableStreamToBlob}.
*
* @param stream The stream to consume.
* @returns A promise that resolves with the concatenated chunks as a {@link String}.
*
* @deprecated Use {@link ReadableStream.json}
*/
function readableStreamToJSON(stream: ReadableStream): Promise<any>;
interface BunMessageEvent<T> {
/**
* @deprecated
@@ -31,6 +84,9 @@ declare module "bun" {
*/
type Errorlike = ErrorLike;
/** @deprecated This is unused in Bun's types and may be removed in the future */
type ShellFunction = (input: Uint8Array<ArrayBuffer>) => Uint8Array<ArrayBuffer>;
interface TLSOptions {
/**
* File path to a TLS key
@@ -59,7 +115,7 @@ declare module "bun" {
}
/** @deprecated This type is unused in Bun's declarations and may be removed in the future */
type ReadableIO = ReadableStream<Uint8Array> | number | undefined;
type ReadableIO = ReadableStream<Uint8Array<ArrayBuffer>> | number | undefined;
}
declare namespace NodeJS {

View File

@@ -1,19 +1,21 @@
/*
This file does not declare any global types.
That should only happen in [./globals.d.ts](./globals.d.ts)
so that our documentation generator can pick it up, as it
expects all globals to be declared in one file.
* This file does not declare any global types.
*
* That should only happen in [./globals.d.ts](./globals.d.ts)
* so that our documentation generator can pick it up, as it
* expects all globals to be declared in one file.
*
* This may change in the future, which would be
* a nice thing as it would allow us to split up
* relevant types into their own files.
*/
declare module "bun" {
type HeadersInit = string[][] | Record<string, string | ReadonlyArray<string>> | Headers;
type BodyInit =
| ReadableStream
| Bun.XMLHttpRequestBodyInit
| URLSearchParams
// Extras that Bun supports:
| AsyncIterable<string | ArrayBuffer | ArrayBufferView>
| AsyncGenerator<string | ArrayBuffer | ArrayBufferView>
| (() => AsyncGenerator<string | ArrayBuffer | ArrayBufferView>);
@@ -26,7 +28,7 @@ declare module "bun" {
? {}
: Omit<import("undici-types").RequestInit, "body" | "headers"> & {
body?: Bun.BodyInit | null | undefined;
headers?: Bun.HeadersInit;
headers?: Bun.HeadersInit | undefined;
};
interface BunHeadersOverride extends LibOrFallbackHeaders {

View File

@@ -999,6 +999,7 @@ interface ArrayBuffer {
* Read-only. The length of the ArrayBuffer (in bytes).
*/
readonly byteLength: number;
/**
* Resize an ArrayBuffer in-place.
*/
@@ -1008,7 +1009,6 @@ interface ArrayBuffer {
* Returns a section of an ArrayBuffer.
*/
slice(begin: number, end?: number): ArrayBuffer;
readonly [Symbol.toStringTag]: string;
}
interface SharedArrayBuffer {
@@ -1284,7 +1284,7 @@ interface ImportMeta {
* )
* ```
*/
readonly main: boolean;
main: boolean;
/** Alias of `import.meta.dir`. Exists for Node.js compatibility */
dirname: string;
@@ -1426,12 +1426,12 @@ interface Blob {
/**
* Returns a promise that resolves to the contents of the blob as a Uint8Array (array of bytes) its the same as `new Uint8Array(await blob.arrayBuffer())`
*/
bytes(): Promise<Uint8Array>;
bytes(): Promise<Uint8Array<ArrayBuffer>>;
/**
* Returns a readable stream of the blob's contents
*/
stream(): ReadableStream<Uint8Array>;
stream(): ReadableStream<Uint8Array<ArrayBuffer>>;
}
declare var Blob: Bun.__internal.UseLibDomIfAvailable<
@@ -1506,14 +1506,14 @@ interface Uint8ArrayConstructor {
alphabet?: "base64" | "base64url";
lastChunkHandling?: "loose" | "strict" | "stop-before-partial";
},
): Uint8Array;
): Uint8Array<ArrayBuffer>;
/**
* Create a new Uint8Array from a hex encoded string
* @param hex The hex encoded string to convert to a Uint8Array
* @returns A new Uint8Array containing the decoded data
*/
fromHex(hex: string): Uint8Array;
fromHex(hex: string): Uint8Array<ArrayBuffer>;
}
interface BroadcastChannel extends Bun.__internal.LibEmptyOrBroadcastChannel {}

View File

@@ -6,14 +6,17 @@ declare module "stream/web" {
* Consume a ReadableStream as text
*/
text(): Promise<string>;
/**
* Consume a ReadableStream as a Uint8Array
*/
bytes(): Promise<Uint8Array>;
bytes(): Promise<Uint8Array<ArrayBuffer>>;
/**
* Consume a ReadableStream as JSON
*/
json(): Promise<any>;
/**
* Consume a ReadableStream as a Blob
*/

View File

@@ -10,10 +10,11 @@
},
"files": [
"./*.d.ts",
"docs/**/*.md",
"docs/*.md",
"CLAUDE.md",
"README.md"
"./vendor/**/*.d.ts",
"./docs/**/*.md",
"./docs/*.md",
"./CLAUDE.md",
"./README.md"
],
"homepage": "https://bun.com",
"dependencies": {
@@ -23,8 +24,7 @@
"@types/react": "^19"
},
"devDependencies": {
"@types/react": "^19",
"typescript": "^5.0.2"
"@types/react": "^19"
},
"scripts": {
"prebuild": "echo $(pwd)",

View File

@@ -487,8 +487,8 @@ declare module "bun" {
* // Process text chunk by chunk
* }
*/
readonly readable: ReadableStream;
stream(): ReadableStream;
readonly readable: ReadableStream<Uint8Array<ArrayBuffer>>;
stream(): ReadableStream<Uint8Array<ArrayBuffer>>;
/**
* The name or path of the file in the bucket.

View File

@@ -1,6 +1,4 @@
declare module "bun" {
type ShellFunction = (input: Uint8Array) => Uint8Array;
type ShellExpression =
| { toString(): string }
| Array<ShellExpression>
@@ -294,7 +292,7 @@ declare module "bun" {
* console.log(output.bytes()); // Uint8Array { byteLength: 6 }
* ```
*/
bytes(): Uint8Array;
bytes(): Uint8Array<ArrayBuffer>;
}
interface ShellOutput {
@@ -361,7 +359,7 @@ declare module "bun" {
* console.log(output.bytes()); // Uint8Array { byteLength: 6 }
* ```
*/
bytes(): Uint8Array;
bytes(): Uint8Array<ArrayBuffer>;
/**
* Read from stdout as a Blob

View File

@@ -383,19 +383,28 @@ declare module "bun:sqlite" {
* ]);
* ```
*/
transaction(insideTransaction: (...args: any) => void): CallableFunction & {
transaction<A extends any[], T>(
insideTransaction: (...args: A) => T,
): {
/**
* uses "BEGIN DEFERRED"
* Execute the transaction
*/
deferred: (...args: any) => void;
(...args: A): T;
/**
* uses "BEGIN IMMEDIATE"
* Execute the transaction using "BEGIN DEFERRED"
*/
immediate: (...args: any) => void;
deferred: (...args: A) => T;
/**
* uses "BEGIN EXCLUSIVE"
* Execute the transaction using "BEGIN IMMEDIATE"
*/
exclusive: (...args: any) => void;
immediate: (...args: A) => T;
/**
* Execute the transaction using "BEGIN EXCLUSIVE"
*/
exclusive: (...args: A) => T;
};
/**
@@ -664,7 +673,7 @@ declare module "bun:sqlite" {
[Symbol.iterator](): IterableIterator<ReturnType>;
/**
* Execute the prepared statement. This returns `undefined`.
* Execute the prepared statement.
*
* @param params optional values to bind to the statement. If omitted, the statement is run with the last bound values or no parameters if there are none.
*

View File

@@ -10,6 +10,7 @@ declare var test: typeof import("bun:test").test;
declare var it: typeof import("bun:test").it;
declare var describe: typeof import("bun:test").describe;
declare var expect: typeof import("bun:test").expect;
declare var expectTypeOf: typeof import("bun:test").expectTypeOf;
declare var beforeAll: typeof import("bun:test").beforeAll;
declare var beforeEach: typeof import("bun:test").beforeEach;
declare var afterEach: typeof import("bun:test").afterEach;

View File

@@ -56,6 +56,11 @@ declare module "bun:test" {
* Restore the previous value of mocks.
*/
restore(): void;
/**
* Reset all mock function state (calls, results, etc.) without restoring their original implementation.
*/
clearAllMocks(): void;
};
/**
@@ -1642,6 +1647,26 @@ declare module "bun:test" {
*/
toHaveReturnedTimes(times: number): void;
/**
* Ensures that a mock function has returned a specific value.
* This matcher uses deep equality, like toEqual(), and supports asymmetric matchers.
*/
toHaveReturnedWith(expected: unknown): void;
/**
* Ensures that a mock function has returned a specific value on its last invocation.
* This matcher uses deep equality, like toEqual(), and supports asymmetric matchers.
*/
toHaveLastReturnedWith(expected: unknown): void;
/**
* Ensures that a mock function has returned a specific value on the nth invocation.
* This matcher uses deep equality, like toEqual(), and supports asymmetric matchers.
* @param n The 1-based index of the function call
* @param expected The expected return value
*/
toHaveNthReturnedWith(n: number, expected: unknown): void;
/**
* Ensures that a mock function is called.
*/
@@ -2182,4 +2207,6 @@ declare module "bun:test" {
export type UnknownFunction = (...args: unknown[]) => unknown;
}
export const expectTypeOf: typeof import("./vendor/expect-type").expectTypeOf;
}

View File

@@ -0,0 +1,283 @@
/*
Copyright 2024 Misha Kaletsky
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
*/
import type { ConstructorOverloadParameters, NumOverloads, OverloadsInfoUnion } from "./overloads";
import type {
IsAny,
IsNever,
IsUnknown,
MutuallyExtends,
OptionalKeys,
ReadonlyKeys,
RequiredKeys,
UnionToTuple,
} from "./utils";
/**
* Represents a deeply branded type.
*
* Recursively walk a type and replace it with a branded type related to the
* original. This is useful for equality-checking stricter than
* `A extends B ? B extends A ? true : false : false`, because it detects the
* difference between a few edge-case types that vanilla TypeScript
* doesn't by default:
* - `any` vs `unknown`
* - `{ readonly a: string }` vs `{ a: string }`
* - `{ a?: string }` vs `{ a: string | undefined }`
*
* __Note__: not very performant for complex types - this should only be used
* when you know you need it. If doing an equality check, it's almost always
* better to use {@linkcode StrictEqualUsingTSInternalIdenticalToOperator}.
*/
export type DeepBrand<T> =
IsNever<T> extends true
? {
type: "never";
}
: IsAny<T> extends true
? {
type: "any";
}
: IsUnknown<T> extends true
? {
type: "unknown";
}
: T extends string | number | boolean | symbol | bigint | null | undefined | void
? {
type: "primitive";
value: T;
}
: T extends new (...args: any[]) => any
? {
type: "constructor";
params: ConstructorOverloadParameters<T>;
instance: DeepBrand<InstanceType<Extract<T, new (...args: any) => any>>>;
}
: T extends (...args: infer P) => infer R
? NumOverloads<T> extends 1
? {
type: "function";
params: DeepBrand<P>;
return: DeepBrand<R>;
this: DeepBrand<ThisParameterType<T>>;
props: DeepBrand<Omit<T, keyof Function>>;
}
: UnionToTuple<OverloadsInfoUnion<T>> extends infer OverloadsTuple
? {
type: "overloads";
overloads: {
[K in keyof OverloadsTuple]: DeepBrand<OverloadsTuple[K]>;
};
}
: never
: T extends any[]
? {
type: "array";
items: {
[K in keyof T]: T[K];
};
}
: {
type: "object";
properties: {
[K in keyof T]: DeepBrand<T[K]>;
};
readonly: ReadonlyKeys<T>;
required: RequiredKeys<T>;
optional: OptionalKeys<T>;
constructorParams: DeepBrand<ConstructorOverloadParameters<T>>;
};
/**
* Checks if two types are strictly equal using branding.
*/
export type StrictEqualUsingBranding<Left, Right> = MutuallyExtends<DeepBrand<Left>, DeepBrand<Right>>;

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,395 @@
/*
Copyright 2024 Misha Kaletsky
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
*/
import type { StrictEqualUsingBranding } from "./branding";
import type { And, Extends, ExtendsExcludingAnyOrNever, IsAny, IsNever, IsUnknown, Not, UsefulKeys } from "./utils";
/**
* Determines the printable type representation for a given type.
*/
export type PrintType<T> =
IsUnknown<T> extends true
? "unknown"
: IsNever<T> extends true
? "never"
: IsAny<T> extends true
? never
: boolean extends T
? "boolean"
: T extends boolean
? `literal boolean: ${T}`
: string extends T
? "string"
: T extends string
? `literal string: ${T}`
: number extends T
? "number"
: T extends number
? `literal number: ${T}`
: bigint extends T
? "bigint"
: T extends bigint
? `literal bigint: ${T}`
: T extends null
? "null"
: T extends undefined
? "undefined"
: T extends (...args: any[]) => any
? "function"
: "...";
/**
* Helper for showing end-user a hint why their type assertion is failing.
* This swaps "leaf" types with a literal message about what the actual and
* expected types are. Needs to check for `Not<IsAny<Actual>>` because
* otherwise `LeafTypeOf<Actual>` returns `never`, which extends everything 🤔
*/
export type MismatchInfo<Actual, Expected> =
And<[Extends<PrintType<Actual>, "...">, Not<IsAny<Actual>>]> extends true
? And<[Extends<any[], Actual>, Extends<any[], Expected>]> extends true
? Array<MismatchInfo<Extract<Actual, any[]>[number], Extract<Expected, any[]>[number]>>
: {
[K in UsefulKeys<Actual> | UsefulKeys<Expected>]: MismatchInfo<
K extends keyof Actual ? Actual[K] : never,
K extends keyof Expected ? Expected[K] : never
>;
}
: StrictEqualUsingBranding<Actual, Expected> extends true
? Actual
: `Expected: ${PrintType<Expected>}, Actual: ${PrintType<Exclude<Actual, Expected>>}`;
/**
* @internal
*/
declare const inverted: unique symbol;
/**
* @internal
*/
type Inverted<T> = {
[inverted]: T;
};
/**
* @internal
*/
declare const expectNull: unique symbol;
export type ExpectNull<T> = {
[expectNull]: T;
result: ExtendsExcludingAnyOrNever<T, null>;
};
/**
* @internal
*/
declare const expectUndefined: unique symbol;
export type ExpectUndefined<T> = {
[expectUndefined]: T;
result: ExtendsExcludingAnyOrNever<T, undefined>;
};
/**
* @internal
*/
declare const expectNumber: unique symbol;
export type ExpectNumber<T> = {
[expectNumber]: T;
result: ExtendsExcludingAnyOrNever<T, number>;
};
/**
* @internal
*/
declare const expectString: unique symbol;
export type ExpectString<T> = {
[expectString]: T;
result: ExtendsExcludingAnyOrNever<T, string>;
};
/**
* @internal
*/
declare const expectBoolean: unique symbol;
export type ExpectBoolean<T> = {
[expectBoolean]: T;
result: ExtendsExcludingAnyOrNever<T, boolean>;
};
/**
* @internal
*/
declare const expectVoid: unique symbol;
export type ExpectVoid<T> = {
[expectVoid]: T;
result: ExtendsExcludingAnyOrNever<T, void>;
};
/**
* @internal
*/
declare const expectFunction: unique symbol;
export type ExpectFunction<T> = {
[expectFunction]: T;
result: ExtendsExcludingAnyOrNever<T, (...args: any[]) => any>;
};
/**
* @internal
*/
declare const expectObject: unique symbol;
export type ExpectObject<T> = {
[expectObject]: T;
result: ExtendsExcludingAnyOrNever<T, object>;
};
/**
* @internal
*/
declare const expectArray: unique symbol;
export type ExpectArray<T> = {
[expectArray]: T;
result: ExtendsExcludingAnyOrNever<T, any[]>;
};
/**
* @internal
*/
declare const expectSymbol: unique symbol;
export type ExpectSymbol<T> = {
[expectSymbol]: T;
result: ExtendsExcludingAnyOrNever<T, symbol>;
};
/**
* @internal
*/
declare const expectAny: unique symbol;
export type ExpectAny<T> = {
[expectAny]: T;
result: IsAny<T>;
};
/**
* @internal
*/
declare const expectUnknown: unique symbol;
export type ExpectUnknown<T> = {
[expectUnknown]: T;
result: IsUnknown<T>;
};
/**
* @internal
*/
declare const expectNever: unique symbol;
export type ExpectNever<T> = {
[expectNever]: T;
result: IsNever<T>;
};
/**
* @internal
*/
declare const expectNullable: unique symbol;
export type ExpectNullable<T> = {
[expectNullable]: T;
result: Not<StrictEqualUsingBranding<T, NonNullable<T>>>;
};
/**
* @internal
*/
declare const expectBigInt: unique symbol;
export type ExpectBigInt<T> = {
[expectBigInt]: T;
result: ExtendsExcludingAnyOrNever<T, bigint>;
};
/**
* Checks if the result of an expecter matches the specified options, and
* resolves to a fairly readable error message if not.
*/
export type Scolder<
Expecter extends {
result: boolean;
},
Options extends {
positive: boolean;
},
> = Expecter["result"] extends Options["positive"]
? () => true
: Options["positive"] extends true
? Expecter
: Inverted<Expecter>;
export {};

View File

@@ -0,0 +1,669 @@
/*
Copyright 2024 Misha Kaletsky
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
*/
import type {
IsNever,
StrictEqualUsingTSInternalIdenticalToOperator,
UnionToIntersection,
UnionToTuple,
} from "./utils";
/**
* The simple(ish) way to get overload info from a function
* {@linkcode FunctionType}. Recent versions of TypeScript will match any
* function against a generic 10-overload type, filling in slots with
* duplicates of the function. So, we can just match against a single type
* and get all the overloads.
*
* For older versions of TypeScript, we'll need to painstakingly do
* ten separate matches.
*/
export type TSPost53OverloadsInfoUnion<FunctionType> = FunctionType extends {
(...args: infer A1): infer R1;
(...args: infer A2): infer R2;
(...args: infer A3): infer R3;
(...args: infer A4): infer R4;
(...args: infer A5): infer R5;
(...args: infer A6): infer R6;
(...args: infer A7): infer R7;
(...args: infer A8): infer R8;
(...args: infer A9): infer R9;
(...args: infer A10): infer R10;
}
?
| ((...p: A1) => R1)
| ((...p: A2) => R2)
| ((...p: A3) => R3)
| ((...p: A4) => R4)
| ((...p: A5) => R5)
| ((...p: A6) => R6)
| ((...p: A7) => R7)
| ((...p: A8) => R8)
| ((...p: A9) => R9)
| ((...p: A10) => R10)
: never;
/**
* A function with `unknown` parameters and return type.
*/
export type UnknownFunction = (...args: unknown[]) => unknown;
/**
* `true` iff {@linkcode FunctionType} is
* equivalent to `(...args: unknown[]) => unknown`,
* which is what an overload variant looks like for a non-existent overload.
* This is useful because older versions of TypeScript end up with
* 9 "useless" overloads and one real one for parameterless/generic functions.
*
* @see {@link https://github.com/microsoft/TypeScript/issues/28867 | Related}
*/
export type IsUselessOverloadInfo<FunctionType> = StrictEqualUsingTSInternalIdenticalToOperator<
FunctionType,
UnknownFunction
>;
/**
* Old versions of TypeScript can sometimes seem to refuse to separate out
* union members unless you put them each in a pointless tuple and add an
* extra `infer X` expression. There may be a better way to work around this
* problem, but since it's not a problem in newer versions of TypeScript,
* it's not a priority right now.
*/
export type Tuplify<Union> = Union extends infer X ? [X] : never;
/**
* For older versions of TypeScript, we need two separate workarounds
* to get overload info. First, we need need to use
* {@linkcode DecreasingOverloadsInfoUnion} to get the overload info for
* functions with 1-10 overloads. Then, we need to filter out the
* "useless" overloads that are present in older versions of TypeScript,
* for parameterless functions. To do this we use
* {@linkcode IsUselessOverloadInfo} to remove useless overloads.
*
* @see {@link https://github.com/microsoft/TypeScript/issues/28867 | Related}
*/
export type TSPre53OverloadsInfoUnion<FunctionType> =
Tuplify<DecreasingOverloadsInfoUnion<FunctionType>> extends infer Tup
? Tup extends [infer Fn]
? IsUselessOverloadInfo<Fn> extends true
? never
: Fn
: never
: never;
/**
* For versions of TypeScript below 5.3, we need to check for 10 overloads,
* then 9, then 8, etc., to get a union of the overload variants.
*/
export type DecreasingOverloadsInfoUnion<F> = F extends {
(...args: infer A1): infer R1;
(...args: infer A2): infer R2;
(...args: infer A3): infer R3;
(...args: infer A4): infer R4;
(...args: infer A5): infer R5;
(...args: infer A6): infer R6;
(...args: infer A7): infer R7;
(...args: infer A8): infer R8;
(...args: infer A9): infer R9;
(...args: infer A10): infer R10;
}
?
| ((...p: A1) => R1)
| ((...p: A2) => R2)
| ((...p: A3) => R3)
| ((...p: A4) => R4)
| ((...p: A5) => R5)
| ((...p: A6) => R6)
| ((...p: A7) => R7)
| ((...p: A8) => R8)
| ((...p: A9) => R9)
| ((...p: A10) => R10)
: F extends {
(...args: infer A1): infer R1;
(...args: infer A2): infer R2;
(...args: infer A3): infer R3;
(...args: infer A4): infer R4;
(...args: infer A5): infer R5;
(...args: infer A6): infer R6;
(...args: infer A7): infer R7;
(...args: infer A8): infer R8;
(...args: infer A9): infer R9;
}
?
| ((...p: A1) => R1)
| ((...p: A2) => R2)
| ((...p: A3) => R3)
| ((...p: A4) => R4)
| ((...p: A5) => R5)
| ((...p: A6) => R6)
| ((...p: A7) => R7)
| ((...p: A8) => R8)
| ((...p: A9) => R9)
: F extends {
(...args: infer A1): infer R1;
(...args: infer A2): infer R2;
(...args: infer A3): infer R3;
(...args: infer A4): infer R4;
(...args: infer A5): infer R5;
(...args: infer A6): infer R6;
(...args: infer A7): infer R7;
(...args: infer A8): infer R8;
}
?
| ((...p: A1) => R1)
| ((...p: A2) => R2)
| ((...p: A3) => R3)
| ((...p: A4) => R4)
| ((...p: A5) => R5)
| ((...p: A6) => R6)
| ((...p: A7) => R7)
| ((...p: A8) => R8)
: F extends {
(...args: infer A1): infer R1;
(...args: infer A2): infer R2;
(...args: infer A3): infer R3;
(...args: infer A4): infer R4;
(...args: infer A5): infer R5;
(...args: infer A6): infer R6;
(...args: infer A7): infer R7;
}
?
| ((...p: A1) => R1)
| ((...p: A2) => R2)
| ((...p: A3) => R3)
| ((...p: A4) => R4)
| ((...p: A5) => R5)
| ((...p: A6) => R6)
| ((...p: A7) => R7)
: F extends {
(...args: infer A1): infer R1;
(...args: infer A2): infer R2;
(...args: infer A3): infer R3;
(...args: infer A4): infer R4;
(...args: infer A5): infer R5;
(...args: infer A6): infer R6;
}
?
| ((...p: A1) => R1)
| ((...p: A2) => R2)
| ((...p: A3) => R3)
| ((...p: A4) => R4)
| ((...p: A5) => R5)
| ((...p: A6) => R6)
: F extends {
(...args: infer A1): infer R1;
(...args: infer A2): infer R2;
(...args: infer A3): infer R3;
(...args: infer A4): infer R4;
(...args: infer A5): infer R5;
}
? ((...p: A1) => R1) | ((...p: A2) => R2) | ((...p: A3) => R3) | ((...p: A4) => R4) | ((...p: A5) => R5)
: F extends {
(...args: infer A1): infer R1;
(...args: infer A2): infer R2;
(...args: infer A3): infer R3;
(...args: infer A4): infer R4;
}
? ((...p: A1) => R1) | ((...p: A2) => R2) | ((...p: A3) => R3) | ((...p: A4) => R4)
: F extends {
(...args: infer A1): infer R1;
(...args: infer A2): infer R2;
(...args: infer A3): infer R3;
}
? ((...p: A1) => R1) | ((...p: A2) => R2) | ((...p: A3) => R3)
: F extends {
(...args: infer A1): infer R1;
(...args: infer A2): infer R2;
}
? ((...p: A1) => R1) | ((...p: A2) => R2)
: F extends (...args: infer A1) => infer R1
? (...p: A1) => R1
: never;
/**
* Get a union of overload variants for a function {@linkcode FunctionType}.
* Does a check for whether we can do the one-shot
* 10-overload matcher (which works for ts\>5.3), and if not,
* falls back to the more complicated utility.
*/
export type OverloadsInfoUnion<FunctionType> =
IsNever<TSPost53OverloadsInfoUnion<(a: 1) => 2>> extends true
? TSPre53OverloadsInfoUnion<FunctionType>
: TSPost53OverloadsInfoUnion<FunctionType>;
/**
* Allows inferring any function using the `infer` keyword.
*/
export type InferFunctionType<FunctionType extends (...args: any) => any> = FunctionType;
/**
* A union type of the parameters allowed for any
* overload of function {@linkcode FunctionType}.
*/
export type OverloadParameters<FunctionType> =
OverloadsInfoUnion<FunctionType> extends InferFunctionType<infer Fn> ? Parameters<Fn> : never;
/**
* A union type of the return types for any overload of
* function {@linkcode FunctionType}.
*/
export type OverloadReturnTypes<FunctionType> =
OverloadsInfoUnion<FunctionType> extends InferFunctionType<infer Fn> ? ReturnType<Fn> : never;
/**
* Takes an overload variants {@linkcode Union},
* produced from {@linkcode OverloadsInfoUnion} and rejects
* the ones incompatible with parameters {@linkcode Args}.
*/
export type SelectOverloadsInfo<Union extends UnknownFunction, Args extends unknown[]> =
Union extends InferFunctionType<infer Fn> ? (Args extends Parameters<Fn> ? Fn : never) : never;
/**
* Creates a new overload (an intersection type) from an existing one,
* which only includes variant(s) which can accept
* {@linkcode Args} as parameters.
*/
export type OverloadsNarrowedByParameters<
FunctionType,
Args extends OverloadParameters<FunctionType>,
> = UnionToIntersection<SelectOverloadsInfo<OverloadsInfoUnion<FunctionType>, Args>>;
/**
* The simple(ish) way to get overload info from a constructor
* {@linkcode ConstructorType}. Recent versions of TypeScript will match any
* constructor against a generic 10-overload type, filling in slots with
* duplicates of the constructor. So, we can just match against a single type
* and get all the overloads.
*
* For older versions of TypeScript,
* we'll need to painstakingly do ten separate matches.
*/
export type TSPost53ConstructorOverloadsInfoUnion<ConstructorType> = ConstructorType extends {
new (...args: infer A1): infer R1;
new (...args: infer A2): infer R2;
new (...args: infer A3): infer R3;
new (...args: infer A4): infer R4;
new (...args: infer A5): infer R5;
new (...args: infer A6): infer R6;
new (...args: infer A7): infer R7;
new (...args: infer A8): infer R8;
new (...args: infer A9): infer R9;
new (...args: infer A10): infer R10;
}
?
| (new (...p: A1) => R1)
| (new (...p: A2) => R2)
| (new (...p: A3) => R3)
| (new (...p: A4) => R4)
| (new (...p: A5) => R5)
| (new (...p: A6) => R6)
| (new (...p: A7) => R7)
| (new (...p: A8) => R8)
| (new (...p: A9) => R9)
| (new (...p: A10) => R10)
: never;
/**
* A constructor function with `unknown` parameters and return type.
*/
export type UnknownConstructor = new (...args: unknown[]) => unknown;
/**
* Same as {@linkcode IsUselessOverloadInfo}, but for constructors.
*/
export type IsUselessConstructorOverloadInfo<FunctionType> = StrictEqualUsingTSInternalIdenticalToOperator<
FunctionType,
UnknownConstructor
>;
/**
* For older versions of TypeScript, we need two separate workarounds to
* get constructor overload info. First, we need need to use
* {@linkcode DecreasingConstructorOverloadsInfoUnion} to get the overload
* info for constructors with 1-10 overloads. Then, we need to filter out the
* "useless" overloads that are present in older versions of TypeScript,
* for parameterless constructors. To do this we use
* {@linkcode IsUselessConstructorOverloadInfo} to remove useless overloads.
*
* @see {@link https://github.com/microsoft/TypeScript/issues/28867 | Related}
*/
export type TSPre53ConstructorOverloadsInfoUnion<ConstructorType> =
Tuplify<DecreasingConstructorOverloadsInfoUnion<ConstructorType>> extends infer Tup
? Tup extends [infer Ctor]
? IsUselessConstructorOverloadInfo<Ctor> extends true
? never
: Ctor
: never
: never;
/**
* For versions of TypeScript below 5.3, we need to check for 10 overloads,
* then 9, then 8, etc., to get a union of the overload variants.
*/
export type DecreasingConstructorOverloadsInfoUnion<ConstructorType> = ConstructorType extends {
new (...args: infer A1): infer R1;
new (...args: infer A2): infer R2;
new (...args: infer A3): infer R3;
new (...args: infer A4): infer R4;
new (...args: infer A5): infer R5;
new (...args: infer A6): infer R6;
new (...args: infer A7): infer R7;
new (...args: infer A8): infer R8;
new (...args: infer A9): infer R9;
new (...args: infer A10): infer R10;
}
?
| (new (...p: A1) => R1)
| (new (...p: A2) => R2)
| (new (...p: A3) => R3)
| (new (...p: A4) => R4)
| (new (...p: A5) => R5)
| (new (...p: A6) => R6)
| (new (...p: A7) => R7)
| (new (...p: A8) => R8)
| (new (...p: A9) => R9)
| (new (...p: A10) => R10)
: ConstructorType extends {
new (...args: infer A1): infer R1;
new (...args: infer A2): infer R2;
new (...args: infer A3): infer R3;
new (...args: infer A4): infer R4;
new (...args: infer A5): infer R5;
new (...args: infer A6): infer R6;
new (...args: infer A7): infer R7;
new (...args: infer A8): infer R8;
new (...args: infer A9): infer R9;
}
?
| (new (...p: A1) => R1)
| (new (...p: A2) => R2)
| (new (...p: A3) => R3)
| (new (...p: A4) => R4)
| (new (...p: A5) => R5)
| (new (...p: A6) => R6)
| (new (...p: A7) => R7)
| (new (...p: A8) => R8)
| (new (...p: A9) => R9)
: ConstructorType extends {
new (...args: infer A1): infer R1;
new (...args: infer A2): infer R2;
new (...args: infer A3): infer R3;
new (...args: infer A4): infer R4;
new (...args: infer A5): infer R5;
new (...args: infer A6): infer R6;
new (...args: infer A7): infer R7;
new (...args: infer A8): infer R8;
}
?
| (new (...p: A1) => R1)
| (new (...p: A2) => R2)
| (new (...p: A3) => R3)
| (new (...p: A4) => R4)
| (new (...p: A5) => R5)
| (new (...p: A6) => R6)
| (new (...p: A7) => R7)
| (new (...p: A8) => R8)
: ConstructorType extends {
new (...args: infer A1): infer R1;
new (...args: infer A2): infer R2;
new (...args: infer A3): infer R3;
new (...args: infer A4): infer R4;
new (...args: infer A5): infer R5;
new (...args: infer A6): infer R6;
new (...args: infer A7): infer R7;
}
?
| (new (...p: A1) => R1)
| (new (...p: A2) => R2)
| (new (...p: A3) => R3)
| (new (...p: A4) => R4)
| (new (...p: A5) => R5)
| (new (...p: A6) => R6)
| (new (...p: A7) => R7)
: ConstructorType extends {
new (...args: infer A1): infer R1;
new (...args: infer A2): infer R2;
new (...args: infer A3): infer R3;
new (...args: infer A4): infer R4;
new (...args: infer A5): infer R5;
new (...args: infer A6): infer R6;
}
?
| (new (...p: A1) => R1)
| (new (...p: A2) => R2)
| (new (...p: A3) => R3)
| (new (...p: A4) => R4)
| (new (...p: A5) => R5)
| (new (...p: A6) => R6)
: ConstructorType extends {
new (...args: infer A1): infer R1;
new (...args: infer A2): infer R2;
new (...args: infer A3): infer R3;
new (...args: infer A4): infer R4;
new (...args: infer A5): infer R5;
}
?
| (new (...p: A1) => R1)
| (new (...p: A2) => R2)
| (new (...p: A3) => R3)
| (new (...p: A4) => R4)
| (new (...p: A5) => R5)
: ConstructorType extends {
new (...args: infer A1): infer R1;
new (...args: infer A2): infer R2;
new (...args: infer A3): infer R3;
new (...args: infer A4): infer R4;
}
? (new (...p: A1) => R1) | (new (...p: A2) => R2) | (new (...p: A3) => R3) | (new (...p: A4) => R4)
: ConstructorType extends {
new (...args: infer A1): infer R1;
new (...args: infer A2): infer R2;
new (...args: infer A3): infer R3;
}
? (new (...p: A1) => R1) | (new (...p: A2) => R2) | (new (...p: A3) => R3)
: ConstructorType extends {
new (...args: infer A1): infer R1;
new (...args: infer A2): infer R2;
}
? (new (...p: A1) => R1) | (new (...p: A2) => R2)
: ConstructorType extends new (...args: infer A1) => infer R1
? new (...p: A1) => R1
: never;
/**
* Get a union of overload variants for a constructor
* {@linkcode ConstructorType}. Does a check for whether we can do the
* one-shot 10-overload matcher (which works for ts\>5.3), and if not,
* falls back to the more complicated utility.
*/
export type ConstructorOverloadsUnion<ConstructorType> =
IsNever<TSPost53ConstructorOverloadsInfoUnion<new (a: 1) => any>> extends true
? TSPre53ConstructorOverloadsInfoUnion<ConstructorType>
: TSPost53ConstructorOverloadsInfoUnion<ConstructorType>;
/**
* Allows inferring any constructor using the `infer` keyword.
*/
export type InferConstructor<ConstructorType extends new (...args: any) => any> = ConstructorType;
/**
* A union type of the parameters allowed for any overload
* of constructor {@linkcode ConstructorType}.
*/
export type ConstructorOverloadParameters<ConstructorType> =
ConstructorOverloadsUnion<ConstructorType> extends InferConstructor<infer Ctor> ? ConstructorParameters<Ctor> : never;
/**
* Calculates the number of overloads for a given function type.
*/
export type NumOverloads<FunctionType> = UnionToTuple<OverloadsInfoUnion<FunctionType>>["length"];

Some files were not shown because too many files have changed in this diff Show More