import type { Server as WebSocketServer, WebSocketHandler, ServerWebSocket, SocketHandler, Socket } from "bun"; export default function ( executionContextId: string, url: string, createBackend: ( executionContextId: string, refEventLoop: boolean, receive: (...messages: string[]) => void, ) => unknown, send: (message: string) => void, close: () => void, ): void { let debug: Debugger | undefined; try { debug = new Debugger(executionContextId, url, createBackend, send, close); } catch (error) { exit("Failed to start inspector:\n", error); } const { protocol, href, host, pathname } = debug.url; if (!protocol.includes("unix")) { console.log(dim("--------------------- Bun Inspector ---------------------"), reset()); console.log(`Listening:\n ${dim(href)}`); if (protocol.includes("ws")) { console.log(`Inspect in browser:\n ${link(`https://debug.bun.sh/#${host}${pathname}`)}`); } console.log(dim("--------------------- Bun Inspector ---------------------"), reset()); } const unix = process.env["BUN_INSPECT_NOTIFY"]; if (unix) { const { protocol, pathname } = parseUrl(unix); if (protocol === "unix:") { notify(pathname); } } } class Debugger { #url: URL; #createBackend: (refEventLoop: boolean, receive: (...messages: string[]) => void) => Writer; constructor( executionContextId: string, url: string, createBackend: ( executionContextId: string, refEventLoop: boolean, receive: (...messages: string[]) => void, ) => unknown, send: (message: string) => void, close: () => void, ) { this.#url = parseUrl(url); this.#createBackend = (refEventLoop, receive) => { const backend = createBackend(executionContextId, refEventLoop, receive); return { write: message => { send.call(backend, message); return true; }, close: () => close.call(backend), }; }; this.#listen(); } get url(): URL { return this.#url; } #listen(): void { const { protocol, hostname, port, pathname } = this.#url; if (protocol === "ws:" || protocol === "ws+tcp:") { const server = Bun.serve({ hostname, port, fetch: this.#fetch.bind(this), websocket: this.#websocket, }); this.#url.hostname = server.hostname; this.#url.port = `${server.port}`; return; } if (protocol === "ws+unix:") { Bun.serve({ unix: pathname, fetch: this.#fetch.bind(this), websocket: this.#websocket, }); return; } throw new TypeError(`Unsupported protocol: '${protocol}' (expected 'ws:', 'ws+unix:', or 'unix:')`); } get #websocket(): WebSocketHandler { return { idleTimeout: 0, closeOnBackpressureLimit: false, open: ws => this.#open(ws, webSocketWriter(ws)), message: (ws, message) => { if (typeof message === "string") { this.#message(ws, message); } else { this.#error(ws, new Error(`Unexpected binary message: ${message.toString()}`)); } }, drain: ws => this.#drain(ws), close: ws => this.#close(ws), }; } #fetch(request: Request, server: WebSocketServer): Response | undefined { const { method, url, headers } = request; const { pathname } = new URL(url); if (method !== "GET") { return new Response(null, { status: 405, // Method Not Allowed }); } switch (pathname) { case "/json/version": return Response.json(versionInfo()); case "/json": case "/json/list": // TODO? } if (!this.#url.protocol.includes("unix") && this.#url.pathname !== pathname) { return new Response(null, { status: 404, // Not Found }); } const data: Connection = { refEventLoop: headers.get("Ref-Event-Loop") === "0", }; if (!server.upgrade(request, { data })) { return new Response(null, { status: 426, // Upgrade Required headers: { "Upgrade": "websocket", }, }); } } get #socket(): SocketHandler { return { open: socket => this.#open(socket, socketWriter(socket)), data: (socket, message) => this.#message(socket, message.toString()), drain: socket => this.#drain(socket), close: socket => this.#close(socket), error: (socket, error) => this.#error(socket, error), connectError: (_, error) => exit("Failed to start inspector:\n", error), }; } #open(connection: ConnectionOwner, writer: Writer): void { const { data } = connection; const { refEventLoop } = data; const client = bufferedWriter(writer); const backend = this.#createBackend(refEventLoop, (...messages: string[]) => { for (const message of messages) { client.write(message); } }); data.client = client; data.backend = backend; } #message(connection: ConnectionOwner, message: string): void { const { data } = connection; const { backend } = data; backend?.write(message); } #drain(connection: ConnectionOwner): void { const { data } = connection; const { client } = data; client?.drain?.(); } #close(connection: ConnectionOwner): void { const { data } = connection; const { backend } = data; backend?.close(); } #error(connection: ConnectionOwner, error: Error): void { const { data } = connection; const { backend } = data; console.error(error); backend?.close(); } } function versionInfo(): unknown { return { "Protocol-Version": "1.3", "Browser": "Bun", // @ts-ignore: Missing types for `navigator` "User-Agent": navigator.userAgent, "WebKit-Version": process.versions.webkit, "Bun-Version": Bun.version, "Bun-Revision": Bun.revision, }; } function webSocketWriter(ws: ServerWebSocket): Writer { return { write: message => !!ws.sendText(message), close: () => ws.close(), }; } function socketWriter(socket: Socket): Writer { return { write: message => !!socket.write(message), close: () => socket.end(), }; } function bufferedWriter(writer: Writer): Writer { let draining = false; let pendingMessages: string[] = []; return { write: message => { if (draining || !writer.write(message)) { pendingMessages.push(message); } return true; }, drain: () => { draining = true; try { for (let i = 0; i < pendingMessages.length; i++) { if (!writer.write(pendingMessages[i])) { pendingMessages = pendingMessages.slice(i); return; } } } finally { draining = false; } }, close: () => { writer.close(); pendingMessages.length = 0; }, }; } const defaultHostname = "localhost"; const defaultPort = 6499; function parseUrl(url: string): URL { try { if (!url) { return new URL(randomId(), `ws://${defaultHostname}:${defaultPort}/`); } else if (url.startsWith("/")) { return new URL(url, `ws://${defaultHostname}:${defaultPort}/`); } else if (/^[a-z+]+:\/\//i.test(url)) { return new URL(url); } else if (/^\d+$/.test(url)) { return new URL(randomId(), `ws://${defaultHostname}:${url}/`); } else if (!url.includes("/") && url.includes(":")) { return new URL(randomId(), `ws://${url}/`); } else if (!url.includes(":")) { const [hostname, pathname] = url.split("/", 2); return new URL(`ws://${hostname}:${defaultPort}/${pathname}`); } else { return new URL(randomId(), `ws://${url}`); } } catch { throw new TypeError(`Invalid hostname or URL: '${url}'`); } } function randomId() { return Math.random().toString(36).slice(2); } const { enableANSIColors } = Bun; function dim(string: string): string { if (enableANSIColors) { return `\x1b[2m${string}\x1b[22m`; } return string; } function link(url: string): string { if (enableANSIColors) { return `\x1b[1m\x1b]8;;${url}\x1b\\${url}\x1b]8;;\x1b\\\x1b[22m`; } return url; } function reset(): string { if (enableANSIColors) { return "\x1b[49m"; } return ""; } function notify(unix: string): void { Bun.connect({ unix, socket: { open: socket => { socket.end("1"); }, data: () => {}, // required or it errors }, }).finally(() => { // Best-effort }); } function exit(...args: unknown[]): never { console.error(...args); process.exit(1); } type ConnectionOwner = { data: Connection; }; type Connection = { refEventLoop: boolean; client?: Writer; backend?: Writer; }; type Writer = { write: (message: string) => boolean; drain?: () => void; close: () => void; }; alue='jarred/new-bund'>jarred/new-bund Unnamed repository; edit this file 'description' to name the repository.
aboutsummaryrefslogtreecommitdiff
AgeCommit message (Collapse)AuthorFilesLines
2023-03-02Ensure prettier knows where to lookGravatar Jarred Sumner 6-5/+24
2023-03-02add tests for scoped dependency aliasing (#2271)Gravatar Alex Lam S.L 3-15/+111
2023-03-02fix tmp dir in fetch.tests.js (#2270)Gravatar Ciro Spaciari 1-1/+2
2023-03-02Remake typings for FFI dlopen/linkSymbols + introduce Pointer type (#2227)Gravatar u9g 4-28/+222
* Give dlopen & linkSymbols typings for exported functions * Fix lookup table * Fully change over to Pointer + fix examples * add back header for typings * Fix tsc errors * Run formatter on ffi.d.ts * Revert args/return type change * Add type tests for ffi --------- Co-authored-by: Colin McDonnell <colinmcd94@gmail.com>
2023-03-02put AbortSignal in WebCore namespace (#2267)Gravatar Ciro Spaciari 9-69/+70
2023-03-02only free sink after promises solves (#2268)Gravatar Ciro Spaciari 2-10/+10
2023-03-02Fix `Bun.sleepSync` to actually use milliseconds (#2242)Gravatar Justin Whear 3-5/+57
* Fix Bun.sleep/sleepSync to actually use milliseconds `Bun.sleepSync` was accidentally treating its argument as seconds rather than milliseconds as the docs stated. This is a breaking change in that the function now behaves as documented. Fixed relevant tests. * sleepSync: add more argument checking, tests
2023-03-01fix(bindings): mark `JSCInitialize` (#2265)Gravatar Derrick Farris 1-0/+1
2023-03-01Add page descriptions (#2260)Gravatar Colin McDonnell 1-46/+132
* Add page descriptions * Update bun install * Description tweaks * Tweaks
2023-03-01fix bun server segfault with abortsignal (#2261)Gravatar Ciro Spaciari 3-133/+79
* removed redundant tests, fixed server segfault * fix onRejectStream, safer unassign signal * fix abort Bun.serve signal.addEventListener on async * move ctx.signal null check up * keep original behavior of streams onAborted
2023-03-01fix(node:http): match Node `http.request()` GET/HEAD w/ body (#2262)Gravatar Derrick Farris 2-2/+31
2023-03-01Add a test for https request in node:httpGravatar Jarred Sumner 2-17/+28
2023-03-01fix(node:http/https): fix passing `URL` objs to `http.request`(#2253) (#2258)Gravatar Derrick Farris 2-43/+63
* fix(node:http/https): fix passing `URL` objs to `http.request`(#2253) * fix(node:http): hoist debug env var * fix(node:http): make body `undefined` when falsy
2023-03-01Revert "Update clap (#2238)"Gravatar Jarred Sumner 16-290/+1840
This reverts commit 7b9a17f9d7106ffd8e553a5192aba60d14ea5e9c.
2023-03-01Revert "Add `-D`, `--dev` flags for bun install (#2240)"Gravatar Jarred Sumner 1-9/+9
This reverts commit ec20fae57f96a835562b154730957ecc4015ba31.
2023-03-01Use GitHub action ID instead of SHA for test workflowGravatar Ashcon Partovi 1-1/+1
2023-03-01avoids segfault after aborted onReject in Bun.serve streams (#2256)Gravatar Ciro Spaciari 1-7/+8
* avoids segfault after aborted on reject * silence err on handleRejectStream after aborted
2023-03-01Run tests in CI for bun-linux-aarch64Gravatar Ashcon Partovi 2-1/+30
2023-03-01Revert spawnSync changeGravatar Jarred Sumner 1-1/+2
cc @FireTheFox
2023-03-01Update bindings.zigGravatar Jarred Sumner 1-1/+3
2023-03-01fix deinit behavior when connection is aborted using ResponseStream and ↵Gravatar Ciro Spaciari 3-34/+174
abort event behavior (#2252) * fix deinit behavior when connection is aborted using ResponseStream * fix abort handling on stream, and get better tests * avoid segfault by trying to deinit 2x when aborted * make tests more reliable * more reliable onResolveStream after aborted * add test case for not firing the abort signal
2023-03-01fix Bun.file.arrayBuffer() segmentation fault on empty file #2248 (#2249)Gravatar Ciro Spaciari 3-7/+23
* fix Bun.file.arrayBuffer() segmentation fault on empty file #2248 * cleanner this.iotask check
2023-03-01Fix async in sqliteGravatar Colin McDonnell 1-2/+2
2023-02-28Forces a specific libdir for c-ares (#2241)Gravatar Justin Whear 1-1/+5
The c-ares build expects lib/libcares.a to exist after cmake, but on my system it was being generated in lib64. This simply sets the cmake variable so that the target ends up where we expect.
2023-02-28Make Bun.gc(true) more aggressiveGravatar Jarred Sumner 1-0/+3
2023-02-28Expose JSC::Options via `BUN_JSC_` prefixGravatar Jarred Sumner 6-8/+47
Example usage: BUN_JSC_logGC=1 bun file.js
2023-02-28fixupGravatar Jarred Sumner 1-1/+1
2023-02-28Fix typecheckGravatar Colin McDonnell 2-1/+4
2023-02-28Fix incorrect Bun version in docs (#2236)Gravatar Derrick Farris 1-1/+1
2023-02-28just some comments fix (#2237)Gravatar Ciro Spaciari 1-4/+2
2023-02-28Add `-D`, `--dev` flags for bun install (#2240)Gravatar Justin Whear 1-9/+9
* remove vendored clap * Update to latest zig-clap Major changes: * Instead of vendoring zig-clap and adding changes, this uses Hejsil/zig-clap directly as a submodule * `cli.zig` and related files have been updated to use new API (no more `flag()` or `option()`) * A workaround for the Run and Auto commands has been implemented that allows us to use the official upstream Minor change: * `-i` now has the long option `--install-fallback`; I didn't spend much time thinking about this name, so suggestions weclome. * add --development and --optional to bun install * Add support for `-D`, `--dev` in bun install, fix `--save`
2023-02-28Document punningGravatar Colin McDonnell 1-1/+18