Compare commits

..

129 Commits

Author SHA1 Message Date
Colin McDonnell
7c9352392d Update readme and regen 2023-06-09 19:21:49 -07:00
Jarred Sumner
fef9853d5a wip 2023-06-05 02:02:13 -07:00
Jarred Sumner
7dae496847 wip 2023-06-04 19:59:22 -07:00
Jarred Sumner
92b060c6e2 fix some CLI things 2023-06-04 19:06:02 -07:00
Jarred Sumner
2a64e8b3bb fixup 2023-06-04 19:05:42 -07:00
Jarred Sumner
fde3b7fbb6 Fix build 2023-06-04 18:54:26 -07:00
Ashcon Partovi
c16e769383 Add types and sample heapsnapshot for JSC and V8 2023-06-04 18:54:26 -07:00
Ashcon Partovi
5badc728d0 Add a DevTools client 2023-06-04 18:54:26 -07:00
Jarred Sumner
c4b3b321c2 slightly more progress 2023-06-04 18:54:26 -07:00
Jarred Sumner
cf599e77d9 Fix C++ compile errors 2023-06-04 18:54:26 -07:00
Jarred Sumner
b727689a9b pushing this but it doesn't fix anything 2023-06-04 18:54:26 -07:00
Ashcon Partovi
4a36470588 Add script to generate DevTools protocol types for JSC and V8 2023-06-04 18:54:26 -07:00
Jarred Sumner
ca08cf6b0a Update BunInspector.cpp 2023-06-04 18:54:26 -07:00
Jarred Sumner
10bd0fac3a WIP support inspector 2023-06-04 18:54:26 -07:00
Jarred Sumner
c4e31551f3 Update .gitattributes 2023-06-03 17:44:28 -07:00
dave caruso
52d11ce845 Add test case (#3186) 2023-06-03 16:38:49 -07:00
dave caruso
7d8b6d487b make hardcoded -> make esm + document needing bun to build bun (#3188) 2023-06-03 16:38:33 -07:00
Jarred Sumner
583c6d3644 Bump 2023-06-03 16:37:53 -07:00
Ciro Spaciari
aabb0b77ce [tests] prisma tests (#3197) 2023-06-03 15:04:19 -07:00
Jarred Sumner
59d7c47e3f Fix crash with path parse in win32 (#3187)
* Update CommonJSModuleRecord.cpp

* smaller

* [node:path] Fix crash, mark TODO

---------

Co-authored-by: Jarred Sumner <709451+Jarred-Sumner@users.noreply.github.com>
2023-06-03 00:14:27 -07:00
Dylan Conway
21bc3a9c39 [bundler] fix json imports, undo some todos 2023-06-02 22:44:19 -07:00
dave caruso
cfd73cec81 fixes with hardcoded modules (#3182)
* fixes with hardcoded modules

* add make hardcoded to make dev

* adjust this message

* remove debugging logs

* this

* restore2
2023-06-02 21:36:05 -07:00
Dylan Conway
3e437a6ae0 check package name length in shouldUnwrapRequire() (#3183)
* make sure length is equal

* add and use `indexEqualAny`
2023-06-02 20:45:48 -07:00
Dylan Conway
d8e7436db0 use NODE_ENV for react-jsx or react-jsxdev (#3184) 2023-06-02 20:35:00 -07:00
dave caruso
f798a0cfe8 fix readableStreamToArrayBuffer (#3181)
* fix discord.js again

* remove one of the async hooks warnings

* clarify hardcoded modules docs
2023-06-02 19:03:16 -07:00
Ciro Spaciari
51846d0277 [napi] some fixes for prisma (#3176)
* patch create object, so it can be used on wrap

* cleanup

* cleanup

* dont override napiRef

* make wrap strong ref

* workaround tests

* remove work around
2023-06-02 17:56:16 -07:00
Colin McDonnell
c4f237572d Update macro/test docs (#3180)
* Add test and fix plugin type

* Update docs and config pages
2023-06-02 17:43:51 -07:00
Dylan Conway
e20e695775 continue if import is standalone (#3178) 2023-06-02 17:07:16 -07:00
Jarred Sumner
132ab63a32 Add comment 2023-06-02 16:39:16 -07:00
Ashcon Partovi
bfd315fc72 Support NO_COLOR environment variable (#3055) 2023-06-02 14:24:16 -07:00
Jarred Sumner
72c2123e07 Maybe we don't need to do this anymore? (#3169) 2023-06-02 14:22:39 -07:00
Colin McDonnell
1f7d62bfd5 Support NodeJS.ProcessEnv 2023-06-02 12:49:31 -07:00
Jozef Steinhübl
4d1c900cac types: expose Bun.Env (#3111)
* expose Bun.Env

* export bun.Env

* fix: use import instead namespace access

* fix(child_process): use import

* tests(env): use module instead namespace
2023-06-02 12:48:19 -07:00
Jarred Sumner
97c522517c Fixes #3161 (#3174)
* Fixes #3161

* Fix the error message

* woops

---------

Co-authored-by: Jarred Sumner <709451+Jarred-Sumner@users.noreply.github.com>
2023-06-02 04:01:25 -07:00
Ashcon Partovi
4c6245b2e5 Add concurrency to workflows, enable cancel-in-progress 2023-06-01 18:38:40 -07:00
Ashcon Partovi
b21a58e564 Fix github annotation when using inspect() (#3166) 2023-06-01 18:25:06 -07:00
dave caruso
4df1d37ddc Bundle and minify .exports.js files. (#3036)
* move all exports.js into src/js

* finalize the sort of this

* and it works

* add test.ts to gitignore

* okay

* convert some to ts just to show

* finish up

* fixup makefile

* minify syntax in dev

* finish rebase

* dont minify all modules

* merge

* finish rebase merge

* flaky test that hangs
2023-06-01 18:16:47 -07:00
Ashcon Partovi
03ffd1c732 Disable typecheck workflow for now 2023-06-01 18:04:45 -07:00
Jarred Sumner
3e84f18cc0 Implement __dirname and __filename, allow direct eval in CommonJS (#3164)
* Implement `__dirname` and `__filename`, allow direct eval in CommonJS

* Fixup dirname and add test

---------

Co-authored-by: Jarred Sumner <709451+Jarred-Sumner@users.noreply.github.com>
2023-06-01 18:04:09 -07:00
Jarred Sumner
42606d6aed Rename ptr to _unsafe_ptr_do_not_use (#3163)
* Rename `ptr` to `_unsafe_ptr_do_not_use`

* Fixup

---------

Co-authored-by: Jarred Sumner <709451+Jarred-Sumner@users.noreply.github.com>
2023-06-01 18:02:41 -07:00
Derrick Farris
c366b62160 Update macros.md (#3165)
Fix a repeated "for"
2023-06-01 17:25:28 -07:00
dave caruso
42d8b7183c random fixes that help vite/sveltekit (#3140)
* existsSync with invalid paths should return false

* partially support file urls (does not do percent encoding)

* add utf16 support for Path.isAbsoluteString

* Update src/resolver/resolver.zig

Co-authored-by: Jarred Sumner <jarred@jarredsumner.com>

* fixups

* revert

* prettier format

---------

Co-authored-by: Jarred Sumner <jarred@jarredsumner.com>
2023-06-01 16:48:37 -07:00
Ciro Spaciari
f9809f0044 [napi] fix thread safe function callback (#3162)
* fixup

* add debug log message to event loop unexpected tag

* fix fmt
2023-06-01 15:19:06 -07:00
dave caruso
4378ef8e97 mark currently known test fails as .todo (#3052)
* start this

* commit

* mark all failing tests as todo

* fasdfad

* bundler tests

* tests

* adjust failing tests to todo

* comment out some more tests

* png as test
2023-06-01 14:37:35 -07:00
dave caruso
2c1694f63b Fix streams breaking on reverted EventEmitter / Make Discord.js work (#2913)
* Revert "Revert "use a lazyily initialized stream for `node:crypto` `createHash` (#2652)""

This reverts commit 613bb4822e.

* Revert "Revert "implement `node:events` in javascript (#2604)""

This reverts commit a4d0a1961a.

* oops

* fix entrypoints stuff

* fix hash copy

* use native events for node streams and crypto

* requested changes

* oops

* make discord.js work

* fix webkit hash

* headers tojson
2023-06-01 14:31:36 -07:00
Jarred Sumner
ae277a0dec Update build-id 2023-06-01 13:58:50 -07:00
Dylan Conway
473b219a37 only add cjs export name if it is assigned (#3159) 2023-06-01 13:57:51 -07:00
Noah Chase
be1833898e fix typo in documentation ("the today" -> "today") (#3160) 2023-06-01 13:34:09 -07:00
Jozef Steinhübl
a64da8f0cb ci: dont run builds in forks (#3156) 2023-06-01 12:35:26 -07:00
Dylan Conway
689434e012 don't rewrite module.exports if assign target is update or replace (#3147)
* don't rewrite `module.exports` if assign target is `update` or `replace`

* missing `is_delete_target`, check de-opt before rewriting
2023-05-31 23:51:32 -07:00
Jarred Sumner
a73a4d1e0b Fix test failure due to cloudflare changing dns ttl 2023-05-31 23:19:32 -07:00
Jarred Sumner
457f325773 slightly clean up this test 2023-05-31 23:18:58 -07:00
Jarred Sumner
5f80681295 [TCP] Ensure we keep the handlers functions alive when an error callback needs to be called
This can cause test failures due to GC'ing the handlers when we are *just* about to call them.
2023-05-31 23:17:18 -07:00
Jarred Sumner
fdcfcce9e7 Clean-up some option parsing in Bun.spawn 2023-05-31 23:15:21 -07:00
Jarred Sumner
1ca70b855c Update launch.json 2023-05-31 23:13:50 -07:00
Ashcon Partovi
e632941c52 Small improvements to bun test (#3071)
* Change status icon for skipped tests from "-" to "»"

* Show file path instead of filename in `bun test`

* Emit collapsable logs when running `bun test` in Github Actions

https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions#grouping-log-lines

* Add fallback for test icons when emojis are not available

* Only check for GITHUB_ACTIONS when running `bun test`

* Emit error annotations when running `bun test` in Github Actions

https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions#setting-an-error-message

* Remove ANSI output from Github annotation, it doesn't work

* Remove outdated code from internal test runner

* Add GithubActionFormatter to handle cases where error name or message is already ANSI

* Fix formatting of test

* Fix #3070

* Implement `bun test --run-todo`

By default, `test.todo()` is no longer run, unless `--run-todo` is specified.

* Fix test that relies on test.todo() being run

* Support vitest-style test options

* Disable GITHUB_ACTION in test harness

* Add types for TestOptions

* Fix bug where test.skip() actually ran

* Implement `test.skipIf()` and `describe.skipIf()`

* Implement `test.runIf()`

* Move DiffFormatter to its own file

* Fix bug where Bun.inspect() would emit a Github annotation

* Introduce `bun test --only`, rename `--run-todo` to `--todo`

* Implement `test.if()`, `describe.if()`, and other test fixes

* Remove unwanted files from last commit

* Fix last reference to --run-todo

* Fix memory issues with printing github actions text

* Update bindings.zig

* Fix bug with `test.only()`

* Remove debug test

* Make the github annotations better

* Improve .vscode/launch.json

* Implement `expect().toBeNil()`

* Remove .only() from test

* Implement toBeBoolean(), toBeTrue(), toBeFalse()

* Add lots of matchers
* toBeNil()
* toBeBoolean()
* toBeTrue()
* toBeFalse()
* toBeNumber()
* toBeInteger()
* toBeFinite()
* toBePositive()
* toBeNegative()
* toBeWithin()
* toBeSymbol()
* toBeFunction()
* toBeDate()
* toBeString()
* toInclude()
* toStartWith()
* toEndWith()

* Fix #3135

* Reduce verbosity of test

* Fix snapshot bug

---------

Co-authored-by: Jarred Sumner <709451+Jarred-Sumner@users.noreply.github.com>
2023-05-31 23:12:04 -07:00
Colin McDonnell
176fade220 Remove bun-macro-relay and update docs (#3145)
* Update macro docs

* Remove bun-macro-relay
2023-05-31 19:51:08 -07:00
Jarred Sumner
110d0752f3 Add "macro" package.json exports condition 2023-05-31 19:01:59 -07:00
Ciro Spaciari
e24d579a32 isolated version of Path compatible with nodejs (#3143) 2023-05-31 18:20:50 -07:00
Mike Roelens
a7c7128cd7 Fix typo in macro docs (#3144) 2023-05-31 18:15:49 -07:00
Jarred Sumner
4c01454376 Make uploading files with fetch()fast (#3125)
* Make file uploads fast

* Add benchmark

* Update README.md

* defaults

* print

* prettier

* smaller

* fix(path) fix parse behavior (#3134)

* Add macro docs (#3139)

* Add macro doc

* Updates

* Tweaks

* Update doc

* Update macro serialization doc

* Update macro doc

* `--no-macros` flag, disable macros in node_modules

* invert base/filename internally (#3141)

* always false

* Fix broken test

* Add a test sendfile() test with large file

---------

Co-authored-by: Jarred Sumner <709451+Jarred-Sumner@users.noreply.github.com>
Co-authored-by: Ciro Spaciari <ciro.spaciari@gmail.com>
Co-authored-by: Colin McDonnell <colinmcd94@gmail.com>
2023-05-31 17:20:30 -07:00
Dylan Conway
611f1d0e24 set optional_chain instead of optional_start (#3142) 2023-05-31 16:13:24 -07:00
Ciro Spaciari
52c6609792 invert base/filename internally (#3141) 2023-05-31 15:13:37 -07:00
Jarred Sumner
7057cb1982 --no-macros flag, disable macros in node_modules 2023-05-31 15:05:49 -07:00
Colin McDonnell
68c1568075 Update macro doc 2023-05-31 13:58:38 -07:00
Colin McDonnell
4b34a7ce2a Update macro serialization doc 2023-05-31 13:43:08 -07:00
Colin McDonnell
b284e09ce5 Update doc 2023-05-31 13:35:11 -07:00
Colin McDonnell
8f55f05871 Add macro docs (#3139)
* Add macro doc

* Updates

* Tweaks
2023-05-31 13:34:35 -07:00
Ciro Spaciari
58fcb60831 fix(path) fix parse behavior (#3134) 2023-05-31 12:07:52 -07:00
Jarred Sumner
557aac6a34 Support FormData & file uploads in fetch body (#3123)
* Fixes #2264

* fixup

* Don't leak HTTP headers

* Include the mime type. It's cleaner

---------

Co-authored-by: Jarred Sumner <709451+Jarred-Sumner@users.noreply.github.com>
2023-05-31 00:43:59 -07:00
Jarred Sumner
0aab11a95d always clone these 2023-05-30 19:39:12 -07:00
Jarred Sumner
7376ae6980 Update modules.md 2023-05-30 15:52:21 -07:00
Jarred Sumner
148c6e7551 Fix wasi 2023-05-30 15:41:27 -07:00
Jarred Sumner
b0c38a3c15 Update ZigGeneratedClasses.h 2023-05-30 13:13:42 -07:00
Jarred Sumner
756e15f6e2 Add another comment 2023-05-30 02:53:28 -07:00
Jarred Sumner
10be26f011 [CommonJS] Handle this better 2023-05-30 02:51:50 -07:00
Jarred Sumner
12a0e2bfdd Fixes #3115 2023-05-30 02:51:27 -07:00
Jarred Sumner
481f916f3f More comments 2023-05-30 00:30:47 -07:00
Jarred Sumner
85565d95d5 Add a comment 2023-05-30 00:26:30 -07:00
Jarred Sumner
fdbf8a5184 Handle Object.defineProperty(module, "exports", {get: getter}) 2023-05-30 00:24:18 -07:00
Jarred Sumner
8e911d38c4 Bump 2023-05-30 00:19:15 -07:00
Jarred Sumner
052df7d48c Cleanup CommonJS changes (#3112)
* Add more GC in test

* Fix handling of functions and re-assignments in CommonJS

* Increase timeout

---------

Co-authored-by: Jarred Sumner <709451+Jarred-Sumner@users.noreply.github.com>
2023-05-29 21:01:39 -07:00
Jarred Sumner
5990a9528f Rename this file so we know what it is 2023-05-29 19:58:40 -07:00
Jarred Sumner
aa6801d5df Update index.md 2023-05-29 12:22:13 -07:00
Jarred Sumner
d0185925ff Update modules.md 2023-05-29 12:15:33 -07:00
Colin McDonnell
9b6913e1a6 More/better docs for JSX, utils, binary data, streams, hashing, bun test, Bun.serve (#3005)
* WIP

* Updates

* Document deepEquals

* WIP

* Update typeS

* Update TLS docs for Bun.serve

* Update types for tls

* Draft binary data page. Add Streams page.

* Update test runner docs

* Add hashing, flesh out utils

* Grammar

* Update types

* Fix

* Add import.meta docs

* Tee
2023-05-29 11:49:51 -07:00
Jarred Sumner
e2de1f5c13 Natively support CommonJS at runtime (#3104)
* Natively support CommonJS at runtime

* cleanup how getters are handled, add fast path

* more consistent with node

* use * As

* Remove thrown modules on exception

* Handle exception better

---------

Co-authored-by: Jarred Sumner <709451+Jarred-Sumner@users.noreply.github.com>
2023-05-29 11:23:10 -07:00
Jarred Sumner
2b04ef4fae Convert module.exports = { foo: 'bar'} to ESM in bun build (#3103)
* Convert `module.exports = { foo: 'bar'}` to ESM in `bun build`

* De-opt for `module.exports = {}`

---------

Co-authored-by: Jarred Sumner <709451+Jarred-Sumner@users.noreply.github.com>
2023-05-28 21:02:51 -07:00
Jarred Sumner
8dfd3dbdbc Add query string to express integration test 2023-05-28 13:30:25 -07:00
Ciro Spaciari
63e2d78e76 [node:net] fix createConnection options passing (#3101)
* fixup createConnection

* fix comment

* fixup comment

* also fix it on tls
2023-05-28 13:20:32 -07:00
Jarred Sumner
1388ec0d60 Fixes #2356 (#3098) 2023-05-27 23:33:47 -07:00
Tiramify (A.K. Daniel)
4ba3ed512a Implement process.env.npm_lifecycle_event (#3097)
* Update run_command.zig

* Update env.test.ts

* Add files via upload

* Update run-process-env.test.ts

* Update env.test.ts

* Update harness.ts
2023-05-27 20:43:08 -07:00
Jarred Sumner
f870f97a94 Fixes #3091 (#3092)
* Fixes #3091

* Update build-id

---------

Co-authored-by: Jarred Sumner <709451+Jarred-Sumner@users.noreply.github.com>
2023-05-27 00:21:54 -07:00
Jarred Sumner
7094997eec Fix crash in Server.prototype.fetch() helper function
cc @cirospaciari, if you use `mimalloc-debug` it catches things like this
2023-05-26 21:18:31 -07:00
Jarred Sumner
79907fe84c Add more debug safety checks 2023-05-26 21:17:10 -07:00
Ashcon Partovi
1a30b4fe29 Implement expect().toBeEmpty() (#3060)
* Implement `expect().toBeEmpty()`

* Fix formatting on test

* Finish up expect().toBeEmpty()

* Update expect.test.ts

---------

Co-authored-by: Jarred Sumner <709451+Jarred-Sumner@users.noreply.github.com>
2023-05-26 19:24:20 -07:00
Jarred Sumner
4298f36fc9 fixup 2023-05-26 16:29:44 -07:00
Jarred Sumner
3a0735e164 Pretty formatter for Headers & URLSearchParams (#3081)
* Pretty formatter for `Headers` & `URLSearchParams`

* cleanup

* console.log on Headers, FormData, URLSearchParams will always quote the keys now

---------

Co-authored-by: Jarred Sumner <709451+Jarred-Sumner@users.noreply.github.com>
2023-05-26 16:28:09 -07:00
Ciro Spaciari
42125b4351 [fetch] change response allocator (#3086)
* change allocator

* cleanup
2023-05-26 15:07:03 -07:00
Jarred Sumner
0f2a79b9c1 Fix crash in test.todo + remove JSC C API usages in bun:test (#3079)
* Fix crash in test.todo

* remove usages of JSC C API in bun:test

* Remove additional JSC-C API usages

* fix `make headers`

* URLSearchParams.length

* FormData length

* URLSearchParams length

* Fix `make headers`

* very fancy length

* Fix bug with exceptions being ignored sometimes

* Add tests for extension toHaveLength

---------

Co-authored-by: Jarred Sumner <709451+Jarred-Sumner@users.noreply.github.com>
2023-05-26 03:32:28 -07:00
Dylan Conway
62f05ec103 avoid using null value 2023-05-25 23:07:18 -07:00
Jiri Spac
1f7ad0cc37 add a bit more info on Elysia (#3062) 2023-05-25 22:04:07 -07:00
Alex Lam S.L
4c566f56c1 [install] fix .bin linking (#3067)
- causes intermittent `bun install` failures on `bun-types`
2023-05-25 21:58:30 -07:00
Dylan Conway
b395836080 jsx runtime from env (#3076)
* use `NODE_ENV` for jsx mode

* check env after `configureRouter`

* get `NODE_ENV` from `options.production`
2023-05-25 21:39:01 -07:00
Dylan Conway
801881dd7d update jsx test for classic runtime 2023-05-25 20:54:39 -07:00
Jarred Sumner
7b4bba787d prettier 2023-05-25 18:14:01 -07:00
Ciro Spaciari
e95f9c6daa [stream] fix flush (#3073)
* fix flush

* remove logs
2023-05-25 16:41:00 -07:00
Jarred Sumner
50bad43dbe Remove paragraph that has incorrect information in the docs 2023-05-25 16:16:16 -07:00
Dialga
48997eaeba Update sqlite.md (#3063)
fix serialize example
2023-05-25 10:14:29 -07:00
Jarred Sumner
f02561530f Bump 2023-05-24 19:54:00 -07:00
Jarred Sumner
88d9bac5ec Support with { type: "macro"} in bun build (#3059)
* [bun macro] Support `assert { type: "macro" }` and `with {type: "macro"}`

* [bun macro] Pass through input as arguments instead of a JSNode

* Fix hang when loading many entry points simultaneously with macros

* do not clone

---------

Co-authored-by: Jarred Sumner <709451+Jarred-Sumner@users.noreply.github.com>
2023-05-24 19:37:57 -07:00
Jarred Sumner
63740a382b Load .env.test, set NODE_ENV=test in bun test, load .env.{test,production,development}.local (#3037)
* Support `.env.test` & `.env.{test,production,development}.local`

* Fix bug preventing inlining of process.env.NODE_ENV by default

* Update env_loader.zig

* add env tests

---------

Co-authored-by: Jarred Sumner <709451+Jarred-Sumner@users.noreply.github.com>
Co-authored-by: dave caruso <me@paperdave.net>
2023-05-24 18:52:50 -07:00
Ciro Spaciari
ed1f62ffff [server.fetch] call when using Request object (#3051)
* patch server.fetch

* add tests and fix types
2023-05-24 18:52:13 -07:00
Dylan Conway
4972934189 fix setting jsxImportSource, jsxFactory, and jsxFragmentFactory (#3057)
* default automatic, merge jsx flags from multiple tsconfigs

* use entire package name

* some tests

* more tests
2023-05-24 18:33:28 -07:00
Jarred Sumner
d73bce058a don't do MMD in CI 2023-05-24 18:31:33 -07:00
Ciro Spaciari
10c018df8e [bun:FileSystemRouter] add more validations on parameters (#3053)
* add validations and allow relative path on  dir

* remove unneed things

* add origin/dir validation tests

* remove workaround

* add back relative path resolution
2023-05-24 16:36:20 -07:00
Jarred Sumner
29969bc130 Add comment 2023-05-24 15:45:39 -07:00
Jarred Sumner
1ea74238cd Remove that field 2023-05-24 15:44:22 -07:00
Jarred Sumner
3361814cab Fix string check 2023-05-24 15:44:02 -07:00
Jarred Sumner
cc84c689ea Fixes #3031 (#3041)
* Fixes #3031

* Leave original input in there

---------

Co-authored-by: Jarred Sumner <709451+Jarred-Sumner@users.noreply.github.com>
2023-05-24 12:02:33 -07:00
Jarred Sumner
b3d5f37598 Implement require.cache (#3045)
Co-authored-by: Jarred Sumner <709451+Jarred-Sumner@users.noreply.github.com>
2023-05-24 12:01:59 -07:00
Alex Lam S.L
31c967206a extend test time-outs (#3048)
- avoid intermittent failures due to network latencies
2023-05-24 08:33:18 -07:00
Ashcon Partovi
c3d402ce47 Implement bun test --timeout (#3040)
You can change the default per-test timeout in `bun test`:
 > bun test --timeout 10

The default timeout is 5000.
2023-05-23 22:33:32 -07:00
Kruithne
d9bdfcf131 Fix incorrect URL in loaders.md (#3025) 2023-05-23 18:08:24 -07:00
Jarred Sumner
6074d4e378 workaround 2023-05-23 18:07:23 -07:00
Jarred Sumner
57d341f390 too noisy 2023-05-23 16:57:02 -07:00
Bill Mill
bf759b62c6 Add pkg-config as a build requirement (#3027)
Without pkg-config, the libarchive build fails in autogen.sh[1]

1: https://github.com/libarchive/libarchive/issues/742
2023-05-23 16:06:35 -07:00
dave caruso
5b0fea7093 fix bun init and update node vm docs (#3014) 2023-05-23 15:02:39 -07:00
Dylan Conway
21002ed405 check if key from exports can be printed as an identifier (#3032)
* check if key can be printed as an identifier

* possibly escape
2023-05-23 14:56:03 -07:00
439 changed files with 88052 additions and 19244 deletions

23
.gitattributes vendored
View File

@@ -8,3 +8,26 @@ src/bun.js/bindings/sqlite/sqlite3_local.h linguist-vendored
*.zig text eol=lf
src/bun.js/bindings/simdutf.cpp linguist-vendored
src/bun.js/bindings/simdutf.h linguist-vendored
src/js/out/WebCoreJSBuiltins.cpp linguist-generated
src/js/out/WebCoreJSBuiltins.h linguist-generated
src/js/out/WebCoreJSBuiltins.d.ts linguist-generated
src/bun.js/bindings/ZigGeneratedClasses.h linguist-generated
src/bun.js/bindings/ZigGeneratedClasses.cpp linguist-generated
src/bun.js/bindings/ZigGeneratedCode.h linguist-generated
src/bun.js/bindings/ZigGeneratedCode.cpp linguist-generated
src/bun.js/bindings/headers.h linguist-generated
src/bun.js/bindings/headers.zig linguist-generated
src/bun.js/bindings/JSSink.h linguist-generated
src/bun.js/bindings/JSSink.zig linguist-generated
src/bun.js/bindings/ZigGeneratedClasses+DOMClientIsoSubspaces.h linguist-generated
src/bun.js/bindings/ZigGeneratedClasses+DOMIsoSubspaces.h linguist-generated
src/bun.js/bindings/ZigGeneratedClasses+lazyStructureHeader.h linguist-generated
src/bun.js/bindings/ZigGeneratedClasses+lazyStructureImpl.h linguist-generated
docs/**/* linguist-documentation

View File

@@ -12,6 +12,7 @@ jobs:
deploy:
name: Deploy site
runs-on: ubuntu-latest
if: github.repository_owner == 'oven-sh'
steps:
- name: Trigger Vercel build
run: curl ${{ secrets.VERCEL_DEPLOY_HOOK }}
run: curl ${{ secrets.VERCEL_DEPLOY_HOOK }}

View File

@@ -14,6 +14,7 @@ jobs:
test:
name: ${{ matrix.tag }}
runs-on: ${{ matrix.os }}
if: github.repository_owner == 'oven-sh'
timeout-minutes: 10
strategy:
fail-fast: false

View File

@@ -1,4 +1,9 @@
name: bun-linux
concurrency:
group: bun-linux-aarch64-${{ github.ref }}
cancel-in-progress: true
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
@@ -21,6 +26,7 @@ jobs:
linux:
name: ${{matrix.tag}}
runs-on: ${{matrix.runner}}
if: github.repository_owner == 'oven-sh'
timeout-minutes: 90
strategy:
matrix:

View File

@@ -1,4 +1,9 @@
name: bun-linux
concurrency:
group: bun-linux-build-${{ github.ref }}
cancel-in-progress: true
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
@@ -30,6 +35,7 @@ jobs:
linux:
name: ${{matrix.tag}}
runs-on: ${{matrix.runner}}
if: github.repository_owner == 'oven-sh'
timeout-minutes: 90
strategy:
fail-fast: false
@@ -184,12 +190,13 @@ jobs:
sudo mv bun /usr/local/bin/bun
bun --version
- id: test
name: Test
name: Test (node runner)
# if: ${{github.event.inputs.use_bun == 'false'}}
run: |
bun install
bun install --cwd test
bun install --cwd packages/bun-internal-test
bun run test || true
node packages/bun-internal-test/src/runner.node.mjs || true
- name: Comment on PR
if: steps.test.outputs.failing_tests != '' && github.event_name == 'pull_request'
uses: thollander/actions-comment-pull-request@v2

View File

@@ -1,4 +1,9 @@
name: bun-macOS-aarch64
concurrency:
group: bun-macOS-aarch64-${{ github.ref }}
cancel-in-progress: true
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
@@ -28,6 +33,7 @@ jobs:
macos-object-files:
name: macOS Object
runs-on: med-ubuntu
if: github.repository_owner == 'oven-sh'
strategy:
matrix:
include:
@@ -100,6 +106,7 @@ jobs:
macOS-cpp:
name: macOS C++
runs-on: ${{ matrix.runner }}
if: github.repository_owner == 'oven-sh'
timeout-minutes: 90
strategy:
matrix:
@@ -237,6 +244,7 @@ jobs:
macOS:
name: macOS Link
runs-on: ${{ matrix.runner }}
if: github.repository_owner == 'oven-sh'
needs: [macOS-cpp, macos-object-files]
timeout-minutes: 90
strategy:
@@ -388,7 +396,7 @@ jobs:
name: Tests ${{matrix.tag}}
runs-on: ${{ matrix.runner }}
needs: [macOS]
if: github.event_name == 'pull_request'
if: github.event_name == 'pull_request' && github.repository_owner == 'oven-sh'
timeout-minutes: 10
outputs:
failing_tests: ${{ steps.test.outputs.failing_tests }}
@@ -421,12 +429,13 @@ jobs:
sudo mv bun /usr/local/bin/bun
bun --version
- id: test
name: Test
name: Test (node runner)
# if: ${{github.event.inputs.use_bun == 'false'}}
run: |
bun install
bun install --cwd test
bun install --cwd packages/bun-internal-test
bun run test || true
node packages/bun-internal-test/src/runner.node.mjs || true
- name: Comment on PR
if: steps.test.outputs.failing_tests != '' && github.event_name == 'pull_request'
uses: thollander/actions-comment-pull-request@v2

View File

@@ -1,4 +1,9 @@
name: bun-macOS-x64-baseline
concurrency:
group: bun-macOS-x64-baseline-${{ github.ref }}
cancel-in-progress: true
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
@@ -28,6 +33,7 @@ jobs:
macos-object-files:
name: macOS Object
runs-on: med-ubuntu
if: github.repository_owner == 'oven-sh'
strategy:
matrix:
include:
@@ -100,6 +106,7 @@ jobs:
macOS-cpp:
name: macOS C++
runs-on: ${{ matrix.runner }}
if: github.repository_owner == 'oven-sh'
timeout-minutes: 90
strategy:
matrix:
@@ -238,6 +245,7 @@ jobs:
macOS:
name: macOS Link
runs-on: ${{ matrix.runner }}
if: github.repository_owner == 'oven-sh'
needs: [macOS-cpp, macos-object-files]
timeout-minutes: 90
strategy:
@@ -392,7 +400,7 @@ jobs:
name: Tests ${{matrix.tag}}
runs-on: ${{ matrix.runner }}
needs: [macOS]
if: github.event_name == 'pull_request'
if: github.event_name == 'pull_request' && github.repository_owner == 'oven-sh'
timeout-minutes: 30
outputs:
failing_tests: ${{ steps.test.outputs.failing_tests }}
@@ -425,12 +433,13 @@ jobs:
sudo mv bun /usr/local/bin/bun
bun --version
- id: test
name: Test
name: Test (node runner)
# if: ${{github.event.inputs.use_bun == 'false'}}
run: |
bun install
bun install --cwd test
bun install --cwd packages/bun-internal-test
bun run test || true
node packages/bun-internal-test/src/runner.node.mjs || true
- name: Comment on PR
if: steps.test.outputs.failing_tests != '' && github.event_name == 'pull_request'
uses: thollander/actions-comment-pull-request@v2

View File

@@ -1,4 +1,9 @@
name: bun-macOS-x64
concurrency:
group: bun-macOS-x64-${{ github.ref }}
cancel-in-progress: true
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
@@ -28,6 +33,7 @@ jobs:
macos-object-files:
name: macOS Object
runs-on: med-ubuntu
if: github.repository_owner == 'oven-sh'
strategy:
matrix:
include:
@@ -100,6 +106,7 @@ jobs:
macOS-cpp:
name: macOS C++
runs-on: ${{ matrix.runner }}
if: github.repository_owner == 'oven-sh'
timeout-minutes: 90
strategy:
matrix:
@@ -240,6 +247,7 @@ jobs:
macOS:
name: macOS Link
runs-on: ${{ matrix.runner }}
if: github.repository_owner == 'oven-sh'
needs: [macOS-cpp, macos-object-files]
timeout-minutes: 90
strategy:
@@ -394,7 +402,7 @@ jobs:
name: Tests ${{matrix.tag}}
runs-on: ${{ matrix.runner }}
needs: [macOS]
if: github.event_name == 'pull_request'
if: github.event_name == 'pull_request' && github.repository_owner == 'oven-sh'
timeout-minutes: 30
outputs:
failing_tests: ${{ steps.test.outputs.failing_tests }}
@@ -427,12 +435,13 @@ jobs:
sudo mv bun /usr/local/bin/bun
bun --version
- id: test
name: Test
name: Test (node runner)
# if: ${{github.event.inputs.use_bun == 'false'}}
run: |
bun install
bun install --cwd test
bun install --cwd packages/bun-internal-test
bun run test || true
node packages/bun-internal-test/src/runner.node.mjs || true
- name: Comment on PR
if: steps.test.outputs.failing_tests != '' && github.event_name == 'pull_request'
uses: thollander/actions-comment-pull-request@v2

View File

@@ -8,6 +8,7 @@ jobs:
sign:
name: Sign Release
runs-on: ubuntu-latest
if: github.repository_owner == 'oven-sh'
defaults:
run:
working-directory: packages/bun-release
@@ -40,6 +41,7 @@ jobs:
name: Release to NPM
runs-on: ubuntu-latest
needs: sign
if: github.repository_owner == 'oven-sh'
defaults:
run:
working-directory: packages/bun-release
@@ -108,6 +110,7 @@ jobs:
name: Release to Dockerhub
runs-on: ubuntu-latest
needs: sign
if: github.repository_owner == 'oven-sh'
steps:
- id: checkout
name: Checkout
@@ -149,6 +152,7 @@ jobs:
name: Upload to S3
runs-on: ubuntu-latest
needs: sign
if: github.repository_owner == 'oven-sh'
defaults:
run:
working-directory: packages/bun-release

View File

@@ -11,6 +11,7 @@ jobs:
npm-types:
name: Release types to NPM
runs-on: ubuntu-latest
if: github.repository_owner == 'oven-sh'
defaults:
run:
working-directory: packages/bun-types

View File

@@ -14,6 +14,7 @@ jobs:
sign:
name: Sign Release
runs-on: ubuntu-latest
if: github.repository_owner == 'oven-sh'
defaults:
run:
working-directory: packages/bun-release
@@ -53,6 +54,7 @@ jobs:
name: Release to NPM
runs-on: ubuntu-latest
needs: sign
if: github.repository_owner == 'oven-sh'
defaults:
run:
working-directory: packages/bun-release
@@ -85,6 +87,7 @@ jobs:
name: Release types to NPM
runs-on: ubuntu-latest
needs: sign
if: github.repository_owner == 'oven-sh'
defaults:
run:
working-directory: packages/bun-types
@@ -127,6 +130,7 @@ jobs:
name: Release to Dockerhub
runs-on: ubuntu-latest
needs: sign
if: github.repository_owner == 'oven-sh'
steps:
- id: checkout
name: Checkout
@@ -177,6 +181,7 @@ jobs:
name: Release to Homebrew
runs-on: ubuntu-latest
needs: sign
if: github.repository_owner == 'oven-sh'
steps:
- id: checkout
name: Checkout
@@ -218,6 +223,7 @@ jobs:
name: Upload to S3
runs-on: ubuntu-latest
needs: sign
if: github.repository_owner == 'oven-sh'
defaults:
run:
working-directory: packages/bun-release

View File

@@ -1,59 +0,0 @@
name: Test
on:
workflow_dispatch:
inputs:
version:
description: "The version of Bun to run"
required: true
default: "canary"
type: string
path:
description: "The path to the test files"
required: true
default: "test/"
type: string
timeout:
description: "The timeout per file in milliseconds"
required: true
default: "15000"
type: number
jobs:
test:
name: ${{ matrix.tag }}
runs-on: ${{ matrix.os }}
timeout-minutes: 10
strategy:
fail-fast: false
matrix:
include:
- os: ubuntu-latest
tag: linux-x64
url: linux/x64?avx2=true
- os: ubuntu-latest
tag: linux-x64-baseline
url: linux/x64?baseline=true
# FIXME: runner fails with "No tests found"?
#- os: macos-latest
# tag: darwin-x64
# url: darwin/x64?avx2=true
- os: macos-latest
tag: darwin-x64-baseline
url: darwin/x64?baseline=true
steps:
- id: checkout
name: Checkout
uses: actions/checkout@v3
with:
submodules: false
- id: setup
name: Setup
uses: oven-sh/setup-bun@v1
with:
bun-download-url: https://bun.sh/download/${{ github.event.inputs.version }}/${{ matrix.url }}
- id: test
name: Test
run: |
bun install
bun install --cwd test
bun x ts-node --esm .github/scripts/test-runner.ts ${{ github.event.inputs.path }} --isolated --timeout=${{ github.event.inputs.timeout }}

View File

@@ -1,34 +0,0 @@
name: typecheck
on:
push:
branches: [main]
paths:
- "packages/bun-types/**"
- "test/**"
pull_request:
branches:
- main
paths:
- "packages/bun-types/**"
- "test/**"
jobs:
tests:
name: check-tests
runs-on: ubuntu-latest
steps:
- name: Checkout repo
uses: actions/checkout@v3
- name: Install bun
uses: oven-sh/setup-bun@v1
with:
bun-version: canary
- name: Install dependencies
run: bun install
- name: Install dependencies
run: bun install
working-directory: test
- name: Typecheck tests
run: bun run typecheck

2
.gitignore vendored
View File

@@ -119,3 +119,5 @@ myscript.sh
cold-jsc-start
cold-jsc-start.d
/test.ts

View File

@@ -1,6 +1,6 @@
src/fallback.html
src/bun.js/WebKit
src/bun.js/builtins/js/*.js
src/js/out
src/*.out.js
src/*out.*.js
src/deps
@@ -11,4 +11,3 @@ test/snapshots-no-hmr
test/js/deno/*.test.ts
test/js/deno/**/*.test.ts
bench/react-hello-world/react-hello-world.node.js
src/bun.js/builtins/WebCoreJSBuiltins.d.ts

View File

@@ -15,15 +15,15 @@
"${workspaceFolder}/src/bun.js/bindings/webcore/",
"${workspaceFolder}/src/bun.js/bindings/sqlite/",
"${workspaceFolder}/src/bun.js/bindings/webcrypto/",
"${workspaceFolder}/src/bun.js/builtins/",
"${workspaceFolder}/src/bun.js/builtins/cpp",
"${workspaceFolder}/src/js/builtins/",
"${workspaceFolder}/src/js/out",
"${workspaceFolder}/src/deps/boringssl/include/",
"${workspaceFolder}/src/deps",
"${workspaceFolder}/src/deps/uws/uSockets/src"
"${workspaceFolder}/src/deps/uws/uSockets/src",
"${workspaceFolder}/src/deps/uws/src"
],
"browse": {
"path": [
"${workspaceFolder}/../webkit-build/include/",
"${workspaceFolder}/bun-webkit/include/",
"${workspaceFolder}/src/bun.js/WebKit/WebKitBuild/Release/",
"${workspaceFolder}/src/bun.js/WebKit/WebKitBuild/Release/ICU/Headers/",
@@ -34,12 +34,13 @@
"${workspaceFolder}/src/bun.js/bindings/sqlite/",
"${workspaceFolder}/src/bun.js/bindings/webcrypto/",
"${workspaceFolder}/src/bun.js/bindings/webcore/",
"${workspaceFolder}/src/bun.js/builtins/*",
"${workspaceFolder}/src/bun.js/builtins/cpp/*",
"${workspaceFolder}/src/js/builtins/*",
"${workspaceFolder}/src/js/out/*",
"${workspaceFolder}/src/bun.js/modules/*",
"${workspaceFolder}/src/deps",
"${workspaceFolder}/src/deps/boringssl/include/",
"${workspaceFolder}/src/deps/uws/uSockets/src"
"${workspaceFolder}/src/deps/uws/uSockets/src",
"${workspaceFolder}/src/deps/uws/src"
],
"limitSymbolsToIncludedHeaders": true,
"databaseFilename": ".vscode/cppdb"

173
.vscode/launch.json generated vendored
View File

@@ -1,130 +1,209 @@
{
// The usage of BUN_GARBAGE_COLLECTOR_LEVEL=2 is important for debugging
// It will force the garbage collector to run after every test and every call to expect()
// it makes our tests very slow
// But it helps catch memory bugs
// SIGHUP must be ignored or the debugger will pause when a spawned subprocess exits:
// { "initCommands": ["process handle -p false -s false -n false SIGHUP"] }
"version": "0.2.0",
"configurations": [
{
"type": "lldb",
"request": "launch",
"name": "bun test",
"name": "bun test [file]",
"program": "bun-debug",
"args": ["test", "${file}"],
"cwd": "${workspaceFolder}",
"cwd": "${fileDirname}",
"env": {
"FORCE_COLOR": "1"
"FORCE_COLOR": "1",
"BUN_DEBUG_QUIET_LOGS": "1",
"BUN_GARBAGE_COLLECTOR_LEVEL": "2"
},
"initCommands": ["process handle -p false -s false -n false SIGHUP"],
"console": "internalConsole"
},
{
"type": "lldb",
"request": "launch",
"name": "bun test --watch",
"name": "bun test [file] (fast)",
"program": "bun-debug",
"args": ["--watch", "test", "${file}"],
"cwd": "${workspaceFolder}",
"args": ["test", "${file}"],
"cwd": "${fileDirname}",
"env": {
"FORCE_COLOR": "1",
"BUN_DEBUG_QUIET_LOGS": "1"
},
"initCommands": ["process handle -p false -s false -n false SIGHUP"],
"console": "internalConsole"
},
{
"type": "lldb",
"request": "launch",
"name": "bun test [file] (verbose)",
"program": "bun-debug",
"args": ["test", "${file}"],
"cwd": "${fileDirname}",
"env": {
"FORCE_COLOR": "1"
},
"initCommands": ["process handle -p false -s false -n false SIGHUP"],
"console": "internalConsole"
},
{
"type": "lldb",
"request": "launch",
"name": "bun test (all)",
"name": "bun test [file] --watch",
"program": "bun-debug",
"args": ["test", "--watch", "${file}"],
"cwd": "${fileDirname}",
"env": {
"FORCE_COLOR": "1",
"BUN_DEBUG_QUIET_LOGS": "1"
},
"initCommands": ["process handle -p false -s false -n false SIGHUP"],
"console": "internalConsole"
},
{
"type": "lldb",
"request": "launch",
"name": "bun test [file] --only",
"program": "bun-debug",
"args": ["test", "--only", "${file}"],
"cwd": "${fileDirname}",
"env": {
"FORCE_COLOR": "1",
"BUN_DEBUG_QUIET_LOGS": "1"
},
"initCommands": ["process handle -p false -s false -n false SIGHUP"],
"console": "internalConsole"
},
{
"type": "lldb",
"request": "launch",
"name": "bun test [*]",
"program": "bun-debug",
"args": ["test"],
"cwd": "${workspaceFolder}/test",
"env": {
"FORCE_COLOR": "1",
"BUN_DEBUG_QUIET_LOGS": "1",
"BUN_GARBAGE_COLLECTOR_LEVEL": "2"
},
"initCommands": ["process handle -p false -s false -n false SIGHUP"],
"console": "internalConsole"
},
{
"type": "lldb",
"request": "launch",
"name": "bun test [*] (fast)",
"program": "bun-debug",
"args": ["test"],
"cwd": "${workspaceFolder}/test",
"env": {
"FORCE_COLOR": "1",
"BUN_GARBAGE_COLLECTOR_LEVEL": "2",
"BUN_DEBUG_QUIET_LOGS": "1"
},
"initCommands": ["process handle -p false -s false -n false SIGHUP"],
"console": "internalConsole"
},
{
"type": "lldb",
"request": "launch",
"name": "bun run current file",
"name": "bun test [*] --only",
"program": "bun-debug",
"args": ["${file}"],
"cwd": "${file}/../../",
"args": ["test", "--only"],
"cwd": "${workspaceFolder}/test",
"env": {
"FORCE_COLOR": "1"
"FORCE_COLOR": "1",
"BUN_DEBUG_QUIET_LOGS": "1"
},
"initCommands": ["process handle -p false -s false -n false SIGHUP"],
"console": "internalConsole"
},
{
"type": "lldb",
"request": "launch",
"name": "bun run (watch)",
"name": "bun run [file]",
"program": "bun-debug",
"args": ["--watch", "${file}"],
"cwd": "${file}/../../",
"args": ["run", "${file}"],
"cwd": "${fileDirname}",
"env": {
"FORCE_COLOR": "1"
"FORCE_COLOR": "1",
"BUN_DEBUG_QUIET_LOGS": "1"
},
"initCommands": ["process handle -p false -s false -n false SIGHUP"],
"console": "internalConsole"
},
{
"type": "lldb",
"request": "launch",
"name": "bun run (hot)",
"name": "bun run [Inspect]",
"program": "bun-debug",
"args": ["--hot", "${file}"],
"cwd": "${file}/../../",
"args": ["--inspect-brk", "${file}"],
"cwd": "${fileDirname}",
"env": {
"FORCE_COLOR": "1"
"FORCE_COLOR": "1",
"BUN_DEBUG_QUIET_LOGS": "1"
},
"initCommands": ["process handle -p false -s false -n false SIGHUP"],
"console": "internalConsole"
},
{
"type": "lldb",
"request": "launch",
"name": "bun run",
"name": "bun run [file] (gc)",
"program": "bun-debug",
"args": ["check.tsx", "-c"],
"cwd": "${env:HOME}/Build/react-ssr",
"args": ["run", "${file}"],
"cwd": "${fileDirname}",
"env": {
"FORCE_COLOR": "1"
},
"console": "internalConsole"
},
{
"type": "lldb",
"request": "launch",
"name": "bun http example",
"program": "bun-debug",
"args": ["run", "examples/http.ts"],
"cwd": "${workspaceFolder}",
"env": {
"FORCE_COLOR": "1"
"FORCE_COLOR": "1",
"BUN_DEBUG_QUIET_LOGS": "1",
"BUN_GARBAGE_COLLECTOR_LEVEL": "2"
},
"initCommands": ["process handle -p false -s false -n false SIGHUP"],
"console": "internalConsole"
},
{
"type": "lldb",
"request": "launch",
"name": "bun http file example",
"name": "bun run [file] (verbose)",
"program": "bun-debug",
"args": ["run", "examples/bun/http-file.ts"],
"cwd": "${workspaceFolder}",
"args": ["run", "${file}"],
"cwd": "${fileDirname}",
"env": {
"FORCE_COLOR": "1"
},
"initCommands": ["process handle -p false -s false -n false SIGHUP"],
"console": "internalConsole"
},
{
"type": "lldb",
"request": "launch",
"name": "bun html-rewriter example",
"name": "bun run [file] --watch",
"program": "bun-debug",
"args": ["run", "examples/bun/html-rewriter.ts"],
"cwd": "${workspaceFolder}",
"args": ["run", "--watch", "${file}"],
"cwd": "${fileDirname}",
"env": {
"FORCE_COLOR": "1"
},
"initCommands": ["process handle -p false -s false -n false SIGHUP"],
"console": "internalConsole"
},
{
"type": "lldb",
"request": "launch",
"name": "bun run [file] --hot",
"program": "bun-debug",
"args": ["run", "--hot", "${file}"],
"cwd": "${fileDirname}",
"env": {
"FORCE_COLOR": "1"
},
"initCommands": ["process handle -p false -s false -n false SIGHUP"],
"console": "internalConsole"
},
{
"type": "lldb",
"request": "launch",
@@ -139,12 +218,14 @@
"request": "launch",
"name": "bun build debug",
"program": "bun-debug",
"args": ["bun", "./test/fixtures/bundle/trivial/index.js"],
"args": ["bun", "${file}"],
"cwd": "${workspaceFolder}",
"console": "internalConsole",
"env": {
"BUN_CONFIG_MINIFY_WHITESPACE": "1"
}
},
// SIGHUP must be ignored or the debugger will pause when a spawned subprocess exits.
"initCommands": ["process handle -p false -s false -n false SIGHUP"]
},
{
"type": "lldb",

19
.vscode/settings.json vendored
View File

@@ -37,8 +37,6 @@
"[yaml]": {
"editor.formatOnSave": true
},
"zig.beforeDebugCmd": "make build-unit ${file} ${filter} ${bin}",
"zig.testCmd": "make test ${file} ${filter} ${bin}",
"[markdown]": {
"editor.unicodeHighlight.ambiguousCharacters": false,
"editor.unicodeHighlight.invisibleCharacters": false,
@@ -78,7 +76,8 @@
"src/deps/c-ares": true,
"src/deps/tinycc": true,
"src/deps/zstd": true,
"test/snippets/package-json-exports/_node_modules_copy": true
"test/snippets/package-json-exports/_node_modules_copy": true,
"src/js/out": true
},
"C_Cpp.files.exclude": {
"**/.vscode": true,
@@ -108,11 +107,6 @@
"editor.defaultFormatter": "xaver.clang-format"
},
"files.associations": {
"*.{setting,comp,fuse,fu}": "lua",
"*.ctk": "json",
"*.json5": "json5",
"*.json": "jsonc",
"*.scriptlib": "lua",
"*.lock": "yarnlock",
"*.idl": "cpp",
"memory": "cpp",
@@ -221,14 +215,7 @@
"regex": "cpp",
"span": "cpp",
"valarray": "cpp",
"codecvt": "cpp",
"hash_map": "cpp",
"source_location": "cpp",
"numbers": "cpp",
"semaphore": "cpp",
"stdfloat": "cpp",
"stop_token": "cpp",
"cfenv": "cpp"
"codecvt": "cpp"
},
"cmake.configureOnOpen": false,
"C_Cpp.errorSquiggles": "enabled",

View File

@@ -59,7 +59,9 @@ The module loader is in [`src/bun.js/module_loader.zig`](src/bun.js/module_loade
### JavaScript Builtins
JavaScript builtins are located in [`src/bun.js/builtins/*.js`](src/bun.js/builtins).
TODO: update this with the new build process that uses TypeScript and `$` instead of `@`.
JavaScript builtins are located in [`src/js/builtins/*.ts`](src/js/builtins).
These files support a JavaScriptCore-only syntax for internal slots. `@` is used to access an internal slot. For example: `new @Array(123)` will create a new `Array` similar to `new Array(123)`, except if a library modifies the `Array` global, it will not affect the internal slot (`@Array`). These names must be allow-listed in `BunBuiltinNames.h` (though JavaScriptCore allowlists some names by default).

View File

@@ -20,6 +20,7 @@ CPU_TARGET ?= native
MARCH_NATIVE = -mtune=$(CPU_TARGET)
NATIVE_OR_OLD_MARCH =
MMD_IF_LOCAL =
DEFAULT_MIN_MACOS_VERSION=
ARCH_NAME :=
DOCKER_BUILDARCH =
@@ -41,8 +42,14 @@ endif
MIN_MACOS_VERSION ?= $(DEFAULT_MIN_MACOS_VERSION)
BUN_BASE_VERSION = 0.6
CI ?= false
AR=
ifeq ($(CI), false)
MMD_IF_LOCAL = -MMD
endif
BUN_OR_NODE = $(shell which bun 2>/dev/null || which node 2>/dev/null)
CXX_VERSION=c++2a
@@ -286,14 +293,14 @@ SRC_WEBCORE_FILES := $(wildcard $(SRC_DIR)/webcore/*.cpp)
SRC_SQLITE_FILES := $(wildcard $(SRC_DIR)/sqlite/*.cpp)
SRC_NODE_OS_FILES := $(wildcard $(SRC_DIR)/node_os/*.cpp)
SRC_IO_FILES := $(wildcard src/io/*.cpp)
SRC_BUILTINS_FILES := $(wildcard src/bun.js/builtins/*.cpp)
SRC_BUILTINS_FILES := $(wildcard src/js/out/*.cpp)
SRC_WEBCRYPTO_FILES := $(wildcard $(SRC_DIR)/webcrypto/*.cpp)
OBJ_FILES := $(patsubst $(SRC_DIR)/%.cpp,$(OBJ_DIR)/%.o,$(SRC_FILES))
WEBCORE_OBJ_FILES := $(patsubst $(SRC_DIR)/webcore/%.cpp,$(OBJ_DIR)/%.o,$(SRC_WEBCORE_FILES))
SQLITE_OBJ_FILES := $(patsubst $(SRC_DIR)/sqlite/%.cpp,$(OBJ_DIR)/%.o,$(SRC_SQLITE_FILES))
NODE_OS_OBJ_FILES := $(patsubst $(SRC_DIR)/node_os/%.cpp,$(OBJ_DIR)/%.o,$(SRC_NODE_OS_FILES))
BUILTINS_OBJ_FILES := $(patsubst src/bun.js/builtins/%.cpp,$(OBJ_DIR)/%.o,$(SRC_BUILTINS_FILES))
BUILTINS_OBJ_FILES := $(patsubst src/js/out/%.cpp,$(OBJ_DIR)/%.o,$(SRC_BUILTINS_FILES))
IO_FILES := $(patsubst src/io/%.cpp,$(OBJ_DIR)/%.o,$(SRC_IO_FILES))
MODULES_OBJ_FILES := $(patsubst $(MODULES_DIR)/%.cpp,$(OBJ_DIR)/%.o,$(MODULES_FILES))
WEBCRYPTO_OBJ_FILES := $(patsubst $(SRC_DIR)/webcrypto/%.cpp,$(OBJ_DIR)/%.o,$(SRC_WEBCRYPTO_FILES))
@@ -302,7 +309,7 @@ DEBUG_OBJ_FILES := $(patsubst $(SRC_DIR)/%.cpp,$(DEBUG_OBJ_DIR)/%.o,$(SRC_FILES)
DEBUG_WEBCORE_OBJ_FILES := $(patsubst $(SRC_DIR)/webcore/%.cpp,$(DEBUG_OBJ_DIR)/%.o,$(SRC_WEBCORE_FILES))
DEBUG_SQLITE_OBJ_FILES := $(patsubst $(SRC_DIR)/sqlite/%.cpp,$(DEBUG_OBJ_DIR)/%.o,$(SRC_SQLITE_FILES))
DEBUG_NODE_OS_OBJ_FILES := $(patsubst $(SRC_DIR)/node_os/%.cpp,$(DEBUG_OBJ_DIR)/%.o,$(SRC_NODE_OS_FILES))
DEBUG_BUILTINS_OBJ_FILES := $(patsubst src/bun.js/builtins/%.cpp,$(DEBUG_OBJ_DIR)/%.o,$(SRC_BUILTINS_FILES))
DEBUG_BUILTINS_OBJ_FILES := $(patsubst src/js/out/%.cpp,$(DEBUG_OBJ_DIR)/%.o,$(SRC_BUILTINS_FILES))
DEBUG_IO_FILES := $(patsubst src/io/%.cpp,$(DEBUG_OBJ_DIR)/%.o,$(SRC_IO_FILES))
DEBUG_MODULES_OBJ_FILES := $(patsubst $(MODULES_DIR)/%.cpp,$(DEBUG_OBJ_DIR)/%.o,$(MODULES_FILES))
DEBUG_WEBCRYPTO_OBJ_FILES := $(patsubst $(SRC_DIR)/webcrypto/%.cpp, $(DEBUG_OBJ_DIR)/%.o, $(SRC_WEBCRYPTO_FILES))
@@ -323,12 +330,12 @@ ALL_JSC_INCLUDE_DIRS := -I$(WEBKIT_RELEASE_DIR)/WTF/Headers \
-I$(WEBKIT_RELEASE_DIR)/WTF/PrivateHeaders
SHARED_INCLUDE_DIR = -I$(realpath src/bun.js/bindings)/ \
-I$(realpath src/bun.js/builtins/) \
-I$(realpath src/js/builtins/) \
-I$(realpath src/js/out/) \
-I$(realpath src/bun.js/bindings) \
-I$(realpath src/bun.js/bindings/webcore) \
-I$(realpath src/bun.js/bindings/webcrypto) \
-I$(realpath src/bun.js/bindings/sqlite) \
-I$(realpath src/bun.js/builtins/cpp) \
-I$(realpath src/bun.js/bindings/node_os) \
-I$(realpath src/bun.js/modules) \
-I$(JSC_INCLUDE_DIR)
@@ -548,7 +555,11 @@ PYTHON=$(shell which python 2>/dev/null || which python3 2>/dev/null || which py
.PHONY: builtins
builtins:
bun src/bun.js/builtins/codegen/index.ts --minify
NODE_ENV=production bun src/js/builtins/codegen/index.ts --minify
.PHONY: esm
esm:
NODE_ENV=production bun src/js/build-esm.ts
.PHONY: generate-builtins
generate-builtins: builtins
@@ -666,6 +677,7 @@ require:
@which aclocal > /dev/null || (echo -e "ERROR: automake is required. Install with:\n\n $(POSIX_PKG_MANAGER) install automake"; exit 1)
@which $(LIBTOOL) > /dev/null || (echo -e "ERROR: libtool is required. Install with:\n\n $(POSIX_PKG_MANAGER) install libtool"; exit 1)
@which ninja > /dev/null || (echo -e "ERROR: Ninja is required. Install with:\n\n $(POSIX_PKG_MANAGER) install $(PKGNAME_NINJA)"; exit 1)
@which pkg-config > /dev/null || (echo -e "ERROR: pkg-config is required. Install with:\n\n $(POSIX_PKG_MANAGER) install pkg-config"; exit 1)
@echo "You have the dependencies installed! Woo"
init-submodules:
@@ -1073,7 +1085,7 @@ dev-obj-linux:
$(ZIG) build obj -Dtarget=x86_64-linux-gnu -Dcpu="$(CPU_TARGET)"
.PHONY: dev
dev: mkdir-dev dev-obj bun-link-lld-debug
dev: mkdir-dev esm dev-obj bun-link-lld-debug
mkdir-dev:
mkdir -p $(DEBUG_PACKAGE_DIR)
@@ -1462,7 +1474,7 @@ $(OBJ_DIR)/%.o: $(SRC_DIR)/%.cpp
$(CXX_WITH_CCACHE) $(CLANG_FLAGS) $(UWS_INCLUDE) \
$(MACOS_MIN_FLAG) \
$(OPTIMIZATION_LEVEL) \
-MMD \
${MMD_IF_LOCAL} \
-fno-exceptions \
-fno-rtti \
-ferror-limit=1000 \
@@ -1473,7 +1485,7 @@ $(OBJ_DIR)/%.o: src/bun.js/modules/%.cpp
$(CXX_WITH_CCACHE) $(CLANG_FLAGS) $(UWS_INCLUDE) \
$(MACOS_MIN_FLAG) \
$(OPTIMIZATION_LEVEL) \
-MMD \
${MMD_IF_LOCAL} \
-fno-exceptions \
-fno-rtti \
-ferror-limit=1000 \
@@ -1484,7 +1496,7 @@ $(OBJ_DIR)/%.o: $(SRC_DIR)/webcore/%.cpp
$(CXX_WITH_CCACHE) $(CLANG_FLAGS) \
$(MACOS_MIN_FLAG) \
$(OPTIMIZATION_LEVEL) \
-MMD \
${MMD_IF_LOCAL} \
-fno-exceptions \
-fno-rtti \
-ferror-limit=1000 \
@@ -1495,7 +1507,7 @@ $(OBJ_DIR)/%.o: $(SRC_DIR)/sqlite/%.cpp
$(CXX_WITH_CCACHE) $(CLANG_FLAGS) \
$(MACOS_MIN_FLAG) \
$(OPTIMIZATION_LEVEL) \
-MMD \
${MMD_IF_LOCAL} \
-fno-exceptions \
-fno-rtti \
-ferror-limit=1000 \
@@ -1506,7 +1518,7 @@ $(OBJ_DIR)/%.o: src/io/%.cpp
$(CXX_WITH_CCACHE) $(CLANG_FLAGS) \
$(MACOS_MIN_FLAG) \
$(OPTIMIZATION_LEVEL) \
-MMD \
${MMD_IF_LOCAL} \
-fno-exceptions \
-fno-rtti \
-ferror-limit=1000 \
@@ -1517,18 +1529,18 @@ $(OBJ_DIR)/%.o: $(SRC_DIR)/node_os/%.cpp
$(CXX_WITH_CCACHE) $(CLANG_FLAGS) \
$(MACOS_MIN_FLAG) \
$(OPTIMIZATION_LEVEL) \
-MMD \
${MMD_IF_LOCAL} \
-fno-exceptions \
-fno-rtti \
-ferror-limit=1000 \
$(EMIT_LLVM) \
-c -o $@ $<
$(OBJ_DIR)/%.o: src/bun.js/builtins/%.cpp
$(OBJ_DIR)/%.o: src/js/out/%.cpp
$(CXX_WITH_CCACHE) $(CLANG_FLAGS) \
$(MACOS_MIN_FLAG) \
$(OPTIMIZATION_LEVEL) \
-MMD \
${MMD_IF_LOCAL} \
-fno-exceptions \
-fno-rtti \
-ferror-limit=1000 \
@@ -1540,7 +1552,7 @@ $(OBJ_DIR)/%.o: src/bun.js/bindings/webcrypto/%.cpp
$(CXX_WITH_CCACHE) $(CLANG_FLAGS) \
$(MACOS_MIN_FLAG) \
$(OPTIMIZATION_LEVEL) \
-MMD \
${MMD_IF_LOCAL} \
-fno-exceptions \
-fno-rtti \
-ferror-limit=1000 \
@@ -1554,7 +1566,7 @@ $(DEBUG_OBJ_DIR)/%.o: $(SRC_DIR)/%.cpp
$(CXX_WITH_CCACHE) $(CLANG_FLAGS) $(UWS_INCLUDE) \
$(MACOS_MIN_FLAG) \
$(DEBUG_OPTIMIZATION_LEVEL) \
-MMD \
${MMD_IF_LOCAL} \
-fno-exceptions \
-fno-rtti \
-ferror-limit=1000 \
@@ -1569,7 +1581,7 @@ $(DEBUG_OBJ_DIR)/%.o: $(SRC_DIR)/webcore/%.cpp
$(CXX_WITH_CCACHE) $(CLANG_FLAGS) \
$(MACOS_MIN_FLAG) \
$(DEBUG_OPTIMIZATION_LEVEL) \
-MMD \
${MMD_IF_LOCAL} \
-fno-exceptions \
-fno-rtti \
-ferror-limit=1000 \
@@ -1582,7 +1594,7 @@ $(DEBUG_OBJ_DIR)/%.o: src/io/%.cpp
$(CXX_WITH_CCACHE) $(CLANG_FLAGS) \
$(MACOS_MIN_FLAG) \
$(DEBUG_OPTIMIZATION_LEVEL) \
-MMD \
${MMD_IF_LOCAL} \
-fno-exceptions \
-fno-rtti \
-ferror-limit=1000 \
@@ -1598,7 +1610,7 @@ $(DEBUG_OBJ_DIR)/%.o: $(SRC_DIR)/sqlite/%.cpp
$(CXX_WITH_CCACHE) $(CLANG_FLAGS) \
$(MACOS_MIN_FLAG) \
$(DEBUG_OPTIMIZATION_LEVEL) \
-MMD \
${MMD_IF_LOCAL} \
-fno-exceptions \
-fno-rtti \
-ferror-limit=1000 \
@@ -1613,7 +1625,7 @@ $(DEBUG_OBJ_DIR)/%.o: $(SRC_DIR)/node_os/%.cpp
$(CXX_WITH_CCACHE) $(CLANG_FLAGS) \
$(MACOS_MIN_FLAG) \
$(DEBUG_OPTIMIZATION_LEVEL) \
-MMD \
${MMD_IF_LOCAL} \
-fno-exceptions \
-fno-rtti \
-ferror-limit=1000 \
@@ -1623,12 +1635,12 @@ $(DEBUG_OBJ_DIR)/%.o: $(SRC_DIR)/node_os/%.cpp
# $(DEBUG_OBJ_DIR) is not included here because it breaks
# detecting if a file needs to be rebuilt
.PHONY: src/bun.js/builtins/%.cpp
$(DEBUG_OBJ_DIR)/%.o: src/bun.js/builtins/%.cpp
.PHONY: src/js/out/builtins/%.cpp
$(DEBUG_OBJ_DIR)/%.o: src/js/out/%.cpp
$(CXX_WITH_CCACHE) $(CLANG_FLAGS) \
$(MACOS_MIN_FLAG) \
$(DEBUG_OPTIMIZATION_LEVEL) \
-MMD \
${MMD_IF_LOCAL} \
-fno-exceptions \
-fno-rtti \
-ferror-limit=1000 \
@@ -1641,7 +1653,7 @@ $(DEBUG_OBJ_DIR)/%.o: src/bun.js/modules/%.cpp
$(CXX_WITH_CCACHE) $(CLANG_FLAGS) \
$(MACOS_MIN_FLAG) \
$(DEBUG_OPTIMIZATION_LEVEL) \
-MMD \
${MMD_IF_LOCAL} \
-fno-exceptions \
-fno-rtti \
-ferror-limit=1000 \
@@ -1655,7 +1667,7 @@ $(DEBUG_OBJ_DIR)/%.o: src/bun.js/bindings/webcrypto/%.cpp
$(CXX_WITH_CCACHE) $(CLANG_FLAGS) \
$(MACOS_MIN_FLAG) \
$(DEBUG_OPTIMIZATION_LEVEL) \
-MMD \
${MMD_IF_LOCAL} \
-fno-exceptions \
-I$(SRC_DIR) \
-fno-rtti \
@@ -1835,7 +1847,7 @@ cold-jsc-start:
$(CXX_WITH_CCACHE) $(CLANG_FLAGS) \
$(MACOS_MIN_FLAG) \
$(OPTIMIZATION_LEVEL) \
-MMD \
${MMD_IF_LOCAL} \
-fno-exceptions \
-fno-rtti \
-ferror-limit=1000 \
@@ -1867,7 +1879,7 @@ regenerate-bindings:
@make bindings -j$(CPU_COUNT)
.PHONY: setup
setup: vendor-dev builtins identifier-cache clean-bindings
setup: vendor-dev identifier-cache clean-bindings
make jsc-check
make bindings -j$(CPU_COUNT)
@echo ""

Binary file not shown.

View File

@@ -0,0 +1,31 @@
import EventEmitter3 from "eventemitter3";
import { group } from "mitata";
import EventEmitterNative from "node:events";
export const implementations = [
{
EventEmitter: EventEmitterNative,
name: process.isBun ? (EventEmitterNative.init ? "bun" : "C++") : "node:events",
monkey: true,
},
// { EventEmitter: EventEmitter3, name: "EventEmitter3" },
].filter(Boolean);
for (const impl of implementations) {
impl.EventEmitter?.setMaxListeners?.(Infinity);
}
export function groupForEmitter(name, cb) {
if (implementations.length === 1) {
return cb({
...implementations[0],
name: `${name}: ${implementations[0].name}`,
});
} else {
return group(name, () => {
for (let impl of implementations) {
cb(impl);
}
});
}
}

View File

@@ -0,0 +1,96 @@
import { bench, run } from "mitata";
import { groupForEmitter } from "./implementations.mjs";
var id = 0;
groupForEmitter("single emit", ({ EventEmitter, name }) => {
const emitter = new EventEmitter();
emitter.on("hello", event => {
event.preventDefault();
});
bench(name, () => {
emitter.emit("hello", {
preventDefault() {
id++;
},
});
});
});
groupForEmitter("on x 10_000 (handler)", ({ EventEmitter, name }) => {
const emitter = new EventEmitter();
bench(name, () => {
var cb = event => {
event.preventDefault();
};
emitter.on("hey", cb);
var called = false;
for (let i = 0; i < 10_000; i++)
emitter.emit("hey", {
preventDefault() {
id++;
called = true;
},
});
if (!called) throw new Error("not called");
});
});
// for (let { impl: EventEmitter, name, monkey } of []) {
// if (monkey) {
// var monkeyEmitter = Object.assign({}, EventEmitter.prototype);
// monkeyEmitter.on("hello", event => {
// event.preventDefault();
// });
// bench(`[monkey] ${className}.emit`, () => {
// var called = false;
// monkeyEmitter.emit("hello", {
// preventDefault() {
// id++;
// called = true;
// },
// });
// if (!called) {
// throw new Error("monkey failed");
// }
// });
// bench(`[monkey] ${className}.on x 10_000 (handler)`, () => {
// var cb = () => {
// event.preventDefault();
// };
// monkeyEmitter.on("hey", cb);
// for (let i = 0; i < 10_000; i++)
// monkey.emit("hey", {
// preventDefault() {
// id++;
// },
// });
// monkeyEmitter.off("hey", cb);
// });
// }
// }
// var target = new EventTarget();
// target.addEventListener("hello", event => {});
// bench("EventTarget.dispatch", () => {
// target.dispatchEvent(event);
// });
// var hey = new Event("hey");
// bench("EventTarget.on x 10_000 (handler)", () => {
// var handler = event => {};
// target.addEventListener("hey", handler);
// for (let i = 0; i < 10_000; i++) target.dispatchEvent(hey);
// target.removeEventListener("hey", handler);
// });
await run();

View File

@@ -0,0 +1,40 @@
import { bench, run } from "mitata";
import { groupForEmitter } from "./implementations.mjs";
var id = 0;
groupForEmitter("test 1", ({ EventEmitter, name }) => {
const emitter = new EventEmitter();
emitter.on("hello", event => {
event.preventDefault();
});
bench(name, () => {
emitter.once("hello", event => {
event.preventDefault();
});
emitter.emit("hello", {
preventDefault() {
id++;
},
});
});
});
groupForEmitter("test 2", ({ EventEmitter, name }) => {
const emitter = new EventEmitter();
bench(name, () => {
emitter.once("hello", event => {
event.preventDefault();
});
emitter.emit("hello", {
preventDefault() {
id++;
},
});
});
});
await run();

View File

@@ -0,0 +1,63 @@
import { bench, run } from "mitata";
import { groupForEmitter } from "./implementations.mjs";
// Psuedo RNG is derived from https://stackoverflow.com/a/424445
let rngState = 123456789;
function nextInt() {
const m = 0x80000000; // 2**31;
const a = 1103515245;
const c = 12345;
rngState = (a * rngState + c) % m;
return rngState;
}
function nextRange(start, end) {
// returns in range [start, end): including start, excluding end
// can't modulu nextInt because of weak randomness in lower bits
const rangeSize = end - start;
const randomUnder1 = nextInt() / 0x7fffffff; // 2**31 - 1
return start + Math.floor(randomUnder1 * rangeSize);
}
const chunks = new Array(1024).fill(null).map((_, j) => {
const arr = new Uint8Array(1024);
for (let i = 0; i < arr.length; i++) {
arr[i] = nextRange(0, 256);
}
return arr;
});
groupForEmitter("stream simulation", ({ EventEmitter, name }) => {
bench(name, () => {
let id = 0;
const stream = new EventEmitter();
stream.on("start", res => {
if (res.status !== 200) throw new Error("not 200");
});
const recived = [];
stream.on("data", req => {
recived.push(req);
});
stream.on("end", ev => {
ev.preventDefault();
});
// simulate a stream
stream.emit("start", { status: 200 });
for (let chunk of chunks) {
stream.emit("data", chunk);
}
stream.emit("end", {
preventDefault() {
id++;
},
});
if (id !== 1) throw new Error("not implemented right");
if (recived.length !== 1024) throw new Error("not implemented right");
});
});
await run();

1986
bench/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,11 +1,13 @@
{
"name": "bench",
"dependencies": {
"mitata": "^0.1.6",
"esbuild": "^0.14.12",
"@swc/core": "^1.2.133",
"@babel/core": "^7.16.10",
"@babel/preset-react": "^7.16.7"
"@babel/preset-react": "^7.16.7",
"@swc/core": "^1.2.133",
"benchmark": "^2.1.4",
"esbuild": "^0.14.12",
"eventemitter3": "^5.0.0",
"mitata": "^0.1.6"
},
"scripts": {
"ffi": "cd ffi && bun run deps && bun run build && bun run bench",

Binary file not shown.

View File

@@ -0,0 +1,29 @@
// https://github.com/oven-sh/bun/issues/2190
import { bench, run } from "mitata";
import { createHash } from "node:crypto";
const data =
"Delightful remarkably mr on announcing themselves entreaties favourable. About to in so terms voice at. Equal an would is found seems of. The particular friendship one sufficient terminated frequently themselves. It more shed went up is roof if loud case. Delay music in lived noise an. Beyond genius really enough passed is up.";
const scenarios = [
{ alg: "md5", digest: "hex" },
{ alg: "md5", digest: "base64" },
{ alg: "sha1", digest: "hex" },
{ alg: "sha1", digest: "base64" },
{ alg: "sha256", digest: "hex" },
{ alg: "sha256", digest: "base64" },
];
for (const { alg, digest } of scenarios) {
bench(`${alg}-${digest}`, () => {
createHash(alg).update(data).digest(digest);
});
if ("Bun" in globalThis) {
bench(`${alg}-${digest} (Bun.CryptoHasher)`, () => {
new Bun.CryptoHasher(alg).update(data).digest(digest);
});
}
}
run();

View File

@@ -3,13 +3,13 @@ import { bench, run } from "./runner.mjs";
import crypto from "node:crypto";
var foo = Buffer.allocUnsafe(16384);
var foo = Buffer.allocUnsafe(512);
foo.fill(123);
// if ("Bun" in globalThis) {
// const { CryptoHasher } = Bun;
// bench("CryptoHasher Blake2b256", () => {
// var hasher = new CryptoHasher("blake2b256");
// bench("Bun.CryptoHasher(sha512)", () => {
// var hasher = new CryptoHasher("sha512");
// hasher.update(foo);
// hasher.digest();
// });

View File

@@ -0,0 +1,26 @@
// https://github.com/oven-sh/bun/issues/2190
import { bench, run } from "mitata";
import { createHash } from "node:crypto";
const data =
"Delightful remarkably mr on announcing themselves entreaties favourable. About to in so terms voice at. Equal an would is found seems of. The particular friendship one sufficient terminated frequently themselves. It more shed went up is roof if loud case. Delay music in lived noise an. Beyond genius really enough passed is up.";
const scenarios = [
{ alg: "md5", digest: "hex" },
{ alg: "md5", digest: "base64" },
{ alg: "sha1", digest: "hex" },
{ alg: "sha1", digest: "base64" },
{ alg: "sha256", digest: "hex" },
{ alg: "sha256", digest: "base64" },
];
for (const { alg, digest } of scenarios) {
bench(`${alg}-${digest}`, () => {
const hasher = createHash(alg);
hasher.write(data);
hasher.end();
hasher.read();
});
}
run();

View File

@@ -1,101 +0,0 @@
// **so this file can run in node**
import { createRequire } from "node:module";
const require = createRequire(import.meta.url);
// --
const EventEmitterNative = require("node:events").EventEmitter;
const TypedEmitter = require("tiny-typed-emitter").TypedEmitter;
const EventEmitter3 = require("eventemitter3").EventEmitter;
import { bench, run } from "../../node_modules/mitata/src/cli.mjs";
const event = new Event("hello");
var id = 0;
for (let [EventEmitter, className] of [
[EventEmitterNative, "EventEmitter"],
[TypedEmitter, "TypedEmitter"],
[EventEmitter3, "EventEmitter3"],
]) {
const emitter = new EventEmitter();
emitter.on("hello", event => {
event.preventDefault();
});
bench(`${className}.emit`, () => {
emitter.emit("hello", {
preventDefault() {
id++;
},
});
});
bench(`${className}.on x 10_000 (handler)`, () => {
var cb = event => {
event.preventDefault();
};
emitter.on("hey", cb);
var called = false;
for (let i = 0; i < 10_000; i++)
emitter.emit("hey", {
preventDefault() {
id++;
called = true;
},
});
emitter.off("hey", cb);
if (!called) throw new Error("not called");
});
if (EventEmitter !== EventEmitter3) {
var monkey = Object.assign({}, EventEmitter.prototype);
monkey.on("hello", event => {
event.preventDefault();
});
bench(`[monkey] ${className}.emit`, () => {
var called = false;
monkey.emit("hello", {
preventDefault() {
id++;
called = true;
},
});
if (!called) {
throw new Error("monkey failed");
}
});
bench(`[monkey] ${className}.on x 10_000 (handler)`, () => {
var cb = () => {
event.preventDefault();
};
monkey.on("hey", cb);
for (let i = 0; i < 10_000; i++)
monkey.emit("hey", {
preventDefault() {
id++;
},
});
monkey.off("hey", cb);
});
}
}
var target = new EventTarget();
target.addEventListener("hello", event => {});
bench("EventTarget.dispatch", () => {
target.dispatchEvent(event);
});
var hey = new Event("hey");
bench("EventTarget.on x 10_000 (handler)", () => {
var handler = event => {};
target.addEventListener("hey", handler);
for (let i = 0; i < 10_000; i++) target.dispatchEvent(hey);
target.removeEventListener("hey", handler);
});
await run();

View File

@@ -1,7 +0,0 @@
{
"dependencies": {
"eventemitter3": "^5.0.0",
"tiny-typed-emitter": "latest"
},
"prettier": "../../.prettierrc.cjs"
}

BIN
bench/sqlite/bun.lockb Executable file

Binary file not shown.

View File

@@ -1,728 +0,0 @@
{
"name": "bench",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "bench",
"dependencies": {
"better-sqlite3": "^8.0.1"
}
},
"node_modules/base64-js": {
"version": "1.5.1",
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
"integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/feross"
},
{
"type": "patreon",
"url": "https://www.patreon.com/feross"
},
{
"type": "consulting",
"url": "https://feross.org/support"
}
]
},
"node_modules/better-sqlite3": {
"version": "8.0.1",
"resolved": "https://registry.npmjs.org/better-sqlite3/-/better-sqlite3-8.0.1.tgz",
"integrity": "sha512-JhTZjpyapA1icCEjIZB4TSSgkGdFgpWZA2Wszg7Cf4JwJwKQmbvuNnJBeR+EYG/Z29OXvR4G//Rbg31BW/Z7Yg==",
"hasInstallScript": true,
"dependencies": {
"bindings": "^1.5.0",
"prebuild-install": "^7.1.0"
}
},
"node_modules/bindings": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz",
"integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==",
"dependencies": {
"file-uri-to-path": "1.0.0"
}
},
"node_modules/bl": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz",
"integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==",
"dependencies": {
"buffer": "^5.5.0",
"inherits": "^2.0.4",
"readable-stream": "^3.4.0"
}
},
"node_modules/buffer": {
"version": "5.7.1",
"resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz",
"integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/feross"
},
{
"type": "patreon",
"url": "https://www.patreon.com/feross"
},
{
"type": "consulting",
"url": "https://feross.org/support"
}
],
"dependencies": {
"base64-js": "^1.3.1",
"ieee754": "^1.1.13"
}
},
"node_modules/chownr": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz",
"integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg=="
},
"node_modules/decompress-response": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz",
"integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==",
"dependencies": {
"mimic-response": "^3.1.0"
},
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/deep-extend": {
"version": "0.6.0",
"resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz",
"integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==",
"engines": {
"node": ">=4.0.0"
}
},
"node_modules/detect-libc": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.1.tgz",
"integrity": "sha512-463v3ZeIrcWtdgIg6vI6XUncguvr2TnGl4SzDXinkt9mSLpBJKXT3mW6xT3VQdDN11+WVs29pgvivTc4Lp8v+w==",
"engines": {
"node": ">=8"
}
},
"node_modules/end-of-stream": {
"version": "1.4.4",
"resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz",
"integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==",
"dependencies": {
"once": "^1.4.0"
}
},
"node_modules/expand-template": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz",
"integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==",
"engines": {
"node": ">=6"
}
},
"node_modules/file-uri-to-path": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz",
"integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw=="
},
"node_modules/fs-constants": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz",
"integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow=="
},
"node_modules/github-from-package": {
"version": "0.0.0",
"resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz",
"integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw=="
},
"node_modules/ieee754": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
"integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/feross"
},
{
"type": "patreon",
"url": "https://www.patreon.com/feross"
},
{
"type": "consulting",
"url": "https://feross.org/support"
}
]
},
"node_modules/inherits": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
},
"node_modules/ini": {
"version": "1.3.8",
"resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz",
"integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew=="
},
"node_modules/lru-cache": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
"integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
"dependencies": {
"yallist": "^4.0.0"
},
"engines": {
"node": ">=10"
}
},
"node_modules/mimic-response": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz",
"integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==",
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/minimist": {
"version": "1.2.6",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz",
"integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q=="
},
"node_modules/mkdirp-classic": {
"version": "0.5.3",
"resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz",
"integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A=="
},
"node_modules/napi-build-utils": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-1.0.2.tgz",
"integrity": "sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg=="
},
"node_modules/node-abi": {
"version": "3.22.0",
"resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.22.0.tgz",
"integrity": "sha512-u4uAs/4Zzmp/jjsD9cyFYDXeISfUWaAVWshPmDZOFOv4Xl4SbzTXm53I04C2uRueYJ+0t5PEtLH/owbn2Npf/w==",
"dependencies": {
"semver": "^7.3.5"
},
"engines": {
"node": ">=10"
}
},
"node_modules/once": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
"integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
"dependencies": {
"wrappy": "1"
}
},
"node_modules/prebuild-install": {
"version": "7.1.1",
"resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.1.tgz",
"integrity": "sha512-jAXscXWMcCK8GgCoHOfIr0ODh5ai8mj63L2nWrjuAgXE6tDyYGnx4/8o/rCgU+B4JSyZBKbeZqzhtwtC3ovxjw==",
"dependencies": {
"detect-libc": "^2.0.0",
"expand-template": "^2.0.3",
"github-from-package": "0.0.0",
"minimist": "^1.2.3",
"mkdirp-classic": "^0.5.3",
"napi-build-utils": "^1.0.1",
"node-abi": "^3.3.0",
"pump": "^3.0.0",
"rc": "^1.2.7",
"simple-get": "^4.0.0",
"tar-fs": "^2.0.0",
"tunnel-agent": "^0.6.0"
},
"bin": {
"prebuild-install": "bin.js"
},
"engines": {
"node": ">=10"
}
},
"node_modules/pump": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz",
"integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==",
"dependencies": {
"end-of-stream": "^1.1.0",
"once": "^1.3.1"
}
},
"node_modules/rc": {
"version": "1.2.8",
"resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz",
"integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==",
"dependencies": {
"deep-extend": "^0.6.0",
"ini": "~1.3.0",
"minimist": "^1.2.0",
"strip-json-comments": "~2.0.1"
},
"bin": {
"rc": "cli.js"
}
},
"node_modules/readable-stream": {
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
"integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
"dependencies": {
"inherits": "^2.0.3",
"string_decoder": "^1.1.1",
"util-deprecate": "^1.0.1"
},
"engines": {
"node": ">= 6"
}
},
"node_modules/safe-buffer": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
"integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/feross"
},
{
"type": "patreon",
"url": "https://www.patreon.com/feross"
},
{
"type": "consulting",
"url": "https://feross.org/support"
}
]
},
"node_modules/semver": {
"version": "7.3.7",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz",
"integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==",
"dependencies": {
"lru-cache": "^6.0.0"
},
"bin": {
"semver": "bin/semver.js"
},
"engines": {
"node": ">=10"
}
},
"node_modules/simple-concat": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz",
"integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/feross"
},
{
"type": "patreon",
"url": "https://www.patreon.com/feross"
},
{
"type": "consulting",
"url": "https://feross.org/support"
}
]
},
"node_modules/simple-get": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz",
"integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/feross"
},
{
"type": "patreon",
"url": "https://www.patreon.com/feross"
},
{
"type": "consulting",
"url": "https://feross.org/support"
}
],
"dependencies": {
"decompress-response": "^6.0.0",
"once": "^1.3.1",
"simple-concat": "^1.0.0"
}
},
"node_modules/string_decoder": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
"integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
"dependencies": {
"safe-buffer": "~5.2.0"
}
},
"node_modules/strip-json-comments": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz",
"integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/tar-fs": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.1.tgz",
"integrity": "sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==",
"dependencies": {
"chownr": "^1.1.1",
"mkdirp-classic": "^0.5.2",
"pump": "^3.0.0",
"tar-stream": "^2.1.4"
}
},
"node_modules/tar-stream": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz",
"integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==",
"dependencies": {
"bl": "^4.0.3",
"end-of-stream": "^1.4.1",
"fs-constants": "^1.0.0",
"inherits": "^2.0.3",
"readable-stream": "^3.1.1"
},
"engines": {
"node": ">=6"
}
},
"node_modules/tunnel-agent": {
"version": "0.6.0",
"resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
"integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==",
"dependencies": {
"safe-buffer": "^5.0.1"
},
"engines": {
"node": "*"
}
},
"node_modules/util-deprecate": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="
},
"node_modules/wrappy": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="
},
"node_modules/yallist": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
}
},
"dependencies": {
"base64-js": {
"version": "1.5.1",
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
"integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA=="
},
"better-sqlite3": {
"version": "8.0.1",
"resolved": "https://registry.npmjs.org/better-sqlite3/-/better-sqlite3-8.0.1.tgz",
"integrity": "sha512-JhTZjpyapA1icCEjIZB4TSSgkGdFgpWZA2Wszg7Cf4JwJwKQmbvuNnJBeR+EYG/Z29OXvR4G//Rbg31BW/Z7Yg==",
"requires": {
"bindings": "^1.5.0",
"prebuild-install": "^7.1.0"
}
},
"bindings": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz",
"integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==",
"requires": {
"file-uri-to-path": "1.0.0"
}
},
"bl": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz",
"integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==",
"requires": {
"buffer": "^5.5.0",
"inherits": "^2.0.4",
"readable-stream": "^3.4.0"
}
},
"buffer": {
"version": "5.7.1",
"resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz",
"integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==",
"requires": {
"base64-js": "^1.3.1",
"ieee754": "^1.1.13"
}
},
"chownr": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz",
"integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg=="
},
"decompress-response": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz",
"integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==",
"requires": {
"mimic-response": "^3.1.0"
}
},
"deep-extend": {
"version": "0.6.0",
"resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz",
"integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA=="
},
"detect-libc": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.1.tgz",
"integrity": "sha512-463v3ZeIrcWtdgIg6vI6XUncguvr2TnGl4SzDXinkt9mSLpBJKXT3mW6xT3VQdDN11+WVs29pgvivTc4Lp8v+w=="
},
"end-of-stream": {
"version": "1.4.4",
"resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz",
"integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==",
"requires": {
"once": "^1.4.0"
}
},
"expand-template": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz",
"integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg=="
},
"file-uri-to-path": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz",
"integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw=="
},
"fs-constants": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz",
"integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow=="
},
"github-from-package": {
"version": "0.0.0",
"resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz",
"integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw=="
},
"ieee754": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
"integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="
},
"inherits": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
},
"ini": {
"version": "1.3.8",
"resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz",
"integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew=="
},
"lru-cache": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
"integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
"requires": {
"yallist": "^4.0.0"
}
},
"mimic-response": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz",
"integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ=="
},
"minimist": {
"version": "1.2.6",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz",
"integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q=="
},
"mkdirp-classic": {
"version": "0.5.3",
"resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz",
"integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A=="
},
"napi-build-utils": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-1.0.2.tgz",
"integrity": "sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg=="
},
"node-abi": {
"version": "3.22.0",
"resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.22.0.tgz",
"integrity": "sha512-u4uAs/4Zzmp/jjsD9cyFYDXeISfUWaAVWshPmDZOFOv4Xl4SbzTXm53I04C2uRueYJ+0t5PEtLH/owbn2Npf/w==",
"requires": {
"semver": "^7.3.5"
}
},
"once": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
"integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
"requires": {
"wrappy": "1"
}
},
"prebuild-install": {
"version": "7.1.1",
"resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.1.tgz",
"integrity": "sha512-jAXscXWMcCK8GgCoHOfIr0ODh5ai8mj63L2nWrjuAgXE6tDyYGnx4/8o/rCgU+B4JSyZBKbeZqzhtwtC3ovxjw==",
"requires": {
"detect-libc": "^2.0.0",
"expand-template": "^2.0.3",
"github-from-package": "0.0.0",
"minimist": "^1.2.3",
"mkdirp-classic": "^0.5.3",
"napi-build-utils": "^1.0.1",
"node-abi": "^3.3.0",
"pump": "^3.0.0",
"rc": "^1.2.7",
"simple-get": "^4.0.0",
"tar-fs": "^2.0.0",
"tunnel-agent": "^0.6.0"
}
},
"pump": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz",
"integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==",
"requires": {
"end-of-stream": "^1.1.0",
"once": "^1.3.1"
}
},
"rc": {
"version": "1.2.8",
"resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz",
"integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==",
"requires": {
"deep-extend": "^0.6.0",
"ini": "~1.3.0",
"minimist": "^1.2.0",
"strip-json-comments": "~2.0.1"
}
},
"readable-stream": {
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
"integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
"requires": {
"inherits": "^2.0.3",
"string_decoder": "^1.1.1",
"util-deprecate": "^1.0.1"
}
},
"safe-buffer": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
"integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="
},
"semver": {
"version": "7.3.7",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz",
"integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==",
"requires": {
"lru-cache": "^6.0.0"
}
},
"simple-concat": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz",
"integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q=="
},
"simple-get": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz",
"integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==",
"requires": {
"decompress-response": "^6.0.0",
"once": "^1.3.1",
"simple-concat": "^1.0.0"
}
},
"string_decoder": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
"integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
"requires": {
"safe-buffer": "~5.2.0"
}
},
"strip-json-comments": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz",
"integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ=="
},
"tar-fs": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.1.tgz",
"integrity": "sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==",
"requires": {
"chownr": "^1.1.1",
"mkdirp-classic": "^0.5.2",
"pump": "^3.0.0",
"tar-stream": "^2.1.4"
}
},
"tar-stream": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz",
"integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==",
"requires": {
"bl": "^4.0.3",
"end-of-stream": "^1.4.1",
"fs-constants": "^1.0.0",
"inherits": "^2.0.3",
"readable-stream": "^3.1.1"
}
},
"tunnel-agent": {
"version": "0.6.0",
"resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
"integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==",
"requires": {
"safe-buffer": "^5.0.1"
}
},
"util-deprecate": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="
},
"wrappy": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="
},
"yallist": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
}
}
}

View File

@@ -0,0 +1 @@
hello.txt

View File

@@ -0,0 +1,35 @@
# HTTP request file upload benchmark
This is a simple benchmark of uploading a file to a web server in different runtimes.
## Usage
Generate a file to upload (default is `hello.txt`):
```bash
bun generate-file.js
```
Run the server:
```bash
node server-node.mjs
```
Run the benchmark in bun:
```bash
bun stream-file-bun.js
```
Run the benchmark in node:
```bash
node stream-file-node.mjs
```
Run the benchmark in deno:
```bash
deno run -A stream-file-deno.js
```

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,15 @@
import { createServer } from "node:http";
const server = createServer((req, res) => {
var chunkSize = 0;
req.on("data", chunk => {
chunkSize += chunk.byteLength;
});
req.on("end", () => {
console.log("Received", chunkSize, "bytes");
res.end(`${chunkSize}`);
});
});
server.listen(parseInt(process.env.PORT ?? "3000"), (err, port) => {
console.log(`http://localhost:${server.address().port}`);
});

View File

@@ -0,0 +1,9 @@
import { file } from "bun";
console.time("stream-file-bun");
const response = await fetch(process.env.URL ?? "http://localhost:3000", {
method: "POST",
body: file(process.env.FILE ?? "hello.txt"),
});
console.timeEnd("stream-file-bun");
console.log("Sent", await response.text(), "bytes");

View File

@@ -0,0 +1,12 @@
const file = await Deno.open(Deno.env.get("FILE") ?? "hello.txt", {
read: true,
});
console.time("stream-file-deno");
const response = await fetch(Deno.env.get("URL") ?? "http://localhost:3000", {
method: "POST",
body: file.readable,
});
console.timeEnd("stream-file-deno");
console.log("Sent", await response.text(), "bytes");

View File

@@ -0,0 +1,19 @@
import { createReadStream } from "node:fs";
import http from "node:http";
console.time("stream-file-node");
createReadStream(process.env.FILE ?? "hello.txt")
.pipe(
http
.request(process.env.URL ?? "http://localhost:3000", {
method: "POST",
})
.on("response", response => {
response.on("data", data => {
console.log("Sent", parseInt(data.toString(), 10), "bytes");
});
}),
)
.on("close", () => {
console.timeEnd("stream-file-node");
});

988
docs/api/binary-data.md Normal file
View File

@@ -0,0 +1,988 @@
This page is intended as an introduction to working with binary data in JavaScript. Bun implements a number of data types and utilities for working with binary data, most of which are Web-standard. Any Bun-specific APIs will be noted as such.
Below is a quick "cheat sheet" that doubles as a table of contents. Click an item in the left column to jump to that section.
{% table %}
---
- [`TypedArray`](#typedarray)
- A family of classes that provide an `Array`-like interface for interacting with binary data. Includes `Uint8Array`, `Uint16Array`, `Int8Array`, and more.
---
- [`Buffer`](#buffer)
- A subclass of `Uint8Array` that implements a wide range of convenience methods. Unlike the other elements in this table, this is a Node.js API (which Bun implements). It can't be used in the browser.
---
- [`DataView`](#dataview)
- A class that provides a `get/set` API for writing some number of bytes to an `ArrayBuffer` at a particular byte offset. Often used reading or writing binary protocols.
---
- [`Blob`](#blob)
- A readonly blob of binary data usually representing a file. Has a MIME `type`, a `size`, and methods for converting to `ArrayBuffer`, `ReadableStream`, and string.
---
<!-- - [`File`](#file)
- _Browser only_. A subclass of `Blob` that represents a file. Has a `name` and `lastModified` timestamp. There is experimental support in Node.js v20; Bun does not support `File` yet; most of its functionality is provided by `BunFile`.
--- -->
- [`BunFile`](#bunfile)
- _Bun only_. A subclass of `Blob` that represents a lazily-loaded file on disk. Created with `Bun.file(path)`.
{% /table %}
## `ArrayBuffer` and views
Until 2009, there was no language-native way to store and manipulate binary data in JavaScript. ECMAScript v5 introduced a range of new mechanisms for this. The most fundamental building block is `ArrayBuffer`, a simple data structure that represents a sequence of bytes in memory.
```ts
// this buffer can store 8 bytes
const buf = new ArrayBuffer(8);
```
Despite the name, it isn't an array and supports none of the array methods and operators one might expect. In fact, there is no way to directly read or write values from an `ArrayBuffer`. There's very little you can do with one except check its size and create "slices" from it.
```ts
const buf = new ArrayBuffer(8);
buf.byteLength; // => 8
const slice = buf.slice(0, 4); // returns new ArrayBuffer
slice.byteLength; // => 4
```
To do anything interesting we need a construct known as a "view". A view is a class that _wraps_ an `ArrayBuffer` instance and lets you read and manipulate the underlying data. There are two types of views: _typed arrays_ and `DataView`.
### `DataView`
The `DataView` class is a lower-level interface for reading and manipulating the data in an `ArrayBuffer`.
Below we create a new `DataView` and set the first byte to 5.
```ts
const buf = new ArrayBuffer(4);
// [0x0, 0x0, 0x0, 0x0]
const dv = new DataView(buf);
dv.setUint8(0, 3); // write value 3 at byte offset 0
dv.getUint8(0); // => 3
// [0x11, 0x0, 0x0, 0x0]
```
Now lets write a `Uint16` at byte offset `1`. This requires two bytes. We're using the value `513`, which is `2 * 256 + 1`; in bytes, that's `00000010 00000001`.
```ts
dv.setUint16(1, 513);
// [0x11, 0x10, 0x1, 0x0]
console.log(dv.getUint16(1)); // => 513
```
We've now assigned a value to the first three bytes in our underlying `ArrayBuffer`. Even though the second and third bytes were created using `setUint16()`, we can still read each of its component bytes using `getUint8()`.
```ts
console.log(dv.getUint8(1)); // => 2
console.log(dv.getUint8(2)); // => 1
```
Attempting to write a value that requires more space than is available in the underlying `ArrayBuffer` will cuase an error. Below we attempt to write a `Float64` (which requires 8 bytes) at byte offset `0`, but there are only four total bytes in the buffer.
```ts
dv.setFloat64(0, 3.1415);
// ^ RangeError: Out of bounds access
```
The following methods are available on `DataView`:
{% table %}
- Getters
- Setters
---
- [`getBigInt64()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/getBigInt64)
- [`setBigInt64()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/setBigInt64)
---
- [`getBigUint64()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/getBigUint64)
- [`setBigUint64()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/setBigUint64)
---
- [`getFloat32()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/getFloat32)
- [`setFloat32()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/setFloat32)
---
- [`getFloat64()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/getFloat64)
- [`setFloat64()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/setFloat64)
---
- [`getInt16()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/getInt16)
- [`setInt16()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/setInt16)
---
- [`getInt32()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/getInt32)
- [`setInt32()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/setInt32)
---
- [`getInt8()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/getInt8)
- [`setInt8()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/setInt8)
---
- [`getUint16()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/getUint16)
- [`setUint16()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/setUint16)
---
- [`getUint32()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/getUint32)
- [`setUint32()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/setUint32)
---
- [`getUint8()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/getUint8)
- [`setUint8()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/setUint8)
{% /table %}
### `TypedArray`
Typed arrays are a family of classes that provide an `Array`-like interface for interacting with data in an `ArrayBuffer`. Whereas a `DataView` lets you write numbers of varying size at a particular offset, a `TypedArray` interprets the underlying bytes as an array of numbers, each of a fixed size.
{% callout %}
**Note** — It's common to refer to this family of classes collectively by their shared superclass `TypedArray`. This class as _internal_ to JavaScript; you can't directly create instances of it, and `TypedArray` is not defined in the global scope. Think of it as an `interface` or an abstract class.
{% /callout %}
```ts
const buffer = new ArrayBuffer(3);
const arr = new Uint8Array(buffer);
// contents are initialized to zero
console.log(arr); // Uint8Array(3) [0, 0, 0]
// assign values like an array
arr[0] = 0;
arr[1] = 10;
arr[2] = 255;
arr[3] = 255; // no-op, out of bounds
```
While an `ArrayBuffer` is a generic sequence of bytes, these typed array classes interpret the bytes as an array of numbers of a given byte size.
The top row contains the raw bytes, and the later rows contain how these bytes will be interpreted when _viewed_ using different typed array classes.
The following classes are typed arrays, along with a description of how they interpret the bytes in an `ArrayBuffer`:
{% table %}
- Class
- Description
---
- [`Uint8Array`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array)
- Every one (1) byte is interpreted as an unsigned 8-bit integer. Range 0 to 255.
---
- [`Uint16Array`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint16Array)
- Every two (2) bytes are interpreted as an unsigned 16-bit integer. Range 0 to 65535.
---
- [`Uint32Array`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint32Array)
- Every four (4) bytes are interpreted as an unsigned 32-bit integer. Range 0 to 4294967295.
---
- [`Int8Array`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Int8Array)
- Every one (1) byte is interpreted as a signed 8-bit integer. Range -128 to 127.
---
- [`Int16Array`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Int16Array)
- Every two (2) bytes are interpreted as a signed 16-bit integer. Range -32768 to 32767.
---
- [`Int32Array`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Int32Array)
- Every four (4) bytes are interpreted as a signed 32-bit integer. Range -2147483648 to 2147483647.
---
- [`Float32Array`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Float32Array)
- Every four (4) bytes are interpreted as a 32-bit floating point number. Range -3.4e38 to 3.4e38.
---
- [`Float64Array`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Float64Array)
- Every eight (8) bytes are interpreted as a 64-bit floating point number. Range -1.7e308 to 1.7e308.
---
- [`BigInt64Array`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt64Array)
- Every eight (8) bytes are interpreted as an unsigned `BigInt`. Range -9223372036854775808 to 9223372036854775807 (though `BigInt` is capable of representing larger numbers).
---
- [`BigUint64Array`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigUint64Array)
- Every eight (8) bytes are interpreted as an unsigned `BigInt`. Range 0 to 18446744073709551615 (though `BigInt` is capable of representing larger numbers).
---
- [`Uint8ClampedArray`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8ClampedArray)
- Same as `Uint8Array`, but automatically "clamps" to the range 0-255 when assigning a value to an element.
{% /table %}
The table below demonstrates how the bytes in an `ArrayBuffer` are interpreted when viewed using different typed array classes.
{% table %}
---
- `ArrayBuffer`
- `00000000`
- `00000001`
- `00000010`
- `00000011`
- `00000100`
- `00000101`
- `00000110`
- `00000111`
---
- `Uint8Array`
- 0
- 1
- 2
- 3
- 4
- 5
- 6
- 7
---
- `Uint16Array`
- 256 (`1 * 256 + 0`) {% colspan=2 %}
- 770 (`3 * 256 + 2`) {% colspan=2 %}
- 1284 (`5 * 256 + 4`) {% colspan=2 %}
- 1798 (`7 * 256 + 6`) {% colspan=2 %}
---
- `Uint32Array`
- 50462976 {% colspan=4 %}
- 117835012 {% colspan=4 %}
---
- `BigUint64Array`
- 506097522914230528n {% colspan=8 %}
{% /table %}
To create a typed array from a pre-defined `ArrayBuffer`:
```ts
// create typed array from ArrayBuffer
const buf = new ArrayBuffer(10);
const arr = new Uint8Array(buf);
arr[0] = 30;
arr[1] = 60;
// all elements are initialized to zero
console.log(arr); // => Uint8Array(10) [ 30, 60, 0, 0, 0, 0, 0, 0, 0, 0 ];
```
If we tried to instantiate a `Uint32Array` from this same `ArrayBuffer`, we'd get an error.
```ts
const buf = new ArrayBuffer(10);
const arr = new Uint32Array(buf);
// ^ RangeError: ArrayBuffer length minus the byteOffset
// is not a multiple of the element size
```
A `Uint32` value requires four bytes (16 bits). Because the `ArrayBuffer` is 10 bytes long, there's no way to cleanly divide its contents into 4-byte chunks.
To fix this, we can create a typed array over a particular "slice" of an `ArrayBuffer`. The `Uint16Array` below only "views" the _first_ 8 bytes of the underlying `ArrayBuffer`. To achieve these, we specify a `byteOffset` of `0` and a `length` of `2`, which indicates the number of `Uint32` numbers we want our array to hold.
```ts
// create typed array from ArrayBuffer slice
const buf = new ArrayBuffer(10);
const arr = new Uint32Array(buf, 0, 2);
/*
buf _ _ _ _ _ _ _ _ _ _ 10 bytes
arr [_______,_______] 2 4-byte elements
*/
arr.byteOffset; // 0
arr.length; // 2
```
You don't need to explicitly create an `ArrayBuffer` instance; you can instead directly specify a length in the typed array constructor:
```ts
const arr2 = new Uint8Array(5);
// all elements are initialized to zero
// => Uint8Array(5) [0, 0, 0, 0, 0]
```
Typed arrays can also be instantiated directly from an array of numbers, or another typed array:
```ts
// from an array of numbers
const arr1 = new Uint8Array([0, 1, 2, 3, 4, 5, 6, 7]);
arr1[0]; // => 0;
arr1[7]; // => 7;
// from another typed array
const arr2 = new Uint8Array(arr);
```
Broadly speaking, typed arrays provide the same methods as regular arrays, with a few exceptions. For example, `push` and `pop` are not available on typed arrays, because they would require resizing the underlying `ArrayBuffer`.
```ts
const arr = new Uint8Array([0, 1, 2, 3, 4, 5, 6, 7]);
// supports common array methods
arr.filter(n => n > 128); // Uint8Array(1) [255]
arr.map(n => n * 2); // Uint8Array(8) [0, 2, 4, 6, 8, 10, 12, 14]
arr.reduce((acc, n) => acc + n, 0); // 28
arr.forEach(n => console.log(n)); // 0 1 2 3 4 5 6 7
arr.every(n => n < 10); // true
arr.find(n => n > 5); // 6
arr.includes(5); // true
arr.indexOf(5); // 5
```
Refer to the [MDN documentation](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray) for more information on the properties and methods of typed arrays.
### `Uint8Array`
It's worth specifically highlighting `Uint8Array`, as it represents a classic "byte array"—a sequence of 8-bit unsigned integers between 0 and 255. This is the most common typed array you'll encounter in JavaScript.
It is the return value of [`TextEncoder#encode`](https://developer.mozilla.org/en-US/docs/Web/API/TextEncoder), and the input type of [`TextDecoder#decode`](https://developer.mozilla.org/en-US/docs/Web/API/TextDecoder), two utility classes designed to translate strings and various binary encodings, most notably `"utf-8"`.
```ts
const encoder = new TextEncoder();
const bytes = encoder.encode("hello world");
// => Uint8Array(11) [ 104, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100 ]
const decoder = new TextDecoder();
const text = decoder.decode(bytes);
// => hello world
```
### `Buffer`
Bun implements `Buffer`, a Node.js API for working with binary data that pre-dates the introduction of typed arrays in the JavaScript spec. It has since been re-implemented as a subclass of `Uint8Array`. It provides a wide range of methods, including several Array-like and `DataView`-like methods.
```ts
const buf = Buffer.from("hello world");
// => Buffer(16) [ 116, 104, 105, 115, 32, 105, 115, 32, 97, 32, 115, 116, 114, 105, 110, 103 ]
buf.length; // => 11
buf[0]; // => 104, ascii for 'h'
buf.writeUInt8(72, 0); // => ascii for 'H'
console.log(buf.toString());
// => Hello world
```
For complete documentation, refer to the [Node.js documentation](https://nodejs.org/api/buffer.html).
## `Blob`
`Blob` is a Web API commonly used for representing files. `Blob` was initially implemented in browsers (unlike `ArrayBuffer` which is part of JavaScript itself), but it is now supported in Node and Bun.
It isn't common to directly create `Blob` instances. More often, you'll recieve instances of `Blob` from an external source (like an `<input type="file">` element in the browser) or library. That said, it is possible to create a `Blob` from one or more string or binary "blob parts".
```ts
const blob = new Blob(["<html>Hello</html>"], {
type: "text/html",
});
blob.type; // => text/html
blob.size; // => 19
```
These parts can be `string`, `ArrayBuffer`, `TypedArray`, `DataView`, or other `Blob` instances. The blob parts are concatenated together in the order they are provided.
```ts
const blob = new Blob([
"<html>",
new Blob(["<body>"]),
new Uint8Array([104, 101, 108, 108, 111]), // "hello" in binary
"</body></html>",
]);
```
The contents of a `Blob` can be asynchronously read in various formats.
```ts
await blob.text(); // => <html><body>hello</body></html>
await blob.arrayBuffer(); // => ArrayBuffer (copies contents)
await blob.stream(); // => ReadableStream
```
### `BunFile`
`BunFile` is a subclass of `Blob` used to represent a lazily-loaded file on disk. Like `File`, it adds a `name` and `lastModified` property. Unlike `File`, it does not require the file to be loaded into memory.
```ts
const file = Bun.file("index.txt");
// => BunFile
```
### `File`
{% callout %}
Browser only. Experimental support in Node.js 20.
{% /callout %}
[`File`](https://developer.mozilla.org/en-US/docs/Web/API/File) is a subclass of `Blob` that adds a `name` and `lastModified` property. It's commonly used in the browser to represent files uploaded via a `<input type="file">` element. Node.js and Bun implement `File`.
```ts
// on browser!
// <input type="file" id="file" />
const files = document.getElementById("file").files;
// => File[]
```
```ts
const file = new File(["<html>Hello</html>"], "index.html", {
type: "text/html",
});
```
Refer to the [MDN documentation](https://developer.mozilla.org/en-US/docs/Web/API/Blob) for complete docs information.
## Streams
Streams are an important abstraction for working with binary data without loading it all into memory at once. They are commonly used for reading and writing files, sending and receiving network requests, and processing large amounts of data.
Bun implements the Web APIs [`ReadableStream`](https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream) and [`WritableStream`](https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream).
{% callout %}
Bun also implements the `node:stream` module, including [`Readable`](https://nodejs.org/api/stream.html#stream_readable_streams), [`Writable`](https://nodejs.org/api/stream.html#stream_writable_streams), and [`Duplex`](https://nodejs.org/api/stream.html#stream_duplex_and_transform_streams). For complete documentation, refer to the Node.js docs.
{% /callout %}
To create a simple readable stream:
```ts
const stream = new ReadableStream({
start(controller) {
controller.enqueue("hello");
controller.enqueue("world");
controller.close();
},
});
```
The contents of this stream can be read chunk-by-chunk with `for await` syntax.
```ts
for await (const chunk of stream) {
console.log(chunk);
// => "hello"
// => "world"
}
```
For a more complete discusson of streams in Bun, see [API > Streams](/docs/api/streams).
## Conversion
Converting from one binary format to another is a common task. This section is intended as a reference.
### From `ArrayBuffer`
Since `ArrayBuffer` stores the data that underlies other binary structures like `TypedArray`, the snippets below are not _converting_ from `ArrayBuffer` to another format. Instead, they are _creating_ a new instance using the data stored underlying data.
#### To `TypedArray`
```ts
new Uint8Array(buf);
```
#### To `DataView`
```ts
new DataView(buf);
```
#### To `Buffer`
```ts
// create Buffer over entire ArrayBuffer
Buffer.from(buf);
// create Buffer over a slice of the ArrayBuffer
Buffer.from(buf, 0, 10);
```
#### To `string`
```ts
new TextDecoder().decode(buf);
```
#### To `number[]`
```ts
Array.from(new Uint8Array(buf));
```
#### To `Blob`
```ts
new Blob([buf], { type: "text/plain" });
```
<!-- #### To `File`
```ts
new File([buf], "filename.txt", { type: "text/plain", lastModified: Date.now() });
``` -->
#### To `ReadableStream`
The following snippet creates a `ReadableStream` and enqueues the entire `ArrayBuffer` as a single chunk.
```ts
new ReadableStream({
start(controller) {
controller.enqueue(buf);
controller.close();
},
});
```
{% details summary="With chunking" %}
To stream the `ArrayBuffer` in chunks, use a `Uint8Array` view and enqueue each chunk.
```ts
const view = new Uint8Array(buf);
const chunkSize = 1024;
new ReadableStream({
start(controller) {
for (let i = 0; i < view.length; i += chunkSize) {
controller.enqueue(view.slice(i, i + chunkSize));
}
controller.close();
},
});
```
{% /details %}
### From `TypedArray`
#### To `ArrayBuffer`
This retrieves the underlying `ArrayBuffer`. Note that a `TypedArray` can be a view of a _slice_ of the underlying buffer, so the sizes may differ.
```ts
arr.buffer;
```
#### To `DataView`
To creates a `DataView` over the same byte range as the TypedArray.
```ts
new DataView(arr.buffer, arr.byteOffset, arr.byteLength);
```
#### To `Buffer`
```ts
Buffer.from(arr);
```
#### To `string`
```ts
new TextDecoder().decode(arr);
```
#### To `number[]`
```ts
Array.from(arr);
```
#### To `Blob`
```ts
new Blob([arr.buffer], { type: "text/plain" });
```
<!-- #### To `File`
```ts
new File([arr.buffer], "filename.txt", { type: "text/plain", lastModified: Date.now() });
``` -->
#### To `ReadableStream`
```ts
new ReadableStream({
start(controller) {
controller.enqueue(arr);
controller.close();
},
});
```
{% details summary="With chunking" %}
To stream the `ArrayBuffer` in chunks, split the `TypedArray` into chunks and enqueue each one individually.
```ts
new ReadableStream({
start(controller) {
for (let i = 0; i < arr.length; i += chunkSize) {
controller.enqueue(arr.slice(i, i + chunkSize));
}
controller.close();
},
});
```
{% /details %}
### From `DataView`
#### To `ArrayBuffer`
```ts
view.buffer;
```
#### To `TypedArray`
Only works if the `byteLength` of the `DataView` is a multiple of the `BYTES_PER_ELEMENT` of the `TypedArray` subclass.
```ts
new Uint8Array(view.buffer, view.byteOffset, view.byteLength);
new Uint16Array(view.buffer, view.byteOffset, view.byteLength / 2);
new Uint32Array(view.buffer, view.byteOffset, view.byteLength / 4);
// etc...
```
#### To `Buffer`
```ts
Buffer.from(view.buffer, view.byteOffset, view.byteLength);
```
#### To `string`
```ts
new TextDecoder().decode(view);
```
#### To `number[]`
```ts
Array.from(view);
```
#### To `Blob`
```ts
new Blob([view.buffer], { type: "text/plain" });
```
<!-- #### To `File`
```ts
new File([view.buffer], "filename.txt", { type: "text/plain", lastModified: Date.now() });
``` -->
#### To `ReadableStream`
```ts
new ReadableStream({
start(controller) {
controller.enqueue(view.buffer);
controller.close();
},
});
```
{% details summary="With chunking" %}
To stream the `ArrayBuffer` in chunks, split the `DataView` into chunks and enqueue each one individually.
```ts
new ReadableStream({
start(controller) {
for (let i = 0; i < view.byteLength; i += chunkSize) {
controller.enqueue(view.buffer.slice(i, i + chunkSize));
}
controller.close();
},
});
```
{% /details %}
### From `Buffer`
#### To `ArrayBuffer`
```ts
buf.buffer;
```
#### To `TypedArray`
```ts
new Uint8Array(buf);
```
#### To `DataView`
```ts
new DataView(buf.buffer, buf.byteOffset, buf.byteLength);
```
#### To `string`
```ts
buf.toString();
```
#### To `number[]`
```ts
Array.from(buf);
```
#### To `Blob`
```ts
new Blob([buf], { type: "text/plain" });
```
<!-- #### To `File`
```ts
new File([buf], "filename.txt", { type: "text/plain", lastModified: Date.now() });
``` -->
#### To `ReadableStream`
```ts
new ReadableStream({
start(controller) {
controller.enqueue(buf);
controller.close();
},
});
```
{% details summary="With chunking" %}
To stream the `ArrayBuffer` in chunks, split the `Buffer` into chunks and enqueue each one individually.
```ts
new ReadableStream({
start(controller) {
for (let i = 0; i < buf.length; i += chunkSize) {
controller.enqueue(buf.slice(i, i + chunkSize));
}
controller.close();
},
});
```
{% /details %}
### From `Blob`
#### To `ArrayBuffer`
The `Blob` class provides a convenience method for this purpose.
```ts
await blob.arrayBuffer();
```
#### To `TypedArray`
```ts
new Uint8Array(await blob.arrayBuffer());
```
#### To `DataView`
```ts
new DataView(await blob.arrayBuffer());
```
#### To `Buffer`
```ts
Buffer.from(await blob.arrayBuffer());
```
#### To `string`
```ts
await blob.text();
```
#### To `number[]`
```ts
Array.from(new Uint8Array(await blob.arrayBuffer()));
```
#### To `ReadableStream`
```ts
blob.stream();
```
<!-- ### From `File` -->
### From `ReadableStream`
It's common to use [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response) as a convenient intermediate representation to make it easier to convert `ReadableStream` to other formats.
```ts
stream; // ReadableStream
const buffer = new Response(stream).arrayBuffer();
```
However this approach is verbose and adds overhead that slows down overall performance unnecessarily. Bun implements a set of optimized convenience functions for converting `ReadableStream` various binary formats.
#### To `ArrayBuffer`
```ts
// with Response
new Response(stream).arrayBuffer();
// with Bun function
Bun.readableStreamToArrayBuffer(stream);
```
#### To `TypedArray`
```ts
// with Response
const buf = await new Response(stream).arrayBuffer();
new Uint8Array(buf);
// with Bun function
new Uint8Array(Bun.readableStreamToArrayBuffer(stream));
```
#### To `DataView`
```ts
// with Response
const buf = await new Response(stream).arrayBuffer();
new DataView(buf);
// with Bun function
new DataView(Bun.readableStreamToArrayBuffer(stream));
```
#### To `Buffer`
```ts
// with Response
const buf = await new Response(stream).arrayBuffer();
Buffer.from(buf);
// with Bun function
Buffer.from(Bun.readableStreamToArrayBuffer(stream));
```
#### To `string`
```ts
// with Response
new Response(stream).text();
// with Bun function
await Bun.readableStreamToString(stream);
```
#### To `number[]`
```ts
// with Response
const buf = await new Response(stream).arrayBuffer();
Array.from(new Uint8Array(buf));
// with Bun function
Array.from(new Uint8Array(Bun.readableStreamToArrayBuffer(stream)));
```
Bun provides a utility for resolving a `ReadableStream` to an array of its chunks. Each chunk may be a string, typed array, or `ArrayBuffer`.
```ts
// with Bun function
Bun.readableStreamToArray(stream);
```
#### To `Blob`
```ts
new Response(stream).blob();
```
<!-- #### To `File`
```ts
new Response(stream)
.blob()
.then(blob => new File([blob], "filename.txt", { type: "text/plain", lastModified: Date.now() }));
``` -->
#### To `ReadableStream`
To split a `ReadableStream` into two streams that can be consumed independently:
```ts
const [a, b] = stream.tee();
```
<!-- - Use Buffer
- TextEncoder
- `Bun.ArrayBufferSink`
- ReadableStream
- AsyncIterator
- TypedArray vs ArrayBuffer vs DataView
- Bun.indexOfLine
- “direct” readablestream
- readable stream has assumptions about
- its very generic
- all data is copies and queued
- direct : no queueing
- just a write function
- you can write strings
- more synchronous
- corking works better -->

139
docs/api/hashing.md Normal file
View File

@@ -0,0 +1,139 @@
{% callout %}
Bun implements the `createHash` and `createHmac` functions from [`node:crypto`](https://nodejs.org/api/crypto.html) in addition to the Bun-native APIs documented below.
{% /callout %}
## `Bun.hash`
`Bun.hash` is a collection of utilities for _non-cryptographic_ hashing. Non-cryptographic hashing algorithms are optimized for speed of computation over collision-resistance or security.
The standard `Bun.hash` functions uses [Wyhash](https://github.com/wangyi-fudan/wyhash) to generate a 64-bit hash from an input of arbitrary size.
```ts
Bun.hash("some data here");
// 976213160445840
```
The input can be a string, `TypedArray`, `DataView`, `ArrayBuffer`, or `SharedArrayBuffer`.
```ts
const arr = new Uint8Array([1, 2, 3, 4]);
Bun.hash("some data here");
Bun.hash(arr);
Bun.hash(arr.buffer);
Bun.hash(new DataView(arr.buffer));
```
Optionally, an integer seed can be specified as the second parameter.
```ts
Bun.hash("some data here", 1234);
// 1173484059023252
```
Additional hashing algorithms are available as properties on `Bun.hash`. The API is the same for each.
```ts
Bun.hash.wyhash("data", 1234); // equivalent to Bun.hash()
Bun.hash.crc32("data", 1234);
Bun.hash.adler32("data", 1234);
Bun.hash.cityHash32("data", 1234);
Bun.hash.cityHash64("data", 1234);
Bun.hash.murmur32v3("data", 1234);
Bun.hash.murmur64v2("data", 1234);
```
## `Bun.CryptoHasher`
`Bun.CryptoHasher` is a general-purpose utility class that lets you incrementally compute a hash of string or binary data using a range of cryptographic hash algorithms. The following algorithms are supported:
- `"blake2b256"`
- `"md4"`
- `"md5"`
- `"ripemd160"`
- `"sha1"`
- `"sha224"`
- `"sha256"`
- `"sha384"`
- `"sha512"`
- `"sha512-256"`
```ts
const hasher = new Bun.CryptoHasher("sha256");
hasher.update("hello world");
hasher.digest();
// Uint8Array(32) [ <byte>, <byte>, ... ]
```
Once initialized, data can be incrementally fed to to the hasher using `.update()`. This method accepts `string`, `TypedArray`, and `ArrayBuffer`.
```ts
const hasher = new Bun.CryptoHasher();
hasher.update("hello world");
hasher.update(new Uint8Array([1, 2, 3]));
hasher.update(new ArrayBuffer(10));
```
If a `string` is passed, an optional second parameter can be used to specify the encoding (default `'utf-8'`). The following encodings are supported:
{% table %}
---
- Binary encodings
- `"base64"` `"base64url"` `"hex"` `"binary"`
---
- Character encodings
- `"utf8"` `"utf-8"` `"utf16le"` `"latin1"`
---
- Legacy character encodings
- `"ascii"` `"binary"` `"ucs2"` `"ucs-2"`
{% /table %}
```ts
hasher.update("hello world"); // defaults to utf8
hasher.update("hello world", "hex");
hasher.update("hello world", "base64");
hasher.update("hello world", "latin1");
```
After the data has been feed into the hasher, a final hash can be computed using `.digest()`. By default, this method returns a `Uint8Array` containing the hash.
```ts
const hasher = new Bun.CryptoHasher();
hasher.update("hello world");
hasher.digest();
// => Uint8Array(32) [ 185, 77, 39, 185, 147, ... ]
```
The `.digest()` method can optionally return the hash as a string. To do so, specify an encoding:
```ts
hasher.digest("base64");
// => "uU0nuZNNPgilLlLX2n2r+sSE7+N6U4DukIj3rOLvzek="
hasher.digest("hex");
// => "b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9"
```
Alternatively, the method can write the hash into a pre-existing `TypedArray` instance. This may be desirable in some performance-sensitive applications.
```ts
const arr = new Uint8Array(32);
hasher.digest(arr);
console.log(arr);
// => Uint8Array(32) [ 185, 77, 39, 185, 147, ... ]
```
<!-- Bun.sha; -->

View File

@@ -1,19 +1,12 @@
The page primarily documents the Bun-native `Bun.serve` API. Bun also implements [`fetch`](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API) and the Node.js [`http`](https://nodejs.org/api/http.html) and [`https`](https://nodejs.org/api/https.html) modules.
{% callout %}
**Note** — This page documents the `Bun.serve` API. This API is heavily optimized and represents the recommended way to build HTTP servers in Bun. Existing Node.js projects may use Bun's [nearly complete](/docs/runtime/nodejs-apis#node_http) implementation of the Node.js [`http`](https://nodejs.org/api/http.html) and [`https`](https://nodejs.org/api/https.html) modules.
These modules have been re-implemented to use Bun's fast internal HTTP infrastructure. Feel free to use these modules directly; frameworks like [Express](https://expressjs.com/) that depend on these modules should work out of the box. For granular compatibility information, see [Runtime > Node.js APIs](/docs/runtime/nodejs-apis).
{% /callout %}
## Send a request (`fetch()`)
To start a high-performance HTTP server with a clean API, the recommended approach is [`Bun.serve`](#start-a-server-bun-serve).
Bun implements the Web `fetch` API for making HTTP requests. The `fetch` function is available in the global scope.
```ts
const response = await fetch("https://bun.sh/manifest.json");
const result = (await response.json()) as any;
console.log(result.icons[0].src);
// => "/logo-square.jpg"
```
## Start a server (`Bun.serve()`)
## `Bun.serve()`
Start an HTTP server in Bun with `Bun.serve`.
@@ -50,7 +43,7 @@ Bun.serve({
});
```
### Error handling
## Error handling
To activate development mode, set `development: true`. By default, development mode is _enabled_ unless `NODE_ENV` is `production`.
@@ -100,36 +93,72 @@ const server = Bun.serve({
server.stop();
```
### TLS
## TLS
Bun supports TLS out of the box, powered by [OpenSSL](https://www.openssl.org/). Enable TLS by passing in a value for `keyFile` and `certFile`; both are required to enable TLS. If needed, supply a `passphrase` to decrypt the `keyFile`.
Bun supports TLS out of the box, powered by [OpenSSL](https://www.openssl.org/). Enable TLS by passing in a value for `key` and `cert`; both are required to enable TLS. If needed, supply a `passphrase` to decrypt the `keyFile`.
```ts
Bun.serve({
fetch(req) {
return new Response("Hello!!!");
},
keyFile: "./key.pem", // path to TLS key
certFile: "./cert.pem", // path to TLS cert
passphrase: "super-secret", // optional passphrase
// can be string, BunFile, TypedArray, Buffer, or array thereof
key: Bun.file("./key.pem"),
cert: Bun.file("./cert.pem"),
// passphrase, only required if key is encrypted
passphrase: "super-secret",
});
```
The root CA and Diffie-Helman parameters can be configured as well.
The `key` and `cert` fields expect the _contents_ of your TLS key and certificate. This can be a string, `BunFile`, `TypedArray`, or `Buffer`.
```ts
Bun.serve({
fetch() {},
// BunFile
key: Bun.file("./key.pem"),
// Buffer
key: fs.readFileSync("./key.pem"),
// string
key: fs.readFileSync("./key.pem", "utf8"),
// array of above
key: [Bun.file('./key1.pem'), Bun.file('./key2.pem']
});
```
{% callout %}
**Note** Earlier versions of Bun supported passing a file path as `keyFile` and `certFile`; this has been deprecated as of `v0.6.3`.
{% /callout %}
Optionally, you can override the trusted CA certificates by passing a value for `ca`. By default, the server will trust the list of well-known CAs curated by Mozilla. When `ca` is specified, the Mozilla list is overwritten.
```ts
Bun.serve({
fetch(req) {
return new Response("Hello!!!");
},
keyFile: "./key.pem", // path to TLS key
certFile: "./cert.pem", // path to TLS cert
caFile: "./ca.pem", // path to root CA certificate
dhParamsFile: "./dhparams.pem", // Diffie Helman parameters
key: Bun.file("./key.pem"), // path to TLS key
cert: Bun.file("./cert.pem"), // path to TLS cert
ca: Bun.file("./ca.pem"), // path to root CA certificate
});
```
### Hot reloading
To override Diffie-Helman parameters:
```ts
Bun.serve({
// ...
dhParamsFile: "./dhparams.pem", // path to Diffie Helman parameters
});
```
## Hot reloading
Thus far, the examples on this page have used the explicit `Bun.serve` API. Bun also supports an alternate syntax.
@@ -153,7 +182,7 @@ $ bun --hot server.ts
It's possible to configure hot reloading while using the explicit `Bun.serve` API; for details refer to [Runtime > Hot reloading](/docs/runtime/hot).
### Streaming files
## Streaming files
To stream a file, return a `Response` object with a `BunFile` object as the body.
@@ -191,7 +220,7 @@ Bun.serve({
});
```
### Benchmarks
## Benchmarks
Below are Bun and Node.js implementations of a simple HTTP server that responds `Bun!` to each incoming `Request`.
@@ -234,7 +263,7 @@ The `Bun.serve` server can handle roughly 2.5x more requests per second than Nod
{% image width="499" alt="image" src="https://user-images.githubusercontent.com/709451/162389032-fc302444-9d03-46be-ba87-c12bd8ce89a0.png" /%}
### Reference
## Reference
{% details summary="See TypeScript definitions" %}

52
docs/api/import-meta.md Normal file
View File

@@ -0,0 +1,52 @@
The `import.meta` object is a way for a module to access information about itself. It's part of the JavaScript language, but its contents are not standardized. Each "host" (browser, runtime, etc) is free to implement any properties it wishes on the `import.meta` object.
Bun implements the following properties.
```ts#/path/to/project/file.ts
import.meta.dir; // => "/path/to/project"
import.meta.file; // => "file.ts"
import.meta.path; // => "/path/to/project/file.ts"
import.meta.main; // `true` if this file is directly executed by `bun run`
// `false` otherwise
import.meta.resolveSync("zod")
// resolve an import specifier relative to the directory
```
{% table %}
---
- `import.meta.dir`
- Absolute path to the directory containing the current fil, e.g. `/path/to/project`. Equivalent to `__dirname` in Node.js.
---
- `import.meta.file`
- The name of the current file, e.g. `index.tsx`. Equivalent to `__filename` in Node.js.
---
- `import.meta.path`
- Absolute path to the current file, e.g. `/path/to/project/index.tx`.
---
- `import.meta.main`
- `boolean` Indicates whether the current file is the entrypoint to the current `bun` process. Is the file being directly executed by `bun run` or is it being imported?
---
- `import.meta.resolve{Sync}`
- Resolve a module specifier (e.g. `"zod"` or `"./file.tsx`) to an absolute path. While file would be imported if the specifier were imported from this file?
```ts
import.meta.resolveSync("zod");
// => "/path/to/project/node_modules/zod/index.ts"
import.meta.resolveSync("./file.tsx");
// => "/path/to/project/file.tsx"
```
{% /table %}

View File

@@ -74,7 +74,7 @@ Note: `close()` is called automatically when the database is garbage collected.
```ts
const olddb = new Database("mydb.sqlite");
const contents = db.serialize(); // => Uint8Array
const contents = olddb.serialize(); // => Uint8Array
const newdb = new Database(contents);
```

172
docs/api/streams.md Normal file
View File

@@ -0,0 +1,172 @@
Streams are an important abstraction for working with binary data without loading it all into memory at once. They are commonly used for reading and writing files, sending and receiving network requests, and processing large amounts of data.
Bun implements the Web APIs [`ReadableStream`](https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream) and [`WritableStream`](https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream).
{% callout %}
Bun also implements the `node:stream` module, including [`Readable`](https://nodejs.org/api/stream.html#stream_readable_streams), [`Writable`](https://nodejs.org/api/stream.html#stream_writable_streams), and [`Duplex`](https://nodejs.org/api/stream.html#stream_duplex_and_transform_streams). For complete documentation, refer to the [Node.js docs](https://nodejs.org/api/stream.html).
{% /callout %}
To create a simple `ReadableStream`:
```ts
const stream = new ReadableStream({
start(controller) {
controller.enqueue("hello");
controller.enqueue("world");
controller.close();
},
});
```
The contents of a `ReadableStream` can be read chunk-by-chunk with `for await` syntax.
```ts
for await (const chunk of stream) {
console.log(chunk);
// => "hello"
// => "world"
}
```
For a more complete discusson of streams in Bun, see [API > Streams](/docs/api/streams).
## Direct `ReadableStream`
Bun implements an optimized version of `ReadableStream` that avoid unnecessary data copying & queue management logic. With a traditional `ReadableStream`, chunks of data are _enqueued_. Each chunk is copied into a queue, where it sits until the stream is ready to send more data.
```ts
const stream = new ReadableStream({
start(controller) {
controller.enqueue("hello");
controller.enqueue("world");
controller.close();
},
});
```
With a direct `ReadableStream`, chunks of data are written directly to the stream. No queueing happens, and there's no need to clone the chunk data into memory. The `controller` API is updated to reflect this; instead of `.enqueue()` you call `.write`.
```ts
const stream = new ReadableStream({
type: "direct",
pull(controller) {
controller.write("hello");
controller.write("world");
},
});
```
When using a direct `ReadableStream`, all chunk queueing is handled by the destination. The consumer of the stream receives exactly what is passed to `controller.write()`, without any encoding or modification.
## `Bun.ArrayBufferSink`
The `Bun.ArrayBufferSink` class is a fast incremental writer for constructing an `ArrayBuffer` of unknown size.
```ts
const sink = new Bun.ArrayBufferSink();
sink.write("h");
sink.write("e");
sink.write("l");
sink.write("l");
sink.write("o");
sink.end();
// ArrayBuffer(5) [ 104, 101, 108, 108, 111 ]
```
To instead retrieve the data as a `Uint8Array`, pass the `asUint8Array` option to the constructor.
```ts-diff
const sink = new Bun.ArrayBufferSink({
+ asUint8Array: true
});
sink.write("h");
sink.write("e");
sink.write("l");
sink.write("l");
sink.write("o");
sink.end();
// Uint8Array(5) [ 104, 101, 108, 108, 111 ]
```
The `.write()` method supports strings, typed arrays, `ArrayBuffer`, and `SharedArrayBuffer`.
```ts
sink.write("h");
sink.write(new Uint8Array([101, 108]));
sink.write(Buffer.from("lo").buffer);
sink.end();
```
Once `.end()` is called, no more data can be written to the `ArrayBufferSink`. However, in the context of buffering a stream, it's useful to continuously write data and periodically `.flush()` the contents (say, into a `WriteableStream`). To support this, pass `stream: true` to the constructor.
```ts
const sink = new Bun.ArrayBufferSink({
stream: true,
});
sink.write("h");
sink.write("e");
sink.write("l");
sink.flush();
// ArrayBuffer(5) [ 104, 101, 108 ]
sink.write("l");
sink.write("o");
sink.flush();
// ArrayBuffer(5) [ 108, 111 ]
```
The `.flush()` method returns the buffered data as an `ArrayBuffer` (or `Uint8Array` if `asUint8Array: true`) and clears internal buffer.
To manually set the size of the internal buffer in bytes, pass a value for `highWaterMark`:
```ts
const sink = new Bun.ArrayBufferSink({
highWaterMark: 1024 * 1024, // 1 MB
});
```
{% details summary="Reference" %}
```ts
/**
* Fast incremental writer that becomes an `ArrayBuffer` on end().
*/
export class ArrayBufferSink {
constructor();
start(options?: {
asUint8Array?: boolean;
/**
* Preallocate an internal buffer of this size
* This can significantly improve performance when the chunk size is small
*/
highWaterMark?: number;
/**
* On {@link ArrayBufferSink.flush}, return the written data as a `Uint8Array`.
* Writes will restart from the beginning of the buffer.
*/
stream?: boolean;
}): void;
write(chunk: string | ArrayBufferView | ArrayBuffer | SharedArrayBuffer): number;
/**
* Flush the internal buffer
*
* If {@link ArrayBufferSink.start} was passed a `stream` option, this will return a `ArrayBuffer`
* If {@link ArrayBufferSink.start} was passed a `stream` option and `asUint8Array`, this will return a `Uint8Array`
* Otherwise, this will return the number of bytes written since the last flush
*
* This API might change later to separate Uint8ArraySink and ArrayBufferSink
*/
flush(): number | Uint8Array | ArrayBuffer;
end(): ArrayBuffer | Uint8Array;
}
```
{% /details %}

View File

@@ -59,7 +59,7 @@ Bun.listen<SocketData>({
});
```
To enable TLS, pass a `tls` object containing `keyFile` and `certFile` properties.
To enable TLS, pass a `tls` object containing `key` and `cert` fields.
```ts
Bun.listen({
@@ -69,13 +69,38 @@ Bun.listen({
data(socket, data) {},
},
tls: {
certFile: "cert.pem",
keyFile: "key.pem",
// can be string, BunFile, TypedArray, Buffer, or array thereof
key: Bun.file("./key.pem"),
cert: Bun.file("./cert.pem"),
},
});
```
The result of `Bun.listen` is a server that conforms to the `TCPSocket` instance.
{% callout %}
**Note** Earlier versions of Bun supported passing a file path as `keyFile` and `certFile`; this has been deprecated as of `v0.6.3`.
{% /callout %}
The `key` and `cert` fields expect the _contents_ of your TLS key and certificate. This can be a string, `BunFile`, `TypedArray`, or `Buffer`.
```ts
Bun.listen({
// ...
tls: {
// BunFile
key: Bun.file("./key.pem"),
// Buffer
key: fs.readFileSync("./key.pem"),
// string
key: fs.readFileSync("./key.pem", "utf8"),
// array of above
key: [Bun.file('./key1.pem'), Bun.file('./key2.pem']
},
});
```
The result of `Bun.listen` is a server that conforms to the `TCPSocket` interface.
```ts
const server = Bun.listen({

View File

@@ -1,4 +1,47 @@
## `Bun.sleep`
## `Bun.version`
A `string` containing the version of the `bun` CLI that is currently running.
```ts
Bun.version;
// => "0.6.4"
```
## `Bun.revision`
The git commit of [Bun](https://github.com/oven-sh/bun) that was compiled to create the current `bun` CLI.
```ts
Bun.revision;
// => "f02561530fda1ee9396f51c8bc99b38716e38296"
```
## `Bun.env`
An alias for `process.env`.
## `Bun.main`
An absolute path to the entrypoint of the current program (the file that was executed with `bun run`).
```ts#script.ts
Bun.main;
// /path/to/script.ts
```
This is particular useful for determining whether a script is being directly executed, as opposed to being imported by another script.
```ts
if (import.meta.path === Bun.main) {
// this script is being directly executed
} else {
// this file is being imported from another script
}
```
This is analogous to the [`require.main = module` trick](https://stackoverflow.com/questions/6398196/detect-if-called-through-require-or-directly-by-command-line) in Node.js.
## `Bun.sleep()`
`Bun.sleep(ms: number)` (added in Bun v0.5.6)
@@ -20,11 +63,33 @@ await Bun.sleep(oneSecondInFuture);
console.log("hello one second later!");
```
## `Bun.which`
## `Bun.sleepSync()`
`Bun.sleepSync(ms: number)` (added in Bun v0.5.6)
A blocking synchronous version of `Bun.sleep`.
```ts
console.log("hello");
Bun.sleepSync(1000); // blocks thread for one second
console.log("hello one second later!");
```
Alternatively, pass a `Date` object to receive a `Promise` that resolves at that point in time.
```ts
const oneSecondInFuture = new Date(Date.now() + 1000);
console.log("hello");
await Bun.sleep(oneSecondInFuture);
console.log("hello one second later!");
```
## `Bun.which()`
`Bun.which(bin: string)`
Find the path to an executable, similar to typing `which` in your terminal.
Returns the path to an executable, similar to typing `which` in your terminal.
```ts
const ls = Bun.which("ls");
@@ -51,11 +116,11 @@ const ls = Bun.which("ls", {
console.log(ls); // null
```
## `Bun.peek`
## `Bun.peek()`
`Bun.peek(prom: Promise)` (added in Bun v0.2.2)
`Bun.peek` is a utility function that lets you read a promise's result without `await` or `.then`, but only if the promise has already fulfilled or rejected.
Reads a promise's result without `await` or `.then`, but only if the promise has already fulfilled or rejected.
```ts
import { peek } from "bun";
@@ -117,9 +182,9 @@ test("peek.status", () => {
});
```
## `Bun.openInEditor`
## `Bun.openInEditor()`
Open a file in your default editor. Bun auto-detects your editor via the `$VISUAL` or `$EDITOR` environment variables.
Opens a file in your default editor. Bun auto-detects your editor via the `$VISUAL` or `$EDITOR` environment variables.
```ts
const currentFile = import.meta.url;
@@ -142,3 +207,290 @@ Bun.openInEditor(import.meta.url, {
column: 5,
});
```
Bun.ArrayBufferSink;
## `Bun.deepEquals()`
Nestedly checks if two objects are equivalent. This is used internally by `expect().toEqual()` in `bun:test`.
```ts
const foo = { a: 1, b: 2, c: { d: 3 } };
// true
Bun.deepEquals(foo, { a: 1, b: 2, c: { d: 3 } });
// false
Bun.deepEquals(foo, { a: 1, b: 2, c: { d: 4 } });
```
A third boolean parameter can be used to enable "strict" mode. This is used by `expect().toStrictEqual()` in the test runner.
```ts
const a = { entries: [1, 2] };
const b = { entries: [1, 2], extra: undefined };
Bun.deepEquals(a, b); // => true
Bun.deepEquals(a, b, true); // => false
```
In strict mode, the following are considered unequal:
```ts
// undefined values
Bun.deepEquals({}, { a: undefined }, true); // false
// undefined in arrays
Bun.deepEquals(["asdf"], ["asdf", undefined], true); // false
// sparse arrays
Bun.deepEquals([, 1], [undefined, 1], true); // false
// object literals vs instances w/ same properties
class Foo {
a = 1;
}
Bun.deepEquals(new Foo(), { a: 1 }, true); // false
```
## `Bun.escapeHTML()`
`Bun.escapeHTML(value: string | object | number | boolean): boolean`
Escapes the following characters from an input string:
- `"` becomes `"&quot;"`
- `&` becomes `"&amp;"`
- `'` becomes `"&#x27;"`
- `<` becomes `"&lt;"`
- `>` becomes `"&gt;"`
This function is optimized for large input. On an M1X, it processes 480 MB/s -
20 GB/s, depending on how much data is being escaped and whether there is non-ascii
text. Non-string types will be converted to a string before escaping.
<!-- ## `Bun.enableANSIColors()` -->
## `Bun.fileURLToPath()`
Converts a `file://` URL to an absolute path.
```ts
const path = Bun.fileURLToPath(new URL("file:///foo/bar.txt"));
console.log(path); // "/foo/bar.txt"
```
## `Bun.pathToFileURL()`
Converts an absolute path to a `file://` URL.
```ts
const url = Bun.pathToFileURL("/foo/bar.txt");
console.log(url); // "file:///foo/bar.txt"
```
<!-- Bun.hash; -->
## `Bun.gzipSync()`
Compresses a `Uint8Array` using zlib's DEFLATE algorithm.
```ts
const buf = Buffer.from("hello".repeat(100)); // Buffer extends Uint8Array
const compressed = Bun.gzipSync(buf);
buf; // => Uint8Array(500)
compressed; // => Uint8Array(30)
```
Optionally, pass a parameters object as the second argument:
{% details summary="zlib compression options"%}
```ts
export type ZlibCompressionOptions = {
/**
* The compression level to use. Must be between `-1` and `9`.
* - A value of `-1` uses the default compression level (Currently `6`)
* - A value of `0` gives no compression
* - A value of `1` gives least compression, fastest speed
* - A value of `9` gives best compression, slowest speed
*/
level?: -1 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9;
/**
* How much memory should be allocated for the internal compression state.
*
* A value of `1` uses minimum memory but is slow and reduces compression ratio.
*
* A value of `9` uses maximum memory for optimal speed. The default is `8`.
*/
memLevel?: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9;
/**
* The base 2 logarithm of the window size (the size of the history buffer).
*
* Larger values of this parameter result in better compression at the expense of memory usage.
*
* The following value ranges are supported:
* - `9..15`: The output will have a zlib header and footer (Deflate)
* - `-9..-15`: The output will **not** have a zlib header or footer (Raw Deflate)
* - `25..31` (16+`9..15`): The output will have a gzip header and footer (gzip)
*
* The gzip header will have no file name, no extra data, no comment, no modification time (set to zero) and no header CRC.
*/
windowBits?:
| -9
| -10
| -11
| -12
| -13
| -14
| -15
| 9
| 10
| 11
| 12
| 13
| 14
| 15
| 25
| 26
| 27
| 28
| 29
| 30
| 31;
/**
* Tunes the compression algorithm.
*
* - `Z_DEFAULT_STRATEGY`: For normal data **(Default)**
* - `Z_FILTERED`: For data produced by a filter or predictor
* - `Z_HUFFMAN_ONLY`: Force Huffman encoding only (no string match)
* - `Z_RLE`: Limit match distances to one (run-length encoding)
* - `Z_FIXED` prevents the use of dynamic Huffman codes
*
* `Z_RLE` is designed to be almost as fast as `Z_HUFFMAN_ONLY`, but give better compression for PNG image data.
*
* `Z_FILTERED` forces more Huffman coding and less string matching, it is
* somewhat intermediate between `Z_DEFAULT_STRATEGY` and `Z_HUFFMAN_ONLY`.
* Filtered data consists mostly of small values with a somewhat random distribution.
*/
strategy?: number;
};
```
{% /details %}
## `Bun.gunzipSync()`
Uncompresses a `Uint8Array` using zlib's INFLATE algorithm.
```ts
const buf = Buffer.from("hello".repeat(100)); // Buffer extends Uint8Array
const compressed = Bun.gunzipSync(buf);
const dec = new TextDecoder();
const uncompressed = Bun.inflateSync(compressed);
dec.decode(uncompressed);
// => "hellohellohello..."
```
## `Bun.deflateSync()`
Compresses a `Uint8Array` using zlib's DEFLATE algorithm.
```ts
const buf = Buffer.from("hello".repeat(100));
const compressed = Bun.deflateSync(buf);
buf; // => Uint8Array(25)
compressed; // => Uint8Array(10)
```
The second argument supports the same set of configuration options as [`Bun.gzipSync`](#bun.gzipSync).
## `Bun.inflateSync()`
Uncompresses a `Uint8Array` using zlib's INFLATE algorithm.
```ts
const buf = Buffer.from("hello".repeat(100));
const compressed = Bun.deflateSync(buf);
const dec = new TextDecoder();
const uncompressed = Bun.inflateSync(compressed);
dec.decode(uncompressed);
// => "hellohellohello..."
```
## `Bun.inspect()`
Serializes an object to a `string` exactly as it would be printed by `console.log`.
```ts
const obj = { foo: "bar" };
const str = Bun.inspect(obj);
// => '{\nfoo: "bar" \n}'
const arr = new Uint8Array([1, 2, 3]);
const str = Bun.inspect(arr);
// => "Uint8Array(3) [ 1, 2, 3 ]"
```
## `Bun.nanoseconds()`
Returns the number of nanoseconds since the current `bun` process started, as a `number`. Useful for high-precision timing and benchmarking.
```ts
Bun.nanoseconds();
// => 7288958
```
## `Bun.readableStreamTo*()`
Bun implements a set of convenience functions for asynchronously consuming the body of a `ReadableStream` and converting it to various binary formats.
```ts
const stream = (await fetch("https://bun.sh")).body;
stream; // => ReadableStream
await Bun.readableStreamToArrayBuffer(stream);
// => ArrayBuffer
await Bun.readableStreamToBlob(stream);
// => Blob
await Bun.readableStreamToJSON(stream);
// => object
await Bun.readableStreamToText(stream);
// => string
// returns all chunks as an array
await Bun.readableStreamToArray(stream);
// => unknown[]
```
## `Bun.resolveSync()`
Resolves a file path or module specifier using Bun's internal module resolution algorithm. The first argument is the path to resolve, and the second argument is the "root". If no match is found, an `Error` is thrown.
```ts
Bun.resolveSync("./foo.ts", "/path/to/project");
// => "/path/to/project/foo.ts"
Bun.resolveSync("zod", "/path/to/project");
// => "/path/to/project/node_modules/zod/index.ts"
```
To resolve relative to the current working directory, pass `process.cwd` or `"."` as the root.
```ts
Bun.resolveSync("./foo.ts", process.cwd());
Bun.resolveSync("./foo.ts", "/path/to/project");
```
To resolve relative to the directory containing the current file, pass `import.meta.dir`.
```ts
Bun.resolveSync("./foo.ts", import.meta.dir);
```

View File

@@ -216,12 +216,12 @@ If a value is specified for `publicPath`, the import will use value as a prefix
---
- `"https://cdn.example.com/"`
- `https://cdn.example.com/`
- `https://cdn.example.com/logo.svg`
{% /table %}
{% callout %}
The location and file name of the copied file is determined by the value of [`naming.asset`](/docs/cli/build#naming).
The location and file name of the copied file is determined by the value of [`naming.asset`](/docs/bundler#naming).
{% /callout %}
This loader is copied into the `outdir` as-is. The name of the copied file is determined using the value of `naming.asset`.

327
docs/bundler/macros.md Normal file
View File

@@ -0,0 +1,327 @@
Macros are a mechanism for running JavaScript functions _at bundle-time_. The value returned from these functions are directly inlined into your bundle.
<!-- embed the result in your (browser) bundle. This is useful for things like embedding the current Git commit hash in your code, making fetch requests to your API at build-time, dead code elimination, and more. -->
As a toy example, consider this simple function that returns a random number.
```ts
export function random() {
return Math.random();
}
```
This is just a regular function in a regular file, but we can use it as a macro like so:
```ts#cli.tsx
import { random } from './random.ts' with { type: 'macro' };
console.log(`Your random number is ${random()}`);
```
{% callout %}
**Note** — Macros are indicated using [_import attribute_](https://github.com/tc39/proposal-import-attributes) syntax. If you haven't seen this syntax before, it's a Stage 3 TC39 proposal that lets you attach additional metadata to `import` statements.
{% /callout %}
Now we'll bundle this file with `bun build`. The bundled file will be printed to stdout.
```bash
$ bun build ./cli.tsx
console.log(`Your random number is ${0.6805550949689833}`);
```
As you can see, the source code of the `random` function occurs nowhere in the bundle. Instead, it is executed _during bundling_ and function call (`random()`) is replaced with the result of the function. Since the source code will never be included in the bundle, macros can safely perform privileged operations like reading from a database.
## When to use macros
If you have several build scripts for small things where you would otherwise have a one-off build script, bundle-time code execution can be easier to maintain. It lives with the rest of your code, it runs with the rest of the build, it is automatically paralellized, and if it fails, the build fails too.
If you find yourself running a lot of code at bundle-time though, consider running a server instead.
## Import attributes
Bun Macros are import statements annotated using either:
- `with { type: 'macro' }` — an [import attribute](https://github.com/tc39/proposal-import-attributes), a Stage 3 ECMA Scrd
- `assert { type: 'macro' }` — an import assertion, an earlier incarnation of import attributes that has now been abandoned (but is [already supported](https://caniuse.com/mdn-javascript_statements_import_import_assertions) by a number of browsers and runtimes)
## Security considerations
Macros must explicitly be imported with `{ type: "macro" }` in order to be executed at bundle-time. These imports have no effect if they are not called, unlike regular JavaScript imports which may have side effects.
You can disable macros entirely by passing the `--no-macros` flag to Bun. It produces a build error like this:
```js
error: Macros are disabled
foo();
^
./hello.js:3:1 53
```
To reduce the potential attack surface for malicious packages, macros cannot be _invoked_ from inside `node_modules/**/*`. If a package attempts to invoke a macro, you'll see an error like this:
```js
error: For security reasons, macros cannot be run from node_modules.
beEvil();
^
node_modules/evil/index.js:3:1 50
```
Your application code can still import macros from `node_modules` and invoke them.
```ts
import {macro} from "some-package" with { type: "macro" };
macro();
```
## Export condition `"macro"`
When shipping a library containing a macro to `npm` or another package registry, use the `"macro"` [export condition](https://nodejs.org/api/packages.html#conditional-exports) to provide a special version of your package exclusively for the macro environment.
```jsonc#package.json
{
"name": "my-package",
"exports": {
"import": "./index.js",
"require": "./index.js",
"default": "./index.js",
"macro": "./index.macro.js"
}
}
```
With this configuration, users can consume your package at runtime or at bundle-time using the same import specifier:
```ts
import pkg from "my-package"; // runtime import
import {macro} from "my-package" with { type: "macro" }; // macro import
```
The first import will resolve to `./node_modules/my-package/index.js`, while the second will be resolved by Bun's bundler to `./node_modules/my-package/index.macro.js`.
## Execution
When Bun's transpiler sees a macro import, it calls the function inside the transpiler using Bun's JavaScript runtime and converts the return value from JavaScript into an AST node. These JavaScript functions are called at bundle-time, not runtime.
Macros are executed synchronously in the transpiler during the visiting phase—before plugins and before the transpiler generates the AST. They are executed in the order they are imported. The transpiler will wait for the macro to finish executing before continuing. The transpiler will also `await` any `Promise` returned by a macro.
Bun's bundler is multi-threaded. As such, macros execute in parallel inside of multiple spawned JavaScript "workers".
## Dead code elimination
The bundler performs dead code elimination _after_ running and inlining macros. So given the following macro:
```ts#returnFalse.ts
export function returnFalse() {
return false;
}
```
...then bundling the following file will produce an empty bundle.
```ts
import {returnFalse} from './returnFalse.ts' with { type: 'macro' };
if (returnFalse()) {
console.log("This code is eliminated");
}
```
## Serializablility
Bun's transpiler needs to be able to serialize the result of the macro so it can be inlined into the AST. All JSON-compatible data structures are supported:
```ts#macro.ts
export function getObject() {
return {
foo: "bar",
baz: 123,
array: [ 1, 2, { nested: "value" }],
};
}
```
Macros can be async, or return `Promise` instances. Bun's transpiler will automatically `await` the `Promise` and inline the result.
```ts#macro.ts
export async function getText() {
return "async value";
}
```
The transpiler implements special logic for serializing common data formats like `Response`, `Blob`, `TypedArray`.
- `TypedArray`: Resolves to a base64-encoded string.
- `Response`: Bun will read the `Content-Type` and serialize accordingly; for instance, a `Response` with type `application/json` will be automatically parsed into an object and `text/plain` will be inlined as a string. Responses with an unrecognized or `undefined` `type` will be base-64 encoded.
- `Blob`: As with `Response`, the serialization depends on the `type` property.
The result of `fetch` is `Promise<Response>`, so it can be directly returned.
```ts#macro.ts
export function getObject() {
return fetch("https://bun.sh")
}
```
Functions and instances of most classes (except those mentioned above) are not serializable.
```ts
export function getText(url: string) {
// this doesn't work!
return () => {};
}
```
## Arguments
Macros can accept inputs, but only in limited cases. The value must be statically known. For example, the following is not allowed:
```ts
import {getText} from './getText.ts' with { type: 'macro' };
export function howLong() {
// the value of `foo` cannot be statically known
const foo = Math.random() ? "foo" : "bar";
const text = getText(`https://example.com/${foo}`);
console.log("The page is ", text.length, " characters long");
}
```
However, if the value of `foo` is known at bundle-time (say, if it's a constant or the result of another macro) then it's allowed:
```ts
import {getText} from './getText.ts' with { type: 'macro' };
import {getFoo} from './getFoo.ts' with { type: 'macro' };
export function howLong() {
// this works because getFoo() is statically known
const foo = getFoo();
const text = getText(`https://example.com/${foo}`);
console.log("The page is", text.length, "characters long");
}
```
This outputs:
```ts
function howLong() {
console.log("The page is", 1322, "characters long");
}
export { howLong };
```
## Examples
### Embed latest git commit hash
{% codetabs %}
```ts#getGitCommitHash.ts
export function getGitCommitHash() {
const {stdout} = Bun.spawnSync({
cmd: ["git", "rev-parse", "HEAD"],
stdout: "pipe",
});
return stdout.toString();
}
```
{% /codetabs %}
<!-- --target=browser so they can clearly see it's for browsers -->
When we build it, the `getGitCommitHash` is replaced with the result of calling the function:
{% codetabs %}
```ts#input
import { getGitCommitHash } from './getGitCommitHash.ts' with { type: 'macro' };
console.log(`The current Git commit hash is ${getGitCommitHash()}`);
```
```bash#output
console.log(`The current Git commit hash is 3ee3259104f`);
```
{% /codetabs %}
You're probably thinking "Why not just use `process.env.GIT_COMMIT_HASH`?" Well, you can do that too. But can you do this with an environment variable?
### Make `fetch()` requests at bundle-time
In this example, we make an outgoing HTTP request using `fetch()`, parse the HTML response using `HTMLRewriter`, and return an object containing the title and meta tagsall at bundle-time.
```ts
export async function extractMetaTags(url: string) {
const response = await fetch(url);
const meta = {
title: "",
};
new HTMLRewriter()
.on("title", {
text(element) {
meta.title += element.text;
},
})
.on("meta", {
element(element) {
const name =
element.getAttribute("name") || element.getAttribute("property") || element.getAttribute("itemprop");
if (name) meta[name] = element.getAttribute("content");
},
})
.transform(response);
return meta;
}
```
<!-- --target=browser so they can clearly see it's for browsers -->
The `extractMetaTags` function is erased at bundle-time and replaced with the result of the function call. This means that the `fetch` request happens at bundle-time, and the result is embedded in the bundle. Also, the branch throwing the error is eliminated since it's unreachable.
{% codetabs %}
```ts#input
import { extractMetaTags } from './meta.ts' with { type: 'macro' };
export const Head = () => {
const headTags = extractMetaTags("https://example.com");
if (headTags.title !== "Example Domain") {
throw new Error("Expected title to be 'Example Domain'");
}
return <head>
<title>{headTags.title}</title>
<meta name="viewport" content={headTags.viewport} />
</head>;
};
```
```ts#output
import { jsx, jsxs } from "react/jsx-runtime";
export const Head = () => {
jsxs("head", {
children: [
jsx("title", {
children: "Example Domain",
}),
jsx("meta", {
name: "viewport",
content: "width=device-width, initial-scale=1",
}),
],
});
};
export { Head };
```
{% /codetabs %}

View File

@@ -31,11 +31,15 @@ Bun.build({
});
```
It can also be "registered" with the Bun runtime using the `Bun.plugin()` function. Once registered, the currently executing `bun` process will incorporate the plugin into its module resolution algorithm.
<!-- It can also be "registered" with the Bun runtime using the `Bun.plugin()` function. Once registered, the currently executing `bun` process will incorporate the plugin into its module resolution algorithm.
```ts
Bun.plugin(myPlugin);
```
import {plugin} from "bun";
plugin(myPlugin);
``` -->
## `--preload`
To consume this plugin, add this file to the `preload` option in your [`bunfig.toml`](/docs/runtime/configuration). Bun automatically loads the files/modules specified in `preload` before running a file.
@@ -80,7 +84,7 @@ plugin(
// application code
```
Bun's plugin API is based on [esbuild](https://esbuild.github.io/plugins). Only [a subset](/docs/bundler/migration#plugin-api) of the esbuild API is implemented, but some esbuild plugins "just work" in Bun, like the official [MDX loader](https://mdxjs.com/packages/esbuild/):
Bun's plugin API is based on [esbuild](https://esbuild.github.io/plugins). Only [a subset](/docs/bundler/vs-esbuild#plugin-api) of the esbuild API is implemented, but some esbuild plugins "just work" in Bun, like the official [MDX loader](https://mdxjs.com/packages/esbuild/):
```jsx
import { plugin } from "bun";
@@ -274,25 +278,25 @@ console.log(mySvelteComponent.render());
## Reading `Bun.build`'s config
Plugins can read and write to the [build config](/docs/cli/build#api) with `build.config`.
Plugins can read and write to the [build config](/docs/bundler#api) with `build.config`.
```ts
Bun.build({
entrypoints: ["./app.ts"],
outdir: "./dist",
sourcemap: 'external',
sourcemap: "external",
plugins: [
{
name: 'demo',
name: "demo",
setup(build) {
console.log(build.config.sourcemap); // "external"
build.config.minify = true; // enable minification
// `plugins` is readonly
console.log(`Number of plugins: ${build.config.plugins.length}`);
}
}
console.log(`Number of plugins: ${build.config.plugins.length}`);
},
},
],
});
```

View File

@@ -1,5 +1,5 @@
{% callout %}
**Note** — Available in the Bun v0.6.0 nightly. Run `bun upgrade --canary` to try it out.
**Note** — Available in Bun v0.6.0 and later.
{% /callout %}
Bun's bundler API is inspired heavily by [esbuild](https://esbuild.github.io/). Migrating to Bun's bundler from esbuild should be relatively painless. This guide will briefly explain why you might consider migrating to Bun's bundler and provide a side-by-side API comparison reference for those who are already familiar with esbuild's API.
@@ -11,9 +11,9 @@ There are a few behavioral differences to note.
## Performance
This is the simplest reason to migrate to Bun's bundler. With an performance-minded API inspired by esbuild coupled with the extensively optimized Zig-based JS/TS parser, Bun's bundler is roughly 50% faster than esbuild on most benchmarks.
With an performance-minded API coupled with the extensively optimized Zig-based JS/TS parser, Bun's bundler is 1.75x faster than esbuild on esbuild's [three.js benchmark](https://github.com/oven-sh/bun/tree/main/bench/bundle).
IMAGE HERE
{% image src="/images/bundler-speed.png" caption="Bundling 10 copies of three.js from scratch, with sourcemaps and minification" /%}
## CLI API

View File

@@ -1,4 +1,11 @@
Bun ships with a built-in test runner.
Bun ships with a fast built-in test runner. Tests are executed with the Bun runtime, and support the following features.
- TypeScript and JSX
- Lifecycle hooks
- Snapshot testing
- UI & DOM testing
- Watch mode with `--watch`
- Script pre-loading with `--preload`
## Run tests
@@ -29,25 +36,7 @@ You can filter the set of tests to run by passing additional positional argument
$ bun test <filter> <filter> ...
```
## Snapshot testing
Snapshots are supported by `bun test`. First, write a test using the `.toMatchSnapshot()` matcher:
```ts
import { test, expect } from "bun:test";
test("snap", () => {
expect("foo").toMatchSnapshot();
});
```
Then generate snapshots with the following command:
```bash
bun test --update-snapshots
```
Snapshots will be stored in a `__snapshots__` directory next to the test file.
The test runner runs all tests in a single process. It loads all `--preload` scripts (see [Lifecycle](/docs/test/lifecycle) for details), then runs all tests. If a test fails, the test runner will exit with a non-zero exit code.
## Watch mode
@@ -57,6 +46,37 @@ Similar to `bun run`, you can pass the `--watch` flag to `bun test` to watch for
$ bun test --watch
```
## Lifecycle hooks
Bun supports the following lifecycle hooks:
| Hook | Description |
| ------------ | --------------------------- |
| `beforeAll` | Runs once before all tests. |
| `beforeEach` | Runs before each test. |
| `afterEach` | Runs after each test. |
| `afterAll` | Runs once after all tests. |
These hooks can be define inside test files, or in a separate file that is preloaded with the `--preload` flag.
```ts
$ bun test --preload ./setup.ts
```
See [Test > Lifecycle](/docs/test/lifecycle) for complete documentation.
## Snapshot testing
Snapshots are supported by `bun test`. See [Test > Snapshots](/docs/test/snapshots) for complete documentation.
## UI & DOM testing
Bun is compatible with popular UI testing libraries:
- [HappyDOM](https://github.com/capricorn86/happy-dom)
- [DOM Testing Library](https://testing-library.com/docs/dom-testing-library/intro/)
- [React Testing Library](https://testing-library.com/docs/react-testing-library/intro)
## Performance
Bun's test runner is fast.

View File

@@ -1,4 +1,7 @@
[Elysia](https://elysiajs.com) is a Bun-first web framework that takes full advantage of Bun's HTTP, file system, and hot reloading APIs.
[Elysia](https://elysiajs.com) is a Bun-first performance focused web framework that takes full advantage of Bun's HTTP, file system, and hot reloading APIs.
Designed with TypeScript in mind, you don't need to understand TypeScript to gain the benefit of TypeScript with Elysia. The library understands what you want and automatically infers the type from your code.
:zap: Elysia is [one of the fastest Bun web frameworks](https://github.com/SaltyAom/bun-http-framework-benchmark)
```ts#server.ts
import { Elysia } from 'elysia'
@@ -18,4 +21,4 @@ $ cd myapp
$ bun run dev
```
Refer to the Elysia [documentation](https://elysiajs.com/quick-start.html) for more information.
Refer to the Elysia [documentation](https://elysiajs.com/quick-start.html) for more information.

View File

@@ -61,3 +61,5 @@ Bun.serve({
},
});
```
React `18.3` and later includes an [SSR optimization](https://github.com/facebook/react/pull/25597) that takes advantage of Bun's "direct" `ReadableStream` implementation.

View File

@@ -9,9 +9,10 @@ $ bun run index.tsx # TS and JSX supported out of the box
The `bun` command-line tool also implements a test runner, script runner, and Node.js-compatible package manager, all significantly faster than existing tools and usable in existing Node.js projects with little to no changes necessary.
```bash
$ bun test # run tests
$ bun run start # run the `start` script
$ bun install <pkg> # install a package
$ bun build ./index.tsx # bundle a project for browsers
$ bun test # run tests
$ bunx cowsay "Hello, world!" # execute a package
```
@@ -26,7 +27,7 @@ Get started with one of the quick links below, or read on to learn more about Bu
{% arrowbutton href="/docs/quickstart" text="Do the quickstart" /%}
{% arrowbutton href="/docs/cli/install" text="Install a package" /%}
{% arrowbutton href="/docs/templates" text="Use a project template" /%}
{% arrowbutton href="/docs/cli/build" text="Bundle code for production" /%}
{% arrowbutton href="/docs/bundler" text="Bundle code for production" /%}
{% arrowbutton href="/docs/api/http" text="Build an HTTP server" /%}
{% arrowbutton href="/docs/api/websockets" text="Build a Websocket server" /%}
{% arrowbutton href="/docs/api/file-io" text="Read and write files" /%}
@@ -59,11 +60,11 @@ Bun is designed as a faster, leaner, more modern replacement for Node.js. Node.j
## Design goals
Bun is designed from the ground-up with the today's JavaScript ecosystem in mind.
Bun is designed from the ground-up with today's JavaScript ecosystem in mind.
- **Speed**. Bun processes start [4x faster than Node.js](https://twitter.com/jarredsumner/status/1499225725492076544) currently (try it yourself!)
- **TypeScript & JSX support**. You can directly execute `.jsx`, `.ts`, and `.tsx` files; Bun's transpiler converts these to vanilla JavaScript before execution.
- **ESM & CommonJS compatibility**. Internally, Bun uses ESM exclusively, but CommonJS modules can be imported as-is.
- **ESM & CommonJS compatibility**. The world is moving towards ES modules (ESM), but millions of packages on npm still require CommonJS. Bun recommends ES modules, but supports CommonJS.
- **Web-standard APIs**. Bun implements standard Web APIs like `fetch`, `WebSocket`, and `ReadableStream`. Bun is powered by the JavaScriptCore engine, which is developed by Apple for Safari, so some APIs like [`Headers`](https://developer.mozilla.org/en-US/docs/Web/API/Headers) and [`URL`](https://developer.mozilla.org/en-US/docs/Web/API/URL) directly use [Safari's implementation](https://github.com/oven-sh/bun/blob/HEAD/src/bun.js/bindings/webcore/JSFetchHeaders.cpp).
- **Node.js compatibility**. In addition to supporting Node-style module resolution, Bun aims for full compatibility with built-in Node.js globals (`process`, `Buffer`) and modules (`path`, `fs`, `http`, etc.) _This is an ongoing effort that is not complete._ Refer to the compatibility page for the current status.

View File

@@ -90,9 +90,9 @@ export default {
page("runtime/typescript", "TypeScript", {
description: `Bun can directly execute TypeScript files without additional configuration.`,
}),
// page("runtime/jsx", "JSX", {
// description: `Bun can directly execute TypeScript files without additional configuration.`,
// }),
page("runtime/jsx", "JSX", {
description: `Bun can directly execute TypeScript files without additional configuration.`,
}),
// page("runtime/apis", "APIs", {
// description: `Bun is a new JavaScript runtime designed to be a faster, leaner, more modern replacement for Node.js.`,
// }),
@@ -154,7 +154,7 @@ export default {
}),
divider("Bundler"),
page("cli/build", "`Bun.build`", {
page("bundler", "`Bun.build`", {
description: "Bundle code for comsumption in the browser with Bun's native bundler.",
}),
// page("bundler/intro", "How bundlers work", {
@@ -169,7 +169,10 @@ export default {
page("bundler/executables", "Executables", {
description: "Compile a TypeScript or JavaScript file to a standalone cross-platform executable",
}),
page("bundler/migration", "Migration", {
page("bundler/macros", "Macros", {
description: `Run JavaScript functions at bundle-time and inline the results into your bundle`,
}),
page("bundler/vs-esbuild", "vs esbuild", {
description: `Guides for migrating from other bundlers to Bun.`,
}),
@@ -181,8 +184,11 @@ export default {
description:
"Write your tests using Jest-like expect matchers, plus setup/teardown hooks, snapshot testing, and more",
}),
page("test/extending", "Extending the test runner", {
description: "Add lifecycle hooks to your tests that run before/after each test, or before/after all tests.",
page("test/lifecycle", "Lifecycle hooks", {
description: "Add lifecycle hooks to your tests that run before/after each test or test run",
}),
page("test/snapshots", "Snapshots", {
description: "Add lifecycle hooks to your tests that run before/after each test or test run",
}),
page("test/hot", "Watch mode", {
description: "Reload your tests automatically on change.",
@@ -221,33 +227,45 @@ export default {
}),
divider("API"),
page("api/http", "HTTP", {
page("api/http", "HTTP server", {
description: `Bun implements Web-standard fetch, plus a Bun-native API for building fast HTTP servers.`,
}), // "`Bun.serve`"),
page("api/websockets", "WebSockets", {
description: `Bun supports server-side WebSockets with on-the-fly compression, TLS support, and a Bun-native pubsub API.`,
}), // "`Bun.serve`"),
page("api/tcp", "TCP Sockets", {
description: `Bun's native API implements Web-standard TCP Sockets, plus a Bun-native API for building fast TCP servers.`,
}), // "`Bun.{listen|connect}`"),
page("api/binary-data", "Binary data", {
description: `How to represent and manipulate binary data in Bun.`,
}), // "`Bun.serve`"),
page("api/streams", "Streams", {
description: `Reading, writing, and manipulating streams of data in Bun.`,
}), // "`Bun.serve`"),
page("api/file-io", "File I/O", {
description: `Read and write files fast with Bun's heavily optimized file system API.`,
}), // "`Bun.write`"),
page("api/import-meta", "import.meta", {
description: `Module-scoped metadata and utilities`,
}), // "`bun:sqlite`"),
page("api/sqlite", "SQLite", {
description: `The fastest SQLite driver for JavaScript is baked directly into Bun.`,
}), // "`bun:sqlite`"),
page("api/file-system-router", "FileSystemRouter", {
description: `Resolve incoming HTTP requests against a local file system directory with Bun's fast, Next.js-compatible router.`,
}), // "`Bun.FileSystemRouter`"),
page("api/tcp", "TCP sockets", {
description: `Bun's native API implements Web-standard TCP Sockets, plus a Bun-native API for building fast TCP servers.`,
}), // "`Bun.{listen|connect}`")
page("api/globals", "Globals", {
description: `Bun implements a range of Web APIs, Node.js APIs, and Bun-native APIs that are available in the global scope.`,
}), // "`Bun.write`"),
page("api/spawn", "Spawn", {
page("api/spawn", "Child processes", {
description: `Spawn sync and async child processes with easily configurable input and output streams.`,
}), // "`Bun.spawn`"),
page("api/transpiler", "Transpiler", {
description: `Bun exposes its internal transpiler as a pluggable API.`,
}), // "`Bun.Transpiler`"),
page("api/hashing", "Hashing", {
description: `Native support for a range of fast hashing algorithms.`,
}), // "`Bun.serve`"),
page("api/console", "Console", {
description: `Bun implements a Node.js-compatible \`console\` object with colorized output and deep pretty-printing.`,
}), // "`Node-API`"),

View File

@@ -2,6 +2,34 @@ Configuring a development environment for Bun can take 10-30 minutes depending o
If you are using Windows, you must use a WSL environment as Bun does not yet compile on Windows natively.
Before starting, you will need to already have a release build of Bun installed, as we use our bundler to transpile and minify our code.
{% codetabs %}
```bash#Native
$ curl -fsSL https://bun.sh/install | bash # for macOS, Linux, and WSL
```
```bash#npm
$ npm install -g bun # the last `npm` command you'll ever need
```
```bash#Homebrew
$ brew tap oven-sh/bun # for macOS and Linux
$ brew install bun
```
```bash#Docker
$ docker pull oven/bun
$ docker run --rm --init --ulimit memlock=-1:-1 oven/bun
```
```bash#proto
$ proto install bun
```
{% /codetabs %}
## Install LLVM
Bun requires LLVM 15 and Clang 15 (`clang` is part of LLVM). This version requirement is to match WebKit (precompiled), as mismatching versions will cause memory allocation failures at runtime. In most cases, you can install LLVM through your system package manager:
@@ -104,13 +132,13 @@ VSCode is the recommended IDE for working on Bun, as it has been configured. Onc
## JavaScript builtins
When you change anything in `src/bun.js/builtins/js/*` or switch branches, run this:
When you change anything in `src/js/builtins/*` or switch branches, run this:
```bash
$ make regenerate-bindings
```
That inlines the JavaScript code into C++ headers using the same builtins generator script that Safari uses.
That inlines the TypeScript code into C++ headers.
{% callout %}
Make sure you have `ccache` installed, otherwise regeneration will take much longer than it should.
@@ -148,11 +176,17 @@ $ make generate-sink
You probably won't need to run that one much.
## Modifying ESM core modules
## Modifying ESM modules
Certain modules like `node:fs`, `node:path`, `node:stream`, and `bun:sqlite` are implemented in JavaScript. These live in `src/bun.js/*.exports.js` files.
Certain modules like `node:fs`, `node:stream`, `bun:sqlite`, and `ws` are implemented in JavaScript. These live in `src/js/{node,bun,thirdparty}` files and are pre-bundled using Bun. The bundled code is committed so CI builds can run without needing a copy of Bun.
While Bun is in beta, you can modify them at runtime in release builds via the environment variable `BUN_OVERRIDE_MODULE_PATH`. When set, Bun will look in the override directory for `<name>.exports.js` before checking the files from `src/bun.js` (which are now baked in to the binary). This lets you test changes to the ESM modules without needing to re-compile Bun.
When these are changed, run:
```
$ make esm
```
In debug builds, Bun automatically loads these from the filesystem, wherever it was compiled, so no need to re-run `make dev`. In release builds, this same behavior can be done via the environment variable `BUN_OVERRIDE_MODULE_PATH`. When set to the repository root, Bun will read from the bundled modules in the repository instead of the ones baked into the binary.
## Release build

View File

@@ -1,4 +1,16 @@
Bun implements a set of native APIs on the `Bun` global object and through a number of built-in modules. These APIs represent the canonical "Bun-native" way to perform some common development tasks. They are all heavily optimized for performance. Click the link in the left column to view the associated documentation.
Bun implements a set of native APIs on the `Bun` global object and through a number of built-in modules. These APIs are heavily optimized and represent the canonical "Bun-native" way to implement some common functionality.
Bun strives to implement standard Web APIs wherever possible. Bun introduces new APIs primarily for server-side tasks where no standard exists, such as file I/O and starting an HTTP server. In these cases, Bun's approach still builds atop standard APIs like `Blob`, `URL`, and `Request`.
```ts
Bun.serve({
fetch(req: Request) {
return new Response("Success!");
},
});
```
Click the link in the right column to jump to the associated documentation.
{% table %}
@@ -7,38 +19,53 @@ Bun implements a set of native APIs on the `Bun` global object and through a num
---
- [HTTP](/docs/api/http)
- `Bun.serve`
- HTTP server
- [`Bun.serve`](/docs/api/http#bun-serve)
---
- [File I/O](/docs/api/file-io)
- `Bun.file` `Bun.write`
- Bundler
- [`Bun.build`](/docs/bundler)
---
- [Processes](/docs/api/spawn)
- `Bun.spawn` `Bun.spawnSync`
- File I/O
- [`Bun.file`](/docs/api/file-io#reading-files-bun-file) [`Bun.write`](/docs/api/file-io#writing-files-bun-write)
---
- [TCP](/docs/api/tcp)
- `Bun.listen` `Bun.connect`
- Child processes
- [`Bun.spawn`](/docs/api/spawn#spawn-a-process-bun-spawn) [`Bun.spawnSync`](/docs/api/spawn#blocking-api-bun-spawnsync)
---
- [Transpiler](/docs/api/transpiler)
- `Bun.Transpiler`
- TCP
- [`Bun.listen`](/docs/api/tcp#start-a-server-bun-listen) [`Bun.connect`](/docs/api/tcp#start-a-server-bun-listen)
---
- [Routing](/docs/api/file-system-router)
- `Bun.FileSystemRouter`
- Transpiler
- [`Bun.Transpiler`](/docs/api/transpiler)
---
- [HTMLRewriter](/docs/api/html-rewriter)
- `HTMLRewriter`
- Routing
- [`Bun.FileSystemRouter`](/docs/api/file-system-router)
---
- HTML Rewriting
- [`HTMLRewriter`](/docs/api/html-rewriter)
---
- Hashing
- [`Bun.hash`](/docs/api/hashing#bun-hash) [`Bun.CryptoHasher`](/docs/api/hashing#bun-cryptohasher)
---
- import.meta
- [`import.meta`](/docs/api/import-meta)
---
@@ -47,29 +74,27 @@ Bun implements a set of native APIs on the `Bun` global object and through a num
--- -->
- [Utils](/docs/api/utils)
- `Bun.peek` `Bun.which`
- SQLite
- [`bun:sqlite`](/docs/api/sqlite)
---
- [SQLite](/docs/api/sqlite)
- `bun:sqlite`
- FFI
- [`bun:ffi`](/docs/api/ffi)
---
- [FFI](/docs/api/ffi)
- `bun:ffi`
- Testing
- [`bun:test`](/docs/cli/test)
---
- [Testing](/docs/api/test)
- `bun:test`
- Node-API
- [`Node-API`](/docs/api/node-api)
---
- [Node-API](/docs/api/node-api)
- `Node-API`
---
- Utilities
- [`Bun.version`](/docs/api/utils#bun-version) [`Bun.revision`](/docs/api/utils#bun-revision) [`Bun.env`](/docs/api/utils#bun-env) [`Bun.main`](/docs/api/utils#bun-main) [`Bun.sleep()`](/docs/api/utils#bun-sleep) [`Bun.sleepSync()`](/docs/api/utils#bun-sleepsync) [`Bun.which()`](/docs/api/utils#bun-which) [`Bun.peek()`](/docs/api/utils#bun-peek) [`Bun.openInEditor()`](/docs/api/utils#bun-openineditor) [`Bun.deepEquals()`](/docs/api/utils#bun-deepequals) [`Bun.escapeHTML()`](/docs/api/utils#bun-escapehtlm) [`Bun.enableANSIColors()`](/docs/api/utils#bun-enableansicolors) [`Bun.fileURLToPath()`](/docs/api/utils#bun-fileurltopath) [`Bun.pathToFileURL()`](/docs/api/utils#bun-pathtofileurl) [`Bun.gzipSync()`](/docs/api/utils#bun-gzipsync) [`Bun.gunzipSync()`](/docs/api/utils#bun-gunzipsync) [`Bun.deflateSync()`](/docs/api/utils#bun-deflatesync) [`Bun.inflateSync()`](/docs/api/utils#bun-inflatesync) [`Bun.inspect()`](/docs/api/utils#bun-inspect) [`Bun.nanoseconds()`](/docs/api/utils#bun-nanoseconds) [`Bun.readableStreamTo*()`](/docs/api/utils#bun-readablestreamto) [`Bun.resolveSync()`](/docs/api/utils#bun-resolvesync)
{% /table %}

View File

@@ -14,12 +14,99 @@ If both a global and local `bunfig` are detected, the results are shallow-merged
## Environment variables
<!-- - `GOMAXPROCS`: For `bun bun`, this sets the maximum number of threads to use. If youre experiencing an issue with `bun bun`, try setting `GOMAXPROCS=1` to force Bun to run single-threaded -->
These environment variables are checked by Bun to detect functionality and toggle features.
- `DISABLE_BUN_ANALYTICS=1` this disables Bun's analytics. Bun records bundle timings (so we can answer with data, "is Bun getting faster?") and feature usage (e.g., "are people actually using macros?"). The request body size is about 60 bytes, so its not a lot of data
- `TMPDIR`: Bun occasionally requires a directory to store intermediate assets during bundling or other operations. If unset, `TMPDIR` defaults to the platform-specific temporary directory (on Linux, `/tmp` and on macOS `/private/tmp`).
{% table %}
## Configure `bun install`
- Name
- Description
---
- `TMPDIR`
- Bun occasionally requires a directory to store intermediate assets during bundling or other operations. If unset, defaults to the platform-specific temporary directory: `/tmp` on Linux, `/private/tmp` on macOS.
---
- `NO_COLOR`
- If `NO_COLOR=1`, then ANSI color output is [disabled](https://no-color.org/).
---
- `FORCE_COLOR`
- If `FORCE_COLOR=1`, then ANSI color output is force enabled, even if `NO_COLOR` is set.
---
- `DO_NOT_TRACK`
- If `DO_NOT_TRACK=1`, then analytics are [disabled](https://do-not-track.dev/). Bun records bundle timings (so we can answer with data, "is Bun getting faster?") and feature usage (e.g., "are people actually using macros?"). The request body size is about 60 bytes, so it's not a lot of data.
{% /table %}
## Runtime
```toml
# scripts to run before `bun run`ning a file or script
# useful for registering plugins
preload = ["./preload.ts"]
# equivalent to corresponding tsconfig compilerOptions
jsx = "react"
jsxFactory = "h"
jsxFragment = "Fragment"
jsxImportSource = "react"
# Set a default framework to use
# By default, Bun will look for an npm package like `bun-framework-${framework}`, followed by `${framework}`
logLevel = "debug"
# publicDir = "public"
# external = ["jquery"]
[define]
# Replace any usage of "process.env.bagel" with the string `lox`.
# The values are parsed as JSON, except single-quoted strings are supported and `'undefined'` becomes `undefined` in JS.
# This will probably change in a future release to be just regular TOML instead. It is a holdover from the CLI argument parsing.
"process.env.bagel" = "'lox'"
[loaders]
# When loading a .bagel file, run the JS parser
".bagel" = "js"
# - "atom"
# If you pass it a file path, it will open with the file path instead
# It will recognize non-GUI editors, but I don't think it will work yet
```
### Debugging
```toml
[debug]
# When navigating to a blob: or src: link, open the file in your editor
# If not, it tries $EDITOR or $VISUAL
# If that still fails, it will try Visual Studio Code, then Sublime Text, then a few others
# This is used by Bun.openInEditor()
editor = "code"
# List of editors:
# - "subl", "sublime"
# - "vscode", "code"
# - "textmate", "mate"
# - "idea"
# - "webstorm"
# - "nvim", "neovim"
# - "vim","vi"
# - "emacs"
```
## Test runner
```toml
[test]
# setup scripts to run before all test files
preload = ["./setup.ts"]
```
## Package manager
Package management is a complex issue; to support a range of use cases, the behavior of `bun install` can be configured in [`bunfig.toml`](/docs/runtime/configuration).
@@ -120,7 +207,9 @@ save = true
print = "yarn"
```
## Configure `bun dev`
## Dev server (`bun dev`)
{% The `bun dev` command is likely to change soon and will likely be deprecated in an upcoming release. We recommend %}
Here is an example:
@@ -128,17 +217,6 @@ Here is an example:
# Set a default framework to use
# By default, Bun will look for an npm package like `bun-framework-${framework}`, followed by `${framework}`
framework = "next"
logLevel = "debug"
# publicDir = "public"
# external = ["jquery"]
[macros]
# Remap any import like this:
# import {graphql} from 'react-relay';
# To:
# import {graphql} from 'macro:bun-macro-relay';
react-relay = { "graphql" = "bun-macro-relay" }
[bundle]
saveTo = "node_modules.bun"
@@ -154,33 +232,4 @@ entryPoints = ["./app/index.ts"]
# Also inherited by Bun.serve
port = 5000
[define]
# Replace any usage of "process.env.bagel" with the string `lox`.
# The values are parsed as JSON, except single-quoted strings are supported and `'undefined'` becomes `undefined` in JS.
# This will probably change in a future release to be just regular TOML instead. It is a holdover from the CLI argument parsing.
"process.env.bagel" = "'lox'"
[loaders]
# When loading a .bagel file, run the JS parser
".bagel" = "js"
[debug]
# When navigating to a blob: or src: link, open the file in your editor
# If not, it tries $EDITOR or $VISUAL
# If that still fails, it will try Visual Studio Code, then Sublime Text, then a few others
# This is used by Bun.openInEditor()
editor = "code"
# List of editors:
# - "subl", "sublime"
# - "vscode", "code"
# - "textmate", "mate"
# - "idea"
# - "webstorm"
# - "nvim", "neovim"
# - "vim","vi"
# - "emacs"
# - "atom"
# If you pass it a file path, it will open with the file path instead
# It will recognize non-GUI editors, but I don't think it will work yet
```

View File

@@ -12,14 +12,291 @@ function Component(props: {message: string}) {
console.log(<Component message="Hello world!" />);
```
Bun implements special logging for JSX to make debugging easier.
## Configuration
```bash
$ bun run react.tsx
<Component message="Hello world!" />
Bun reads your `tsconfig.json` or `jsconfig.json` configuration files to determines how to perform the JSX transform internally. To avoid using either of these, the following options can also be defined in [`bunfig.json`](/docs/runtime/configuration).
The following compiler options are respected.
### [`jsx`](https://www.typescriptlang.org/tsconfig#jsx)
How JSX constructs are transformed into vanilla JavaScript internally. The table below lists the possible values of `jsx`, along with their transpilation of the following simple JSX component:
```tsx
<Box width={5}>Hello</Box>
```
<!-- ### Prop punning
{% table %}
- Compiler options
- Transpiled output
---
- ```json
{
"jsx": "react"
}
```
- ```tsx
import { createElement } from "react";
createElement("Box", { width: 5 }, "Hello");
```
---
- ```json
{
"jsx": "react-jsx"
}
```
- ```tsx
import { jsx } from "react/jsx-runtime";
jsx("Box", { width: 5 }, "Hello");
```
---
- ```json
{
"jsx": "react-jsxdev"
}
```
- ```tsx
import { jsxDEV } from "react/jsx-dev-runtime";
jsxDEV("Box", { width: 5, children: "Hello" }, undefined, false, undefined, this);
```
The `jsxDEV` variable name is a convention used by React. The `DEV` suffix is a visible way to indicate that the code is intended for use in development. The development version of React is slowers and includes additional validity checks & debugging tools.
---
- ```json
{
"jsx": "preserve"
}
```
- ```tsx
// JSX is not transpiled
// "preserve" is not supported by Bun currently
<Box width={5}>Hello</Box>
```
{% /table %}
<!-- {% table %}
- `react`
- `React.createElement("Box", {width: 5}, "Hello")`
---
- `react-jsx`
- `jsx("Box", {width: 5}, "Hello")`
---
- `react-jsxdev`
- `jsxDEV("Box", {width: 5}, "Hello", void 0, false)`
---
- `preserve`
- `<Box width={5}>Hello</Box>` Left as-is; not yet supported by Bun.
{% /table %} -->
### [`jsxFactory`](https://www.typescriptlang.org/tsconfig#jsxFactory)
{% callout %}
**Note** — Only applicable when `jsx` is `react`.
{% /callout %}
The function name used to represent JSX constructs. Default value is `"createElement"`. This is useful for libraries like [Preact](https://preactjs.com/) that use a different function name (`"h"`).
{% table %}
- Compiler options
- Transpiled output
---
- ```json
{
"jsx": "react",
"jsxFactory": "h"
}
```
- ```tsx
import { createhElement } from "react";
h("Box", { width: 5 }, "Hello");
```
{% /table %}
### [`jsxFragmentFactory`](https://www.typescriptlang.org/tsconfig#jsxFragmentFactory)
{% callout %}
**Note** — Only applicable when `jsx` is `react`.
{% /callout %}
The function name used to represent [JSX fragments](https://react.dev/reference/react/Fragment) such as `<>Hello</>`; only applicable when `jsx` is `react`. Default value is `"Fragment"`.
{% table %}
- Compiler options
- Transpiled output
---
- ```json
{
"jsx": "react",
"jsxFactory": "myjsx",
"jsxFragmentFactory": "MyFragment"
}
```
- ```tsx
// input
<>Hello</>;
// output
import { myjsx, MyFragment } from "react";
createElement("Box", { width: 5 }, "Hello");
```
{% /table %}
### [`jsxImportSource`](https://www.typescriptlang.org/tsconfig#jsxImportSource)
{% callout %}
**Note** — Only applicable when `jsx` is `react-jsx` or `react-jsxdev`.
{% /callout %}
The module from which the component factory function (`createElement`, `jsx`, `jsxDEV`, etc) will be imported. Default value is `"react"`. This will typically be necessary when using a component library like Preact.
{% table %}
- Compiler options
- Transpiled output
---
- ```jsonc
{
"jsx": "react"
// jsxImportSource is not defined
// default to "react"
}
```
- ```tsx
import { jsx } from "react/jsx-runtime";
jsx("Box", { width: 5, children: "Hello" });
```
---
- ```jsonc
{
"jsx": "react-jsx",
"jsxImportSource": "preact"
}
```
- ```tsx
import { jsx } from "preact/jsx-runtime";
jsx("Box", { width: 5, children: "Hello" });
```
---
- ```jsonc
{
"jsx": "react-jsxdev",
"jsxImportSource": "preact"
}
```
- ```tsx
// /jsx-runtime is automatically appended
import { jsxDEV } from "preact/jsx-dev-runtime";
jsxDEV("Box", { width: 5, children: "Hello" }, undefined, false, undefined, this);
```
{% /table %}
### JSX pragma
All of these values can be set on a per-file basis using _pragmas_. A pragma is a special comment that sets a compiler option in a particular file.
{% table %}
- Pragma
- Equivalent config
---
- ```ts
// @jsx h
```
- ```jsonc
{
"jsxFactory": "h"
}
```
---
- ```ts
// @jsxFrag MyFragment
```
- ```jsonc
{
"jsxFragmentFactory": "MyFragment"
}
```
---
- ```ts
// @jsxImportSource preact
```
- ```jsonc
{
"jsxImportSource": "preact"
}
```
{% /table %}
## Logging
Bun implements special logging for JSX to make debugging easier. Given the following file:
```tsx#index.tsx
import { Stack, UserCard } from "./components";
console.log(
<Stack>
<UserCard name="Dom" bio="Street racer and Corona lover" />
<UserCard name="Jakob" bio="Super spy and Dom's secret brother" />
</Stack>
);
```
Bun will pretty-print the component tree when logged:
{% image src="https://github.com/oven-sh/bun/assets/3084745/d29db51d-6837-44e2-b8be-84fc1b9e9d97" / %}
## Prop punning
The Bun runtime also supports "prop punning" for JSX. This is a shorthand syntax useful for assigning a variable to a prop with the same name.
@@ -32,4 +309,4 @@ function Div(props: {className: string;}) {
// with punning
return <div {className} />;
}
``` -->
```

View File

@@ -121,7 +121,7 @@ Bun respects subpath [`"exports"`](https://nodejs.org/api/packages.html#subpath-
"name": "foo",
"exports": {
".": "./index.js",
"./package.json": "./package.json" # subpath
"./package.json": "./package.json" // subpath
}
}
```
@@ -156,3 +156,90 @@ In the spirit of treating TypeScript as a first-class citizen, the Bun runtime w
```
If you aren't a TypeScript user, you can create a [`jsconfig.json`](https://code.visualstudio.com/docs/languages/jsconfig) in your project root to achieve the same behavior.
## CommonJS
Bun has native support for CommonJS modules (added in Bun v0.6.5). ES Modules are the recommended module format, but CommonJS modules are still widely used in the Node.js ecosystem. Bun supports both module formats, so that existing CommonJS packages can be used.
In Bun's JavaScript runtime, `require` can be used by both ES Modules and CommonJS modules.
In Bun, you can `require()` ESM modules from CommonJS modules.
| Module Type | `require()` | `import * as` |
| ----------- | ---------------- | ----------------------------------------------------------------------- |
| ES Module | Module Namespace | Module Namespace |
| CommonJS | module.exports | `default` is `module.exports`, keys of module.exports are named exports |
If the target module is an ES Module, `require` returns the module namespace object (equivalent to `import * as`).
If the target module is a CommonJS module, `require` returns the `module.exports` object.
### What is a CommonJS module?
In 2016, ECMAScript added support for [ES Modules](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules). ES Modules are the standard for JavaScript modules. However, millions of npm packages still use CommonJS modules.
CommonJS modules are modules that use `module.exports` to export values. Typically, `require` is used to import CommonJS modules.
```ts
// my-commonjs.cjs
const stuff = require("./stuff");
module.exports = { stuff };
```
The biggest difference between CommonJS and ES Modules is that CommonJS modules are synchronous, while ES Modules are asynchronous. There are other differences too, like ES Modules support top-level `await` and CommonJS modules don't. ES Modules are always in [strict mode](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Strict_mode), while CommonJS modules are not. Browsers do not have native support for CommonJS modules, but they do have native support for ES Modules (`<script type="module">`). CommonJS modules are not statically analyzable, while ES Modules only allow static imports and exports.
### Importing CommonJS from ESM
You can `import` or `require` CommonJS modules from ESM modules.
```ts
import { stuff } from "./my-commonjs.cjs";
import Stuff from "./my-commonjs.cjs";
const myStuff = require("./my-commonjs.cjs");
```
### Importing ESM from CommonJS
```ts
// this works in Bun v0.6.5+
// It does not work in Node.js
const { stuff } = require("./my-esm.mjs");
```
### Importing CommonJS from CommonJS
You can `require()` CommonJS modules from CommonJS modules.
```ts
const { stuff } = require("./my-commonjs.cjs");
```
#### Top-level await
If you are using top-level await, you must use `import()` to import ESM modules from CommonJS modules.
```ts
import("./my-esm.js").then(({ stuff }) => {
// ...
});
// this will throw an error if "my-esm.js" uses top-level await
const { stuff } = require("./my-esm.js");
```
#### Low-level details of CommonJS interop in Bun
Bun's JavaScript runtime has native support for CommonJS as of Bun v0.6.5.
When Bun's JavaScript transpiler detects usages of `module.exports`, it treats the file as CommonJS. The module loader will then wrap the transpiled module in a function shaped like this:
```js
(function (module, exports, require) {
// transpiled module
})(module, exports, require);
```
`module`, `exports`, and `require` are very much like the `module`, `exports`, and `require` in Node.js. These are assigned via a [`with scope`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/with) in C++. An internal `Map` stores the `exports` object to handle cyclical `require` calls before the module is fully loaded.
Once the CommonJS module is successfully evaluated, a Synthetic Module Record is created with the `default` ES Module [export set to `module.exports`](https://github.com/oven-sh/bun/blob/9b6913e1a674ceb7f670f917fc355bb8758c6c72/src/bun.js/bindings/CommonJSModuleRecord.cpp#L212-L213) and keys of the `module.exports` object are re-exported as named exports (if the `module.exports` object is an object).
When using Bun's bundler, this works differently. The bundler will wrap the CommonJS module in a `require_${moduleName}` function which returns the `module.exports` object.

View File

@@ -81,7 +81,7 @@ This page is updated regularly to reflect compatibility status of the latest ver
- {% anchor id="node_events" %} [`node:events`](https://nodejs.org/api/events.html) {% /anchor %}
- 🟡
- Missing `EventEmitterAsyncResource`. `EventEmitter` is missing `{get}set}MaxListeners` `usingDomains` `init`.
- Missing `EventEmitterAsyncResource` `events.on`.
---
@@ -104,8 +104,8 @@ This page is updated regularly to reflect compatibility status of the latest ver
---
- {% anchor id="node_https" %} [`node:https`](https://nodejs.org/api/https.html) {% /anchor %}
- 🟡
- See `node:http`.
- 🟢
- Fully implemented.
---
@@ -236,8 +236,8 @@ This page is updated regularly to reflect compatibility status of the latest ver
---
- {% anchor id="node_vm" %} [`node:vm`](https://nodejs.org/api/vm.html) {% /anchor %}
- 🔴
- Not implemented.
- 🟡
- Partially implemented.
---

View File

@@ -1,162 +0,0 @@
Like the runtime, `bun:test` also supports `--preload` scripts. These scripts are loaded before any tests are run. This is useful for setting up test fixtures, mocking, and configuring the test environment.
{% codetabs %}
```ts#preloaded.ts
import { beforeAll, beforeEach, afterEach, afterAll } from "bun:test";
beforeAll(() => {
console.log("beforeAll");
});
beforeEach(() => {
console.log("beforeEach");
});
afterEach(() => {
console.log("afterEach");
});
afterAll(() => {
console.log("afterAll");
});
```
{% /codetabs %}
Test file:
```ts
import { expect, test } from "bun:test";
test("1 + 1", () => {
expect(1 + 1).toEqual(2);
console.log("1 + 1");
});
```
Run the test with `--preload`:
```sh
$ bun test --preload=preloaded.ts
```
It outputs:
```sh
beforeAll
beforeEach
1 + 1
afterEach
afterAll
```
## Configuration
To save yourself from having to type `--preload` every time you run tests, you can add it to your `bunfig.toml`:
```toml
[test]
preload = ["./preloaded.ts"]
```
## List of lifecycle hooks
The following lifecycle hooks are available in `--preload`:
| Hook | Description |
| ------------ | --------------------------- |
| `beforeAll` | Runs once before all tests. |
| `beforeEach` | Runs before each test. |
| `afterEach` | Runs after each test. |
| `afterAll` | Runs once after all tests. |
Calling `expect`, `test`, or any other test function inside a lifecycle hook will throw an error. Calling `test` inside `beforeAll`, `afterAll`, `beforeEach` or `afterEach` will also throw an error.
You can use `console.log` or any other function otherwise inside a lifecycle hook.
We haven't implemented timer simulation, test isolation, or `Math.random` mocking yet. If you need these features, please [open an issue](https://bun.sh/issues).
### The lifecycle of bun:test
The test runner is a single process that runs all tests. It loads all `--preload` scripts, then runs all tests. If a test fails, the test runner will exit with a non-zero exit code.
Before running each test, it transpiles the source code and all dependencies into vanilla JavaScript using Bun's transpiler and module resolver. This means you can use TypeScript, JSX, ESM, and CommonJS in your tests.
#### Globals
Like Jest, you can use `describe`, `test`, `expect`, and other functions without importing them.
But unlike Jest, they are not globals. They are imported from `bun:test` and are exclusively available in test files or when preloading scripts.
```ts
typeof globalThis.describe; // "undefined"
typeof describe; // "function"
```
This works via a transpiler integration in Bun. When `describe`, `expect`, `it`, etc are used in a test file, the transpiler auto-imports from `bun:test`. This transpiler plugin is only enabled inside test files and when preloading scripts. If you try to use Jest globals in other files, you will get an error.
Every `describe`, `test`, and `expect` is scoped to the current test file. Importing from `"bun:test"` creates a new scope. This means you can't use `describe` from one test file in another test file because belong to different scopes.
## Loaders & Resolvers
{% note %}
Plugin support is not implemented yet. **There is a bug and this feature is not working**.
{% /note %}
`bun:test` supports the same plugin API as bun's runtime and bun's bundler. See [Plugins](/docs/bundler/plugins#usage) for more information.
## Example loader
{% codetabs %}
```ts#loader.ts
import { plugin } from 'bun';
plugin({
name: 'My loader',
setup(build) {
build.onResolve({ filter: /\.txt$/ }, (args) => {
return {
path: args.path,
namespace: 'my-loader',
};
});
build.onLoad({ filter: /my-loader:.txt$/ }, (args) => {
return {
contents: 'Hello world!',
loader: 'text',
};
});
},
});
```
{% /codetabs %}
Now in your test file, you can import `.txt` files:
```ts#my-test.test.ts
import { expect, test } from "bun:test";
import text from "./hello.txt";
test("text is 'Hello world!'", () => {
expect(text).toEqual("Hello world!");
});
```
To run the test, you need to add `loader.ts` to `preload`:
```toml
[test]
preload = ["loader.ts"]
```
Or you can pass --preload to the command line:
```sh
$ bun test --preload=loader.ts
```
TODO: `expect.extend`

81
docs/test/lifecycle.md Normal file
View File

@@ -0,0 +1,81 @@
The test runner supports the following lifecycle hooks. This is useful for loading test fixtures, mocking data, and configuring the test environment.
| Hook | Description |
| ------------ | --------------------------- |
| `beforeAll` | Runs once before all tests. |
| `beforeEach` | Runs before each test. |
| `afterEach` | Runs after each test. |
| `afterAll` | Runs once after all tests. |
Perform per-test setup and teardown logic with `beforeEach` and `afterEach`.
```ts
import { expect, test } from "bun:test";
beforeEach(() => {
console.log("running test.");
});
afterEach(() => {
console.log("done with test.");
});
// tests...
```
Perform per-scope setup and teardown logic with `beforeAll` and `afterAll`. The _scope_ is determined by where the hook is defined.
To scope the hooks to a particular `describe` block:
```ts
import { describe, beforeAll } from "bun:test";
describe("test group", () => {
beforeAll(() => {
// setup
});
// tests...
});
```
To scope the hooks to a test file:
```ts
import { describe, beforeAll } from "bun:test";
describe("test group", () => {
beforeAll(() => {
// setup
});
// tests...
});
```
To scope the hooks to an entire multi-file test run, define the hooks in a separate file.
```ts#setup.ts
import { beforeAll, afterAll } from "bun:test";
beforeAll(() => {
// global setup
});
afterAll(() => {
// global teardown
});
```
Then use `--preload` to run the setup script before any test files.
```ts
bun test --preload ./setup.ts
```
To avoid typing `--preload` every time you run tests, it can be added to your `bunfig.toml`:
```toml
[test]
preload = ["./setup.ts"]
```

15
docs/test/snapshots.md Normal file
View File

@@ -0,0 +1,15 @@
Snapshot tests are written using the `.toMatchSnapshot()` matcher:
```ts
import { test, expect } from "bun:test";
test("snap", () => {
expect("foo").toMatchSnapshot();
});
```
The first time this test is run, the argument to `expect` will be serialized and written to a special snapshot file in a `__snapshots__` directory alongside the test file. On future runs, the argument is compared against the snapshot on disk. Snapshots can be re-generated with the following command:
```bash
$ bun test --update-snapshots
```

View File

@@ -12,6 +12,17 @@ test("2 + 2", () => {
});
```
{% details summary="Jest-style globals" %}
As in Jest, you can use `describe`, `test`, `expect`, and other functions without importing them. Unlike Jest, they are not injected into the global scope. Instead, the Bun transpiler will automatically inject an import from `bun:test` internally.
```ts
typeof globalThis.describe; // "undefined"
typeof describe; // "function"
```
This transpiler integration only occurs during `bun test`, and only for test files & preloaded scripts. In practice there's no significant difference to the end user.
{% /details %}
Tests can be grouped into suites with `describe`.
```ts#math.test.ts
@@ -52,7 +63,7 @@ test("2 * 2", done => {
});
```
Skip individual tests with `test.skip`.
Skip individual tests with `test.skip`. These tests will not be run.
```ts
import { expect, test } from "bun:test";
@@ -63,41 +74,14 @@ test.skip("wat", () => {
});
```
## Setup and teardown
Perform per-test setup and teardown logic with `beforeEach` and `afterEach`.
Mark a test as a todo with `test.todo`. These tests _will_ be run, and the test runner will expect them to fail. If they pass, you will be prompted to mark it as a regular test.
```ts
import { expect, test } from "bun:test";
beforeEach(() => {
console.log("running test.");
test.todo("fix this", () => {
myTestFunction();
});
afterEach(() => {
console.log("done with test.");
});
// tests...
```
Perform per-scope setup and teardown logic with `beforeAll` and `afterAll`. At the top-level, the _scope_ is the current file; in a `describe` block, the scope is the block itself.
```ts
import { expect, test, beforeAll, afterAll } from "bun:test";
let db: Database;
beforeAll(() => {
// connect to database
db = initializeDatabase();
});
afterAll(() => {
// close connection
db.close();
});
// tests...
```
## Matchers
@@ -106,6 +90,8 @@ Bun implements the following matchers. Full Jest compatibility is on the roadmap
{% table %}
---
- 🟢
- [`.not`](https://jestjs.io/docs/expect#not)

169
packages/bun-devtools/.gitignore vendored Normal file
View File

@@ -0,0 +1,169 @@
# Based on https://raw.githubusercontent.com/github/gitignore/main/Node.gitignore
# Logs
logs
_.log
npm-debug.log_
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
.pnpm-debug.log*
# Diagnostic reports (https://nodejs.org/api/report.html)
report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json
# Runtime data
pids
_.pid
_.seed
\*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
\*.lcov
# nyc test coverage
.nyc_output
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules/
jspm_packages/
# Snowpack dependency directory (https://snowpack.dev/)
web_modules/
# TypeScript cache
\*.tsbuildinfo
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Optional stylelint cache
.stylelintcache
# Microbundle cache
.rpt2_cache/
.rts2_cache_cjs/
.rts2_cache_es/
.rts2_cache_umd/
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
\*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variable files
.env
.env.development.local
.env.test.local
.env.production.local
.env.local
# parcel-bundler cache (https://parceljs.org/)
.cache
.parcel-cache
# Next.js build output
.next
out
# Nuxt.js build / generate output
.nuxt
dist
# Gatsby files
.cache/
# Comment in the public line in if your project uses Gatsby and not Next.js
# https://nextjs.org/blog/next-9-1#public-directory-support
# public
# vuepress build output
.vuepress/dist
# vuepress v2.x temp and cache directory
.temp
.cache
# Docusaurus cache and generated files
.docusaurus
# Serverless directories
.serverless/
# FuseBox cache
.fusebox/
# DynamoDB Local files
.dynamodb/
# TernJS port file
.tern-port
# Stores VSCode versions used for testing VSCode extensions
.vscode-test
# yarn v2
.yarn/cache
.yarn/unplugged
.yarn/build-state.yml
.yarn/install-state.gz
.pnp.\*

View File

@@ -0,0 +1,3 @@
# bun-devtools
A set of auto-generated TypeScript types for the WebKit debugger protocol.

BIN
packages/bun-devtools/bun.lockb Executable file

Binary file not shown.

File diff suppressed because one or more lines are too long

14
packages/bun-devtools/heap/jsc.d.ts vendored Normal file
View File

@@ -0,0 +1,14 @@
export namespace JSC {
/**
* @link https://github.com/WebKit/webkit/blob/main/Source/JavaScriptCore/heap/HeapSnapshotBuilder.h
*/
export type HeapSnapshot = {
version: 2;
type: "Inspector";
nodes: number[];
nodeClassNames: string[];
edges: number[];
edgeTypes: string[];
edgeNames: string[];
};
}

30
packages/bun-devtools/heap/v8.d.ts vendored Normal file
View File

@@ -0,0 +1,30 @@
export namespace V8 {
/**
* @link https://github.com/julianburr/chrome-heap-snapshot-parser/blob/master/index.js#L72
* @link https://stackoverflow.com/questions/69802133/chrome-heap-snapshot-structure-explanation
*/
export type HeapSnapshot = {
snapshot: {
meta: {
node_fields: string[];
node_types: [string[], ...string[]]; // ?
edge_fields: string[];
edge_types: [string[], ...string[]]; // ?
trace_function_info_fields: string[];
trace_node_fields: string[];
sample_fields: string[];
location_fields: string[];
node_count: number;
edge_count: number;
trace_function_count: number;
};
};
nodes: number[];
edges: number[];
trace_tree: unknown[];
trace_function_infos: unknown[];
samples: unknown[];
locations: number[];
strings: string[];
};
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,2 @@
export * from "./protocol/jsc";
export * from "./protocol/v8";

View File

@@ -0,0 +1,24 @@
{
"name": "bun-devtools",
"module": "./index.ts",
"version": "0.0.2",
"type": "module",
"exports": {
".": {
"import": "./index.ts",
"require": "./index.ts"
}
},
"scripts": {
"generate-protocol": "bun run scripts/generate-protocol.ts"
},
"files": [
"index.ts",
"package.json",
"tsconfig.json",
"protocol"
],
"peerDependencies": {
"typescript": "^5.0.0"
}
}

1953
packages/bun-devtools/protocol/jsc.d.ts vendored Normal file

File diff suppressed because it is too large Load Diff

3422
packages/bun-devtools/protocol/v8.d.ts vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,129 @@
// A DevTools client for JavaScriptCore.
import type { JSC } from "..";
type ClientOptions = {
url: string | URL;
event?: (event: JSC.Event<keyof JSC.EventMap>) => void;
request?: (request: JSC.Request<keyof JSC.RequestMap>) => void;
response?: (response: JSC.Response<keyof JSC.ResponseMap>) => void;
};
class Client {
#webSocket: WebSocket;
#requestId: number;
#pendingMessages: string[];
#pendingRequests: Map<number, AbortController>;
#ready: Promise<void>;
constructor(options: ClientOptions) {
this.#webSocket = new WebSocket(options.url);
this.#requestId = 1;
this.#pendingMessages = [];
this.#pendingRequests = new Map();
this.#ready = new Promise((resolve, reject) => {
this.#webSocket.addEventListener("open", () => {
for (const message of this.#pendingMessages) {
this.#send(message);
}
this.#pendingMessages.length = 0;
resolve();
});
this.#webSocket.addEventListener("message", ({ data }) => {
let response;
try {
response = { ...JSON.parse(data) };
} catch {
console.error("Received an invalid message:", data);
return;
}
const { id, error, result, method, params } = response;
if (method && params) {
options.event?.(response);
} else if (id && (result || error)) {
try {
options.response?.(response);
} finally {
const abort = this.#pendingRequests.get(id ?? -1);
if (!abort) {
console.error("Received an unexpected message:", response);
return;
}
if (error) {
abort.abort(new Error(JSON.stringify(error)));
} else {
abort.abort(result);
}
}
} else {
console.error("Received an unexpected message:", response);
}
});
this.#webSocket.addEventListener("error", (error) => {
reject(error);
});
this.#webSocket.addEventListener("close", ({ code, reason = ""}) => {
reject(new Error(`WebSocket closed: ${code} ${reason}`.trimEnd()));
});
});
}
get ready(): Promise<void> {
return this.#ready;
}
#send(message: string): void {
const { readyState } = this.#webSocket;
if (readyState === WebSocket.OPEN) {
this.#webSocket.send(message);
} else if (readyState === WebSocket.CONNECTING) {
this.#pendingMessages.push(message);
} else {
const closed = readyState === WebSocket.CLOSING ? "closing" : "closed";
throw new Error(`WebSocket is ${closed}`);
}
}
async fetch<T extends keyof JSC.RequestMap>(method: T, params: JSC.Request<T>["params"]): Promise<JSC.Response<T>> {
const request: JSC.Request<T> = {
id: this.#requestId++,
method,
params,
};
return new Promise((resolve, reject) => {
const abort = new AbortController();
abort.signal.addEventListener("abort", () => {
this.#pendingRequests.delete(request.id);
const { reason } = abort.signal;
if (reason instanceof Error) {
reject(reason);
} else {
resolve(reason);
}
});
this.#pendingRequests.set(request.id, abort);
this.#send(JSON.stringify(request));
});
}
}
const client = new Client({
url: "ws://localhost:9229",
event: (event) => console.log("EVENT:", event),
request: (request) => console.log("REQUEST:", request),
response: (response) => console.log("RESPONSE:", response),
});
await client.ready;
while (true) {
const [method, ...param] = prompt(">")?.split(" ") ?? [];
if (!method.trim()) {
continue;
}
const params = !param?.length ? {} : JSON.parse(eval(`JSON.stringify(${param.join(" ")})`));
try {
await client.fetch(method.trim() as any, params);
} catch (error) {
console.error(error);
}
}

View File

@@ -0,0 +1,255 @@
import { join } from "node:path";
import { writeFileSync, mkdirSync } from "node:fs";
import { spawnSync } from "node:child_process";
async function download<V>(url: string): Promise<V> {
const response = await fetch(url);
if (!response.ok) {
throw new Error(`${response.status}: ${url}`);
}
return response.json();
}
type Protocol = {
name: string;
version: {
major: number;
minor: number;
};
domains: Domain[];
};
type Domain = {
domain: string;
types: Property[];
commands?: {
name: string;
description?: string;
parameters?: Property[];
returns?: Property[];
}[];
events?: {
name: string;
description?: string;
parameters: Property[];
}[];
};
type Property = {
id?: string;
type?: string;
name?: string;
description?: string;
optional?: boolean;
} & (
| {
type: "array";
items?: Property;
}
| {
type: "object";
properties?: Property[];
}
| {
type: "string";
enum?: string[];
}
| {
$ref: string;
}
);
function format(property: Property): string {
if (property.id) {
const comment = property.description
? `/** ${property.description} */\n`
: "";
const body = format({ ...property, id: undefined });
return `${comment}export type ${property.id} = ${body};\n`;
}
if (property.type === "array") {
const type = "items" in property ? format(property.items!) : "unknown";
return `Array<${type}>`;
}
if (property.type === "object") {
if (!("properties" in property)) {
return "Record<string, unknown>";
}
if (property.properties!.length === 0) {
return "{}";
}
const properties = property
.properties!.map((property) => {
const comment = property.description
? `/** ${property.description} */\n`
: "";
const name = `${property.name}${property.optional ? "?" : ""}`;
return `${comment} ${name}: ${format(property)};`;
})
.join("\n");
return `{\n${properties}}`;
}
if (property.type === "string") {
if (!("enum" in property)) {
return "string";
}
return property.enum!.map((v) => `"${v}"`).join(" | ");
}
if ("$ref" in property) {
if (/^Page|DOM|Security|CSS|IO|Emulation\./.test(property.$ref)) {
return "unknown";
}
return property.$ref;
}
if (property.type === "integer") {
return "number";
}
return property.type;
}
function formatAll(protocol: Protocol): string {
let body = "";
const append = (property: Property) => {
body += format(property);
};
const titlize = (name: string) =>
name.charAt(0).toUpperCase() + name.slice(1);
const events = new Map();
const commands = new Map();
for (const domain of protocol.domains) {
body += `export namespace ${domain.domain} {`;
for (const type of domain.types ?? []) {
append(type);
}
for (const event of domain.events ?? []) {
const symbol = `${domain.domain}.${event.name}`;
const title = titlize(event.name);
events.set(symbol, `${domain.domain}.${title}`);
append({
id: `${title}Event`,
type: "object",
description: `\`${symbol}\``,
properties: event.parameters ?? [],
});
}
for (const command of domain.commands ?? []) {
const symbol = `${domain.domain}.${command.name}`;
const title = titlize(command.name);
commands.set(symbol, `${domain.domain}.${title}`);
append({
id: `${title}Request`,
type: "object",
description: `\`${symbol}\``,
properties: command.parameters ?? [],
});
append({
id: `${title}Response`,
type: "object",
description: `\`${symbol}\``,
properties: command.returns ?? [],
});
}
body += "};";
}
for (const type of ["Event", "Request", "Response"]) {
const source = type === "Event" ? events : commands;
append({
id: `${type}Map`,
type: "object",
properties: [...source.entries()].map(([name, title]) => ({
name: `"${name}"`,
$ref: `${title}${type}`,
})),
});
}
body += `export type Event<T extends keyof EventMap> = {
method: T;
params: EventMap[T];
};
export type Request<T extends keyof RequestMap> = {
id: number;
method: T;
params: RequestMap[T];
};
export type Response<T extends keyof ResponseMap> = {
id: number;
} & ({
method?: T;
result: ResponseMap[T];
} | {
error: {
code?: string;
message: string;
};
});`;
return `export namespace ${protocol.name.toUpperCase()} {${body}};`;
}
async function downloadV8(): Promise<Protocol> {
const baseUrl =
"https://raw.githubusercontent.com/ChromeDevTools/devtools-protocol/master/json";
const filter = [
"Runtime",
"Network",
"Console",
"Debugger",
"Profiler",
"HeapProfiler",
];
return Promise.all([
download<Protocol>(`${baseUrl}/js_protocol.json`),
download<Protocol>(`${baseUrl}/browser_protocol.json`),
]).then(([js, browser]) => ({
name: "v8",
version: js.version,
domains: [...js.domains, ...browser.domains]
.filter((domain) => filter.includes(domain.domain))
.sort((a, b) => a.domain.localeCompare(b.domain)),
}));
}
async function downloadJsc(): Promise<Protocol> {
const baseUrl =
"https://raw.githubusercontent.com/WebKit/WebKit/main/Source/JavaScriptCore/inspector/protocol";
return {
name: "jsc",
version: {
major: 1,
minor: 3,
},
domains: await Promise.all([
download<Domain>(`${baseUrl}/Debugger.json`),
download<Domain>(`${baseUrl}/Heap.json`),
download<Domain>(`${baseUrl}/ScriptProfiler.json`),
download<Domain>(`${baseUrl}/Runtime.json`),
download<Domain>(`${baseUrl}/Network.json`),
download<Domain>(`${baseUrl}/Console.json`),
download<Domain>(`${baseUrl}/GenericTypes.json`),
]).then((domains) =>
domains.sort((a, b) => a.domain.localeCompare(b.domain))
),
};
}
async function run(cwd: string) {
const [jsc, v8] = await Promise.all([downloadJsc(), downloadV8()]);
try {
mkdirSync(cwd);
} catch (error) {
if (error.code !== "EEXIST") {
throw error;
}
}
const write = (name: string, data: string) => {
writeFileSync(join(cwd, name), data);
spawnSync("bunx", ["prettier", "--write", name], { cwd, stdio: "ignore" });
};
// Note: Can be uncommented to inspect the JSON protocol files.
// write("devtools/jsc.json", JSON.stringify(jsc));
// write("devtools/v8.json", JSON.stringify(v8));
write("jsc.d.ts", "// GENERATED - DO NOT EDIT\n" + formatAll(jsc));
write("v8.d.ts", "// GENERATED - DO NOT EDIT\n" + formatAll(v8));
}
run(join(__dirname, "..", "protocol"))
.catch(console.error);

View File

@@ -0,0 +1,18 @@
{
"compilerOptions": {
"lib": ["ESNext"],
"module": "esnext",
"target": "esnext",
"moduleResolution": "bundler",
"moduleDetection": "force",
"allowImportingTsExtensions": true,
"strict": true,
"downlevelIteration": true,
"skipLibCheck": true,
"jsx": "preserve",
"allowSyntheticDefaultImports": true,
"forceConsistentCasingInFileNames": true,
"allowJs": true,
"noEmit": true
}
}

View File

@@ -14,7 +14,7 @@
"prettier": "^2.8.2"
},
"scripts": {
"test": "bun scripts/run-bun-tests.ts",
"test": "node src/runner.node.mjs",
"test:ecosystem": "bun scripts/run-ecosystem-tests.ts"
}
}

View File

@@ -318,7 +318,7 @@ export function parseTest(stderr: string, options: ParseTestOptions = {}): Parse
};
};
const parseTestLine = (line: string): Test | undefined => {
const match = /^(✓|‚úì|✗|‚úó|-|✎) (.*)$/.exec(line);
const match = /^(✓|‚úì|✗|‚úó|»|-|✎) (.*)$/.exec(line);
if (!match) {
return undefined;
}
@@ -333,6 +333,7 @@ export function parseTest(stderr: string, options: ParseTestOptions = {}): Parse
case "‚úó":
status = "fail";
break;
case "»":
case "-":
status = "skip";
break;

View File

@@ -23,30 +23,6 @@ function* findTests(dir, query) {
}
}
function dump(buf) {
var offset = 0,
length = buf.byteLength;
while (offset < length) {
try {
const wrote = writeSync(1, buf);
offset += wrote;
if (offset < length) {
try {
fsyncSync(1);
} catch (e) {}
buf = buf.slice(wrote);
}
} catch (e) {
if (e.code === "EAGAIN") {
continue;
}
throw e;
}
}
}
var failingTests = [];
async function runTest(path) {
@@ -58,7 +34,7 @@ async function runTest(path) {
status: exitCode,
error: timedOut,
} = spawnSync("bun", ["test", path], {
stdio: ["ignore", "pipe", "pipe"],
stdio: "inherit",
timeout: 1000 * 60 * 3,
env: {
...process.env,
@@ -75,64 +51,8 @@ async function runTest(path) {
failingTests.push(name);
if (timedOut) console.error(timedOut);
}
if (isAction && !passed) {
findErrors(stdout);
findErrors(stderr);
}
if (isAction) {
const prefix = passed ? "PASS" : `FAIL`;
action.startGroup(`${prefix} - ${name}`);
}
stdout && stdout?.byteLength && dump(stdout);
stderr && stderr?.byteLength && dump(stderr);
if (isAction) {
action.endGroup();
}
}
function findErrors(data) {
const text = new StringDecoder().write(new Buffer(data.buffer)).replaceAll(/\u001b\[.*?m/g, "");
let index = 0;
do {
index = text.indexOf("error: ", index);
if (index === -1) {
break;
}
const messageEnd = text.indexOf("\n", index);
if (messageEnd === -1) {
break;
}
const message = text.slice(index + 7, messageEnd);
index = text.indexOf("at ", index);
if (index === -1) {
break;
}
const startAt = index;
index = text.indexOf("\n", index);
if (index === -1) {
break;
}
const at = text.slice(startAt + 3, index);
let file = at.slice(0, at.indexOf(":"));
if (file.length === 0) {
continue;
}
const startLine = at.slice(at.indexOf(":") + 1, at.indexOf(":") + 1 + at.slice(at.indexOf(":") + 1).indexOf(":"));
const startColumn = at.slice(at.indexOf(":") + 1 + at.slice(at.indexOf(":") + 1).indexOf(":") + 1);
if (file.startsWith("/")) {
file = relative(cwd, file);
}
action.error(message, { file, startLine, startColumn });
} while (index !== -1);
}
var tests = [];
var testFileNames = [];
for (const path of findTests(resolve(cwd, "test"))) {

View File

@@ -1,123 +0,0 @@
# bun-macro-relay
This lets you use Facebook's [Relay](https://github.com/facebook/relay) framework (GraphQL) with bun.
Specifically, this implements the bun equivalent of [`babel-plugin-relay`](https://github.com/facebook/relay/tree/main/packages/babel-plugin-relay). It parses `graphql` queries, but does not compile/save them to your artifacts directory, you still need [`relay-compiler`](https://github.com/facebook/relay/tree/main/packages/relay-compiler) for that.
## Installation
```
npm install -D bun-macro-relay
```
## Usage
With three lines in your project's `bunfig.toml`, `react-relay` works automatically with bun.
Add this to your `bunfig.toml`:
```toml
[macros]
react-relay = {graphql = "bun-macro-relay"}
relay-runtime = {graphql = "bun-macro-relay"}
```
This tells bun to automatically pretend every import statement to `react-relay` with a `graphql` import came from `macro:bun-macro-relay/bun-macro-relay.tsx`.
Effectively, it applies this diff in-memory so you can use `bun-macro-relay` without making other changes to your code:
```js
// bun will remap this import:
import { graphql } from "react-relay";
// To this:
import { graphql } from "macro:bun-macro-relay/bun-macro-relay.tsx";
```
You can still use the other imports from `react-relay`. It only affects the `graphql` export from `react-relay`.
```js
// bun will remap this import:
import { graphql, useFragment } from "react-relay";
// To this:
import { graphql } from "macro:bun-macro-relay/bun-macro-relay.tsx";
import { useFragment } from "react-relay";
```
Ultimately, the `graphql` import should no longer appear in transpiled output:
```js
import { useFragment } from "react-relay";
```
If you'd rather not modify your project's `package.json`, you can do this instead:
```js
import { graphql } from "macro:bun-macro-relay";
```
## Configuration
For performance reasons, `bun-macro-relay` does not read `relay-config`. That means your Relay configuration will _not_ be honored.
Fortunately, the only configuration option relevant to `bun-macro-relay` is modifying the artifacts directory (the directory where `relay-compiler` saves compiled `.graphql` files).
You can still change that with `bun-macro-relay`.
### Changing the artifacts directory
Pass the `BUN_MACRO_RELAY_ARTIFACT_DIRECTORY` environment variable to bun:
```bash
BUN_MACRO_RELAY_ARTIFACT_DIRECTORY="__generated__" bun
```
You can also save it in `.env`, `.env.local`, or `.env.dev`. The path should be relative to the directory containing the project's package.json without a leading `.` or `./`. You can also pass it an absolute path.
## What does `bun-macro-relay` actually do?
1. Parses GraphQL (using the same `graphql` npm package as babel-plugin-relay)
2. Injects an import to the correct compiled GraphQL file in the Relay artifacts directory
3. Replaces the use of the `graphql` template literal with the `default` import from the compiled GraphQL file.
Here's an example.
Input:
```tsx
import { graphql, useLazyLoadQuery } from "react-relay";
const Tweet = () => {
const data = useLazyLoadQuery(
graphql`
query TweetQuery {
...Tweet_tweet
}
`,
{}
);
if (!data.tweet) return null;
return <TweetComponent tweet={data.tweet} />;
};
```
Output:
```jsx
import TweetQuery from "../__generated__/TweetQuery.graphql.ts";
import { useLazyLoadQuery } from "react-relay";
const Tweet = () => {
const data = useLazyLoadQuery(TweetQuery, {});
if (!data.tweet) return null;
return <TweetComponent tweet={data.tweet} />;
};
```
bun automatically transpiles JSX & TypeScript, but that's not relevant to this example.
### What does `bun-macro-relay` not do?
1. This first version doesn't hash the contents of the `graphql` query, so it won't detect when the GraphQL query is out of sync with the compiled `.graphql` file in development. However, if you're running Relay's CLI, bun's hot module reloading will automatically update. As long as you run Relay's CLI, it shouldn't matter. This will be fixed eventually (have to expose a native MD5 hashing function)
2. Compile GraphQL. You still need to use `relay-compiler` for that.

View File

@@ -1,3 +0,0 @@
export class FooOperation {}
export default FooOperation;

View File

@@ -1,79 +0,0 @@
import { parse, print } from "graphql/index.js";
import { resolve } from "path";
//
// 1. Parse the GraphQL tag.
// 2. From the parsed GraphQL query, get the AST definition.
// 3. From the AST definition, inject an import to that file inside the artifact directory
// 4. (TODO) MD5 the printed source text
// 5. (TODO) At runtime, if md5 !== import.md5, then warn the user that the query has changed
// but the file hasn't been updated so it must be reloaded.
// 6. Replace the TemplateLiteral with the default identifier from the injected import
let artifactDirectory: string = `__generated__`;
const { RELAY_ARTIFACT_DIRECTORY, BUN_MACRO_RELAY_ARTIFACT_DIRECTORY } =
Bun.env;
if (RELAY_ARTIFACT_DIRECTORY) {
artifactDirectory = RELAY_ARTIFACT_DIRECTORY;
}
if (BUN_MACRO_RELAY_ARTIFACT_DIRECTORY) {
artifactDirectory = BUN_MACRO_RELAY_ARTIFACT_DIRECTORY;
}
artifactDirectory = resolve(artifactDirectory);
export function graphql(node) {
let query;
if (node instanceof <call />) {
query = node.arguments[0].toString();
} else if (node instanceof <template />) {
query = node.toString();
}
if (typeof query !== "string" || query.length === 0) {
throw new Error("BunMacroRelay: Unexpected empty graphql string.");
}
const ast = parse(query);
if (ast.definitions.length === 0) {
throw new Error("BunMacroRelay: Unexpected empty graphql tag.");
}
const definition = ast.definitions[0];
if (
definition.kind !== "FragmentDefinition" &&
definition.kind !== "OperationDefinition"
) {
throw new Error(
`BunMacroRelay: Expected a fragment, mutation, query, or subscription, got "${definition.kind}"`,
);
}
const graphqlDefinition = definition;
const definitionName = graphqlDefinition.name && graphqlDefinition.name.value;
if (!definitionName) {
throw new Error("GraphQL operations and fragments must contain names");
}
const identifiername = `${definitionName}_$gql`;
const importStmt = (
<import
default={identifiername}
path={`${artifactDirectory}/${definitionName}.graphql`}
/>
);
return (
<>
<inject>{importStmt}</inject>
<id to={importStmt.namespace[identifiername]} pure />
</>
);
}

Binary file not shown.

View File

@@ -1,15 +0,0 @@
{
"name": "bun-macro-relay",
"version": "0.1.2",
"module": "bun-macro-relay.tsx",
"license": "MIT",
"peerDependencies": {
"graphql": "^15.0.0"
},
"devDependencies": {
"graphql": "^15.6.0"
},
"files": [
"bun-macro-relay.tsx"
]
}

View File

@@ -1,11 +0,0 @@
import { graphql } from "react-relay";
export const Foo = () => {
const definition = graphql`
query FooOperation {
foo
}
`;
return <div>{definition.operation.name}</div>;
};

View File

@@ -1,6 +0,0 @@
{
"compilerOptions": {
"baseUrl": ".",
"paths": {}
}
}

View File

@@ -547,6 +547,7 @@ declare module "buffer" {
type: "Buffer";
data: number[];
};
/**
* Returns `true` if both `buf` and `otherBuffer` have exactly the same bytes,`false` otherwise. Equivalent to `buf.compare(otherBuffer) === 0`.
*

View File

@@ -33,7 +33,41 @@ declare module "bun:test" {
*/
export type Describe = {
(label: string, fn: () => void): void;
skip: (label: string, fn: () => void) => void;
/**
* Skips all other tests, except this group of tests.
*
* @param label the label for the tests
* @param fn the function that defines the tests
*/
only(label: string, fn: () => void): void;
/**
* Skips this group of tests.
*
* @param label the label for the tests
* @param fn the function that defines the tests
*/
skip(label: string, fn: () => void): void;
/**
* Marks this group of tests as to be written or to be fixed.
*
* @param label the label for the tests
* @param fn the function that defines the tests
*/
todo(label: string, fn?: () => void): void;
/**
* Runs this group of tests, only if `condition` is true.
*
* This is the opposite of `describe.skipIf()`.
*
* @param condition if these tests should run
*/
if(condition: boolean): (label: string, fn: () => void) => void;
/**
* Skips this group of tests, if `condition` is true.
*
* @param condition if these tests should be skipped
*/
skipIf(condition: boolean): (label: string, fn: () => void) => void;
};
/**
* Describes a group of related tests.
@@ -122,6 +156,31 @@ declare module "bun:test" {
| (() => void | Promise<unknown>)
| ((done: (err?: unknown) => void) => void),
): void;
export type TestOptions = {
/**
* Sets the timeout for the test in milliseconds.
*
* If the test does not complete within this time, the test will fail with:
* ```ts
* 'Timeout: test {name} timed out after 5000ms'
* ```
*
* @default 5000 // 5 seconds
*/
timeout?: number;
/**
* Sets the number of times to retry the test if it fails.
*
* @default 0
*/
retry?: number;
/**
* Sets the number of times to repeat the test, regardless of whether it passed or failed.
*
* @default 0
*/
repeats?: number;
};
/**
* Runs a test.
*
@@ -135,8 +194,13 @@ declare module "bun:test" {
* expect(response.ok).toBe(true);
* });
*
* test("can set a timeout", async () => {
* await Bun.sleep(100);
* }, 50); // or { timeout: 50 }
*
* @param label the label for the test
* @param fn the test function
* @param options the test timeout or options
*/
export type Test = {
(
@@ -145,45 +209,44 @@ declare module "bun:test" {
| (() => void | Promise<unknown>)
| ((done: (err?: unknown) => void) => void),
/**
* @default 300_000 milliseconds (5 minutes)
*
* After this many milliseconds, the test will fail with an error message like:
* ```ts
* 'Timeout: test "name" timed out after 300_000ms'
* ```
* - If a `number`, sets the timeout for the test in milliseconds.
* - If an `object`, sets the options for the test.
* - `timeout` sets the timeout for the test in milliseconds.
* - `retry` sets the number of times to retry the test if it fails.
* - `repeats` sets the number of times to repeat the test, regardless of whether it passed or failed.
*/
timeoutMs?: number,
options?: number | TestOptions,
): void;
/**
* Skips all other tests, except this test.
* @deprecated Not yet implemented.
*
* @param label the label for the test
* @param fn the test function
* @param timeoutMs the timeout for the test
* @param options the test timeout or options
*/
only(
label: string,
fn:
| (() => void | Promise<unknown>)
| ((done: (err?: unknown) => void) => void),
timeoutMs?: number,
options?: number | TestOptions,
): void;
/**
* Skips this test.
*
* @param label the label for the test
* @param fn the test function
* @param options the test timeout or options
*/
skip(
label: string,
fn:
| (() => void | Promise<unknown>)
| ((done: (err?: unknown) => void) => void),
timeoutMs?: number,
options?: number | TestOptions,
): void;
/**
* Indicate a test is yet to be written or implemented correctly.
* Marks this test as to be written or to be fixed.
*
* When a test function is passed, it will be marked as `todo` in the test results
* as long the test does not pass. When the test passes, the test will be marked as
@@ -192,13 +255,45 @@ declare module "bun:test" {
*
* @param label the label for the test
* @param fn the test function
* @param options the test timeout or options
*/
todo(
label: string,
fn?:
| (() => void | Promise<unknown>)
| ((done: (err?: unknown) => void) => void),
options?: number | TestOptions,
): void;
/**
* Runs this test, if `condition` is true.
*
* This is the opposite of `test.skipIf()`.
*
* @param condition if the test should run
*/
if(
condition: boolean,
): (
label: string,
fn:
| (() => void | Promise<unknown>)
| ((done: (err?: unknown) => void) => void),
options?: number | TestOptions,
) => void;
/**
* Skips this test, if `condition` is true.
*
* @param condition if the test should be skipped
*/
skipIf(
condition: boolean,
): (
label: string,
fn:
| (() => void | Promise<unknown>)
| ((done: (err?: unknown) => void) => void),
options?: number | TestOptions,
) => void;
};
/**
* Runs a test.
@@ -523,6 +618,170 @@ declare module "bun:test" {
* @param hint Hint used to identify the snapshot in the snapshot file.
*/
toMatchSnapshot(propertyMatchers?: Object, hint?: string): void;
/**
* Asserts that a value is empty.
*
* @example
* expect("").toBeEmpty();
* expect([]).toBeEmpty();
* expect({}).toBeEmpty();
* expect(new Set()).toBeEmpty();
*/
toBeEmpty(): void;
/**
* Asserts that a value is `null` or `undefined`.
*
* @example
* expect(null).toBeNil();
* expect(undefined).toBeNil();
*/
toBeNil(): void;
/**
* Asserts that a value is a `boolean`.
*
* @example
* expect(true).toBeBoolean();
* expect(false).toBeBoolean();
* expect(null).not.toBeBoolean();
* expect(0).not.toBeBoolean();
*/
toBeBoolean(): void;
/**
* Asserts that a value is `true`.
*
* @example
* expect(true).toBeTrue();
* expect(false).not.toBeTrue();
* expect(1).not.toBeTrue();
*/
toBeTrue(): void;
/**
* Asserts that a value is `false`.
*
* @example
* expect(false).toBeFalse();
* expect(true).not.toBeFalse();
* expect(0).not.toBeFalse();
*/
toBeFalse(): void;
/**
* Asserts that a value is a `number`.
*
* @example
* expect(1).toBeNumber();
* expect(3.14).toBeNumber();
* expect(NaN).toBeNumber();
* expect(BigInt(1)).not.toBeNumber();
*/
toBeNumber(): void;
/**
* Asserts that a value is a `number`, and is an integer.
*
* @example
* expect(1).toBeInteger();
* expect(3.14).not.toBeInteger();
* expect(NaN).not.toBeInteger();
*/
toBeInteger(): void;
/**
* Asserts that a value is a `number`, and is not `NaN` or `Infinity`.
*
* @example
* expect(1).toBeFinite();
* expect(3.14).toBeFinite();
* expect(NaN).not.toBeFinite();
* expect(Infinity).not.toBeFinite();
*/
toBeFinite(): void;
/**
* Asserts that a value is a positive `number`.
*
* @example
* expect(1).toBePositive();
* expect(-3.14).not.toBePositive();
* expect(NaN).not.toBePositive();
*/
toBePositive(): void;
/**
* Asserts that a value is a negative `number`.
*
* @example
* expect(-3.14).toBeNegative();
* expect(1).not.toBeNegative();
* expect(NaN).not.toBeNegative();
*/
toBeNegative(): void;
/**
* Asserts that a value is a number between a start and end value.
*
* @param start the start number (inclusive)
* @param end the end number (exclusive)
*/
toBeWithin(start: number, end: number): void;
/**
* Asserts that a value is a `symbol`.
*
* @example
* expect(Symbol("foo")).toBeSymbol();
* expect("foo").not.toBeSymbol();
*/
toBeSymbol(): void;
/**
* Asserts that a value is a `function`.
*
* @example
* expect(() => {}).toBeFunction();
*/
toBeFunction(): void;
/**
* Asserts that a value is a `Date` object.
*
* To check if a date is valid, use `toBeValidDate()` instead.
*
* @example
* expect(new Date()).toBeDate();
* expect(new Date(null)).toBeDate();
* expect("2020-03-01").not.toBeDate();
*/
toBeDate(): void;
/**
* Asserts that a value is a valid `Date` object.
*
* @example
* expect(new Date()).toBeValidDate();
* expect(new Date(null)).not.toBeValidDate();
* expect("2020-03-01").not.toBeValidDate();
*/
toBeValidDate(): void;
/**
* Asserts that a value is a `string`.
*
* @example
* expect("foo").toBeString();
* expect(new String("bar")).toBeString();
* expect(123).not.toBeString();
*/
toBeString(): void;
/**
* Asserts that a value includes a `string`.
*
* For non-string values, use `toContain()` instead.
*
* @param expected the expected substring
*/
toInclude(expected: string): void;
/**
* Asserts that a value starts with a `string`.
*
* @param expected the string to start with
*/
toStartWith(expected: string): void;
/**
* Asserts that a value ends with a `string`.
*
* @param expected the string to end with
*/
toEndWith(expected: string): void;
};
}

View File

@@ -2,27 +2,6 @@ interface VoidFunction {
(): void;
}
declare namespace Bun {
interface Env extends Dict<string> {
NODE_ENV: string;
/**
* The timezone used by Intl, Date, etc.
*
* To change the timezone, set `Bun.env.TZ` or `process.env.TZ` to the time zone you want to use.
*
* You can view the current timezone with `Intl.DateTimeFormat().resolvedOptions().timeZone`
*
* @example
* ```js
* Bun.env.TZ = "America/Los_Angeles";
* console.log(Intl.DateTimeFormat().resolvedOptions().timeZone); // "America/Los_Angeles"
* ```
*/
TZ?: string;
}
}
/**
*
* Bun.js runtime APIs
@@ -43,6 +22,26 @@ declare namespace Bun {
declare module "bun" {
type ArrayBufferView = TypedArray | DataView;
import { Encoding as CryptoEncoding } from "crypto";
export interface Env extends Dict<string>, NodeJS.ProcessEnv {
NODE_ENV: string;
/**
* The timezone used by Intl, Date, etc.
*
* To change the timezone, set `Bun.env.TZ` or `process.env.TZ` to the time zone you want to use.
*
* You can view the current timezone with `Intl.DateTimeFormat().resolvedOptions().timeZone`
*
* @example
* ```js
* Bun.env.TZ = "America/Los_Angeles";
* console.log(Intl.DateTimeFormat().resolvedOptions().timeZone); // "America/Los_Angeles"
* ```
*/
TZ?: string;
}
/**
* The environment variables of the process
*
@@ -51,7 +50,7 @@ declare module "bun" {
* Changes to `process.env` at runtime won't automatically be reflected in the default value. For that, you can pass `process.env` explicitly.
*
*/
export const env: Bun.Env;
export const env: Env;
export const origin: string;
/**
@@ -282,7 +281,7 @@ declare module "bun" {
* @returns A promise that resolves with the concatenated chunks or the concatenated chunks as an `ArrayBuffer`.
*/
export function readableStreamToArrayBuffer(
stream: ReadableStream,
stream: ReadableStream<ArrayBufferView | ArrayBufferLike>,
): Promise<ArrayBuffer> | ArrayBuffer;
/**
@@ -323,7 +322,7 @@ declare module "bun" {
*
*/
export function readableStreamToArray<T>(
stream: ReadableStream,
stream: ReadableStream<T>,
): Promise<T[]> | T[];
/**
@@ -667,6 +666,10 @@ declare module "bun" {
* A UNIX timestamp indicating when the file was last modified.
*/
lastModified: number;
/**
* The name or path of the file, as specified in the constructor.
*/
name?: number;
}
/**
@@ -1668,7 +1671,7 @@ declare module "bun" {
*
* @deprecated since v0.6.3 - Use `key: Bun.file(path)` instead.
*/
keyFile: string;
keyFile?: string;
/**
* File path to a TLS certificate
*
@@ -1676,7 +1679,7 @@ declare module "bun" {
*
* @deprecated since v0.6.3 - Use `cert: Bun.file(path)` instead.
*/
certFile: string;
certFile?: string;
/**
* Passphrase for the TLS key
@@ -1824,7 +1827,7 @@ declare module "bun" {
* consistently in all cases and it doesn't yet call the `error` handler
* consistently. This needs to be fixed
*/
fetch(request: Request): Response | Promise<Response>;
fetch(request: Request | string): Response | Promise<Response>;
/**
* Upgrade a {@link Request} to a {@link ServerWebSocket}
@@ -2761,7 +2764,7 @@ declare module "bun" {
* ```
*/
namespace?: string;
external: boolean;
external?: boolean;
}
type OnResolveCallback = (

View File

@@ -704,7 +704,7 @@ declare module "child_process" {
uid?: number | undefined;
gid?: number | undefined;
cwd?: string | URL | undefined;
env?: Partial<Bun.Env> | undefined;
env?: Partial<import("bun").Env> | undefined;
}
interface CommonOptions extends ProcessEnvOptions {
/**

View File

@@ -187,6 +187,7 @@ declare namespace NodeJS {
(id: string): any;
resolve: RequireResolve;
}
interface ProcessEnv {}
type Signals =
| "SIGABRT"
| "SIGALRM"
@@ -367,7 +368,7 @@ interface Process {
platform: Platform;
argv: string[];
execArgv: string[];
env: Bun.Env;
env: import("bun").Env;
/** Whether you are using Bun */
isBun: 1; // FIXME: this should actually return a boolean
@@ -2388,6 +2389,9 @@ interface UnderlyingSource<R = any> {
cancel?: UnderlyingSourceCancelCallback;
pull?: UnderlyingSourcePullCallback<R>;
start?: UnderlyingSourceStartCallback<R>;
/**
* Mode "bytes" is not currently supported.
*/
type?: undefined;
}

View File

@@ -1,22 +1,24 @@
import { expectType } from "tsd";
declare module "bun" {
export interface Env {
FOO: "FOO";
}
}
declare global {
namespace Bun {
interface Env {
WHATEVER: "WHATEVER";
namespace NodeJS {
interface ProcessEnv {
BAR: "BAR";
}
}
}
expectType<"WHATEVER">(process.env.WHATEVER);
expectType<"FOO">(process.env.FOO);
expectType<"BAR">(process.env.BAR);
export {};
new Bun.Transpiler({
macro: {
"react-relay": {
graphql: "bun-macro-relay/bun-macro-relay.tsx",
},
},
});
Event;
process.env.FOO;
process.env.BAR;
process.env.OTHER;
Bun.env.FOO;
Bun.env.BAR;

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