Compare commits

...

58 Commits

Author SHA1 Message Date
Ashcon Partovi
2c68e26d8d Fix double quotes in console.log messages 2023-08-25 11:53:21 -07:00
Ashcon Partovi
0731dfa0a4 Fix build 2023-08-25 11:31:47 -07:00
Ashcon Partovi
9e2ddb936e Prepare for extension 2 2023-08-24 22:24:28 -07:00
Ashcon Partovi
dacd00d708 Improve preview code 2023-08-24 20:11:20 -07:00
Ashcon Partovi
a068e9c8db watch mode with dap 2023-08-24 20:11:20 -07:00
Ashcon Partovi
936f112b6a Prepare for extension 2023-08-24 20:11:20 -07:00
Ashcon Partovi
627de5b29d More source map fixes 2023-08-24 20:11:20 -07:00
Ashcon Partovi
526f7d8541 Fix source maps partly 2023-08-24 20:11:20 -07:00
Ashcon Partovi
df6cca0fc2 Fix console.log formatting 2023-08-24 20:11:20 -07:00
Ashcon Partovi
e1c00ebaaf Check bun version if it fails 2023-08-24 20:11:20 -07:00
Ashcon Partovi
6313655c8e Make source maps safer if exception occurs 2023-08-24 20:11:20 -07:00
Ashcon Partovi
f0ebdfff74 More fixes 2023-08-24 20:11:20 -07:00
Jarred Sumner
c4a2209841 Update package.json 2023-08-24 20:11:20 -07:00
Jarred Sumner
cc6065583e wip 2023-08-24 20:11:20 -07:00
Jarred Sumner
87636ca02a add some package.jsons 2023-08-24 20:11:20 -07:00
Ashcon Partovi
40aa26ad7e Source map support 2023-08-24 20:11:20 -07:00
Ashcon Partovi
1881c51509 Start of source map support, breakpoints work 2023-08-24 20:11:20 -07:00
Ashcon Partovi
1cddf16076 Prepare for source maps 2023-08-24 20:11:20 -07:00
Ashcon Partovi
eb27f18c2e Fix attaching 2023-08-24 20:11:20 -07:00
Ashcon Partovi
26f5869cdb More improvements, fix formatting in debug console 2023-08-24 20:11:20 -07:00
Ashcon Partovi
5dc79f1924 Improve support for \debug-adapter-protocol\ 2023-08-24 20:11:19 -07:00
Ashcon Partovi
2bcbafe7d3 Fix debugger not updating after reload with --hot 2023-08-24 20:09:32 -07:00
Colin McDonnell
f7f734788c Update tsconfig.base.json 2023-08-24 19:54:03 -07:00
Colin McDonnell
2cd1d59387 Update toml import type test 2023-08-24 19:52:53 -07:00
Colin McDonnell
b70210a005 Use noEmit 2023-08-24 19:51:14 -07:00
Code Hz
b9c2309c8a Remove conflict option in tsconfig-for-init.json (#4284)
fix https://github.com/oven-sh/bun/issues/4283
2023-08-24 19:41:23 -07:00
Jarred Sumner
43c4da8c9a Update build-id 2023-08-24 19:39:54 -07:00
Jarred Sumner
8a48e8bb0b Report extra memory more (#4289)
* Report memory allocated in fetch

* Memory size reporting to `Headers`

* Fixup memory reporting allocator

* Make these tests do more

* cleanup some of this

---------

Co-authored-by: Jarred Sumner <709451+Jarred-Sumner@users.noreply.github.com>
2023-08-24 19:39:00 -07:00
Dylan Conway
097ae4e982 fix build 2023-08-24 19:05:40 -07:00
Alex Lam S.L
213f5bef9d [install] fix stale life-cycle scripts from lockfile (#4307)
fixes #4269
2023-08-24 17:18:51 -07:00
Alex Lam S.L
e115638cba [install] fix crash when installing package that uses loose semver pre-release (#4302)
- also fix parsing of `1.2.3pre+build`

fixes #4266
2023-08-24 17:17:48 -07:00
Ai Hoshino
6e57556fad Fix(node:http): fix URL formatting when using a proxy. (#4297)
Close: #4295
2023-08-24 17:17:18 -07:00
Ai Hoshino
339d2c7f19 Make the server not crash if an error occurs in dev build. (#4300)
Close: #4298
2023-08-24 17:16:51 -07:00
Jarred Sumner
d2bef4fbea Don't inline require/import errors at runtime (#4306)
Co-authored-by: Jarred Sumner <709451+Jarred-Sumner@users.noreply.github.com>
2023-08-24 17:03:05 -07:00
Jason
19aa9d93de update zig to 0.11.0 (#4233)
* WIP

* backup

* more change

* json related error sovled

* number related issue solved

* revert WriterType changed before

* destroy -> free

* jsonStringify related issues solved

* fix mem.free expected []T or *[_]T, passed [*]const u8

* fix expected []T or *[_]T, passed [*:0]const u8

* fix build script

* fix build script, for real

* replace 0.11.0-dev.4006+bf827d0b5 to 0.12.0-dev.161+6a5463951

* fix build on macOS, COPYFILE.DATA -> COPYFILE_DATA

* fix the last destroy on [*]ptr issue

---------

Co-authored-by: Dylan Conway <35280289+dylan-conway@users.noreply.github.com>
2023-08-24 16:13:14 -07:00
jhmaster
55eb4ffe8f Update bun-polyfills & bun-wasm (#4246)
* automate Bun.version & revision polyfills

* polyfill Bun.gc

* bun:jsc module initial polyfills

* update peechy schema

* bun-polyfills: fix some project configs

* bun-wasm: lots of fixes

* bun-polyfills: Bun.Transpiler impl.

* revision hash update
2023-08-24 14:39:28 -07:00
Jarred Sumner
a051a6f620 Fix performance regression in reading from the request body (#4291)
Co-authored-by: Jarred Sumner <709451+Jarred-Sumner@users.noreply.github.com>
2023-08-24 14:36:39 -07:00
Jarred Sumner
9c68abdb8d wip (#4282)
Co-authored-by: Jarred Sumner <709451+Jarred-Sumner@users.noreply.github.com>
2023-08-24 01:32:22 -07:00
Jarred Sumner
ad326b7734 [Inspector] Fix bug with sourcemaps including internal metadata bytes 2023-08-23 22:22:55 -07:00
Colin McDonnell
aa08c35c06 Add Debugger docs and a couple guides (#4281)
* Add debugger docs. Add guides.

* Add files
2023-08-23 18:15:21 -07:00
dave caruso
20d42dfaa3 Fix <const T>() => (#4278) 2023-08-23 17:22:54 -07:00
VietnamecDevelopment
3556fa3b1e Update globals.d.ts (#4276) 2023-08-23 16:25:05 -07:00
Code Hz
5e07fd4fbc Fix ffi type (#4265)
* add readonly so it works with as const

* split ffi type infer to args and returns

* add JSCallback to FFITypeToArgsType

* add read functions

* simplify FFITypeOrString

* fix cstring type

* fix pointer type test

* fix readonly

* add unknown handling

* trigger action

* use Parameters

* add read type test

* add other read function to tests
2023-08-23 15:56:46 -07:00
Jarred Sumner
c60385716b Bunch of streams fixes (#4251)
* Update WebKit

* Don't do async hooks things when async hooks are not enabled

* Smarter scheduling of event loop tasks with the http server

* less exciting approach

* Bump WebKit

* Another approach

* Fix body-stream tests

* Fixes #1886

* Fix UAF in fetch body streaming

* Missing from commit

* Fix leak

* Fix the other leak

* Fix test

* Fix crash

* missing duperef

* Make this code clearer

* Ignore empty chunks

* Fixes #3969

* Delete flaky test

* Update bun-linux-build.yml

* Fix memory issue

* fix result body, and .done status before the last callback, dont touch headers after sent once

* refactor HTTPClientResult

* less flasky corrupted test

* oops

* fix mutex invalid state

* fix onProgressUpdate deinit/unlock

* fix onProgressUpdate deinit/unlock

* oops

* remove verbose

* fix posible null use

* avoid http null

* metadata can still be used onReject after toResponse

* dont leak task.http

* fix flask tests

* less flask close tests

---------

Co-authored-by: Jarred Sumner <709451+Jarred-Sumner@users.noreply.github.com>
Co-authored-by: cirospaciari <ciro.spaciari@gmail.com>
2023-08-23 14:05:05 -07:00
Quentin
f3266ff436 docs: remove broken DNS link which is also not present in the official docs (#4268) 2023-08-23 10:36:26 -07:00
xxxhussein
b01764b31e Fix more types. (#4273) 2023-08-23 10:35:51 -07:00
Jozef Steinhübl
851763174e ask for bun --revision instead bun -v (#4256) 2023-08-23 00:42:33 -07:00
dave caruso
8518fbb573 fix yield (#4264) 2023-08-22 21:54:59 -07:00
Colin McDonnell
d86084dd8e Fix Bun.inspect types 2023-08-22 14:28:01 -07:00
dave caruso
52802a4c55 fix fsevents and stub for qwikcity (#4247)
* fix test

* ok

* cm

* EE

* remove the hack we didnt need
2023-08-21 23:39:56 -07:00
Dylan Conway
44e4d5852a fix stdin stream unref and resuming (#4250)
* fix stream unref and resuming stream

* fix `child-process-stdio` test
2023-08-21 23:39:41 -07:00
Colin McDonnell
3a45f2c71b Docs and types for v0.8.0 (#4199)
* Improve test documentation

* Update nodejs compat docs with tty

* Add debugger guide

* Document Bun.inspect.custom, improve bun test nav

* Address reviews

* Update Bun.file types

* Add Nuxt guide

* Add tty types
2023-08-21 21:34:03 -07:00
Jarred Sumner
9eeb7bdbff Bun v0.8 2023-08-21 21:26:45 -07:00
Jarred Sumner
ed14b64e65 Make the code generator less duplicative 2023-08-21 21:24:49 -07:00
Jarred Sumner
bca1bcf29c import errors have code set to ERR_MODULE_NOT_FOUND and require errors have code set to MODULE_NOT_FOUND (#4244)
* ResolveMessage

* Fix it

---------

Co-authored-by: Jarred Sumner <709451+Jarred-Sumner@users.noreply.github.com>
2023-08-21 21:11:27 -07:00
Ciro Spaciari
9027484ae1 fetch(stream) add stream support for compressed and uncompressed data (#4127)
* streams non compressed data in 64kb chunks (at least)

* fmt

* wip remove pause

* fix default streaming and buffering

* fix atomic lags

* fix size

* make chunked encoding work again (WIP streaming chunked)

* WIP: chunked encoding streaming

* fix end of streamings

* working streaming + compression

* add fixes + tests

* fmt + fix proxy

* fix oopsies

* codegen after merge

* fmt + fixes

* more fixes

* more fixes and logs

* avoid double free

* check empty before pop

* check empty on pop

* fix copy to real when complete

* remove unnecessary logs

* better has_schedule_callback swap, body locked size helper, remove isEmpty from unbounded_queue pop

* fix response ref, fix body_size

* add deflate support, fix error throw, add more tests

* codegen after merge

* remove logs, add connection close test

* fix macOS build

* fix redirect error option

* make body_size more clear

* support new Reponse(response)

* toString DOMWrapper objects properly instead of supporting response in Response constructor

* ignore headers with no name, add more tests

* oops

* handle transform with fetch

* add gz image stream test

* remove duplicate test

* fix missing chunk on macOS under pressure

* oops include all OS

* some fixes

* compare buffers instead of sizes

* refactor err.err and protect it
2023-08-21 20:30:34 -07:00
dave caruso
91eacade97 Fix inquirer (#4245) 2023-08-21 19:32:41 -07:00
Jarred Sumner
6a02edef5d Update module_loader.zig 2023-08-21 19:04:18 -07:00
230 changed files with 53998 additions and 10760 deletions

View File

@@ -14,7 +14,7 @@ body:
- type: input
attributes:
label: What version of Bun is running?
description: Copy the output of `bun -v`
description: Copy the output of `bun --revision`
- type: input
attributes:
label: What platform is your computer?

View File

@@ -36,7 +36,7 @@ jobs:
arch: aarch64
build_arch: arm64
runner: linux-arm64
webkit_url: "https://github.com/oven-sh/WebKit/releases/download/2023-aug3-4/bun-webkit-linux-arm64-lto.tar.gz"
webkit_url: "https://github.com/oven-sh/WebKit/releases/download/2023-aug3-5/bun-webkit-linux-arm64-lto.tar.gz"
webkit_basename: "bun-webkit-linux-arm64-lto"
build_machine_arch: aarch64

View File

@@ -46,7 +46,7 @@ jobs:
arch: x86_64
build_arch: amd64
runner: big-ubuntu
webkit_url: "https://github.com/oven-sh/WebKit/releases/download/2023-aug3-4/bun-webkit-linux-amd64-lto.tar.gz"
webkit_url: "https://github.com/oven-sh/WebKit/releases/download/2023-aug3-5/bun-webkit-linux-amd64-lto.tar.gz"
webkit_basename: "bun-webkit-linux-amd64-lto"
build_machine_arch: x86_64
- cpu: nehalem
@@ -54,7 +54,7 @@ jobs:
arch: x86_64
build_arch: amd64
runner: big-ubuntu
webkit_url: "https://github.com/oven-sh/WebKit/releases/download/2023-aug3-4/bun-webkit-linux-amd64-lto.tar.gz"
webkit_url: "https://github.com/oven-sh/WebKit/releases/download/2023-aug3-5/bun-webkit-linux-amd64-lto.tar.gz"
webkit_basename: "bun-webkit-linux-amd64-lto"
build_machine_arch: x86_64

View File

@@ -117,7 +117,7 @@ jobs:
# obj: bun-obj-darwin-x64-baseline
# runner: macos-11
# artifact: bun-obj-darwin-x64-baseline
# webkit_url: "https://github.com/oven-sh/WebKit/releases/download/2023-aug3-4/bun-webkit-macos-amd64-lto.tar.gz"
# webkit_url: "https://github.com/oven-sh/WebKit/releases/download/2023-aug3-5/bun-webkit-macos-amd64-lto.tar.gz"
# dependencies: true
# compile_obj: false
# - cpu: haswell
@@ -126,7 +126,7 @@ jobs:
# obj: bun-obj-darwin-x64
# runner: macos-11
# artifact: bun-obj-darwin-x64
# webkit_url: "https://github.com/oven-sh/WebKit/releases/download/2023-aug3-4/bun-webkit-macos-amd64-lto.tar.gz"
# webkit_url: "https://github.com/oven-sh/WebKit/releases/download/2023-aug3-5/bun-webkit-macos-amd64-lto.tar.gz"
# dependencies: true
# compile_obj: false
# - cpu: nehalem
@@ -135,7 +135,7 @@ jobs:
# obj: bun-obj-darwin-x64-baseline
# runner: macos-11
# artifact: bun-obj-darwin-x64-baseline
# webkit_url: "https://github.com/oven-sh/WebKit/releases/download/2023-aug3-4/bun-webkit-macos-amd64-lto.tar.gz"
# webkit_url: "https://github.com/oven-sh/WebKit/releases/download/2023-aug3-5/bun-webkit-macos-amd64-lto.tar.gz"
# dependencies: false
# compile_obj: true
# - cpu: haswell
@@ -144,7 +144,7 @@ jobs:
# obj: bun-obj-darwin-x64
# runner: macos-11
# artifact: bun-obj-darwin-x64
# webkit_url: "https://github.com/oven-sh/WebKit/releases/download/2023-aug3-4/bun-webkit-macos-amd64-lto.tar.gz"
# webkit_url: "https://github.com/oven-sh/WebKit/releases/download/2023-aug3-5/bun-webkit-macos-amd64-lto.tar.gz"
# dependencies: false
# compile_obj: true
- cpu: native
@@ -152,7 +152,7 @@ jobs:
tag: bun-darwin-aarch64
obj: bun-obj-darwin-aarch64
artifact: bun-obj-darwin-aarch64
webkit_url: "https://github.com/oven-sh/WebKit/releases/download/2023-aug3-4/bun-webkit-macos-arm64-lto.tar.gz"
webkit_url: "https://github.com/oven-sh/WebKit/releases/download/2023-aug3-5/bun-webkit-macos-arm64-lto.tar.gz"
runner: macos-arm64
dependencies: true
compile_obj: true
@@ -257,7 +257,7 @@ jobs:
# package: bun-darwin-x64
# runner: macos-11
# artifact: bun-obj-darwin-x64-baseline
# webkit_url: "https://github.com/oven-sh/WebKit/releases/download/2023-aug3-4/bun-webkit-macos-amd64-lto.tar.gz"
# webkit_url: "https://github.com/oven-sh/WebKit/releases/download/2023-aug3-5/bun-webkit-macos-amd64-lto.tar.gz"
# - cpu: haswell
# arch: x86_64
# tag: bun-darwin-x64
@@ -265,14 +265,14 @@ jobs:
# package: bun-darwin-x64
# runner: macos-11
# artifact: bun-obj-darwin-x64
# webkit_url: "https://github.com/oven-sh/WebKit/releases/download/2023-aug3-4/bun-webkit-macos-amd64-lto.tar.gz"
# webkit_url: "https://github.com/oven-sh/WebKit/releases/download/2023-aug3-5/bun-webkit-macos-amd64-lto.tar.gz"
- cpu: native
arch: aarch64
tag: bun-darwin-aarch64
obj: bun-obj-darwin-aarch64
package: bun-darwin-aarch64
artifact: bun-obj-darwin-aarch64
webkit_url: "https://github.com/oven-sh/WebKit/releases/download/2023-aug3-4/bun-webkit-macos-arm64-lto.tar.gz"
webkit_url: "https://github.com/oven-sh/WebKit/releases/download/2023-aug3-5/bun-webkit-macos-arm64-lto.tar.gz"
runner: macos-arm64
steps:
- uses: actions/checkout@v3

View File

@@ -117,7 +117,7 @@ jobs:
obj: bun-obj-darwin-x64-baseline
runner: macos-11
artifact: bun-obj-darwin-x64-baseline
webkit_url: "https://github.com/oven-sh/WebKit/releases/download/2023-aug3-4/bun-webkit-macos-amd64-lto.tar.gz"
webkit_url: "https://github.com/oven-sh/WebKit/releases/download/2023-aug3-5/bun-webkit-macos-amd64-lto.tar.gz"
dependencies: true
compile_obj: false
# - cpu: haswell
@@ -126,7 +126,7 @@ jobs:
# obj: bun-obj-darwin-x64
# runner: macos-11
# artifact: bun-obj-darwin-x64
# webkit_url: "https://github.com/oven-sh/WebKit/releases/download/2023-aug3-4/bun-webkit-macos-amd64-lto.tar.gz"
# webkit_url: "https://github.com/oven-sh/WebKit/releases/download/2023-aug3-5/bun-webkit-macos-amd64-lto.tar.gz"
# dependencies: true
# compile_obj: false
- cpu: nehalem
@@ -135,7 +135,7 @@ jobs:
obj: bun-obj-darwin-x64-baseline
runner: macos-11
artifact: bun-obj-darwin-x64-baseline
webkit_url: "https://github.com/oven-sh/WebKit/releases/download/2023-aug3-4/bun-webkit-macos-amd64-lto.tar.gz"
webkit_url: "https://github.com/oven-sh/WebKit/releases/download/2023-aug3-5/bun-webkit-macos-amd64-lto.tar.gz"
dependencies: false
compile_obj: true
# - cpu: haswell
@@ -144,7 +144,7 @@ jobs:
# obj: bun-obj-darwin-x64
# runner: macos-11
# artifact: bun-obj-darwin-x64
# webkit_url: "https://github.com/oven-sh/WebKit/releases/download/2023-aug3-4/bun-webkit-macos-amd64-lto.tar.gz"
# webkit_url: "https://github.com/oven-sh/WebKit/releases/download/2023-aug3-5/bun-webkit-macos-amd64-lto.tar.gz"
# dependencies: false
# compile_obj: true
# - cpu: native
@@ -152,7 +152,7 @@ jobs:
# tag: bun-darwin-aarch64
# obj: bun-obj-darwin-aarch64
# artifact: bun-obj-darwin-aarch64
# webkit_url: "https://github.com/oven-sh/WebKit/releases/download/2023-aug3-4/bun-webkit-macos-amd64-lto.tar.gz"
# webkit_url: "https://github.com/oven-sh/WebKit/releases/download/2023-aug3-5/bun-webkit-macos-amd64-lto.tar.gz"
# runner: macos-arm64
# dependencies: true
# compile_obj: true
@@ -258,7 +258,7 @@ jobs:
package: bun-darwin-x64
runner: macos-11
artifact: bun-obj-darwin-x64-baseline
webkit_url: "https://github.com/oven-sh/WebKit/releases/download/2023-aug3-4/bun-webkit-macos-amd64-lto.tar.gz"
webkit_url: "https://github.com/oven-sh/WebKit/releases/download/2023-aug3-5/bun-webkit-macos-amd64-lto.tar.gz"
# - cpu: haswell
# arch: x86_64
# tag: bun-darwin-x64
@@ -266,14 +266,14 @@ jobs:
# package: bun-darwin-x64
# runner: macos-11
# artifact: bun-obj-darwin-x64
# webkit_url: "https://github.com/oven-sh/WebKit/releases/download/2023-aug3-4/bun-webkit-macos-amd64-lto.tar.gz"
# webkit_url: "https://github.com/oven-sh/WebKit/releases/download/2023-aug3-5/bun-webkit-macos-amd64-lto.tar.gz"
# - cpu: native
# arch: aarch64
# tag: bun-darwin-aarch64
# obj: bun-obj-darwin-aarch64
# package: bun-darwin-aarch64
# artifact: bun-obj-darwin-aarch64
# webkit_url: "https://github.com/oven-sh/WebKit/releases/download/2023-aug3-4/bun-webkit-macos-amd64-lto.tar.gz"
# webkit_url: "https://github.com/oven-sh/WebKit/releases/download/2023-aug3-5/bun-webkit-macos-amd64-lto.tar.gz"
# runner: macos-arm64
steps:
- uses: actions/checkout@v3

View File

@@ -117,7 +117,7 @@ jobs:
# obj: bun-obj-darwin-x64-baseline
# runner: macos-11
# artifact: bun-obj-darwin-x64-baseline
# webkit_url: "https://github.com/oven-sh/WebKit/releases/download/2023-aug3-4/bun-webkit-macos-amd64-lto.tar.gz"
# webkit_url: "https://github.com/oven-sh/WebKit/releases/download/2023-aug3-5/bun-webkit-macos-amd64-lto.tar.gz"
# dependencies: true
# compile_obj: false
- cpu: haswell
@@ -126,7 +126,7 @@ jobs:
obj: bun-obj-darwin-x64
runner: macos-11
artifact: bun-obj-darwin-x64
webkit_url: "https://github.com/oven-sh/WebKit/releases/download/2023-aug3-4/bun-webkit-macos-amd64-lto.tar.gz"
webkit_url: "https://github.com/oven-sh/WebKit/releases/download/2023-aug3-5/bun-webkit-macos-amd64-lto.tar.gz"
dependencies: true
compile_obj: false
# - cpu: nehalem
@@ -135,7 +135,7 @@ jobs:
# obj: bun-obj-darwin-x64-baseline
# runner: macos-11
# artifact: bun-obj-darwin-x64-baseline
# webkit_url: "https://github.com/oven-sh/WebKit/releases/download/2023-aug3-4/bun-webkit-macos-amd64-lto.tar.gz"
# webkit_url: "https://github.com/oven-sh/WebKit/releases/download/2023-aug3-5/bun-webkit-macos-amd64-lto.tar.gz"
# dependencies: false
# compile_obj: true
- cpu: haswell
@@ -144,7 +144,7 @@ jobs:
obj: bun-obj-darwin-x64
runner: macos-11
artifact: bun-obj-darwin-x64
webkit_url: "https://github.com/oven-sh/WebKit/releases/download/2023-aug3-4/bun-webkit-macos-amd64-lto.tar.gz"
webkit_url: "https://github.com/oven-sh/WebKit/releases/download/2023-aug3-5/bun-webkit-macos-amd64-lto.tar.gz"
dependencies: false
compile_obj: true
# - cpu: native
@@ -152,7 +152,7 @@ jobs:
# tag: bun-darwin-aarch64
# obj: bun-obj-darwin-aarch64
# artifact: bun-obj-darwin-aarch64
# webkit_url: "https://github.com/oven-sh/WebKit/releases/download/2023-aug3-4/bun-webkit-macos-arm64-lto.tar.gz"
# webkit_url: "https://github.com/oven-sh/WebKit/releases/download/2023-aug3-5/bun-webkit-macos-arm64-lto.tar.gz"
# runner: macos-arm64
# dependencies: true
# compile_obj: true
@@ -260,7 +260,7 @@ jobs:
# package: bun-darwin-x64
# runner: macos-11
# artifact: bun-obj-darwin-x64-baseline
# webkit_url: "https://github.com/oven-sh/WebKit/releases/download/2023-aug3-4/bun-webkit-macos-amd64-lto.tar.gz"
# webkit_url: "https://github.com/oven-sh/WebKit/releases/download/2023-aug3-5/bun-webkit-macos-amd64-lto.tar.gz"
- cpu: haswell
arch: x86_64
tag: bun-darwin-x64
@@ -268,14 +268,14 @@ jobs:
package: bun-darwin-x64
runner: macos-11
artifact: bun-obj-darwin-x64
webkit_url: "https://github.com/oven-sh/WebKit/releases/download/2023-aug3-4/bun-webkit-macos-amd64-lto.tar.gz"
webkit_url: "https://github.com/oven-sh/WebKit/releases/download/2023-aug3-5/bun-webkit-macos-amd64-lto.tar.gz"
# - cpu: native
# arch: aarch64
# tag: bun-darwin-aarch64
# obj: bun-obj-darwin-aarch64
# package: bun-darwin-aarch64
# artifact: bun-obj-darwin-aarch64
# webkit_url: "https://github.com/oven-sh/WebKit/releases/download/2023-aug3-4/bun-webkit-macos-arm64-lto.tar.gz"
# webkit_url: "https://github.com/oven-sh/WebKit/releases/download/2023-aug3-5/bun-webkit-macos-arm64-lto.tar.gz"
# runner: macos-arm64
steps:
- uses: actions/checkout@v3

View File

@@ -1,7 +1,7 @@
name: zig-fmt
env:
ZIG_VERSION: 0.11.0-dev.4006+bf827d0b5
ZIG_VERSION: 0.12.0-dev.163+6780a6bbf
on:
pull_request:

3
.gitignore vendored
View File

@@ -130,3 +130,6 @@ src/js/out/tmp
src/js/out/DebugPath.h
make-dev-stats.csv
.uuid
tsconfig.tsbuildinfo

View File

@@ -10,9 +10,9 @@ ARG ARCH=x86_64
ARG BUILD_MACHINE_ARCH=x86_64
ARG TRIPLET=${ARCH}-linux-gnu
ARG BUILDARCH=amd64
ARG WEBKIT_TAG=2023-aug3-4
ARG WEBKIT_TAG=2023-aug3-5
ARG ZIG_TAG=jul1
ARG ZIG_VERSION="0.11.0-dev.4006+bf827d0b5"
ARG ZIG_VERSION="0.12.0-dev.163+6780a6bbf"
ARG WEBKIT_BASENAME="bun-webkit-linux-$BUILDARCH"
ARG ZIG_FOLDERNAME=zig-linux-${BUILD_MACHINE_ARCH}-${ZIG_VERSION}
@@ -20,7 +20,7 @@ ARG ZIG_FILENAME=${ZIG_FOLDERNAME}.tar.xz
ARG WEBKIT_URL="https://github.com/oven-sh/WebKit/releases/download/$WEBKIT_TAG/${WEBKIT_BASENAME}.tar.gz"
ARG ZIG_URL="https://ziglang.org/builds/${ZIG_FILENAME}"
ARG GIT_SHA=""
ARG BUN_BASE_VERSION=0.7
ARG BUN_BASE_VERSION=0.8
FROM bitnami/minideb:bullseye as bun-base

View File

@@ -38,7 +38,7 @@ NATIVE_OR_OLD_MARCH = -march=nehalem
endif
MIN_MACOS_VERSION ?= $(DEFAULT_MIN_MACOS_VERSION)
BUN_BASE_VERSION = 0.7
BUN_BASE_VERSION = 0.8
CI ?= false

View File

@@ -123,7 +123,6 @@ bun upgrade --canary
- [HTMLRewriter](https://bun.sh/docs/api/html-rewriter)
- [Testing](https://bun.sh/docs/api/test)
- [Utils](https://bun.sh/docs/api/utils)
- [DNS](https://bun.sh/docs/api/dns)
- [Node-API](https://bun.sh/docs/api/node-api)
## Contributing

View File

@@ -1,4 +1,5 @@
const std = @import("std");
const pathRel = std.fs.path.relative;
const Wyhash = @import("./src/wyhash.zig").Wyhash;
var is_debug_build = false;
fn moduleSource(comptime out: []const u8) FileSource {
@@ -96,6 +97,7 @@ const BunBuildOptions = struct {
}
};
// relative to the prefix
var output_dir: []const u8 = "";
fn panicIfNotFound(comptime filepath: []const u8) []const u8 {
var file = std.fs.cwd().openFile(filepath, .{ .optimize = .read_only }) catch |err| {
@@ -172,13 +174,12 @@ pub fn build(b: *Build) !void {
var triplet = triplet_buf[0 .. osname.len + cpuArchName.len + 1];
if (b.option([]const u8, "output-dir", "target to install to") orelse std.os.getenv("OUTPUT_DIR")) |output_dir_| {
output_dir = b.pathFromRoot(output_dir_);
output_dir = try pathRel(b.allocator, b.install_prefix, output_dir_);
} else {
const output_dir_base = try std.fmt.bufPrint(&output_dir_buf, "{s}{s}", .{ bin_label, triplet });
output_dir = b.pathFromRoot(output_dir_base);
output_dir = try pathRel(b.allocator, b.install_prefix, output_dir_base);
}
std.fs.cwd().makePath(output_dir) catch {};
is_debug_build = optimize == OptimizeMode.Debug;
const bun_executable_name = if (optimize == std.builtin.OptimizeMode.Debug) "bun-debug" else "bun";
const root_src = if (target.getOsTag() == std.Target.Os.Tag.freestanding)
@@ -186,12 +187,12 @@ pub fn build(b: *Build) !void {
else
"root.zig";
const min_version: std.SemanticVersion = if (target.getOsTag() != .freestanding and !target.isWindows())
const min_version: std.SemanticVersion = if (target.getOsTag() != .freestanding)
target.getOsVersionMin().semver
else
.{ .major = 0, .minor = 0, .patch = 0 };
const max_version: std.SemanticVersion = if (target.getOsTag() != .freestanding and !target.isWindows())
const max_version: std.SemanticVersion = if (target.getOsTag() != .freestanding)
target.getOsVersionMax().semver
else
.{ .major = 0, .minor = 0, .patch = 0 };
@@ -202,6 +203,7 @@ pub fn build(b: *Build) !void {
.root_source_file = FileSource.relative(root_src),
.target = target,
.optimize = optimize,
.main_pkg_path = .{ .cwd_relative = b.pathFromRoot(".") },
});
var default_build_options: BunBuildOptions = brk: {
@@ -239,8 +241,6 @@ pub fn build(b: *Build) !void {
};
{
obj.setMainPkgPath(b.pathFromRoot("."));
try addInternalPackages(
b,
obj,
@@ -271,9 +271,15 @@ pub fn build(b: *Build) !void {
std.io.getStdErr().writer().print("Output: {s}/{s}\n\n", .{ output_dir, bun_executable_name }) catch unreachable;
defer obj_step.dependOn(&obj.step);
obj.emit_bin = .{
.emit_to = b.fmt("{s}/{s}.o", .{ output_dir, bun_executable_name }),
};
var install = b.addInstallFileWithDir(
obj.getEmittedBin(),
.{ .custom = output_dir },
b.fmt("{s}.o", .{bun_executable_name}),
);
install.step.dependOn(&obj.step);
obj_step.dependOn(&install.step);
var actual_build_options = default_build_options;
if (b.option(bool, "generate-sizes", "Generate sizes of things") orelse false) {
actual_build_options.sizegen = true;
@@ -290,7 +296,8 @@ pub fn build(b: *Build) !void {
if (target.getCpuArch().isX86()) obj.disable_stack_probing = true;
if (b.option(bool, "for-editor", "Do not emit bin, just check for errors") orelse false) {
obj.emit_bin = .no_emit;
// obj.emit_bin = .no_emit;
obj.generated_bin = null;
}
if (target.getOsTag() == .linux) {
@@ -308,9 +315,10 @@ pub fn build(b: *Build) !void {
.root_source_file = FileSource.relative("src/bindgen.zig"),
.target = target,
.optimize = optimize,
.main_pkg_path = obj.main_pkg_path,
});
defer headers_step.dependOn(&headers_obj.step);
try configureObjectStep(b, headers_obj, @TypeOf(target), target, obj.main_pkg_path.?);
try configureObjectStep(b, headers_obj, headers_step, @TypeOf(target), target);
var headers_build_options = default_build_options;
headers_build_options.bindgen = true;
headers_obj.addOptions("build_options", default_build_options.step(b));
@@ -318,21 +326,22 @@ pub fn build(b: *Build) !void {
}
{
const wasm = b.step("bun-wasm", "Build WASM");
var wasm_step = b.addStaticLibrary(.{
const wasm_step = b.step("bun-wasm", "Build WASM");
var wasm = b.addStaticLibrary(.{
.name = "bun-wasm",
.root_source_file = FileSource.relative("root_wasm.zig"),
.target = target,
.optimize = optimize,
.main_pkg_path = obj.main_pkg_path,
});
defer wasm.dependOn(&wasm_step.step);
wasm_step.strip = false;
defer wasm_step.dependOn(&wasm.step);
wasm.strip = false;
// wasm_step.link_function_sections = true;
// wasm_step.link_emit_relocs = true;
// wasm_step.single_threaded = true;
try configureObjectStep(b, wasm_step, @TypeOf(target), target, obj.main_pkg_path.?);
try configureObjectStep(b, wasm, wasm_step, @TypeOf(target), target);
var build_opts = default_build_options;
wasm_step.addOptions("build_options", build_opts.step(b));
wasm.addOptions("build_options", build_opts.step(b));
}
{
@@ -342,9 +351,10 @@ pub fn build(b: *Build) !void {
.root_source_file = FileSource.relative("misctools/http_bench.zig"),
.target = target,
.optimize = optimize,
.main_pkg_path = obj.main_pkg_path,
});
defer headers_step.dependOn(&headers_obj.step);
try configureObjectStep(b, headers_obj, @TypeOf(target), target, obj.main_pkg_path.?);
try configureObjectStep(b, headers_obj, headers_step, @TypeOf(target), target);
headers_obj.addOptions("build_options", default_build_options.step(b));
}
@@ -355,9 +365,10 @@ pub fn build(b: *Build) !void {
.root_source_file = FileSource.relative("misctools/machbench.zig"),
.target = target,
.optimize = optimize,
.main_pkg_path = obj.main_pkg_path,
});
defer headers_step.dependOn(&headers_obj.step);
try configureObjectStep(b, headers_obj, @TypeOf(target), target, obj.main_pkg_path.?);
try configureObjectStep(b, headers_obj, headers_step, @TypeOf(target), target);
headers_obj.addOptions("build_options", default_build_options.step(b));
}
@@ -368,9 +379,10 @@ pub fn build(b: *Build) !void {
.root_source_file = FileSource.relative("misctools/fetch.zig"),
.target = target,
.optimize = optimize,
.main_pkg_path = obj.main_pkg_path,
});
defer headers_step.dependOn(&headers_obj.step);
try configureObjectStep(b, headers_obj, @TypeOf(target), target, obj.main_pkg_path.?);
try configureObjectStep(b, headers_obj, headers_step, @TypeOf(target), target);
headers_obj.addOptions("build_options", default_build_options.step(b));
}
@@ -381,9 +393,10 @@ pub fn build(b: *Build) !void {
.root_source_file = FileSource.relative("src/bench/string-handling.zig"),
.target = target,
.optimize = optimize,
.main_pkg_path = obj.main_pkg_path,
});
defer headers_step.dependOn(&headers_obj.step);
try configureObjectStep(b, headers_obj, @TypeOf(target), target, obj.main_pkg_path.?);
try configureObjectStep(b, headers_obj, headers_step, @TypeOf(target), target);
headers_obj.addOptions("build_options", default_build_options.step(b));
}
@@ -394,9 +407,10 @@ pub fn build(b: *Build) !void {
.root_source_file = FileSource.relative("src/sha.zig"),
.target = target,
.optimize = optimize,
.main_pkg_path = obj.main_pkg_path,
});
defer headers_step.dependOn(&headers_obj.step);
try configureObjectStep(b, headers_obj, @TypeOf(target), target, obj.main_pkg_path.?);
try configureObjectStep(b, headers_obj, headers_step, @TypeOf(target), target);
headers_obj.addOptions("build_options", default_build_options.step(b));
}
@@ -407,9 +421,10 @@ pub fn build(b: *Build) !void {
.root_source_file = FileSource.relative("src/sourcemap/vlq_bench.zig"),
.target = target,
.optimize = optimize,
.main_pkg_path = obj.main_pkg_path,
});
defer headers_step.dependOn(&headers_obj.step);
try configureObjectStep(b, headers_obj, @TypeOf(target), target, obj.main_pkg_path.?);
try configureObjectStep(b, headers_obj, headers_step, @TypeOf(target), target);
headers_obj.addOptions("build_options", default_build_options.step(b));
}
@@ -420,9 +435,10 @@ pub fn build(b: *Build) !void {
.root_source_file = FileSource.relative("misctools/tgz.zig"),
.target = target,
.optimize = optimize,
.main_pkg_path = obj.main_pkg_path,
});
defer headers_step.dependOn(&headers_obj.step);
try configureObjectStep(b, headers_obj, @TypeOf(target), target, obj.main_pkg_path.?);
try configureObjectStep(b, headers_obj, headers_step, @TypeOf(target), target);
headers_obj.addOptions("build_options", default_build_options.step(b));
}
@@ -436,16 +452,23 @@ pub fn build(b: *Build) !void {
var headers_obj: *CompileStep = b.addTest(.{
.root_source_file = FileSource.relative(test_file orelse "src/main.zig"),
.target = target,
.main_pkg_path = obj.main_pkg_path,
});
headers_obj.filter = test_filter;
if (test_bin_) |test_bin| {
headers_obj.name = std.fs.path.basename(test_bin);
if (std.fs.path.dirname(test_bin)) |dir| headers_obj.emit_bin = .{
.emit_to = b.fmt("{s}/{s}", .{ dir, headers_obj.name }),
};
if (std.fs.path.dirname(test_bin)) |dir| {
var install = b.addInstallFileWithDir(
headers_obj.getEmittedBin(),
.{ .custom = try std.fs.path.relative(b.allocator, output_dir, dir) },
headers_obj.name,
);
install.step.dependOn(&headers_obj.step);
headers_step.dependOn(&install.step);
}
}
try configureObjectStep(b, headers_obj, @TypeOf(target), target, obj.main_pkg_path.?);
try configureObjectStep(b, headers_obj, headers_step, @TypeOf(target), target);
headers_step.dependOn(&headers_obj.step);
headers_obj.addOptions("build_options", default_build_options.step(b));
@@ -456,9 +479,7 @@ pub fn build(b: *Build) !void {
pub var original_make_fn: ?*const fn (step: *std.build.Step) anyerror!void = null;
pub fn configureObjectStep(b: *std.build.Builder, obj: *CompileStep, comptime Target: type, target: Target, main_pkg_path: []const u8) !void {
obj.setMainPkgPath(main_pkg_path);
pub fn configureObjectStep(b: *std.build.Builder, obj: *CompileStep, obj_step: *std.build.Step, comptime Target: type, target: Target) !void {
// obj.setTarget(target);
try addInternalPackages(b, obj, std.heap.page_allocator, b.zig_exe, target);
@@ -466,11 +487,16 @@ pub fn configureObjectStep(b: *std.build.Builder, obj: *CompileStep, comptime Ta
// obj.setBuildMode(optimize);
obj.bundle_compiler_rt = false;
if (obj.emit_bin == .default)
obj.emit_bin = .{
.emit_to = b.fmt("{s}/{s}.o", .{ output_dir, obj.name }),
};
if (obj.emit_directory == null) {
var install = b.addInstallFileWithDir(
obj.getEmittedBin(),
.{ .custom = output_dir },
b.fmt("{s}.o", .{obj.name}),
);
install.step.dependOn(&obj.step);
obj_step.dependOn(&install.step);
}
if (target.getOsTag() != .freestanding) obj.linkLibC();
if (target.getOsTag() != .freestanding) obj.bundle_compiler_rt = false;

BIN
bun.lockb

Binary file not shown.

View File

@@ -428,6 +428,21 @@ const str = Bun.inspect(arr);
// => "Uint8Array(3) [ 1, 2, 3 ]"
```
## `Bun.inspect.custom`
This is the symbol that Bun uses to implement `Bun.inspect`. You can override this to customize how your objects are printed. It is identical to `util.inspect.custom` in Node.js.
```ts
class Foo {
[Bun.inspect.custom]() {
return "foo";
}
}
const foo = new Foo();
console.log(foo); // => "foo"
```
## `Bun.nanoseconds()`
Returns the number of nanoseconds since the current `bun` process started, as a `number`. Useful for high-precision timing and benchmarking.

View File

@@ -30,14 +30,50 @@ The runner recursively searches the working directory for files that match the f
- `*.spec.{js|jsx|ts|tsx}`
- `*_spec.{js|jsx|ts|tsx}`
You can filter the set of tests to run by passing additional positional arguments to `bun test`. Any file in the directory with an _absolute path_ that contains one of the filters will run. Commonly, these filters will be file or directory names; glob patterns are not yet supported.
You can filter the set of _test files_ to run by passing additional positional arguments to `bun test`. Any test file with a path that matches one of the filters will run. Commonly, these filters will be file or directory names; glob patterns are not yet supported.
```bash
$ bun test <filter> <filter> ...
```
To filter by _test name_, use the `-t`/`--test-name-pattern` flag.
```sh
# run all tests or test suites with "addition" in the name
$ bun test --test-name-pattern addition
```
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.
## Timeouts
Use the `--timeout` flag to specify a _per-test_ timeout in milliseconds. If a test times out, it will be marked as failed. The default value is `5000`.
```bash
# default value is 5000
$ bun test --timeout 20
```
## Rerun tests
Use the `--rerun-each` flag to run each test multiple times. This is useful for detecting flaky or non-deterministic test failures.
```sh
$ bun test --rerun-each 100
```
## Bail out with `--bail`
Use the `--bail` flag to abort the test run early after a pre-determined number of test failures. By default Bun will run all tests and report all failures, but sometimes in CI environments it's preferable to terminate earlier to reduce CPU usage.
```sh
# bail after 1 failure
$ bun test --bail
# bail after 10 failure
$ bun test --bail 10
```
## Watch mode
Similar to `bun run`, you can pass the `--watch` flag to `bun test` to watch for changes and re-run tests.

View File

@@ -0,0 +1,65 @@
---
name: Build an app with Nuxt and Bun
---
Bun supports [Nuxt](https://nuxt.com) out of the box. Initialize a Nuxt app with official `nuxi` CLI.
```sh
$ bunx nuxi init my-nuxt-app
Nuxi 3.6.5
✨ Nuxt project is created with v3 template. Next steps:
cd my-nuxt-app
Install dependencies with npm install or yarn install or pnpm install
Start development server with npm run dev or yarn dev or pnpm run dev
```
---
Then move into the project directory and install dependencies.
```sh
$ cd my-app
$ bun install
bun install v0.8.0
+ @nuxt/devtools@0.8.0
+ @types/node@18.17.6
+ nuxt@3.6.5
Nuxi 3.6.5
✔ Types generated in .nuxt
776 packages installed [1.72s]
```
---
To start the dev server, run `bun run dev` from the project root. This will execute the `nuxt dev` command (as defined in the `"dev"` script in `package.json`).
{% callout %}
The `nuxt` CLI uses Node.js by default; passing the `--bun` flag forces the dev server to use the Bun runtime instead.
{% /callout %}
```
$ bun --bun run dev
$ nuxt dev
Nuxi 3.6.5
Nuxt 3.6.5 with Nitro 2.5.2
> Local: http://localhost:3000/
> Network: http://192.168.0.21:3000/
> Network: http://[fd8a:d31d:481c:4883:1c64:3d90:9f83:d8a2]:3000/
✔ Nuxt DevTools is enabled v0.8.0 (experimental)
Vite client warmed up in 547ms
✔ Nitro built in 244 ms
```
---
Once the dev server spins up, open [http://localhost:3000](http://localhost:3000) to see the app. The app will render Nuxt's built-in `WelcomePage` template component.
To start developing your app, replace `<WelcomePage />` in `app.vue` with your own UI.
{% image src="https://github.com/oven-sh/bun/assets/3084745/2c683ecc-3298-4bb0-b8c0-cf4cfaea1daa" caption="Demo Nuxt app running on localhost" /%}
---
Refer to the [Nuxt website](https://nuxt.com/docs) for complete documentation.

View File

@@ -0,0 +1,82 @@
---
name: Debugging Bun with the web debugger
---
Bun speaks the [WebKit Inspector Protocol](https://github.com/oven-sh/bun/blob/main/packages/bun-vscode/types/jsc.d.ts). To enable debugging when running code with Bun, use the `--inspect` flag. For demonstration purposes, consider the following simple web server.
```ts#server.ts
Bun.serve({
fetch(req){
console.log(req.url);
return new Response("Hello, world!");
}
})
```
---
Let's run this file with the `--inspect` flag.
This automatically starts a WebSocket server on an available port that can be used to introspect the running Bun process. Various debugging tools can connect to this server to provide an interactive debugging experience.
Bun hosts a web-based debugger at [debug.bun.sh](https://debug.bun.sh). It is a modified version of WebKit's [Web Inspector Interface](https://webkit.org/web-inspector/web-inspector-interface/), which will look familiar to Safari users.
```sh
$ bun --inspect server.ts
------------------ Bun Inspector ------------------
Listening at:
ws://localhost:6499/0tqxs9exrgrm
Inspect in browser:
https://debug.bun.sh/#localhost:6499/0tqxs9exrgrm
------------------ Bun Inspector ------------------
```
---
Open the provided `debug.bun.sh` URL in your browser to start a debugging session. From this interface, you'll be able to view the source code of the running file, view and set breakpoints, and execute code with the built-in console.
{% image src="https://github.com/oven-sh/bun/assets/3084745/e6a976a8-80cc-4394-8925-539025cc025d" alt="Screenshot of Bun debugger, Console tab" /%}
---
Let's set a breakpoint. Navigate to the Sources tab; you should see the code from earlier. Click on the line number `3` to set a breakpoint on our `console.log(req.url)` statement.
{% image src="https://github.com/oven-sh/bun/assets/3084745/3b69c7e9-25ff-4f9d-acc4-caa736862935" alt="screenshot of Bun debugger" /%}
---
Then visit [`http://localhost:3000`](http://localhost:3000) in your web browser. This will send an HTTP request to our `localhost` web server. It will seem like the page isn't loading. Why? Because the program has paused execution at the breakpoint we set earlier.
Note how the UI has changed.
{% image src="https://github.com/oven-sh/bun/assets/3084745/8b565e58-5445-4061-9bc4-f41090dfe769" alt="screenshot of Bun debugger" /%}
---
At this point there's a lot we can do to introspect the current execution environment. We can use the console at the bottom to run arbitrary code in the context of the program, with full access to the variables in scope at our breakpoint.
{% image src="https://github.com/oven-sh/bun/assets/3084745/f4312b76-48ba-4a7d-b3b6-6205968ac681" /%}
---
On the right side of the Sources pane, we can see all local variables currently in scope, and drill down to see their properties and methods. Here, we're inspecting the `req` variable.
{% image src="https://github.com/oven-sh/bun/assets/3084745/63d7f843-5180-489c-aa94-87c486e68646" /%}
---
In the upper left of the Sources pane, we can control the execution of the program.
{% image src="https://github.com/oven-sh/bun/assets/3084745/41b76deb-7371-4461-9d5d-81b5a6d2f7a4" /%}
---
Here's a cheat sheet explaining the functions of the control flow buttons.
- _Continue script execution_ — continue running the program until the next breakpoint or exception.
- _Step over_ — The program will continue to the next line.
- _Step into_ — If the current statement contains a function call, the debugger will "step into" the called function.
- _Step out_ — If the current statement is a function call, the debugger will finish executing the call, then "step out" of the function to the location where it was called.
{% image src="https://github-production-user-asset-6210df.s3.amazonaws.com/3084745/261510346-6a94441c-75d3-413a-99a7-efa62365f83d.png" /%}

View File

@@ -0,0 +1,23 @@
---
name: Detect when code is executed with Bun
---
The recommended way to conditionally detect when code is being executed with `bun` is to check for the existence of the `Bun` global.
This is similar to how you'd check for the existence of the `window` variable to detect when code is being executed in a browser.
```ts
if (typeof Bun !== "undefined") {
// this code will only run when the file is run with Bun
}
```
---
In TypeScript environments, the previous approach will result in a type error unless `bun-types` is globally installed. To avoid this, you can check `process.versions` instead.
```ts
if (process.versions.bun) {
// this code will only run when the file is run with Bun
}
```

View File

@@ -8,7 +8,7 @@ The `Bun.password.hash()` function provides a fast, built-in mechanism for secur
const password = "super-secure-pa$$word";
const hash = await Bun.password.hash(password);
// => $argon2id$v=19$m=65536,t=2,p=1$tFq+9AVr1bfPxQdh6E8DQRhEXg/M/SqYCNu6gVdRRNs$GzJ8PuBi+K+BVojzPfS5mjnC8OpLGtv8KJqF99eP6a4
// => $argon2id$v=19$m=65536,t=2,p=1$tFq+9AVr1bfPxQdh6E8DQRhEXg/M/...
```
---

View File

@@ -132,6 +132,9 @@ export default {
page("runtime/configuration", "Configuration", {
description: `Bun's runtime is configurable with environment variables and the bunfig.toml config file.`,
}),
page("runtime/debugger", "Debugger", {
description: `Debug your code with Bun's web-based debugger or VS Code extension`,
}),
page("runtime/framework", "Framework API", {
disabled: true,
description:
@@ -188,13 +191,13 @@ export default {
page("cli/test", "`bun test`", {
description: "Bun's test runner uses Jest-compatible syntax but runs 100x faster.",
}),
page("test/hot", "Watch mode", {
description: "Reload your tests automatically on change.",
}),
page("test/writing", "Writing tests", {
description:
"Write your tests using Jest-like expect matchers, plus setup/teardown hooks, snapshot testing, and more",
}),
page("test/hot", "Watch mode", {
description: "Reload your tests automatically on change.",
}),
page("test/lifecycle", "Lifecycle hooks", {
description: "Add lifecycle hooks to your tests that run before/after each test or test run",
}),

View File

@@ -112,7 +112,7 @@ Zig can be installed either with our npm package [`@oven/zig`](https://www.npmjs
```bash
$ bun install -g @oven/zig
$ zigup 0.11.0-dev.4006+bf827d0b5
$ zigup 0.12.0-dev.163+6780a6bbf
```
{% callout %}

90
docs/runtime/debugger.md Normal file
View File

@@ -0,0 +1,90 @@
---
name: Debugger
---
Bun speaks the [WebKit Inspector Protocol](https://github.com/oven-sh/bun/blob/main/packages/bun-vscode/types/jsc.d.ts). For demonstration purposes, consider the following simple web server.
```ts#server.ts
Bun.serve({
fetch(req){
console.log(req.url);
return new Response("Hello, world!");
}
})
```
### `--inspect`
To enable debugging when running code with Bun, use the `--inspect` flag. This automatically starts a WebSocket server on an available port that can be used to introspect the running Bun process.
```sh
$ bun --inspect server.ts
------------------ Bun Inspector ------------------
Listening at:
ws://localhost:6499/0tqxs9exrgrm
Inspect in browser:
https://debug.bun.sh/#localhost:6499/0tqxs9exrgrm
------------------ Bun Inspector ------------------
```
### `--inspect-brk`
The `--inspect-brk` flag behaves identically to `--inspect`, except it automatically injects a breakpoint at the first line of the executed script. This is useful for debugging scripts that run quickly and exit immediately.
### `--inspect-wait`
The `--inspect-wait` flag behaves identically to `--inspect`, except the code will not execute until a debugger has attached to the running process.
### Setting a port or URL for the debugger
Regardless of which flag you use, you can optionally specify a port number, URL prefix, or both.
```sh
$ bun --inspect=4000 server.ts
$ bun --inspect=localhost:4000 server.ts
$ bun --inspect=localhost:4000/prefix server.ts
```
## Debuggers
Various debugging tools can connect to this server to provide an interactive debugging experience. Bun hosts a web-based debugger at [debug.bun.sh](https://debug.bun.sh). It is a modified version of WebKit's [Web Inspector Interface](https://webkit.org/web-inspector/web-inspector-interface/), which will look familiar to Safari users.
### `debug.bun.sh`
Bun hosts a web-based debugger at [debug.bun.sh](https://debug.bun.sh). It is a modified version of WebKit's [Web Inspector Interface](https://webkit.org/web-inspector/web-inspector-interface/), which will look familiar to Safari users.
Open the provided `debug.bun.sh` URL in your browser to start a debugging session. From this interface, you'll be able to view the source code of the running file, view and set breakpoints, and execute code with the built-in console.
{% image src="https://github.com/oven-sh/bun/assets/3084745/e6a976a8-80cc-4394-8925-539025cc025d" alt="Screenshot of Bun debugger, Console tab" /%}
Let's set a breakpoint. Navigate to the Sources tab; you should see the code from earlier. Click on the line number `3` to set a breakpoint on our `console.log(req.url)` statement.
{% image src="https://github.com/oven-sh/bun/assets/3084745/3b69c7e9-25ff-4f9d-acc4-caa736862935" alt="screenshot of Bun debugger" /%}
Then visit [`http://localhost:3000`](http://localhost:3000) in your web browser. This will send an HTTP request to our `localhost` web server. It will seem like the page isn't loading. Why? Because the program has paused execution at the breakpoint we set earlier.
Note how the UI has changed.
{% image src="https://github.com/oven-sh/bun/assets/3084745/8b565e58-5445-4061-9bc4-f41090dfe769" alt="screenshot of Bun debugger" /%}
At this point there's a lot we can do to introspect the current execution environment. We can use the console at the bottom to run arbitrary code in the context of the program, with full access to the variables in scope at our breakpoint.
{% image src="https://github.com/oven-sh/bun/assets/3084745/f4312b76-48ba-4a7d-b3b6-6205968ac681" /%}
On the right side of the Sources pane, we can see all local variables currently in scope, and drill down to see their properties and methods. Here, we're inspecting the `req` variable.
{% image src="https://github.com/oven-sh/bun/assets/3084745/63d7f843-5180-489c-aa94-87c486e68646" /%}
In the upper left of the Sources pane, we can control the execution of the program.
{% image src="https://github.com/oven-sh/bun/assets/3084745/41b76deb-7371-4461-9d5d-81b5a6d2f7a4" /%}
Here's a cheat sheet explaining the functions of the control flow buttons.
- _Continue script execution_ — continue running the program until the next breakpoint or exception.
- _Step over_ — The program will continue to the next line.
- _Step into_ — If the current statement contains a function call, the debugger will "step into" the called function.
- _Step out_ — If the current statement is a function call, the debugger will finish executing the call, then "step out" of the function to the location where it was called.
{% image src="https://github-production-user-asset-6210df.s3.amazonaws.com/3084745/261510346-6a94441c-75d3-413a-99a7-efa62365f83d.png" /%}

View File

@@ -138,7 +138,7 @@ This page is updated regularly to reflect compatibility status of the latest ver
### [`node:tty`](https://nodejs.org/api/tty.html)
🟡 Missing `tty.ReadStream` and `tty.WriteStream`.
🟢 Fully implemented.
### [`node:url`](https://nodejs.org/api/url.html)

View File

@@ -1,12 +1,11 @@
`bun:test` supports seeing which lines of code are covered by tests. To use this feature, pass `--coverage` to the CLI:
Bun's test runner now supports built-in _code coverage reporting_. This makes it easy to see how much of the codebase is covered by tests, and find areas that are not currently well-tested.
```sh
bun test --coverage
```
## Enabling coverage
It will print out a coverage report to the console:
`bun:test` supports seeing which lines of code are covered by tests. To use this feature, pass `--coverage` to the CLI. It will print out a coverage report to the console:
```js
$ bun test --coverage
-------------|---------|---------|-------------------
File | % Funcs | % Lines | Uncovered Line #s
-------------|---------|---------|-------------------
@@ -26,32 +25,45 @@ All files | 38.89 | 42.11 |
-------------|---------|---------|-------------------
```
If coverage is below a threshold, `bun:test` will exit with a non-zero exit code to indicate the failure.
### Configuring coverage
`bunfig.toml` supports configuring coverage:
To always enable coverage reporting by default, add the following line to your `bunfig.toml`:
```toml
[test]
# Always enable coverage
# always enable coverage
coverage = true
# Anything less than 90% coverage will fail the test
# coverageThreshold = 0.9
coverageThreshold = { line = 0.9, function = 0.9 }
# Don't include .test.* files in coverage reports
coverageSkipTestFiles = true
# Disable sourcemap support in coverage reports
# By default, coverage reports will automatically use Bun's internal sourcemap.
# You probably don't want to configure this
# coverageIgnoreSourcemaps = false
```
`coverageThreshold` can be either a number or an object with `line` and `function` keys. When a number, it is treated as both the line and function threshold.
By default coverage reports will _include_ test files and _exclude_ sourcemaps. This is usually what you want, but it can be configured otherwise in `bunfig.toml`.
Coverage support was added in Bun v0.7.3.
```toml
[test]
coverageSkipTestFiles = true # default false
```
### Coverage thresholds
{% callout %}
**Note** — Support for coverage reporting was added in Bun v0.7.3.
{% /callout %}
It is possible to specify a coverage threshold in `bunfig.toml`. If your test suite does not meet or exceed this threshold, `bun test` will exit with a non-zero exit code to indicate the failure.
```toml
[test]
# to require 90% line-level and function-level coverage
coverageThreshold = 0.9
# to set different thresholds for lines and functions
coverageThreshold = { line = 0.9, function = 0.9 }
```
### Sourcemaps
Internally, Bun transpiles all files by default, so Bun automatically generates an internal [source map](https://web.dev/source-maps/) that maps lines of your original source code onto Bun's internal representation. If for any reason you want to disable this, set `test.coverageIgnoreSourcemaps` to `false`; this will rarely be desirable outside of advanced use cases.
```toml
[test]
coverageIgnoreSourcemaps = true # default false
```

View File

@@ -187,7 +187,17 @@ pub fn main() anyerror!void {
var ctx = try default_allocator.create(HTTP.HTTPChannelContext);
ctx.* = .{
.channel = channel,
.http = try HTTP.AsyncHTTP.init(default_allocator, args.method, args.url, args.headers, args.headers_buf, response_body_string, args.body, 0, HTTP.FetchRedirect.follow),
.http = try HTTP.AsyncHTTP.init(
default_allocator,
args.method,
args.url,
args.headers,
args.headers_buf,
response_body_string,
args.body,
0,
HTTP.FetchRedirect.follow,
),
};
ctx.http.callback = HTTP.HTTPChannelContext.callback;
var batch = HTTPThread.Batch{};

View File

@@ -9,6 +9,7 @@
"prettier": "^2.4.1",
"react": "next",
"react-dom": "next",
"source-map-js": "^1.0.2",
"typescript": "^5.0.2"
},
"private": true,
@@ -17,7 +18,7 @@
"build-fallback": "esbuild --target=esnext --bundle src/fallback.ts --format=iife --platform=browser --minify > src/fallback.out.js",
"postinstall": "bash .scripts/postinstall.sh",
"typecheck": "tsc --noEmit && cd test && bun run typecheck",
"fmt": "prettier --write --cache './{src,test,bench}/**/*.{mjs,ts,tsx,js,jsx}'",
"fmt": "prettier --write --cache './{src,test,bench,packages/{bun-inspector-*,bun-vscode,bun-debug-adapter-protocol}}/**/*.{mjs,ts,tsx,js,jsx}'",
"lint": "eslint './**/*.d.ts' --cache",
"lint:fix": "eslint './**/*.d.ts' --cache --fix"
},
@@ -25,7 +26,7 @@
"@types/react": "^18.0.25",
"@typescript-eslint/eslint-plugin": "^5.31.0",
"@typescript-eslint/parser": "^5.31.0",
"bun-webkit": "0.0.1-fd79ce3120a692f4aed314c3da3dd452b4aa865f"
"bun-webkit": "0.0.1-48c1316e907ca597e27e5a7624160dc18a4df8ec"
},
"version": "0.0.0",
"prettier": "./.prettierrc.cjs"

View File

@@ -0,0 +1,2 @@
protocol/*/protocol.json linguist-generated=true
protocol/*/index.d.ts linguist-generated=true

View File

@@ -0,0 +1 @@
protocol/*.json

View File

@@ -0,0 +1,3 @@
# bun-debug-adapter-protocol
https://microsoft.github.io/debug-adapter-protocol/overview

Binary file not shown.

View File

@@ -0,0 +1,2 @@
export type * from "./src/protocol";
export * from "./src/debugger/adapter";

View File

@@ -0,0 +1,8 @@
{
"name": "bun-debug-adapter-protocol",
"version": "0.0.1",
"dependencies": {
"semver": "^7.5.4",
"source-map-js": "^1.0.2"
}
}

View File

@@ -0,0 +1,176 @@
import type { Protocol, Type } from "../src/protocol/schema";
import { writeFileSync } from "node:fs";
import { spawnSync } from "node:child_process";
run().catch(console.error);
async function run() {
const cwd = new URL("../protocol/", import.meta.url);
const runner = "Bun" in globalThis ? "bunx" : "npx";
const write = (name: string, data: string) => {
const path = new URL(name, cwd);
writeFileSync(path, data);
spawnSync(runner, ["prettier", "--write", path.pathname], { cwd, stdio: "ignore" });
};
const schema: Protocol = await download(
"https://microsoft.github.io/debug-adapter-protocol/debugAdapterProtocol.json",
);
write("protocol.json", JSON.stringify(schema));
const types = formatProtocol(schema);
write("index.d.ts", `// GENERATED - DO NOT EDIT\n${types}`);
}
function formatProtocol(protocol: Protocol, extraTs?: string): string {
const { definitions } = protocol;
const requestMap = new Map();
const responseMap = new Map();
const eventMap = new Map();
let body = `export namespace DAP {`;
loop: for (const [key, definition] of Object.entries(definitions)) {
if (/[a-z]+Request$/i.test(key)) {
continue;
}
if (/[a-z]+Arguments$/i.test(key)) {
const name = key.replace(/(Request)?Arguments$/, "");
const requestName = `${name}Request`;
requestMap.set(toMethod(name), requestName);
body += formatType(definition, requestName);
continue;
}
if ("allOf" in definition) {
const { allOf } = definition;
for (const type of allOf) {
if (type.type !== "object") {
continue;
}
const { description, properties = {} } = type;
if (/[a-z]+Event$/i.test(key)) {
const { event, body: type = {} } = properties;
if (!event || !("enum" in event)) {
continue;
}
const [eventKey] = event.enum ?? [];
eventMap.set(eventKey, key);
const eventType: Type = {
type: "object",
description,
...type,
};
body += formatType(eventType, key);
continue loop;
}
if (/[a-z]+Response$/i.test(key)) {
const { body: type = {} } = properties;
const bodyType: Type = {
type: "object",
description,
...type,
};
const name = key.replace(/Response$/, "");
responseMap.set(toMethod(name), key);
body += formatType(bodyType, key);
continue loop;
}
}
}
body += formatType(definition, key);
}
for (const [key, name] of responseMap) {
if (requestMap.has(key)) {
continue;
}
const requestName = `${name.replace(/Response$/, "")}Request`;
requestMap.set(key, requestName);
body += formatType({ type: "object", properties: {} }, requestName);
}
body += formatMapType("RequestMap", requestMap);
body += formatMapType("ResponseMap", responseMap);
body += formatMapType("EventMap", eventMap);
if (extraTs) {
body += extraTs;
}
return body + "};";
}
function formatMapType(key: string, typeMap: Map<string, string>): string {
const type: Type = {
type: "object",
required: [...typeMap.keys()],
properties: Object.fromEntries([...typeMap.entries()].map(([key, value]) => [key, { $ref: value }])),
};
return formatType(type, key);
}
function formatType(type: Type, key?: string): string {
const { description, type: kind } = type;
let body = "";
if (key) {
if (description) {
body += `\n${toComment(description)}\n`;
}
body += `export type ${key}=`;
}
if (kind === "boolean") {
body += "boolean";
} else if (kind === "number" || kind === "integer") {
body += "number";
} else if (kind === "string") {
const { enum: choices } = type;
if (choices) {
body += choices.map(value => `"${value}"`).join("|");
} else {
body += "string";
}
} else if (kind === "array") {
const { items } = type;
const itemType = items ? formatType(items) : "unknown";
body += `${itemType}[]`;
} else if (kind === "object") {
const { properties, required } = type;
if (!properties || Object.keys(properties).length === 0) {
body += "{}";
} else {
body += "{";
for (const [key, { description, ...type }] of Object.entries(properties)) {
if (description) {
body += `\n${toComment(description)}`;
}
const delimit = required?.includes(key) ? ":" : "?:";
body += `\n${key}${delimit}${formatType(type)};`;
}
body += "}";
}
} else if ("$ref" in type) {
const { $ref: ref } = type;
body += ref.split("/").pop() || "unknown";
} else if ("allOf" in type) {
const { allOf } = type;
body += allOf.map(type => formatType(type)).join("&");
} else {
body += "unknown";
}
if (key) {
body += ";";
}
return body;
}
function toMethod(name: string): string {
return `${name.substring(0, 1).toLowerCase()}${name.substring(1)}`;
}
function toComment(description?: string): string {
if (!description) {
return "";
}
const lines = ["/**", ...description.split("\n").map(line => ` * ${line.trim()}`), "*/"];
return lines.join("\n");
}
async function download<T>(url: string | URL): Promise<T> {
const response = await fetch(url);
if (!response.ok) {
throw new Error(`Failed to download ${url}: ${response.statusText}`);
}
return response.json();
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,271 @@
import type { DAP } from "../protocol";
const capabilities: DAP.Capabilities = {
/**
* The debug adapter supports the `configurationDone` request.
* @see configurationDone
*/
supportsConfigurationDoneRequest: true,
/**
* The debug adapter supports function breakpoints using the `setFunctionBreakpoints` request.
* @see setFunctionBreakpoints
*/
supportsFunctionBreakpoints: true,
/**
* The debug adapter supports conditional breakpoints.
* @see setBreakpoints
* @see setInstructionBreakpoints
* @see setFunctionBreakpoints
* @see setExceptionBreakpoints
* @see setDataBreakpoints
*/
supportsConditionalBreakpoints: true,
/**
* The debug adapter supports breakpoints that break execution after a specified number of hits.
* @see setBreakpoints
* @see setInstructionBreakpoints
* @see setFunctionBreakpoints
* @see setExceptionBreakpoints
* @see setDataBreakpoints
*/
supportsHitConditionalBreakpoints: true,
/**
* The debug adapter supports a (side effect free) `evaluate` request for data hovers.
* @see evaluate
*/
supportsEvaluateForHovers: true,
/**
* Available exception filter options for the `setExceptionBreakpoints` request.
* @see setExceptionBreakpoints
*/
exceptionBreakpointFilters: [
{
filter: "all",
label: "Caught Exceptions",
default: false,
supportsCondition: true,
description: "Breaks on all throw errors, even if they're caught later.",
conditionDescription: `error.name == "CustomError"`,
},
{
filter: "uncaught",
label: "Uncaught Exceptions",
default: false,
supportsCondition: true,
description: "Breaks only on errors or promise rejections that are not handled.",
conditionDescription: `error.name == "CustomError"`,
},
],
/**
* The debug adapter supports stepping back via the `stepBack` and `reverseContinue` requests.
* @see stepBack
* @see reverseContinue
*/
supportsStepBack: false,
/**
* The debug adapter supports setting a variable to a value.
* @see setVariable
*/
supportsSetVariable: false,
/**
* The debug adapter supports restarting a frame.
* @see restartFrame
*/
supportsRestartFrame: false,
/**
* The debug adapter supports the `gotoTargets` request.
* @see gotoTargets
*/
supportsGotoTargetsRequest: false,
/**
* The debug adapter supports the `stepInTargets` request.
* @see stepInTargets
*/
supportsStepInTargetsRequest: false,
/**
* The debug adapter supports the `completions` request.
* @see completions
*/
supportsCompletionsRequest: false,
/**
* The set of characters that should trigger completion in a REPL.
* If not specified, the UI should assume the `.` character.
* @see completions
*/
completionTriggerCharacters: [".", "[", '"', "'"],
/**
* The debug adapter supports the `modules` request.
* @see modules
*/
supportsModulesRequest: false,
/**
* The set of additional module information exposed by the debug adapter.
* @see modules
*/
additionalModuleColumns: [],
/**
* Checksum algorithms supported by the debug adapter.
*/
supportedChecksumAlgorithms: [],
/**
* The debug adapter supports the `restart` request.
* In this case a client should not implement `restart` by terminating
* and relaunching the adapter but by calling the `restart` request.
* @see restart
*/
supportsRestartRequest: false,
/**
* The debug adapter supports `exceptionOptions` on the `setExceptionBreakpoints` request.
* @see setExceptionBreakpoints
*/
supportsExceptionOptions: false,
/**
* The debug adapter supports a `format` attribute on the `stackTrace`, `variables`, and `evaluate` requests.
* @see stackTrace
* @see variables
* @see evaluate
*/
supportsValueFormattingOptions: false,
/**
* The debug adapter supports the `exceptionInfo` request.
* @see exceptionInfo
*/
supportsExceptionInfoRequest: true,
/**
* The debug adapter supports the `terminateDebuggee` attribute on the `disconnect` request.
* @see disconnect
*/
supportTerminateDebuggee: true,
/**
* The debug adapter supports the `suspendDebuggee` attribute on the `disconnect` request.
* @see disconnect
*/
supportSuspendDebuggee: false,
/**
* The debug adapter supports the delayed loading of parts of the stack,
* which requires that both the `startFrame` and `levels` arguments and
* the `totalFrames` result of the `stackTrace` request are supported.
* @see stackTrace
*/
supportsDelayedStackTraceLoading: true,
/**
* The debug adapter supports the `loadedSources` request.
* @see loadedSources
*/
supportsLoadedSourcesRequest: true,
/**
* The debug adapter supports log points by interpreting the `logMessage` attribute of the `SourceBreakpoint`.
* @see setBreakpoints
*/
supportsLogPoints: true,
/**
* The debug adapter supports the `terminateThreads` request.
* @see terminateThreads
*/
supportsTerminateThreadsRequest: false,
/**
* The debug adapter supports the `setExpression` request.
* @see setExpression
*/
supportsSetExpression: false,
/**
* The debug adapter supports the `terminate` request.
* @see terminate
*/
supportsTerminateRequest: true,
/**
* The debug adapter supports data breakpoints.
* @see setDataBreakpoints
*/
supportsDataBreakpoints: false,
/**
* The debug adapter supports the `readMemory` request.
* @see readMemory
*/
supportsReadMemoryRequest: false,
/**
* The debug adapter supports the `writeMemory` request.
* @see writeMemory
*/
supportsWriteMemoryRequest: false,
/**
* The debug adapter supports the `disassemble` request.
* @see disassemble
*/
supportsDisassembleRequest: false,
/**
* The debug adapter supports the `cancel` request.
* @see cancel
*/
supportsCancelRequest: false,
/**
* The debug adapter supports the `breakpointLocations` request.
* @see breakpointLocations
*/
supportsBreakpointLocationsRequest: true,
/**
* The debug adapter supports the `clipboard` context value in the `evaluate` request.
* @see evaluate
*/
supportsClipboardContext: false,
/**
* The debug adapter supports stepping granularities (argument `granularity`) for the stepping requests.
* @see stepIn
*/
supportsSteppingGranularity: false,
/**
* The debug adapter supports adding breakpoints based on instruction references.
* @see setInstructionBreakpoints
*/
supportsInstructionBreakpoints: false,
/**
* The debug adapter supports `filterOptions` as an argument on the `setExceptionBreakpoints` request.
* @see setExceptionBreakpoints
*/
supportsExceptionFilterOptions: true,
/**
* The debug adapter supports the `singleThread` property on the execution requests
* (`continue`, `next`, `stepIn`, `stepOut`, `reverseContinue`, `stepBack`).
*/
supportsSingleThreadExecutionRequests: false,
};
export default capabilities;

View File

@@ -0,0 +1,36 @@
"use strict";
export default {
fetch(request) {
const animal = getAnimal(request.url);
const voice = animal.talk();
return new Response(voice);
},
};
function getAnimal(query) {
switch (query.split("/").pop()) {
case "dog":
return new Dog();
case "cat":
return new Cat();
}
return new Bird();
}
class Dog {
name = "dog";
talk() {
return "woof";
}
}
class Cat {
name = "cat";
talk() {
return "meow";
}
}
class Bird {
name = "bird";
talk() {
return "chirp";
}
}
//# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsicGFja2FnZXMvYnVuLWRlYnVnLWFkYXB0ZXItcHJvdG9jb2wvZGVidWdnZXIvZml4dHVyZXMvd2l0aC1zb3VyY2VtYXAudHMiXSwKICAic291cmNlc0NvbnRlbnQiOiBbImV4cG9ydCBkZWZhdWx0IHtcbiAgZmV0Y2gocmVxdWVzdDogUmVxdWVzdCk6IFJlc3BvbnNlIHtcbiAgICBjb25zdCBhbmltYWwgPSBnZXRBbmltYWwocmVxdWVzdC51cmwpO1xuICAgIGNvbnN0IHZvaWNlID0gYW5pbWFsLnRhbGsoKTtcbiAgICByZXR1cm4gbmV3IFJlc3BvbnNlKHZvaWNlKTtcbiAgfSxcbn07XG5cbmZ1bmN0aW9uIGdldEFuaW1hbChxdWVyeTogc3RyaW5nKTogQW5pbWFsIHtcbiAgc3dpdGNoIChxdWVyeS5zcGxpdChcIi9cIikucG9wKCkpIHtcbiAgICBjYXNlIFwiZG9nXCI6XG4gICAgICByZXR1cm4gbmV3IERvZygpO1xuICAgIGNhc2UgXCJjYXRcIjpcbiAgICAgIHJldHVybiBuZXcgQ2F0KCk7XG4gIH1cbiAgcmV0dXJuIG5ldyBCaXJkKCk7XG59XG5cbmludGVyZmFjZSBBbmltYWwge1xuICByZWFkb25seSBuYW1lOiBzdHJpbmc7XG4gIHRhbGsoKTogc3RyaW5nO1xufVxuXG5jbGFzcyBEb2cgaW1wbGVtZW50cyBBbmltYWwge1xuICBuYW1lID0gXCJkb2dcIjtcblxuICB0YWxrKCk6IHN0cmluZyB7XG4gICAgcmV0dXJuIFwid29vZlwiO1xuICB9XG59XG5cbmNsYXNzIENhdCBpbXBsZW1lbnRzIEFuaW1hbCB7XG4gIG5hbWUgPSBcImNhdFwiO1xuXG4gIHRhbGsoKTogc3RyaW5nIHtcbiAgICByZXR1cm4gXCJtZW93XCI7XG4gIH1cbn1cblxuY2xhc3MgQmlyZCBpbXBsZW1lbnRzIEFuaW1hbCB7XG4gIG5hbWUgPSBcImJpcmRcIjtcblxuICB0YWxrKCk6IHN0cmluZyB7XG4gICAgcmV0dXJuIFwiY2hpcnBcIjtcbiAgfVxufVxuIl0sCiAgIm1hcHBpbmdzIjogIjtBQUFBLGVBQWU7QUFBQSxFQUNiLE1BQU0sU0FBNEI7QUFDaEMsVUFBTSxTQUFTLFVBQVUsUUFBUSxHQUFHO0FBQ3BDLFVBQU0sUUFBUSxPQUFPLEtBQUs7QUFDMUIsV0FBTyxJQUFJLFNBQVMsS0FBSztBQUFBLEVBQzNCO0FBQ0Y7QUFFQSxTQUFTLFVBQVUsT0FBdUI7QUFDeEMsVUFBUSxNQUFNLE1BQU0sR0FBRyxFQUFFLElBQUksR0FBRztBQUFBLElBQzlCLEtBQUs7QUFDSCxhQUFPLElBQUksSUFBSTtBQUFBLElBQ2pCLEtBQUs7QUFDSCxhQUFPLElBQUksSUFBSTtBQUFBLEVBQ25CO0FBQ0EsU0FBTyxJQUFJLEtBQUs7QUFDbEI7QUFPQSxNQUFNLElBQXNCO0FBQUEsRUFDMUIsT0FBTztBQUFBLEVBRVAsT0FBZTtBQUNiLFdBQU87QUFBQSxFQUNUO0FBQ0Y7QUFFQSxNQUFNLElBQXNCO0FBQUEsRUFDMUIsT0FBTztBQUFBLEVBRVAsT0FBZTtBQUNiLFdBQU87QUFBQSxFQUNUO0FBQ0Y7QUFFQSxNQUFNLEtBQXVCO0FBQUEsRUFDM0IsT0FBTztBQUFBLEVBRVAsT0FBZTtBQUNiLFdBQU87QUFBQSxFQUNUO0FBQ0Y7IiwKICAibmFtZXMiOiBbXQp9Cg==

View File

@@ -0,0 +1,46 @@
export default {
fetch(request: Request): Response {
const animal = getAnimal(request.url);
const voice = animal.talk();
return new Response(voice);
},
};
function getAnimal(query: string): Animal {
switch (query.split("/").pop()) {
case "dog":
return new Dog();
case "cat":
return new Cat();
}
return new Bird();
}
interface Animal {
readonly name: string;
talk(): string;
}
class Dog implements Animal {
name = "dog";
talk(): string {
return "woof";
}
}
class Cat implements Animal {
name = "cat";
talk(): string {
return "meow";
}
}
class Bird implements Animal {
name = "bird";
talk(): string {
return "chirp";
}
}

View File

@@ -0,0 +1,20 @@
export default {
fetch(request) {
return new Response(a());
},
};
function a() {
return b();
}
function b() {
return c();
}
function c() {
function d() {
return "hello";
}
return d();
}

View File

@@ -0,0 +1,31 @@
import { test, expect } from "bun:test";
import { readFileSync } from "node:fs";
import { SourceMap } from "./sourcemap";
test("works without source map", () => {
const sourceMap = getSourceMap("without-sourcemap.js");
expect(sourceMap.generatedLocation({ line: 7 })).toEqual({ line: 7, column: 0, verified: true });
expect(sourceMap.generatedLocation({ line: 7, column: 2 })).toEqual({ line: 7, column: 2, verified: true });
expect(sourceMap.originalLocation({ line: 11 })).toEqual({ line: 11, column: 0, verified: true });
expect(sourceMap.originalLocation({ line: 11, column: 2 })).toEqual({ line: 11, column: 2, verified: true });
});
test("works with source map", () => {
const sourceMap = getSourceMap("with-sourcemap.js");
// FIXME: Columns don't appear to be accurate for `generatedLocation`
expect(sourceMap.generatedLocation({ line: 3 })).toMatchObject({ line: 4, verified: true });
expect(sourceMap.generatedLocation({ line: 27 })).toMatchObject({ line: 20, verified: true });
expect(sourceMap.originalLocation({ line: 32 })).toEqual({ line: 43, column: 4, verified: true });
expect(sourceMap.originalLocation({ line: 13 })).toEqual({ line: 13, column: 6, verified: true });
});
function getSourceMap(filename: string): SourceMap {
const { pathname } = new URL(`./fixtures/${filename}`, import.meta.url);
const source = readFileSync(pathname, "utf-8");
const match = source.match(/\/\/# sourceMappingURL=(.*)$/m);
if (match) {
const [, url] = match;
return SourceMap(url);
}
return SourceMap();
}

View File

@@ -0,0 +1,193 @@
import type { LineRange, MappedPosition } from "source-map-js";
import { SourceMapConsumer } from "source-map-js";
export type LocationRequest = {
line?: number;
column?: number;
url?: string;
};
export type Location = {
line: number; // 0-based
column: number; // 0-based
} & (
| {
verified: true;
}
| {
verified?: false;
message?: string;
}
);
export interface SourceMap {
generatedLocation(request: LocationRequest): Location;
originalLocation(request: LocationRequest): Location;
}
class ActualSourceMap implements SourceMap {
#sourceMap: SourceMapConsumer;
#sources: string[];
constructor(sourceMap: SourceMapConsumer) {
this.#sourceMap = sourceMap;
this.#sources = (sourceMap as any)._absoluteSources;
}
#getSource(url?: string): string {
const sources = this.#sources;
if (!sources.length) {
return "";
}
if (sources.length === 1 || !url) {
return sources[0];
}
for (const source of sources) {
if (url.endsWith(source)) {
return source;
}
}
return "";
}
generatedLocation(request: LocationRequest): Location {
const { line, column, url } = request;
let lineRange: LineRange;
try {
const source = this.#getSource(url);
lineRange = this.#sourceMap.generatedPositionFor({
line: lineTo1BasedLine(line),
column: columnToColumn(column),
source,
});
} catch (error) {
return {
line: lineToLine(line),
column: columnToColumn(column),
verified: false,
message: unknownToError(error),
};
}
if (!locationIsValid(lineRange)) {
return {
line: lineToLine(line),
column: columnToColumn(column),
verified: false,
};
}
const { line: gline, column: gcolumn } = lineRange;
return {
line: lineToLine(gline),
column: columnToColumn(gcolumn),
verified: true,
};
}
originalLocation(request: LocationRequest): Location {
const { line, column } = request;
let mappedPosition: MappedPosition;
try {
mappedPosition = this.#sourceMap.originalPositionFor({
line: lineTo1BasedLine(line),
column: columnToColumn(column),
});
} catch (error) {
return {
line: lineToLine(line),
column: columnToColumn(column),
verified: false,
message: unknownToError(error),
};
}
if (!locationIsValid(mappedPosition)) {
return {
line: lineToLine(line),
column: columnToColumn(column),
verified: false,
};
}
const { line: oline, column: ocolumn } = mappedPosition;
return {
line: lineTo0BasedLine(oline),
column: columnToColumn(ocolumn),
verified: true,
};
}
}
class NoopSourceMap implements SourceMap {
generatedLocation(request: LocationRequest): Location {
const { line, column } = request;
return {
line: lineToLine(line),
column: columnToColumn(column),
verified: true,
};
}
originalLocation(request: LocationRequest): Location {
const { line, column } = request;
return {
line: lineToLine(line),
column: columnToColumn(column),
verified: true,
};
}
}
const defaultSourceMap = new NoopSourceMap();
export function SourceMap(url?: string): SourceMap {
if (!url || !url.startsWith("data:")) {
return defaultSourceMap;
}
try {
const [_, base64] = url.split(",", 2);
const decoded = Buffer.from(base64, "base64url").toString("utf8");
const schema = JSON.parse(decoded);
const sourceMap = new SourceMapConsumer(schema);
return new ActualSourceMap(sourceMap);
} catch (error) {
console.warn("Failed to parse source map URL", url);
}
return defaultSourceMap;
}
function lineTo1BasedLine(line?: number): number {
return numberIsValid(line) ? line + 1 : 1;
}
function lineTo0BasedLine(line?: number): number {
return numberIsValid(line) ? line - 1 : 0;
}
function lineToLine(line?: number): number {
return numberIsValid(line) ? line : 0;
}
function columnToColumn(column?: number): number {
return numberIsValid(column) ? column : 0;
}
function locationIsValid(location: Location): location is Location {
const { line, column } = location;
return numberIsValid(line) && numberIsValid(column);
}
function numberIsValid(number?: number): number is number {
return typeof number === "number" && isFinite(number) && number >= 0;
}
function unknownToError(error: unknown): string {
if (error instanceof Error) {
const { message } = error;
return message;
}
return String(error);
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,37 @@
export type Protocol = {
$schema: string;
title: string;
description: string;
type: "object";
definitions: Record<string, Type>;
};
export type Type = {
description?: string;
} & (
| {
type: "number" | "integer" | "boolean";
}
| {
type: "string";
enum?: string[];
enumDescriptions?: string[];
}
| {
type: "object";
properties?: Record<string, Type>;
required?: string[];
}
| {
type: "array";
items?: Type;
}
| {
type?: undefined;
$ref: string;
}
| {
type?: undefined;
allOf: Type[];
}
);

View File

@@ -3,18 +3,19 @@
"lib": ["ESNext"],
"module": "esnext",
"target": "esnext",
"moduleResolution": "bundler",
"moduleResolution": "nodenext",
"moduleDetection": "force",
"allowImportingTsExtensions": true,
"noEmit": true,
"composite": true,
"strict": true,
"downlevelIteration": true,
"skipLibCheck": true,
"jsx": "preserve",
"allowSyntheticDefaultImports": true,
"forceConsistentCasingInFileNames": true,
"inlineSourceMap": true,
"allowJs": true,
"noEmit": true,
"types": [
"bun-types" // add Bun global
]
}
"outDir": "dist",
},
"include": ["src", "scripts", "../bun-types/index.d.ts", "../bun-inspector-protocol/src"]
}

View File

@@ -1,4 +1,5 @@
{
"name": "bun-ecosystem-ci",
"private": true,
"dependencies": {
"globby": "^13.1.3"
@@ -11,4 +12,4 @@
"format": "prettier --write src",
"test": "bun run src/runner.ts"
}
}
}

View File

@@ -1,4 +1,4 @@
# web-inspector-bun
# bun-devtools-frontend
This is the WebKit Web Inspector bundled as standalone assets.

View File

@@ -0,0 +1,20 @@
{
"compilerOptions": {
"lib": ["ESNext"],
"module": "esnext",
"target": "esnext",
"moduleResolution": "nodenext",
"moduleDetection": "force",
"strict": true,
"downlevelIteration": true,
"skipLibCheck": true,
"allowSyntheticDefaultImports": true,
"forceConsistentCasingInFileNames": true,
"inlineSourceMap": true,
"allowJs": true,
"noImplicitAny": false,
"outDir": "dist",
"types": ["node"]
},
"include": [".", "../bun-types/index.d.ts"]
}

View File

@@ -0,0 +1,2 @@
protocol/*/protocol.json linguist-generated=true
protocol/*/index.d.ts linguist-generated=true

View File

@@ -0,0 +1,2 @@
protocol/*.json
protocol/v8

View File

@@ -0,0 +1 @@
# bun-inspector-protocol

Binary file not shown.

View File

@@ -0,0 +1,4 @@
export type * from "./src/protocol";
export type * from "./src/inspector";
export * from "./src/util/preview";
export * from "./src/inspector/websocket";

View File

@@ -0,0 +1,7 @@
{
"name": "bun-inspector-protocol",
"version": "0.0.1",
"dependencies": {
"ws": "^8.13.0"
}
}

View File

@@ -0,0 +1,202 @@
import type { Protocol, Domain, Property } from "../src/protocol/schema";
import { readFileSync, writeFileSync } from "node:fs";
import { spawnSync } from "node:child_process";
run().catch(console.error);
async function run() {
const cwd = new URL("../protocol/", import.meta.url);
const runner = "Bun" in globalThis ? "bunx" : "npx";
const write = (name: string, data: string) => {
const path = new URL(name, cwd);
writeFileSync(path, data);
spawnSync(runner, ["prettier", "--write", path.pathname], { cwd, stdio: "ignore" });
};
const base = readFileSync(new URL("protocol.d.ts", cwd), "utf-8");
const baseNoComments = base.replace(/\/\/.*/g, "");
const jsc = await downloadJsc();
write("jsc/protocol.json", JSON.stringify(jsc));
write("jsc/index.d.ts", "// GENERATED - DO NOT EDIT\n" + formatProtocol(jsc, baseNoComments));
const v8 = await downloadV8();
write("v8/protocol.json", JSON.stringify(v8));
write("v8/index.d.ts", "// GENERATED - DO NOT EDIT\n" + formatProtocol(v8, baseNoComments));
}
function formatProtocol(protocol: Protocol, extraTs?: string): string {
const { name, domains } = protocol;
const eventMap = new Map();
const commandMap = new Map();
let body = `export namespace ${name} {`;
for (const { domain, types = [], events = [], commands = [] } of domains) {
body += `export namespace ${domain} {`;
for (const type of types) {
body += formatProperty(type);
}
for (const { name, description, parameters = [] } of events) {
const symbol = `${domain}.${name}`;
const title = toTitle(name);
eventMap.set(symbol, `${domain}.${title}`);
body += formatProperty({
id: `${title}Event`,
type: "object",
description: `${description}\n@event \`${symbol}\``,
properties: parameters,
});
}
for (const { name, description, parameters = [], returns = [] } of commands) {
const symbol = `${domain}.${name}`;
const title = toTitle(name);
commandMap.set(symbol, `${domain}.${title}`);
body += formatProperty({
id: `${title}Request`,
type: "object",
description: `${description}\n@request \`${symbol}\``,
properties: parameters,
});
body += formatProperty({
id: `${title}Response`,
type: "object",
description: `${description}\n@response \`${symbol}\``,
properties: returns,
});
}
body += "};";
}
for (const type of ["Event", "Request", "Response"]) {
const sourceMap = type === "Event" ? eventMap : commandMap;
body += formatProperty({
id: `${type}Map`,
type: "object",
properties: [...sourceMap.entries()].map(([name, title]) => ({
name: `"${name}"`,
type: undefined,
$ref: `${title}${type}`,
})),
});
}
if (extraTs) {
body += extraTs;
}
return body + "};";
}
function formatProperty(property: Property): string {
const { id, description, type, optional } = property;
let body = "";
if (id) {
if (description) {
body += `\n${toComment(description)}\n`;
}
body += `export type ${id}=`;
}
if (type === "boolean") {
body += "boolean";
} else if (type === "number" || type === "integer") {
body += "number";
} else if (type === "string") {
const { enum: choices } = property;
if (choices) {
body += choices.map(value => `"${value}"`).join("|");
} else {
body += "string";
}
} else if (type === "array") {
const { items } = property;
const itemType = items ? formatProperty(items) : "unknown";
body += `${itemType}[]`;
} else if (type === "object") {
const { properties } = property;
if (!properties) {
body += "Record<string, unknown>";
} else if (properties.length === 0) {
body += "{}";
} else {
body += "{";
for (const { name, description, ...property } of properties) {
if (description) {
body += `\n${toComment(description)}`;
}
const delimit = property.optional ? "?:" : ":";
body += `\n${name}${delimit}${formatProperty({ ...property, id: undefined })};`;
}
body += "}";
}
} else if ("$ref" in property) {
body += property.$ref;
} else {
body += "unknown";
}
if (optional) {
body += "|undefined";
}
if (id) {
body += ";";
}
return body;
}
/**
* @link https://github.com/ChromeDevTools/devtools-protocol/tree/master/json
*/
async function downloadV8(): Promise<Protocol> {
const baseUrl = "https://raw.githubusercontent.com/ChromeDevTools/devtools-protocol/master/json";
const domains = ["Runtime", "Console", "Debugger", "Memory", "HeapProfiler", "Profiler", "Network", "Inspector"];
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 => !domains.includes(domain.domain))
.sort((a, b) => a.domain.localeCompare(b.domain)),
}));
}
/**
* @link https://github.com/WebKit/WebKit/tree/main/Source/JavaScriptCore/inspector/protocol
*/
async function downloadJsc(): Promise<Protocol> {
const baseUrl = "https://raw.githubusercontent.com/WebKit/WebKit/main/Source/JavaScriptCore/inspector/protocol";
const domains = [
"Runtime",
"Console",
"Debugger",
"Heap",
"ScriptProfiler",
"CPUProfiler",
"GenericTypes",
"Network",
"Inspector",
];
return {
name: "JSC",
version: {
major: 1,
minor: 3,
},
domains: await Promise.all(domains.map(domain => download<Domain>(`${baseUrl}/${domain}.json`))).then(domains =>
domains.sort((a, b) => a.domain.localeCompare(b.domain)),
),
};
}
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();
}
function toTitle(name: string): string {
return name.charAt(0).toUpperCase() + name.slice(1);
}
function toComment(description?: string): string {
if (!description) {
return "";
}
const lines = ["/**", ...description.split("\n").map(line => ` * ${line.trim()}`), "*/"];
return lines.join("\n");
}

View File

@@ -0,0 +1,49 @@
import type { JSC } from "..";
/**
* A client that can send and receive messages to/from a debugger.
*/
export abstract class Inspector {
constructor(listener?: InspectorListener);
/**
* Starts the inspector.
*/
start(...args: unknown[]): void;
/**
* Sends a request to the debugger.
*/
send<M extends keyof JSC.RequestMap & keyof JSC.ResponseMap>(
method: M,
params?: JSC.RequestMap[M],
): Promise<JSC.ResponseMap[M]>;
/**
* Accepts a message from the debugger.
* @param message the unparsed message from the debugger
*/
accept(message: string): void;
/**
* If the inspector is closed.
*/
get closed(): boolean;
/**
* Closes the inspector.
*/
close(...args: unknown[]): void;
}
export type InspectorListener = {
/**
* Defines a handler when a debugger event is received.
*/
[M in keyof JSC.EventMap]?: (event: JSC.EventMap[M]) => void;
} & {
/**
* Defines a handler when the debugger is connected or reconnected.
*/
["Inspector.connected"]?: () => void;
/**
* Defines a handler when the debugger is disconnected.
* @param error the error that caused the disconnect, if any
*/
["Inspector.disconnected"]?: (error?: Error) => void;
};

View File

@@ -0,0 +1,217 @@
import type { Inspector, InspectorListener } from ".";
import type { JSC } from "../protocol";
import { WebSocket } from "ws";
export type WebSocketInspectorOptions = {
url?: string | URL;
listener?: InspectorListener;
logger?: (...messages: unknown[]) => void;
};
/**
* An inspector that communicates with a debugger over a WebSocket.
*/
export class WebSocketInspector implements Inspector {
#url?: URL;
#webSocket?: WebSocket;
#requestId: number;
#pendingRequests: Map<number, (result: unknown) => void>;
#pendingMessages: string[];
#listener: InspectorListener;
#log: (...messages: unknown[]) => void;
constructor({ url, listener, logger }: WebSocketInspectorOptions) {
this.#url = url ? new URL(url) : undefined;
this.#requestId = 1;
this.#pendingRequests = new Map();
this.#pendingMessages = [];
this.#listener = listener ?? {};
this.#log = logger ?? (() => {});
}
start(url?: string | URL): void {
if (url) {
this.#url = new URL(url);
}
if (this.#url) {
this.#connect();
}
}
#connect(): void {
if (!this.#url) {
return;
}
this.#webSocket?.close();
let webSocket: WebSocket;
try {
this.#log("connecting:", this.#url.href);
webSocket = new WebSocket(this.#url, {
headers: {
"Ref-Event-Loop": "0",
},
});
} catch (error) {
this.#close(unknownToError(error));
return;
}
webSocket.addEventListener("open", () => {
this.#log("connected");
for (const message of this.#pendingMessages) {
this.#send(message);
}
this.#pendingMessages.length = 0;
this.#listener["Inspector.connected"]?.();
});
webSocket.addEventListener("message", ({ data }) => {
if (typeof data === "string") {
this.accept(data);
}
});
webSocket.addEventListener("error", event => {
this.#log("error:", event);
this.#close(unknownToError(event));
});
webSocket.addEventListener("unexpected-response", () => {
this.#log("unexpected-response");
this.#close(new Error("WebSocket upgrade failed"));
});
webSocket.addEventListener("close", ({ code, reason }) => {
this.#log("closed:", code, reason);
if (code === 1001) {
this.#close();
} else {
this.#close(new Error(`WebSocket closed: ${code} ${reason}`.trimEnd()));
}
});
this.#webSocket = webSocket;
}
send<M extends keyof JSC.RequestMap & keyof JSC.ResponseMap>(
method: M,
params?: JSC.RequestMap[M] | undefined,
): Promise<JSC.ResponseMap[M]> {
const id = this.#requestId++;
const request = { id, method, params };
this.#log("-->", request);
return new Promise((resolve, reject) => {
const done = (result: any) => {
this.#pendingRequests.delete(id);
if (result instanceof Error) {
reject(result);
} else {
resolve(result);
}
};
this.#pendingRequests.set(id, done);
this.#send(JSON.stringify(request));
});
}
#send(message: string): void {
if (this.#webSocket) {
const { readyState } = this.#webSocket!;
if (readyState === WebSocket.OPEN) {
this.#webSocket.send(message);
}
return;
}
if (!this.#pendingMessages.includes(message)) {
this.#pendingMessages.push(message);
}
}
accept(message: string): void {
let event: JSC.Event | JSC.Response;
try {
event = JSON.parse(message);
} catch (error) {
this.#log("Failed to parse message:", message);
return;
}
this.#log("<--", event);
if (!("id" in event)) {
const { method, params } = event;
try {
this.#listener[method]?.(params as any);
} catch (error) {
this.#log(`Failed to accept ${method} event:`, error);
}
return;
}
const { id } = event;
const resolve = this.#pendingRequests.get(id);
if (!resolve) {
this.#log("Failed to accept response with unknown ID:", id);
return;
}
this.#pendingRequests.delete(id);
if ("error" in event) {
const { error } = event;
const { message } = error;
resolve(new Error(message));
} else {
const { result } = event;
resolve(result);
}
}
get closed(): boolean {
if (!this.#webSocket) {
return true;
}
const { readyState } = this.#webSocket;
switch (readyState) {
case WebSocket.CLOSED:
case WebSocket.CLOSING:
return true;
}
return false;
}
close(code?: number, reason?: string): void {
this.#webSocket?.close(code ?? 1001, reason);
}
#close(error?: Error): void {
try {
this.#listener["Inspector.disconnected"]?.(error);
} finally {
for (const resolve of this.#pendingRequests.values()) {
resolve(error ?? new Error("WebSocket closed"));
}
this.#pendingRequests.clear();
}
}
}
function unknownToError(input: unknown): Error {
if (input instanceof Error) {
return input;
}
if (typeof input === "object" && input !== null && "message" in input) {
const { message } = input;
return new Error(`${message}`);
}
return new Error(`${input}`);
}

View File

@@ -0,0 +1 @@
export type { JSC } from "./jsc";

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,28 @@
// @ts-nocheck
// The content of this file is included in each generated protocol file.
export type Event<T extends keyof EventMap = keyof EventMap> = {
readonly method: T;
readonly params: EventMap[T];
};
export type Request<T extends keyof RequestMap = keyof RequestMap> = {
readonly id: number;
readonly method: T;
readonly params: RequestMap[T];
};
export type Response<T extends keyof ResponseMap = keyof ResponseMap> = {
readonly id: number;
} & (
| {
readonly method?: T;
readonly result: ResponseMap[T];
}
| {
readonly error: {
readonly code?: string;
readonly message: string;
};
}
);

View File

@@ -0,0 +1,58 @@
// Represents the schema of the protocol.json file.
export type Protocol = {
readonly name: string;
readonly version: {
readonly major: number;
readonly minor: number;
};
readonly domains: readonly Domain[];
};
export type Domain = {
readonly domain: string;
readonly dependencies?: readonly string[];
readonly types: readonly Property[];
readonly commands?: readonly Command[];
readonly events?: readonly Event[];
};
export type Command = {
readonly name: string;
readonly description?: string;
readonly parameters?: readonly Property[];
readonly returns?: readonly Property[];
};
export type Event = {
readonly name: string;
readonly description?: string;
readonly parameters: readonly Property[];
};
export type Property = {
readonly id?: string;
readonly name?: string;
readonly description?: string;
readonly optional?: boolean;
} & (
| {
readonly type: "array";
readonly items?: Property;
}
| {
readonly type: "object";
readonly properties?: readonly Property[];
}
| {
readonly type: "string";
readonly enum?: readonly string[];
}
| {
readonly type: "boolean" | "number" | "integer";
}
| {
readonly type: undefined;
readonly $ref: string;
}
);

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,113 @@
import type { JSC } from "../protocol";
export function remoteObjectToString(remoteObject: JSC.Runtime.RemoteObject, topLevel?: boolean): string {
const { type, subtype, value, description, className, preview } = remoteObject;
switch (type) {
case "undefined":
return "undefined";
case "boolean":
case "number":
return description ?? JSON.stringify(value);
case "string":
if (topLevel) {
return String(value ?? description);
}
return JSON.stringify(value ?? description);
case "symbol":
case "bigint":
return description!;
case "function":
return description!.replace("function", "ƒ") || "ƒ";
}
switch (subtype) {
case "null":
return "null";
case "regexp":
case "date":
case "error":
return description!;
}
if (preview) {
return objectPreviewToString(preview);
}
if (className) {
return className;
}
return description || "Object";
}
export function objectPreviewToString(objectPreview: JSC.Runtime.ObjectPreview): string {
const { type, subtype, entries, properties, overflow, description, size } = objectPreview;
if (type !== "object") {
return remoteObjectToString(objectPreview);
}
let items: string[];
if (entries) {
items = entries.map(entryPreviewToString).sort();
} else if (properties) {
if (isIndexed(subtype)) {
items = properties.map(indexedPropertyPreviewToString).sort();
} else {
items = properties.map(namedPropertyPreviewToString).sort();
}
} else {
items = ["…"];
}
if (overflow) {
items.push("…");
}
let label: string;
if (description === "Object") {
label = "";
} else if (size === undefined) {
label = description!;
} else {
label = `${description}(${size})`;
}
if (!items.length) {
return label || "{}";
}
if (label) {
label += " ";
}
if (isIndexed(subtype)) {
return `${label}[${items.join(", ")}]`;
}
return `${label}{${items.join(", ")}}`;
}
function propertyPreviewToString(propertyPreview: JSC.Runtime.PropertyPreview): string {
const { type, value, ...preview } = propertyPreview;
if (type === "accessor") {
return "ƒ";
}
return remoteObjectToString({ ...preview, type, description: value });
}
function entryPreviewToString(entryPreview: JSC.Runtime.EntryPreview): string {
const { key, value } = entryPreview;
if (key) {
return `${objectPreviewToString(key)} => ${objectPreviewToString(value)}`;
}
return objectPreviewToString(value);
}
function namedPropertyPreviewToString(propertyPreview: JSC.Runtime.PropertyPreview): string {
const { name, valuePreview } = propertyPreview;
if (valuePreview) {
return `${name}: ${objectPreviewToString(valuePreview)}`;
}
return `${name}: ${propertyPreviewToString(propertyPreview)}`;
}
function indexedPropertyPreviewToString(propertyPreview: JSC.Runtime.PropertyPreview): string {
const { valuePreview } = propertyPreview;
if (valuePreview) {
return objectPreviewToString(valuePreview);
}
return propertyPreviewToString(propertyPreview);
}
function isIndexed(type?: JSC.Runtime.RemoteObject["subtype"]): boolean {
return type === "array" || type === "set" || type === "weakset";
}

View File

@@ -0,0 +1,143 @@
// Bun Snapshot v1, https://goo.gl/fbAQLP
exports[`remoteObjectToString 1`] = `"undefined"`;
exports[`remoteObjectToString 2`] = `"null"`;
exports[`remoteObjectToString 3`] = `"true"`;
exports[`remoteObjectToString 4`] = `"false"`;
exports[`remoteObjectToString 5`] = `"0"`;
exports[`remoteObjectToString 6`] = `"1"`;
exports[`remoteObjectToString 7`] = `"3.141592653589793"`;
exports[`remoteObjectToString 8`] = `"-2.718281828459045"`;
exports[`remoteObjectToString 9`] = `"NaN"`;
exports[`remoteObjectToString 10`] = `"Infinity"`;
exports[`remoteObjectToString 11`] = `"-Infinity"`;
exports[`remoteObjectToString 12`] = `"0n"`;
exports[`remoteObjectToString 13`] = `"1n"`;
exports[`remoteObjectToString 14`] = `"10000000000000n"`;
exports[`remoteObjectToString 15`] = `"-10000000000000n"`;
exports[`remoteObjectToString 16`] = `""""`;
exports[`remoteObjectToString 17`] = `"" ""`;
exports[`remoteObjectToString 18`] = `""Hello""`;
exports[`remoteObjectToString 19`] = `""Hello World""`;
exports[`remoteObjectToString 20`] = `"Array(0)"`;
exports[`remoteObjectToString 21`] = `"Array(3) [1, 2, 3]"`;
exports[`remoteObjectToString 22`] = `"Array(4) ["a", 1, null, undefined]"`;
exports[`remoteObjectToString 23`] = `"Array(2) [1, Array]"`;
exports[`remoteObjectToString 24`] = `"Array(1) [Array]"`;
exports[`remoteObjectToString 25`] = `"{}"`;
exports[`remoteObjectToString 26`] = `"{a: 1}"`;
exports[`remoteObjectToString 27`] = `"{a: 1, b: 2, c: 3}"`;
exports[`remoteObjectToString 28`] = `"{a: Object}"`;
exports[`remoteObjectToString 29`] = `
"ƒ() {
}"
`;
exports[`remoteObjectToString 30`] = `
"ƒ namedFunction() {
}"
`;
exports[`remoteObjectToString 31`] = `
"class {
}"
`;
exports[`remoteObjectToString 32`] = `
"class namedClass {
}"
`;
exports[`remoteObjectToString 33`] = `
"class namedClass {
a() {
}
b = 1;
c = [
null,
undefined,
"a",
{
a: 1,
b: 2,
c: 3
}
];
}"
`;
exports[`remoteObjectToString 34`] = `"Wed Dec 31 1969 16:00:00 GMT-0800 (Pacific Standard Time)"`;
exports[`remoteObjectToString 35`] = `"Invalid Date"`;
exports[`remoteObjectToString 36`] = `"/(?:)/"`;
exports[`remoteObjectToString 37`] = `"/abc/"`;
exports[`remoteObjectToString 38`] = `"/abc/g"`;
exports[`remoteObjectToString 39`] = `"/abc/"`;
exports[`remoteObjectToString 40`] = `"Set(0)"`;
exports[`remoteObjectToString 41`] = `"Set(3) [1, 2, 3]"`;
exports[`remoteObjectToString 42`] = `"WeakSet(0)"`;
exports[`remoteObjectToString 43`] = `"WeakSet(3) [{a: 1}, {b: 2}, {c: 3}]"`;
exports[`remoteObjectToString 44`] = `"Map(0)"`;
exports[`remoteObjectToString 45`] = `"Map(3) {"a" => 1, "b" => 2, "c" => 3}"`;
exports[`remoteObjectToString 46`] = `"WeakMap(0)"`;
exports[`remoteObjectToString 47`] = `"WeakMap(3) {{a: 1} => 1, {b: 2} => 2, {c: 3} => 3}"`;
exports[`remoteObjectToString 48`] = `"Symbol()"`;
exports[`remoteObjectToString 49`] = `"Symbol(namedSymbol)"`;
exports[`remoteObjectToString 50`] = `"Error"`;
exports[`remoteObjectToString 51`] = `"TypeError: This is a TypeError"`;
exports[`remoteObjectToString 52`] = `"Headers {append: ƒ, delete: ƒ, get: ƒ, getAll: ƒ, has: ƒ, …}"`;
exports[`remoteObjectToString 53`] = `"Headers {a: "1", append: ƒ, b: "2", delete: ƒ, get: ƒ, …}"`;
exports[`remoteObjectToString 54`] = `"Request {arrayBuffer: ƒ, blob: ƒ, body: null, bodyUsed: false, cache: "default", …}"`;
exports[`remoteObjectToString 55`] = `"Request {arrayBuffer: ƒ, blob: ƒ, body: ReadableStream, bodyUsed: false, cache: "default", …}"`;
exports[`remoteObjectToString 56`] = `"Response {arrayBuffer: ƒ, blob: ƒ, body: null, bodyUsed: false, clone: ƒ, …}"`;
exports[`remoteObjectToString 57`] = `"Response {arrayBuffer: ƒ, blob: ƒ, body: ReadableStream, bodyUsed: false, clone: ƒ, …}"`;

View File

@@ -0,0 +1,99 @@
console.log(
undefined,
null,
true,
false,
0,
1,
Math.PI,
-Math.E,
NaN,
Infinity,
-Infinity,
BigInt(0),
BigInt(1),
BigInt("10000000000000"),
BigInt("-10000000000000"),
"",
" ",
"Hello",
"Hello World",
[],
[1, 2, 3],
["a", 1, null, undefined],
[1, [2, [3, [4, [5, [6, [7, [8, [9, [10]]]]]]]]]],
[[[[[]]]]],
{},
{ a: 1 },
{ a: 1, b: 2, c: 3 },
{ a: { b: { c: { d: { e: { f: { g: { h: { i: { j: 10 } } } } } } } } } },
function () {},
function namedFunction() {},
class {},
class namedClass {},
class namedClass {
a() {}
b = 1;
c = [
null,
undefined,
"a",
{
a: 1,
b: 2,
c: 3,
},
];
},
new Date(0),
new Date(NaN),
new RegExp(),
new RegExp("abc"),
new RegExp("abc", "g"),
/abc/,
new Set(),
new Set([1, 2, 3]),
new WeakSet(),
new WeakSet([{ a: 1 }, { b: 2 }, { c: 3 }]),
new Map(),
new Map([
["a", 1],
["b", 2],
["c", 3],
]),
new WeakMap(),
new WeakMap([
[{ a: 1 }, 1],
[{ b: 2 }, 2],
[{ c: 3 }, 3],
]),
Symbol(),
Symbol("namedSymbol"),
new Error(),
new TypeError("This is a TypeError"),
//"a".repeat(10000),
//["a"].fill("a", 0, 10000),
new Headers(),
new Headers({
a: "1",
b: "2",
}),
new Request("https://example.com/"),
new Request("https://example.com/", {
method: "POST",
headers: {
a: "1",
b: "2",
},
body: '{"example":true}',
}),
new Response(),
new Response('{"example":true}', {
status: 200,
statusText: "OK",
headers: {
a: "1",
b: "2",
},
}),
);

View File

@@ -0,0 +1,61 @@
import { beforeAll, afterAll, test, expect } from "bun:test";
import type { PipedSubprocess } from "bun";
import { spawn } from "bun";
import type { JSC } from "../..";
import { WebSocketInspector, remoteObjectToString } from "../..";
let subprocess: PipedSubprocess | undefined;
let objects: JSC.Runtime.RemoteObject[] = [];
beforeAll(async () => {
subprocess = spawn({
cwd: import.meta.dir,
cmd: [process.argv0, "--inspect-wait=0", "preview.js"],
stdout: "pipe",
stderr: "pipe",
stdin: "pipe",
});
const decoder = new TextDecoder();
let url: URL;
for await (const chunk of subprocess!.stdout) {
const text = decoder.decode(chunk);
if (text.includes("ws://")) {
url = new URL(/(ws:\/\/.*)/.exec(text)![0]);
break;
}
}
objects = await new Promise((resolve, reject) => {
const inspector = new WebSocketInspector({
url,
listener: {
["Inspector.connected"]: () => {
inspector.send("Inspector.enable");
inspector.send("Runtime.enable");
inspector.send("Console.enable");
inspector.send("Debugger.enable");
inspector.send("Debugger.resume");
inspector.send("Inspector.initialized");
},
["Inspector.disconnected"]: error => {
reject(error);
},
["Console.messageAdded"]: ({ message }) => {
const { parameters } = message;
resolve(parameters!);
inspector.close();
},
},
});
inspector.start();
});
});
afterAll(() => {
subprocess?.kill();
});
test("remoteObjectToString", () => {
for (const object of objects) {
expect(remoteObjectToString(object)).toMatchSnapshot();
}
});

View File

@@ -0,0 +1,18 @@
{
"compilerOptions": {
"lib": ["ESNext"],
"module": "ESNext",
"target": "ESNext",
"moduleResolution": "NodeNext",
"moduleDetection": "force",
"strict": true,
"downlevelIteration": true,
"skipLibCheck": true,
"allowSyntheticDefaultImports": true,
"forceConsistentCasingInFileNames": true,
"inlineSourceMap": true,
"allowJs": true,
"outDir": "dist",
},
"include": [".", "../bun-types/index.d.ts"]
}

View File

@@ -1,4 +1,6 @@
{
"name": "bun-lambda",
"private": true,
"devDependencies": {
"bun-types": "^0.7.0",
"jszip": "^3.10.1",

View File

@@ -1,13 +1,13 @@
{
"compilerOptions": {
"lib": [
"ESNext"
],
"lib": ["ESNext"],
"module": "esnext",
"target": "esnext",
"moduleResolution": "bundler",
"moduleDetection": "force",
"allowImportingTsExtensions": true,
"noEmit": true,
"composite": true,
"strict": true,
"downlevelIteration": true,
"skipLibCheck": true,
@@ -19,8 +19,5 @@
"bun-types" // add Bun global
]
},
"include": [
"**/*.ts",
"modules.d.ts"
]
}
"include": ["**/*.ts", "modules.d.ts"]
}

View File

@@ -1,13 +1,13 @@
{
"compilerOptions": {
"lib": [
"ESNext"
],
"lib": ["ESNext"],
"module": "esnext",
"target": "esnext",
"moduleResolution": "bundler",
"moduleDetection": "force",
"allowImportingTsExtensions": true,
"noEmit": true,
"composite": true,
"strict": true,
"downlevelIteration": true,
"skipLibCheck": true,
@@ -19,8 +19,5 @@
"bun-types" // add Bun global
]
},
"include": [
"**/*.ts",
"modules.d.ts"
]
}
"include": ["**/*.ts", "modules.d.ts"]
}

Binary file not shown.

View File

@@ -15,11 +15,13 @@
"node": "node --enable-source-maps --import ./dist/src/repl.js",
"clean": "rm -rf dist",
"preprocess": "bun tools/updateversions.ts",
"build": "bun run clean && bun run preprocess && bunx tsc && bunx copyfiles \"./**/*.wasm\" dist",
"build": "bun run clean && bun run preprocess && bunx tsc && bunx copyfiles \"./lib/**/*.wasm\" dist",
"build/wasm": "bun run build/zighash",
"build/zighash": "cd lib/zighash && bun run build && cd ../.."
},
"dependencies": {
"bun-wasm": "link:bun-wasm",
"chalk": "^5.3.0",
"js-md4": "^0.3.2",
"open-editor": "^4.0.0",
"supports-color": "^9.4.0",

View File

@@ -16,6 +16,7 @@ import {
} from './bun/hashes.js';
import { ArrayBufferSink as ArrayBufferSinkPolyfill } from './bun/arraybuffersink.js';
import { FileBlob, NodeJSStreamFileBlob } from './bun/fileblob.js';
import TranspilerImpl from './bun/transpiler.js';
import fs from 'node:fs';
import v8 from 'node:v8';
import path from 'node:path';
@@ -32,7 +33,7 @@ export const main = path.resolve(process.cwd(), process.argv[1] ?? 'repl') satis
//? These are automatically updated on build by tools/updateversions.ts, do not edit manually.
export const version = '0.7.4' satisfies typeof Bun.version;
export const revision = '7088d7e182635a58a50860302da0b1abc42c7ce7' satisfies typeof Bun.revision;
export const revision = '56816a3ec845a4b9fc40ade34dbe5c0033433d51' satisfies typeof Bun.revision;
export const gc = (globalThis.gc ? (() => (globalThis.gc!(), process.memoryUsage().heapUsed)) : (() => {
const err = new Error('[bun-polyfills] Garbage collection polyfills are only available when Node.js is ran with the --expose-gc flag.');
@@ -71,6 +72,8 @@ export const unsafe = {
}
} satisfies typeof Bun['unsafe'];
export const Transpiler = TranspilerImpl satisfies typeof Bun.Transpiler;
export const SHA1 = SHA1Polyfill satisfies typeof Bun.SHA1;
export const MD5 = MD5Polyfill satisfies typeof Bun.MD5;
export const MD4 = MD4Polyfill satisfies typeof Bun.MD4;

View File

@@ -1,103 +1,96 @@
import type { JavaScriptLoader, TranspilerOptions, Transpiler as BunTranspiler, Import } from 'bun';
import { NotImplementedError } from '../../utils/errors.js';
import { transformSync, scan, init } from 'bun-wasm';
import { Message } from 'bun-wasm/schema';
import $ from 'chalk';
// TODO: Possible implementation with WASM builds of bun with just the transpiler?
// NOTE: This is possible to implement with something like SWC, and was previously done,
// but it has lots of quirks due to the differences between SWC and Bun, so the plan is
// to not do that unless there is actual demand for using Bun.Transpiler in Node.js before
// the WASM build is worked on. The signatures are here for now as a placeholder.
await init();
enum InternalImportKind {
'entry-point' = 1, // entry_point
'import-statement' = 2, // stmt
'require-call' = 3, // require
'dynamic-import' = 4, // dynamic
'require-resolve' = 5, // require_resolve
'import-rule' = 6, // at
'url-token' = 7, // url
'internal' = 8, // internal
}
export type ScanImportsEntry = {
kind: 'import-statement' | 'dynamic-import';
path: string;
};
export default class Transpiler implements BunTranspiler {
constructor(options?: TranspilerOptions) {
this.#options = options ?? {};
this.#rootFile = 'input.tsx'; // + (this.#options.loader ?? 'tsx');
//? ^ NOTE: with current bun-wasm builds, the loader option is ignored and hardcoded to tsx
}
#options: TranspilerOptions;
#rootFile: string;
#decoder?: TextDecoder;
#internallyCalled: boolean = false;
async transform(code: StringOrBuffer, loader: JavaScriptLoader): Promise<string> {
if (typeof code !== 'string') code = new TextDecoder().decode(code);
throw new NotImplementedError('Bun.Transpiler', this.transform);
this.#internallyCalled = true;
return this.transformSync(code, loader);
}
transformSync(code: StringOrBuffer, ctx: object): string;
transformSync(code: StringOrBuffer, loader: JavaScriptLoader, ctx: object): string;
transformSync(code: StringOrBuffer, loader?: JavaScriptLoader | undefined): string;
transformSync(code: StringOrBuffer, loader?: JavaScriptLoader | object, ctx: object = {}): string {
if (typeof code !== 'string') code = new TextDecoder().decode(code);
if (typeof loader !== 'string') loader = 'js';
throw new NotImplementedError('Bun.Transpiler', this.transformSync);
if (!code) return ''; // wasm dies with empty string input
if (typeof code !== 'string' && !(code instanceof Uint8Array)) throw new TypeError('code must be a string or Uint8Array');
if (typeof loader !== 'string') loader = this.#options.loader;
const result = transformSync(code, this.#rootFile, loader);
// status 1 = success, status 2 = error
if (result.status === 2) throw formatBuildErrors(result.errors, this.#internallyCalled ? this.transform : this.transformSync);
this.#internallyCalled = false;
this.#decoder ??= new TextDecoder();
return this.#decoder.decode(result.files[0].data);
}
scan(code: StringOrBuffer): { exports: string[]; imports: Import[]; } {
if (typeof code !== 'string') code = new TextDecoder().decode(code);
throw new NotImplementedError('Bun.Transpiler', this.scan);
//return {
// imports: this.scanImports(code),
// exports: this.#scanExports(code)
//};
if (!code) return { exports: [], imports: [] }; // wasm dies with empty string input
if (typeof code !== 'string' && !(code instanceof Uint8Array)) throw new TypeError('code must be a string or Uint8Array');
const result = scan(code, this.#rootFile, this.#options.loader);
if (result.errors.length) throw formatBuildErrors(result.errors, this.#internallyCalled ? this.scanImports : this.scan);
this.#internallyCalled = false;
result.imports.forEach(imp => (imp.kind as unknown) = InternalImportKind[imp.kind]);
return {
exports: result.exports,
imports: result.imports as unknown as Import[],
};
}
scanImports(code: StringOrBuffer): {
kind: 'import-statement' | 'dynamic-import';
path: string;
}[] {
if (typeof code !== 'string') code = new TextDecoder().decode(code);
throw new NotImplementedError('Bun.Transpiler', this.scanImports);
//const imports: { kind: 'import-statement' | 'dynamic-import', path: string }[] = [];
//this.#scanTopLevelImports(code).forEach(x => imports.push({ kind: 'import-statement', path: x }));
//this.#scanDynamicImports(code).forEach(x => imports.push({ kind: 'dynamic-import', path: x }));
//return imports;
scanImports(code: StringOrBuffer): ScanImportsEntry[] {
this.#internallyCalled = true;
return this.scan(code).imports.filter(imp => imp.kind === 'import-statement' || imp.kind === 'dynamic-import') as ScanImportsEntry[];
}
/*#scanDynamicImports(code: string): string[] {
return this.parseSync(code, {
syntax: this.#syntax, target: 'es2022', tsx: this.#options.loader === 'tsx'
}).body.filter(x => x.type === 'ExpressionStatement' && x.expression.type === 'CallExpression' && x.expression.callee.type === 'Import')
.map(i => (((i as swc.ExpressionStatement).expression as swc.CallExpression).arguments[0].expression as swc.StringLiteral).value);
}*/
/*#scanTopLevelImports(code: string): string[] {
return this.parseSync(code, {
syntax: this.#syntax, target: 'es2022', tsx: this.#options.loader === 'tsx'
}).body.filter(x => x.type === 'ImportDeclaration' || x.type === 'ExportAllDeclaration' || x.type === 'ExportNamedDeclaration')
.filter(i => !(i as swc.ImportDeclaration).typeOnly)
.map(i => (i as swc.ImportDeclaration).source.value);
}*/
/*#scanExports(code: string, includeDefault: boolean = false): string[] {
const parsed = this.parseSync(code, {
syntax: this.#syntax, target: 'es2022', tsx: this.#options.loader === 'tsx'
}).body;
const exports = [];
exports.push(parsed.filter(x => x.type === 'ExportDeclaration' && !x.declaration.declare)
.flatMap(i => ((i as swc.ExportDeclaration).declaration as swc.ClassDeclaration).identifier?.value ??
((i as swc.ExportDeclaration).declaration as swc.VariableDeclaration).declarations.map(d => (d.id as swc.Identifier).value)
)
);
exports.push(parsed.filter(x => x.type === 'ExportNamedDeclaration')
.flatMap(i => (i as swc.ExportNamedDeclaration).specifiers
.filter(s => s.type === 'ExportSpecifier' && !s.isTypeOnly)
.map(s => (s as swc.NamedExportSpecifier).exported?.value ?? (s as swc.NamedExportSpecifier).orig.value)
)
);
if (includeDefault) exports.push(this.#scanDefaultExport(code) ?? []);
return exports.flat();
}*/
/*#scanDefaultExport(code: string): 'default' | undefined {
const parsed = this.parseSync(code, {
syntax: this.#syntax, target: 'es2022', tsx: this.#options.loader === 'tsx'
}).body;
const defaultExportDecl = parsed.find(x => x.type === 'ExportDefaultDeclaration') as swc.ExportDefaultDeclaration | undefined;
if (!defaultExportDecl) {
const defaultExportExpr = parsed.find(x => x.type === 'ExportDefaultExpression') as swc.ExportDefaultExpression | undefined;
if (!defaultExportExpr) return undefined;
if (!defaultExportExpr.expression.type.startsWith('Ts')) return 'default';
else return undefined;
}
if (!defaultExportDecl.decl.type.startsWith('Ts') && !Reflect.get(defaultExportDecl.decl, 'declare')) return 'default';
else return undefined;
}*/
#options: TranspilerOptions;
}
function formatBuildErrors(buildErrors: Message[], caller: Transpiler[keyof Transpiler]): AggregateError {
const formatted = buildErrors.map(err => {
const loc = err.data.location;
const str = `${$.redBright('error')}${$.gray(':')} ${$.bold(err.data.text)}\n` +
(loc
? `${highlightErrorChar(loc.line_text, loc.offset)}\n` +
$.redBright.bold('^'.padStart(loc.column)) + '\n' +
`${$.bold(loc.file)}${$.gray(':')}${$.yellowBright(loc.line)}${$.gray(':')}${$.yellowBright(loc.column)} ${$.gray(loc.offset)}`
: ''
);
return { __proto__: Error.prototype, stack: str };
});
const aggregate = new AggregateError(formatted, `Input code has ${formatted.length} error${formatted.length === 1 ? '' : 's'}`);
Error.captureStackTrace(aggregate, caller);
aggregate.name = 'BuildError';
return aggregate;
}
function highlightErrorChar(str: string, at: number): string {
return str.slice(0, at) + $.red(str[at]) + str.slice(at + 1);
}

View File

@@ -15,7 +15,6 @@ globalThis.Bun = bun as typeof bun & {
mmap: typeof import('bun').mmap;
connect: typeof import('bun').connect;
listen: typeof import('bun').listen;
Transpiler: typeof import('bun').Transpiler;
password: typeof import('bun').password;
CryptoHashInterface: typeof import('bun').CryptoHashInterface;
CryptoHasher: typeof import('bun').CryptoHasher;

View File

@@ -15,5 +15,5 @@
"outDir": "dist",
"types": ["node"]
},
"include": [".", "../bun-types/index.d.ts"],
"include": ["src", "lib", "../bun-types/index.d.ts"],
}

View File

@@ -1,4 +1,5 @@
{
"name": "bun-release-action",
"private": true,
"dependencies": {
"aws4fetch": "^1.0.17",

View File

@@ -2308,7 +2308,7 @@ declare module "bun" {
*
* @param args
*/
export function inspect(arg: any, options: BunInspectOptions): string;
export function inspect(arg: any, options?: BunInspectOptions): string;
export namespace inspect {
/**
* That can be used to declare custom inspect functions.

View File

@@ -350,7 +350,7 @@ declare module "bun:ffi" {
type UNTYPED = never;
export type Pointer = number & {};
interface FFITypeToType {
interface FFITypeToArgsType {
[FFIType.char]: number;
[FFIType.int8_t]: number;
[FFIType.i8]: number;
@@ -365,22 +365,54 @@ declare module "bun:ffi" {
[FFIType.int]: number;
[FFIType.uint32_t]: number;
[FFIType.u32]: number;
[FFIType.int64_t]: number;
[FFIType.i64]: number;
[FFIType.uint64_t]: number;
[FFIType.u64]: number;
[FFIType.int64_t]: number | bigint;
[FFIType.i64]: number | bigint;
[FFIType.uint64_t]: number | bigint;
[FFIType.u64]: number | bigint;
[FFIType.double]: number;
[FFIType.f64]: number;
[FFIType.float]: number;
[FFIType.f32]: number;
[FFIType.bool]: boolean;
[FFIType.ptr]: Pointer;
[FFIType.pointer]: Pointer;
[FFIType.ptr]: TypedArray | Pointer | CString | null;
[FFIType.pointer]: TypedArray | Pointer | CString | null;
[FFIType.void]: void;
[FFIType.cstring]: TypedArray | Pointer | CString | null;
[FFIType.i64_fast]: number | bigint;
[FFIType.u64_fast]: number | bigint;
[FFIType.function]: Pointer | JSCallback; // cannot be null
}
interface FFITypeToReturnsType {
[FFIType.char]: number;
[FFIType.int8_t]: number;
[FFIType.i8]: number;
[FFIType.uint8_t]: number;
[FFIType.u8]: number;
[FFIType.int16_t]: number;
[FFIType.i16]: number;
[FFIType.uint16_t]: number;
[FFIType.u16]: number;
[FFIType.int32_t]: number;
[FFIType.i32]: number;
[FFIType.int]: number;
[FFIType.uint32_t]: number;
[FFIType.u32]: number;
[FFIType.int64_t]: bigint;
[FFIType.i64]: bigint;
[FFIType.uint64_t]: bigint;
[FFIType.u64]: bigint;
[FFIType.double]: number;
[FFIType.f64]: number;
[FFIType.float]: number;
[FFIType.f32]: number;
[FFIType.bool]: boolean;
[FFIType.ptr]: Pointer | null;
[FFIType.pointer]: Pointer | null;
[FFIType.void]: void;
[FFIType.cstring]: CString;
[FFIType.i64_fast]: number | bigint;
[FFIType.u64_fast]: number | bigint;
[FFIType.function]: (...args: any[]) => any;
[FFIType.function]: Pointer | null;
}
interface FFITypeStringToType {
["char"]: FFIType.char;
@@ -417,36 +449,7 @@ declare module "bun:ffi" {
export type FFITypeOrString =
| FFIType
| "char"
| "int8_t"
| "i8"
| "uint8_t"
| "u8"
| "int16_t"
| "i16"
| "uint16_t"
| "u16"
| "int32_t"
| "i32"
| "int"
| "uint32_t"
| "u32"
| "int64_t"
| "i64"
| "uint64_t"
| "u64"
| "double"
| "f64"
| "float"
| "f32"
| "bool"
| "ptr"
| "pointer"
| "void"
| "cstring"
| "function"
| "usize"
| "callback";
| keyof FFITypeStringToType;
interface FFIFunction {
/**
@@ -477,7 +480,7 @@ declare module "bun:ffi" {
* }
* ```
*/
args?: FFITypeOrString[];
readonly args?: readonly FFITypeOrString[];
/**
* Return type to a FFI function (C ABI)
*
@@ -505,7 +508,7 @@ declare module "bun:ffi" {
* }
* ```
*/
returns?: FFITypeOrString;
readonly returns?: FFITypeOrString;
/**
* Function pointer to the native function
@@ -516,7 +519,7 @@ declare module "bun:ffi" {
* This is useful if the library has already been loaded
* or if the module is also using Node-API.
*/
ptr?: number | bigint;
readonly ptr?: number | bigint;
/**
* Can C/FFI code call this function from a separate thread?
@@ -533,10 +536,10 @@ declare module "bun:ffi" {
*
* @default false
*/
threadsafe?: boolean;
readonly threadsafe?: boolean;
}
type Symbols = Record<string, FFIFunction>;
type Symbols = Readonly<Record<string, FFIFunction>>;
// /**
// * Compile a callback function
@@ -546,7 +549,7 @@ declare module "bun:ffi" {
// */
// export function callback(ffi: FFIFunction, cb: Function): number;
export interface Library<Fns extends Record<string, Narrow<FFIFunction>>> {
export interface Library<Fns extends Readonly<Record<string, Narrow<FFIFunction>>>> {
symbols: ConvertFns<Fns>;
/**
@@ -577,12 +580,14 @@ declare module "bun:ffi" {
| (T extends object ? { [K in keyof T]: Narrow<T[K]> } : never)
| Extract<{} | null | undefined, T>;
type ConvertFns<Fns extends Record<string, FFIFunction>> = {
type ConvertFns<Fns extends Readonly<Record<string, FFIFunction>>> = {
[K in keyof Fns]: (
...args: Fns[K]["args"] extends infer A extends FFITypeOrString[]
? { [L in keyof A]: FFITypeToType[ToFFIType<A[L]>] }
: never
) => FFITypeToType[ToFFIType<NonNullable<Fns[K]["returns"]>>];
...args: Fns[K]["args"] extends infer A extends readonly FFITypeOrString[]
? { [L in keyof A]: FFITypeToArgsType[ToFFIType<A[L]>] }
: [unknown] extends [Fns[K]["args"]] ? [] : never
) => [unknown] extends [Fns[K]["returns"]]
? void
: FFITypeToReturnsType[ToFFIType<NonNullable<Fns[K]["returns"]>>];
};
/**
@@ -750,6 +755,165 @@ declare module "bun:ffi" {
byteLength?: number,
): ArrayBuffer;
export namespace read {
/**
* The read function behaves similarly to DataView,
* but it's usually faster because it doesn't need to create a DataView or ArrayBuffer.
*
* @param ptr The memory address to read
* @param byteOffset bytes to skip before reading
*
* While there are some checks to catch invalid pointers, this is a difficult
* thing to do safely. Passing an invalid pointer can crash the program and
* reading beyond the bounds of the pointer will crash the program or cause
* undefined behavior. Use with care!
*/
export function u8(ptr: Pointer, byteOffset?: number): number;
/**
* The read function behaves similarly to DataView,
* but it's usually faster because it doesn't need to create a DataView or ArrayBuffer.
*
* @param ptr The memory address to read
* @param byteOffset bytes to skip before reading
*
* While there are some checks to catch invalid pointers, this is a difficult
* thing to do safely. Passing an invalid pointer can crash the program and
* reading beyond the bounds of the pointer will crash the program or cause
* undefined behavior. Use with care!
*/
export function i8(ptr: Pointer, byteOffset?: number): number;
/**
* The read function behaves similarly to DataView,
* but it's usually faster because it doesn't need to create a DataView or ArrayBuffer.
*
* @param ptr The memory address to read
* @param byteOffset bytes to skip before reading
*
* While there are some checks to catch invalid pointers, this is a difficult
* thing to do safely. Passing an invalid pointer can crash the program and
* reading beyond the bounds of the pointer will crash the program or cause
* undefined behavior. Use with care!
*/
export function u16(ptr: Pointer, byteOffset?: number): number;
/**
* The read function behaves similarly to DataView,
* but it's usually faster because it doesn't need to create a DataView or ArrayBuffer.
*
* @param ptr The memory address to read
* @param byteOffset bytes to skip before reading
*
* While there are some checks to catch invalid pointers, this is a difficult
* thing to do safely. Passing an invalid pointer can crash the program and
* reading beyond the bounds of the pointer will crash the program or cause
* undefined behavior. Use with care!
*/
export function i16(ptr: Pointer, byteOffset?: number): number;
/**
* The read function behaves similarly to DataView,
* but it's usually faster because it doesn't need to create a DataView or ArrayBuffer.
*
* @param ptr The memory address to read
* @param byteOffset bytes to skip before reading
*
* While there are some checks to catch invalid pointers, this is a difficult
* thing to do safely. Passing an invalid pointer can crash the program and
* reading beyond the bounds of the pointer will crash the program or cause
* undefined behavior. Use with care!
*/
export function u32(ptr: Pointer, byteOffset?: number): number;
/**
* The read function behaves similarly to DataView,
* but it's usually faster because it doesn't need to create a DataView or ArrayBuffer.
*
* @param ptr The memory address to read
* @param byteOffset bytes to skip before reading
*
* While there are some checks to catch invalid pointers, this is a difficult
* thing to do safely. Passing an invalid pointer can crash the program and
* reading beyond the bounds of the pointer will crash the program or cause
* undefined behavior. Use with care!
*/
export function i32(ptr: Pointer, byteOffset?: number): number;
/**
* The read function behaves similarly to DataView,
* but it's usually faster because it doesn't need to create a DataView or ArrayBuffer.
*
* @param ptr The memory address to read
* @param byteOffset bytes to skip before reading
*
* While there are some checks to catch invalid pointers, this is a difficult
* thing to do safely. Passing an invalid pointer can crash the program and
* reading beyond the bounds of the pointer will crash the program or cause
* undefined behavior. Use with care!
*/
export function f32(ptr: Pointer, byteOffset?: number): number;
/**
* The read function behaves similarly to DataView,
* but it's usually faster because it doesn't need to create a DataView or ArrayBuffer.
*
* @param ptr The memory address to read
* @param byteOffset bytes to skip before reading
*
* While there are some checks to catch invalid pointers, this is a difficult
* thing to do safely. Passing an invalid pointer can crash the program and
* reading beyond the bounds of the pointer will crash the program or cause
* undefined behavior. Use with care!
*/
export function u64(ptr: Pointer, byteOffset?: number): bigint;
/**
* The read function behaves similarly to DataView,
* but it's usually faster because it doesn't need to create a DataView or ArrayBuffer.
*
* @param ptr The memory address to read
* @param byteOffset bytes to skip before reading
*
* While there are some checks to catch invalid pointers, this is a difficult
* thing to do safely. Passing an invalid pointer can crash the program and
* reading beyond the bounds of the pointer will crash the program or cause
* undefined behavior. Use with care!
*/
export function i64(ptr: Pointer, byteOffset?: number): bigint;
/**
* The read function behaves similarly to DataView,
* but it's usually faster because it doesn't need to create a DataView or ArrayBuffer.
*
* @param ptr The memory address to read
* @param byteOffset bytes to skip before reading
*
* While there are some checks to catch invalid pointers, this is a difficult
* thing to do safely. Passing an invalid pointer can crash the program and
* reading beyond the bounds of the pointer will crash the program or cause
* undefined behavior. Use with care!
*/
export function f64(ptr: Pointer, byteOffset?: number): number;
/**
* The read function behaves similarly to DataView,
* but it's usually faster because it doesn't need to create a DataView or ArrayBuffer.
*
* @param ptr The memory address to read
* @param byteOffset bytes to skip before reading
*
* While there are some checks to catch invalid pointers, this is a difficult
* thing to do safely. Passing an invalid pointer can crash the program and
* reading beyond the bounds of the pointer will crash the program or cause
* undefined behavior. Use with care!
*/
export function ptr(ptr: Pointer, byteOffset?: number): number;
/**
* The read function behaves similarly to DataView,
* but it's usually faster because it doesn't need to create a DataView or ArrayBuffer.
*
* @param ptr The memory address to read
* @param byteOffset bytes to skip before reading
*
* While there are some checks to catch invalid pointers, this is a difficult
* thing to do safely. Passing an invalid pointer can crash the program and
* reading beyond the bounds of the pointer will crash the program or cause
* undefined behavior. Use with care!
*/
export function intptr(ptr: Pointer, byteOffset?: number): number;
}
/**
* Get the pointer backing a {@link TypedArray} or {@link ArrayBuffer}
*

View File

@@ -602,9 +602,9 @@ interface Process {
getgroups: () => number[];
// setgroups?: (groups: ReadonlyArray<string | number>) => void;
dlopen(module: { exports: any }, filename: string, flags?: number): void;
stdin: import("stream").Duplex & { isTTY: boolean };
stdout: import("stream").Writable & { isTTY: boolean };
stderr: import("stream").Writable & { isTTY: boolean };
stdin: import("tty").ReadStream;
stdout: import("tty").WriteStream;
stderr: import("tty").WriteStream;
/**
*
@@ -739,11 +739,10 @@ interface BlobInterface {
type BlobPart = string | Blob | BufferSource;
interface BlobPropertyBag {
/** Set a default "type" */
/** Set a default "type". Not yet implemented. */
type?: string;
/** Not implemented in Bun yet. */
endings?: "transparent" | "native";
// endings?: "transparent" | "native";
}
/**
@@ -3673,7 +3672,7 @@ declare module "*.txt" {
}
declare module "*.toml" {
var contents: unknown;
var contents: any;
export = contents;
}

View File

@@ -75,7 +75,7 @@ const tsConfig = {
skipLibCheck: true,
jsx: "react-jsx",
allowImportingTsExtensions: true,
emitDeclarationOnly: true,
noEmit: true,
composite: true,
allowSyntheticDefaultImports: true,
forceConsistentCasingInFileNames: true,

View File

@@ -4,6 +4,8 @@ import {
suffix,
CString,
Pointer,
JSCallback,
read,
// FFIFunction,
// ConvertFns,
// Narrow,
@@ -27,6 +29,14 @@ const lib = dlopen(
args: [FFIType.i32, FFIType.i32],
returns: FFIType.i32,
},
ptr_type: {
args: [FFIType.pointer],
returns: FFIType.pointer,
},
fn_type: {
args: [FFIType.function],
returns: FFIType.function,
},
allArgs: {
args: [
FFIType.char, // string
@@ -67,6 +77,17 @@ const lib = dlopen(
tsd.expectType<CString>(lib.symbols.sqlite3_libversion());
tsd.expectType<number>(lib.symbols.add(1, 2));
tsd.expectType<Pointer | null>(lib.symbols.ptr_type(0));
tc.assert<
tc.IsExact<
(typeof lib)["symbols"]["ptr_type"],
TypedArray | Pointer | CString
>
>;
tsd.expectType<Pointer | null>(lib.symbols.fn_type(0));
tc.assert<tc.IsExact<(typeof lib)["symbols"]["fn_type"], Pointer | JSCallback>>;
tc.assert<
tc.IsExact<
(typeof lib)["symbols"]["allArgs"],
@@ -103,3 +124,41 @@ tc.assert<
]
>
>;
const as_const_test = {
sqlite3_libversion: {
args: [],
returns: FFIType.cstring,
},
multi_args: {
args: [FFIType.i32, FFIType.f32],
returns: FFIType.void,
},
no_returns: {
args: [FFIType.i32],
},
no_args: {
returns: FFIType.i32,
},
} as const;
const lib2 = dlopen(path, as_const_test);
tsd.expectType<CString>(lib2.symbols.sqlite3_libversion());
tsd.expectType<void>(lib2.symbols.multi_args(1, 2));
tc.assert<tc.IsExact<ReturnType<(typeof lib2)["symbols"]["no_returns"]>, void>>;
tc.assert<tc.IsExact<Parameters<(typeof lib2)["symbols"]["no_args"]>, []>>;
tsd.expectType<number>(read.u8(0));
tsd.expectType<number>(read.u8(0, 0));
tsd.expectType<number>(read.i8(0, 0));
tsd.expectType<number>(read.u16(0, 0));
tsd.expectType<number>(read.i16(0, 0));
tsd.expectType<number>(read.u32(0, 0));
tsd.expectType<number>(read.i32(0, 0));
tsd.expectType<bigint>(read.u64(0, 0));
tsd.expectType<bigint>(read.i64(0, 0));
tsd.expectType<number>(read.f32(0, 0));
tsd.expectType<number>(read.f64(0, 0));
tsd.expectType<number>(read.ptr(0, 0));
tsd.expectType<number>(read.intptr(0, 0));

View File

@@ -1,4 +1,4 @@
import { expectType } from "tsd";
import data from "../../../bunfig.toml";
expectType<unknown>(data);
expectType<any>(data);

View File

@@ -0,0 +1,49 @@
import * as tty from "tty";
const rs = new tty.ReadStream(234, {
allowHalfOpen: true,
readable: true,
signal: new AbortSignal(),
writable: true,
});
const ws = new tty.WriteStream(234);
process.stdin.setRawMode(true);
process.stdin.setRawMode(false);
process.stdin.isRaw;
process.stdin.setRawMode(true).isRaw;
rs.isRaw;
rs.setRawMode(true);
rs.setRawMode(false);
rs.setRawMode(true).isRaw;
rs.isTTY;
ws.isPaused;
ws.isTTY;
ws.bytesWritten;
ws.bytesRead;
ws.columns;
ws.rows;
ws.isTTY;
ws.clearLine(1);
ws.clearLine(0);
ws.clearScreenDown();
ws.cursorTo(1);
ws.cursorTo(1, 2);
ws.cursorTo(1, () => {});
ws.cursorTo(1, 2, () => {});
ws.moveCursor(1, 2);
ws.moveCursor(1, 2, () => {});
ws.clearLine(1, () => {});
ws.clearLine(0, () => {});
ws.clearScreenDown(() => {});
ws.cursorTo(1, () => {});
process.stdout.clearLine;
process.stdout.clearScreenDown;
process.stdout.cursorTo;
process.stdout.moveCursor;
process.stdout.getColorDepth;
process.stdout.getWindowSize;

View File

@@ -1,10 +1,8 @@
{
"compilerOptions": {
"composite": true,
"emitDeclarationOnly": true,
"lib": [
"ESNext"
],
"noEmit": true,
"lib": ["ESNext"],
"baseUrl": ".",
"rootDir": ".",
"outFile": "./types.d.ts",
@@ -12,14 +10,11 @@
"target": "esnext",
"disableSolutionSearching": true
},
"include": [
"./*.d.ts",
"dist"
],
"include": ["./*.d.ts", "dist"],
"exclude": [
"node_modules",
"./node_modules",
"./node_modules/*",
"./node_modules/@types/node/index.d.ts"
]
}
}

View File

@@ -1,4 +1,31 @@
/**
* The `tty` module provides the `tty.ReadStream` and `tty.WriteStream` classes.
* In most cases, it will not be necessary or possible to use this module directly.
* However, it can be accessed using:
*
* ```js
* const tty = require('tty');
* ```
*
* When Node.js detects that it is being run with a text terminal ("TTY")
* attached, `process.stdin` will, by default, be initialized as an instance of`tty.ReadStream` and both `process.stdout` and `process.stderr` will, by
* default, be instances of `tty.WriteStream`. The preferred method of determining
* whether Node.js is being run within a TTY context is to check that the value of
* the `process.stdout.isTTY` property is `true`:
*
* ```console
* $ node -p -e "Boolean(process.stdout.isTTY)"
* true
* $ node -p -e "Boolean(process.stdout.isTTY)" | cat
* false
* ```
*
* In most cases, there should be little to no reason for an application to
* manually create instances of the `tty.ReadStream` and `tty.WriteStream`classes.
* @see [source](https://github.com/nodejs/node/blob/v18.0.0/lib/tty.js)
*/
declare module "tty" {
import * as net from "node:net";
/**
* The `tty.isatty()` method returns `true` if the given `fd` is associated with
* a TTY and `false` if it is not, including whenever `fd` is not a non-negative
@@ -7,10 +34,175 @@ declare module "tty" {
* @param fd A numeric file descriptor
*/
function isatty(fd: number): boolean;
// TODO: tty-browserify only polyfills functions that throws errors, wouldn't make sense to have types at the moment
var ReadStream: Function;
var WriteStream: Function;
/**
* Represents the readable side of a TTY. In normal circumstances `process.stdin` will be the only `tty.ReadStream` instance in a Node.js
* process and there should be no reason to create additional instances.
* @since v0.5.8
*/
class ReadStream extends net.Socket {
constructor(fd: number, options?: net.SocketConstructorOpts);
/**
* A `boolean` that is `true` if the TTY is currently configured to operate as a
* raw device. Defaults to `false`.
* @since v0.7.7
*/
isRaw: boolean;
/**
* Allows configuration of `tty.ReadStream` so that it operates as a raw device.
*
* When in raw mode, input is always available character-by-character, not
* including modifiers. Additionally, all special processing of characters by the
* terminal is disabled, including echoing input
* characters. Ctrl+C will no longer cause a `SIGINT` when
* in this mode.
* @since v0.7.7
* @param mode If `true`, configures the `tty.ReadStream` to operate as a raw device. If `false`, configures the `tty.ReadStream` to operate in its default mode. The `readStream.isRaw`
* property will be set to the resulting mode.
* @return The read stream instance.
*/
setRawMode(mode: boolean): this;
/**
* A `boolean` that is always `true` for `tty.ReadStream` instances.
* @since v0.5.8
*/
isTTY: boolean;
}
/**
* -1 - to the left from cursor
* 0 - the entire line
* 1 - to the right from cursor
*/
type Direction = -1 | 0 | 1;
/**
* Represents the writable side of a TTY. In normal circumstances,`process.stdout` and `process.stderr` will be the only`tty.WriteStream` instances created for a Node.js process and there
* should be no reason to create additional instances.
* @since v0.5.8
*/
class WriteStream extends net.Socket {
constructor(fd: number);
addListener(event: string, listener: (...args: any[]) => void): this;
addListener(event: "resize", listener: () => void): this;
emit(event: string | symbol, ...args: any[]): boolean;
emit(event: "resize"): boolean;
on(event: string, listener: (...args: any[]) => void): this;
on(event: "resize", listener: () => void): this;
once(event: string, listener: (...args: any[]) => void): this;
once(event: "resize", listener: () => void): this;
prependListener(event: string, listener: (...args: any[]) => void): this;
prependListener(event: "resize", listener: () => void): this;
prependOnceListener(
event: string,
listener: (...args: any[]) => void,
): this;
prependOnceListener(event: "resize", listener: () => void): this;
/**
* `writeStream.clearLine()` clears the current line of this `WriteStream` in a
* direction identified by `dir`.
* @since v0.7.7
* @param callback Invoked once the operation completes.
* @return `false` if the stream wishes for the calling code to wait for the `'drain'` event to be emitted before continuing to write additional data; otherwise `true`.
*/
clearLine(dir: Direction, callback?: () => void): boolean;
/**
* `writeStream.clearScreenDown()` clears this `WriteStream` from the current
* cursor down.
* @since v0.7.7
* @param callback Invoked once the operation completes.
* @return `false` if the stream wishes for the calling code to wait for the `'drain'` event to be emitted before continuing to write additional data; otherwise `true`.
*/
clearScreenDown(callback?: () => void): boolean;
/**
* `writeStream.cursorTo()` moves this `WriteStream`'s cursor to the specified
* position.
* @since v0.7.7
* @param callback Invoked once the operation completes.
* @return `false` if the stream wishes for the calling code to wait for the `'drain'` event to be emitted before continuing to write additional data; otherwise `true`.
*/
cursorTo(x: number, y?: number, callback?: () => void): boolean;
cursorTo(x: number, callback: () => void): boolean;
/**
* `writeStream.moveCursor()` moves this `WriteStream`'s cursor _relative_ to its
* current position.
* @since v0.7.7
* @param callback Invoked once the operation completes.
* @return `false` if the stream wishes for the calling code to wait for the `'drain'` event to be emitted before continuing to write additional data; otherwise `true`.
*/
moveCursor(dx: number, dy: number, callback?: () => void): boolean;
/**
* Returns:
*
* * `1` for 2,
* * `4` for 16,
* * `8` for 256,
* * `24` for 16,777,216 colors supported.
*
* Use this to determine what colors the terminal supports. Due to the nature of
* colors in terminals it is possible to either have false positives or false
* negatives. It depends on process information and the environment variables that
* may lie about what terminal is used.
* It is possible to pass in an `env` object to simulate the usage of a specific
* terminal. This can be useful to check how specific environment settings behave.
*
* To enforce a specific color support, use one of the below environment settings.
*
* * 2 colors: `FORCE_COLOR = 0` (Disables colors)
* * 16 colors: `FORCE_COLOR = 1`
* * 256 colors: `FORCE_COLOR = 2`
* * 16,777,216 colors: `FORCE_COLOR = 3`
*
* Disabling color support is also possible by using the `NO_COLOR` and`NODE_DISABLE_COLORS` environment variables.
* @since v9.9.0
* @param [env=process.env] An object containing the environment variables to check. This enables simulating the usage of a specific terminal.
*/
getColorDepth(env?: object): number;
/**
* Returns `true` if the `writeStream` supports at least as many colors as provided
* in `count`. Minimum support is 2 (black and white).
*
* This has the same false positives and negatives as described in `writeStream.getColorDepth()`.
*
* ```js
* process.stdout.hasColors();
* // Returns true or false depending on if `stdout` supports at least 16 colors.
* process.stdout.hasColors(256);
* // Returns true or false depending on if `stdout` supports at least 256 colors.
* process.stdout.hasColors({ TMUX: '1' });
* // Returns true.
* process.stdout.hasColors(2 ** 24, { TMUX: '1' });
* // Returns false (the environment setting pretends to support 2 ** 8 colors).
* ```
* @since v11.13.0, v10.16.0
* @param [count=16] The number of colors that are requested (minimum 2).
* @param [env=process.env] An object containing the environment variables to check. This enables simulating the usage of a specific terminal.
*/
hasColors(count?: number): boolean;
hasColors(env?: object): boolean;
hasColors(count: number, env?: object): boolean;
/**
* `writeStream.getWindowSize()` returns the size of the TTY
* corresponding to this `WriteStream`. The array is of the type`[numColumns, numRows]` where `numColumns` and `numRows` represent the number
* of columns and rows in the corresponding TTY.
* @since v0.7.7
*/
getWindowSize(): [number, number];
/**
* A `number` specifying the number of columns the TTY currently has. This property
* is updated whenever the `'resize'` event is emitted.
* @since v0.7.7
*/
columns: number;
/**
* A `number` specifying the number of rows the TTY currently has. This property
* is updated whenever the `'resize'` event is emitted.
* @since v0.7.7
*/
rows: number;
/**
* A `boolean` that is always `true`.
* @since v0.5.8
*/
isTTY: boolean;
}
}
declare module "node:tty" {
export * from "tty";

2
packages/bun-vscode/.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
node_modules
extension

View File

@@ -5,10 +5,7 @@
"name": "Extension",
"type": "extensionHost",
"request": "launch",
"args": [
"--extensionDevelopmentPath=${workspaceFolder}",
"${workspaceFolder}/example"
],
"args": ["--extensionDevelopmentPath=${workspaceFolder}", "${workspaceFolder}/example"],
"outFiles": ["${workspaceFolder}/dist/**/*.js"],
"preLaunchTask": "Build (watch)"
},

View File

@@ -5,5 +5,5 @@
"search.exclude": {
"out": true // set this to false to include "out" folder in search results
},
"typescript.tsc.autoDetect": "off",
}
"typescript.tsc.autoDetect": "off"
}

View File

@@ -4,13 +4,15 @@
{
"label": "Build",
"type": "shell",
"command": "bun run build"
"command": "bun run build",
"problemMatcher": "$esbuild"
},
{
"label": "Build (watch)",
"type": "shell",
"command": "bun run build:watch",
"isBackground": true
"isBackground": true,
"problemMatcher": "$esbuild-watch"
}
]
}
}

View File

@@ -1 +1,22 @@
# Debug Adapter Protocol for Bun
# Bun for Visual Studio Code
![Visual Studio Marketplace Version](https://img.shields.io/visual-studio-marketplace/v/oven.vscode-bun)
![Visual Studio Marketplace Installs](https://img.shields.io/visual-studio-marketplace/i/oven.vscode-bun)
![Visual Studio Marketplace Downloads](https://img.shields.io/visual-studio-marketplace/d/oven.vscode-bun)
<img align="right" src="https://user-images.githubusercontent.com/709451/182802334-d9c42afe-f35d-4a7b-86ea-9985f73f20c3.png" height="150px" style="float: right; padding: 30px;">
This extension adds support for using [Bun](https://bun.sh/) with Visual Studio Code. Bun is an all-in-one toolkit for JavaScript and TypeScript apps.
At its core is the _Bun runtime_, a fast JavaScript runtime designed as a drop-in replacement for Node.js. It's written in Zig and powered by JavaScriptCore under the hood, dramatically reducing startup times and memory usage.
<div align="center">
<a href="https://bun.sh/docs">Documentation</a>
<span>&nbsp;&nbsp;•&nbsp;&nbsp;</span>
<a href="https://discord.com/invite/CXdq2DP29u">Discord</a>
<span>&nbsp;&nbsp;•&nbsp;&nbsp;</span>
<a href="https://github.com/oven-sh/bun/issues/new">Issues</a>
<span>&nbsp;&nbsp;•&nbsp;&nbsp;</span>
<a href="https://github.com/oven-sh/bun/issues/159">Roadmap</a>
<br/>
</div>

View File

@@ -1,5 +0,0 @@
* Off-by-one for debug lines
* Formatting values in console (some code is wired up)
* Play button on debugger actually starting Bun
* bun debug or --inspect command added to Bun, not need Bun.serve
* Breakpoint actually setting

Binary file not shown.

View File

@@ -4,16 +4,15 @@
{
"type": "bun",
"request": "launch",
"name": "Debug",
"program": "${workspaceFolder}/example.js",
"stopOnEntry": true
"name": "Debug Bun",
"program": "${file}",
"watch": true
},
{
"type": "bun",
"request": "attach",
"name": "Attach",
"program": "${workspaceFolder}/example.js",
"stopOnEntry": true
"name": "Attach to Bun",
"url": "ws://localhost:6499/",
}
]
}

View File

@@ -0,0 +1,30 @@
// @bun
// example.ts
var a = function (request) {
b(request);
};
var b = function (request) {
c(request);
};
var c = function (request) {
console.log(request);
};
var example_default = {
async fetch(request, server) {
a(request);
const coolThing = new SuperCoolThing();
coolThing.doCoolThing();
debugger;
return new Response(request.url);
},
};
class SuperCoolThing {
doCoolThing() {
console.log("super cool thing!");
}
}
export { example_default as default };
//# debugId=9BB0B773A8E4771564756e2164756e21
//# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsiZXhhbXBsZS50cyJdLAogICJzb3VyY2VzQ29udGVudCI6IFsKICAgICJpbXBvcnQgdHlwZSB7IFNlcnZlciB9IGZyb20gXCJidW5cIjtcblxuZXhwb3J0IGRlZmF1bHQge1xuICBhc3luYyBmZXRjaChyZXF1ZXN0OiBSZXF1ZXN0LCBzZXJ2ZXI6IFNlcnZlcik6IFByb21pc2U8UmVzcG9uc2U+IHtcbiAgICBhKHJlcXVlc3QpO1xuICAgIGNvbnN0IGNvb2xUaGluZzogQ29vbFRoaW5nID0gbmV3IFN1cGVyQ29vbFRoaW5nKCk7XG4gICAgY29vbFRoaW5nLmRvQ29vbFRoaW5nKCk7XG4gICAgZGVidWdnZXI7XG4gICAgcmV0dXJuIG5ldyBSZXNwb25zZShyZXF1ZXN0LnVybCk7XG4gIH1cbn07XG5cbi8vIGFcbmZ1bmN0aW9uIGEocmVxdWVzdDogUmVxdWVzdCk6IHZvaWQge1xuICBiKHJlcXVlc3QpO1xufVxuXG4vLyBiXG5mdW5jdGlvbiBiKHJlcXVlc3Q6IFJlcXVlc3QpOiB2b2lkIHtcbiAgYyhyZXF1ZXN0KTtcbn1cblxuLy8gY1xuZnVuY3Rpb24gYyhyZXF1ZXN0OiBSZXF1ZXN0KSB7XG4gIGNvbnNvbGUubG9nKHJlcXVlc3QpO1xufVxuXG5pbnRlcmZhY2UgQ29vbFRoaW5nIHtcbiAgZG9Db29sVGhpbmcoKTogdm9pZDtcbn1cblxuY2xhc3MgU3VwZXJDb29sVGhpbmcgaW1wbGVtZW50cyBDb29sVGhpbmcge1xuICBkb0Nvb2xUaGluZygpOiB2b2lkIHtcbiAgICBjb25zb2xlLmxvZyhcInN1cGVyIGNvb2wgdGhpbmchXCIpO1xuICB9XG59XG4iCiAgXSwKICAibWFwcGluZ3MiOiAiOztBQS8vLy8vZkFhQSxJQUFTLFlBQUMsQ0FBQyxTQUF3QjtBQUNqQyxJQUFFLE9BQU87QUFBQTtBQUlYLElBQVMsWUFBQyxDQUFDLFNBQXdCO0FBQ2pDLElBQUUsT0FBTztBQUFBO0FBSVgsSUFBUyxZQUFDLENBQUMsU0FBa0I7QUFDM0IsVUFBUSxJQUFJLE9BQU87QUFBQTtBQXRCckIsSUFBZTtBQUFBLE9BQ1AsTUFBSyxDQUFDLFNBQWtCLFFBQW1DO0FBQy9ELE1BQUUsT0FBTztBQUNULFVBQU0sWUFBdUIsSUFBSTtBQUNqQyxjQUFVLFlBQVk7QUFDdEI7QUFDQSxXQUFPLElBQUksU0FBUyxRQUFRLEdBQUc7QUFBQTtBQUVuQztBQXFCQTtBQUFBLE1BQU0sZUFBb0M7QUFBQSxFQUN4QyxXQUFXLEdBQVM7QUFDbEIsWUFBUSxJQUFJLG1CQUFtQjtBQUFBO0FBRW5DOyIsCiAgImRlYnVnSWQiOiAiOUJCMEI3NzNBOEU0NzcxNTY0NzU2ZTIxNjQ3NTZlMjEiLAogICJuYW1lcyI6IFtdCn0=

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