mirror of
https://github.com/oven-sh/bun
synced 2026-02-09 10:28:47 +00:00
Compare commits
209 Commits
jarred/tim
...
don/docs/b
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d7ae1abb3f | ||
|
|
fee911194a | ||
|
|
358a1db422 | ||
|
|
8929d65f0e | ||
|
|
f14e26bc85 | ||
|
|
43f7a241b9 | ||
|
|
7021c42cf2 | ||
|
|
1b10b61423 | ||
|
|
bb9128c0e8 | ||
|
|
f38d35f7c9 | ||
|
|
f0dfa109bb | ||
|
|
27cf65a1e2 | ||
|
|
e83b5fb720 | ||
|
|
ee89130991 | ||
|
|
0a4f36644f | ||
|
|
a1ab2a4780 | ||
|
|
451c1905a8 | ||
|
|
accccbfdaf | ||
|
|
8e0c8a143e | ||
|
|
9ea577efc0 | ||
|
|
54416dad05 | ||
|
|
8f4575c0e4 | ||
|
|
c7edb24520 | ||
|
|
325acfc230 | ||
|
|
7f60375cca | ||
|
|
dac7f22997 | ||
|
|
f5836c2013 | ||
|
|
70ddfb55e6 | ||
|
|
934e41ae59 | ||
|
|
f4ae8c7254 | ||
|
|
2a9569cec4 | ||
|
|
31060a5e2a | ||
|
|
5c0fa6dc21 | ||
|
|
53f311fdd9 | ||
|
|
b40f5c9669 | ||
|
|
317e9d23ab | ||
|
|
11bb3573ea | ||
|
|
39cf0906d1 | ||
|
|
1d655a0232 | ||
|
|
a548c2ec54 | ||
|
|
7740271359 | ||
|
|
75144ab881 | ||
|
|
1dbeed20a9 | ||
|
|
3af6f7a5fe | ||
|
|
1bfccf707b | ||
|
|
21853d08de | ||
|
|
b6502189e8 | ||
|
|
f4ab2e4986 | ||
|
|
57cda4a445 | ||
|
|
49ca2c86e7 | ||
|
|
a08a9c5bfb | ||
|
|
ee8a839500 | ||
|
|
8ee962d79f | ||
|
|
4c3d652f00 | ||
|
|
c21fca08e2 | ||
|
|
77fde278e8 | ||
|
|
517af630e7 | ||
|
|
d8e5335268 | ||
|
|
db492575c8 | ||
|
|
9e580f8413 | ||
|
|
6ba2ba41c6 | ||
|
|
57381d43ed | ||
|
|
90c67c4b79 | ||
|
|
cf9f2bf98e | ||
|
|
8ebd5d53da | ||
|
|
60acfb17f0 | ||
|
|
8735a3f4d6 | ||
|
|
a07844ea13 | ||
|
|
1656bca9ab | ||
|
|
43af1a2283 | ||
|
|
84a21234d4 | ||
|
|
fefdaefb97 | ||
|
|
50eaea19cb | ||
|
|
438d8555c6 | ||
|
|
163a51c0f6 | ||
|
|
8df7064f73 | ||
|
|
99ee90a58f | ||
|
|
46c43d954c | ||
|
|
b37054697b | ||
|
|
5d50281f1a | ||
|
|
6bef525704 | ||
|
|
687a0ab5a4 | ||
|
|
60ae19bded | ||
|
|
be41c884b4 | ||
|
|
73d1b2ff67 | ||
|
|
2312b2c0f2 | ||
|
|
eae2c889ed | ||
|
|
ddd87fef12 | ||
|
|
f36d480919 | ||
|
|
7b566e2cfc | ||
|
|
6ae4158762 | ||
|
|
8dc95b041a | ||
|
|
211fd4fa06 | ||
|
|
10665821c4 | ||
|
|
a3585ff961 | ||
|
|
2aeff10a85 | ||
|
|
f2c8e63ae1 | ||
|
|
40bfda0f87 | ||
|
|
9888570456 | ||
|
|
a1690cd708 | ||
|
|
e602e2b887 | ||
|
|
eae2d61f12 | ||
|
|
8e246e1e67 | ||
|
|
f30ca39242 | ||
|
|
9f68db4818 | ||
|
|
f1cd5abfaa | ||
|
|
a8a7da3466 | ||
|
|
da9c980d26 | ||
|
|
27cf0d5eaf | ||
|
|
b5cbf16cb8 | ||
|
|
2024fa09d7 | ||
|
|
46b2a58c25 | ||
|
|
d871b2ebdc | ||
|
|
dc51dab7bc | ||
|
|
e39305dd91 | ||
|
|
6e1f1c4da7 | ||
|
|
21a42a0dee | ||
|
|
982083b3e9 | ||
|
|
40e222c43b | ||
|
|
c8634668e7 | ||
|
|
fa9bb75ad3 | ||
|
|
c47e402025 | ||
|
|
a3f48c1d47 | ||
|
|
de048cb474 | ||
|
|
0c5ee31707 | ||
|
|
c820b0c5e1 | ||
|
|
d09e381cbc | ||
|
|
53d631f1bd | ||
|
|
74768449bc | ||
|
|
294adc2269 | ||
|
|
ff97424667 | ||
|
|
cbf8d7cad6 | ||
|
|
0c41951b58 | ||
|
|
50b36696f8 | ||
|
|
de4182f305 | ||
|
|
4214cc0aaa | ||
|
|
d1c77f5061 | ||
|
|
45e01cdaf2 | ||
|
|
2fc19daeec | ||
|
|
60c0b9ab96 | ||
|
|
7f948f9c3e | ||
|
|
66fb9f1097 | ||
|
|
062a5b9bf8 | ||
|
|
5bedf15462 | ||
|
|
d7aee40387 | ||
|
|
26f08fabd7 | ||
|
|
05b48ce57c | ||
|
|
1ed87f4e83 | ||
|
|
b089558674 | ||
|
|
45df1dbba0 | ||
|
|
beb32770f0 | ||
|
|
3eec297282 | ||
|
|
b0b6c979ee | ||
|
|
7d69ac03ec | ||
|
|
d7e08abce8 | ||
|
|
d907942966 | ||
|
|
fe0e737f7b | ||
|
|
8da959df85 | ||
|
|
d7a047a533 | ||
|
|
c260223127 | ||
|
|
e834a80b7b | ||
|
|
7011dd6524 | ||
|
|
cde668b54c | ||
|
|
01db86e915 | ||
|
|
85376147a4 | ||
|
|
d2ecce272c | ||
|
|
7ee0b428d6 | ||
|
|
9482e4c86a | ||
|
|
42276a9500 | ||
|
|
ae8f78c84d | ||
|
|
9636852224 | ||
|
|
5f72715a42 | ||
|
|
c60b5dd4d6 | ||
|
|
42c474a21f | ||
|
|
04078fbf61 | ||
|
|
28ebbb3f20 | ||
|
|
96fa32bcc1 | ||
|
|
b3246b6971 | ||
|
|
0345414ded | ||
|
|
01d214b276 | ||
|
|
fdd181d68d | ||
|
|
5c7df736bf | ||
|
|
29870cb572 | ||
|
|
32223e90e3 | ||
|
|
31198cdbd9 | ||
|
|
971f2b1ed7 | ||
|
|
832cf91e88 | ||
|
|
2e010073aa | ||
|
|
4c93b72906 | ||
|
|
7091fd5791 | ||
|
|
e5edd388a0 | ||
|
|
b887270e25 | ||
|
|
fc0d0ad8d3 | ||
|
|
ddfc8555f7 | ||
|
|
6d0739f7d9 | ||
|
|
fdd750e4b5 | ||
|
|
9a5afe371a | ||
|
|
5123561889 | ||
|
|
ba7f59355f | ||
|
|
a79f92df9e | ||
|
|
8bc88763ec | ||
|
|
4a0e982bb2 | ||
|
|
013fdddc6e | ||
|
|
a9ca465ad0 | ||
|
|
cd4d75ee7b | ||
|
|
aa2e109f5f | ||
|
|
45e3c9da70 | ||
|
|
cee026b87e | ||
|
|
1a68ce05dc |
@@ -1,12 +1,12 @@
|
||||
ARG LLVM_VERSION="18"
|
||||
ARG REPORTED_LLVM_VERSION="18.1.8"
|
||||
ARG LLVM_VERSION="19"
|
||||
ARG REPORTED_LLVM_VERSION="19.1.7"
|
||||
ARG OLD_BUN_VERSION="1.1.38"
|
||||
ARG DEFAULT_CFLAGS="-mno-omit-leaf-frame-pointer -fno-omit-frame-pointer -ffunction-sections -fdata-sections -faddrsig -fno-unwind-tables -fno-asynchronous-unwind-tables"
|
||||
ARG DEFAULT_CXXFLAGS="-flto=full -fwhole-program-vtables -fforce-emit-vtables"
|
||||
ARG BUILDKITE_AGENT_TAGS="queue=linux,os=linux,arch=${TARGETARCH}"
|
||||
|
||||
FROM --platform=$BUILDPLATFORM ubuntu:20.04 as base-arm64
|
||||
FROM --platform=$BUILDPLATFORM ubuntu:18.04 as base-amd64
|
||||
FROM --platform=$BUILDPLATFORM ubuntu:20.04 as base-amd64
|
||||
FROM base-$TARGETARCH as base
|
||||
|
||||
ARG LLVM_VERSION
|
||||
|
||||
@@ -107,9 +107,9 @@ const buildPlatforms = [
|
||||
{ os: "linux", arch: "aarch64", distro: "amazonlinux", release: "2023", features: ["docker"] },
|
||||
{ os: "linux", arch: "x64", distro: "amazonlinux", release: "2023", features: ["docker"] },
|
||||
{ os: "linux", arch: "x64", baseline: true, distro: "amazonlinux", release: "2023", features: ["docker"] },
|
||||
{ os: "linux", arch: "aarch64", abi: "musl", distro: "alpine", release: "3.20" },
|
||||
{ os: "linux", arch: "x64", abi: "musl", distro: "alpine", release: "3.20" },
|
||||
{ os: "linux", arch: "x64", abi: "musl", baseline: true, distro: "alpine", release: "3.20" },
|
||||
{ os: "linux", arch: "aarch64", abi: "musl", distro: "alpine", release: "3.21" },
|
||||
{ os: "linux", arch: "x64", abi: "musl", distro: "alpine", release: "3.21" },
|
||||
{ os: "linux", arch: "x64", abi: "musl", baseline: true, distro: "alpine", release: "3.21" },
|
||||
{ os: "windows", arch: "x64", release: "2019" },
|
||||
{ os: "windows", arch: "x64", baseline: true, release: "2019" },
|
||||
];
|
||||
@@ -134,9 +134,9 @@ const testPlatforms = [
|
||||
{ os: "linux", arch: "x64", baseline: true, distro: "ubuntu", release: "24.04", tier: "latest" },
|
||||
{ os: "linux", arch: "x64", baseline: true, distro: "ubuntu", release: "22.04", tier: "previous" },
|
||||
{ os: "linux", arch: "x64", baseline: true, distro: "ubuntu", release: "20.04", tier: "oldest" },
|
||||
{ os: "linux", arch: "aarch64", abi: "musl", distro: "alpine", release: "3.20", tier: "latest" },
|
||||
{ os: "linux", arch: "x64", abi: "musl", distro: "alpine", release: "3.20", tier: "latest" },
|
||||
{ os: "linux", arch: "x64", abi: "musl", baseline: true, distro: "alpine", release: "3.20", tier: "latest" },
|
||||
{ os: "linux", arch: "aarch64", abi: "musl", distro: "alpine", release: "3.21", tier: "latest" },
|
||||
{ os: "linux", arch: "x64", abi: "musl", distro: "alpine", release: "3.21", tier: "latest" },
|
||||
{ os: "linux", arch: "x64", abi: "musl", baseline: true, distro: "alpine", release: "3.21", tier: "latest" },
|
||||
{ os: "windows", arch: "x64", release: "2019", tier: "oldest" },
|
||||
{ os: "windows", arch: "x64", release: "2019", baseline: true, tier: "oldest" },
|
||||
];
|
||||
|
||||
@@ -201,6 +201,8 @@ function create_release() {
|
||||
local artifacts=(
|
||||
bun-darwin-aarch64.zip
|
||||
bun-darwin-aarch64-profile.zip
|
||||
bun-darwin-x64.zip
|
||||
bun-darwin-x64-profile.zip
|
||||
bun-linux-aarch64.zip
|
||||
bun-linux-aarch64-profile.zip
|
||||
bun-linux-x64.zip
|
||||
|
||||
488
.cursor/rules/zig-javascriptcore-classes.mdc
Normal file
488
.cursor/rules/zig-javascriptcore-classes.mdc
Normal file
@@ -0,0 +1,488 @@
|
||||
---
|
||||
description: How Zig works with JavaScriptCore bindings generator
|
||||
globs:
|
||||
alwaysApply: false
|
||||
---
|
||||
# Bun's JavaScriptCore Class Bindings Generator
|
||||
|
||||
This document explains how Bun's class bindings generator works to bridge Zig and JavaScript code through JavaScriptCore (JSC).
|
||||
|
||||
## Architecture Overview
|
||||
|
||||
Bun's binding system creates a seamless bridge between JavaScript and Zig, allowing Zig implementations to be exposed as JavaScript classes. The system has several key components:
|
||||
|
||||
1. **Zig Implementation** (.zig files)
|
||||
2. **JavaScript Interface Definition** (.classes.ts files)
|
||||
3. **Generated Code** (C++/Zig files that connect everything)
|
||||
|
||||
## Class Definition Files
|
||||
|
||||
### JavaScript Interface (.classes.ts)
|
||||
|
||||
The `.classes.ts` files define the JavaScript API using a declarative approach:
|
||||
|
||||
```typescript
|
||||
// Example: encoding.classes.ts
|
||||
define({
|
||||
name: "TextDecoder",
|
||||
constructor: true,
|
||||
JSType: "object",
|
||||
finalize: true,
|
||||
proto: {
|
||||
decode: {
|
||||
// Function definition
|
||||
args: 1,
|
||||
},
|
||||
encoding: {
|
||||
// Getter with caching
|
||||
getter: true,
|
||||
cache: true,
|
||||
},
|
||||
fatal: {
|
||||
// Read-only property
|
||||
getter: true,
|
||||
},
|
||||
ignoreBOM: {
|
||||
// Read-only property
|
||||
getter: true,
|
||||
}
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
Each class definition specifies:
|
||||
- The class name
|
||||
- Whether it has a constructor
|
||||
- JavaScript type (object, function, etc.)
|
||||
- Properties and methods in the `proto` field
|
||||
- Caching strategy for properties
|
||||
- Finalization requirements
|
||||
|
||||
### Zig Implementation (.zig)
|
||||
|
||||
The Zig files implement the native functionality:
|
||||
|
||||
```zig
|
||||
// Example: TextDecoder.zig
|
||||
pub const TextDecoder = struct {
|
||||
// Internal state
|
||||
encoding: []const u8,
|
||||
fatal: bool,
|
||||
ignoreBOM: bool,
|
||||
|
||||
// Use generated bindings
|
||||
pub usingnamespace JSC.Codegen.JSTextDecoder;
|
||||
pub usingnamespace bun.New(@This());
|
||||
|
||||
// Constructor implementation - note use of globalObject
|
||||
pub fn constructor(
|
||||
globalObject: *JSGlobalObject,
|
||||
callFrame: *JSC.CallFrame,
|
||||
) bun.JSError!*TextDecoder {
|
||||
// Implementation
|
||||
}
|
||||
|
||||
// Prototype methods - note return type includes JSError
|
||||
pub fn decode(
|
||||
this: *TextDecoder,
|
||||
globalObject: *JSGlobalObject,
|
||||
callFrame: *JSC.CallFrame,
|
||||
) bun.JSError!JSC.JSValue {
|
||||
// Implementation
|
||||
}
|
||||
|
||||
// Getters
|
||||
pub fn getEncoding(this: *TextDecoder, globalObject: *JSGlobalObject) JSC.JSValue {
|
||||
return JSC.JSValue.createStringFromUTF8(globalObject, this.encoding);
|
||||
}
|
||||
|
||||
pub fn getFatal(this: *TextDecoder, globalObject: *JSGlobalObject) JSC.JSValue {
|
||||
return JSC.JSValue.jsBoolean(this.fatal);
|
||||
}
|
||||
|
||||
// Cleanup - note standard pattern of using deinit/deref
|
||||
pub fn deinit(this: *TextDecoder) void {
|
||||
// Release any retained resources
|
||||
}
|
||||
|
||||
pub fn finalize(this: *TextDecoder) void {
|
||||
this.deinit();
|
||||
// Or sometimes this is used to free memory instead
|
||||
bun.default_allocator.destroy(this);
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
Key components in the Zig file:
|
||||
- The struct containing native state
|
||||
- `usingnamespace JSC.Codegen.JS<ClassName>` to include generated code
|
||||
- `usingnamespace bun.New(@This())` for object creation helpers
|
||||
- Constructor and methods using `bun.JSError!JSValue` return type for proper error handling
|
||||
- Consistent use of `globalObject` parameter name instead of `ctx`
|
||||
- Methods matching the JavaScript interface
|
||||
- Getters/setters for properties
|
||||
- Proper resource cleanup pattern with `deinit()` and `finalize()`
|
||||
|
||||
## Code Generation System
|
||||
|
||||
The binding generator produces C++ code that connects JavaScript and Zig:
|
||||
|
||||
1. **JSC Class Structure**: Creates C++ classes for the JS object, prototype, and constructor
|
||||
2. **Memory Management**: Handles GC integration through JSC's WriteBarrier
|
||||
3. **Method Binding**: Connects JS function calls to Zig implementations
|
||||
4. **Type Conversion**: Converts between JS values and Zig types
|
||||
5. **Property Caching**: Implements the caching system for properties
|
||||
|
||||
The generated C++ code includes:
|
||||
- A JSC wrapper class (`JSTextDecoder`)
|
||||
- A prototype class (`JSTextDecoderPrototype`)
|
||||
- A constructor function (`JSTextDecoderConstructor`)
|
||||
- Function bindings (`TextDecoderPrototype__decodeCallback`)
|
||||
- Property getters/setters (`TextDecoderPrototype__encodingGetterWrap`)
|
||||
|
||||
## CallFrame Access
|
||||
|
||||
The `CallFrame` object provides access to JavaScript execution context:
|
||||
|
||||
```zig
|
||||
pub fn decode(
|
||||
this: *TextDecoder,
|
||||
globalObject: *JSGlobalObject,
|
||||
callFrame: *JSC.CallFrame
|
||||
) bun.JSError!JSC.JSValue {
|
||||
// Get arguments
|
||||
const input = callFrame.argument(0);
|
||||
const options = callFrame.argument(1);
|
||||
|
||||
// Get this value
|
||||
const thisValue = callFrame.thisValue();
|
||||
|
||||
// Implementation with error handling
|
||||
if (input.isUndefinedOrNull()) {
|
||||
return globalObject.throw("Input cannot be null or undefined", .{});
|
||||
}
|
||||
|
||||
// Return value or throw error
|
||||
return JSC.JSValue.jsString(globalObject, "result");
|
||||
}
|
||||
```
|
||||
|
||||
CallFrame methods include:
|
||||
- `argument(i)`: Get the i-th argument
|
||||
- `argumentCount()`: Get the number of arguments
|
||||
- `thisValue()`: Get the `this` value
|
||||
- `callee()`: Get the function being called
|
||||
|
||||
## Property Caching and GC-Owned Values
|
||||
|
||||
The `cache: true` option in property definitions enables JSC's WriteBarrier to efficiently store values:
|
||||
|
||||
```typescript
|
||||
encoding: {
|
||||
getter: true,
|
||||
cache: true, // Enable caching
|
||||
}
|
||||
```
|
||||
|
||||
### C++ Implementation
|
||||
|
||||
In the generated C++ code, caching uses JSC's WriteBarrier:
|
||||
|
||||
```cpp
|
||||
JSC_DEFINE_CUSTOM_GETTER(TextDecoderPrototype__encodingGetterWrap, (...)) {
|
||||
auto& vm = JSC::getVM(lexicalGlobalObject);
|
||||
Zig::GlobalObject *globalObject = reinterpret_cast<Zig::GlobalObject*>(lexicalGlobalObject);
|
||||
auto throwScope = DECLARE_THROW_SCOPE(vm);
|
||||
JSTextDecoder* thisObject = jsCast<JSTextDecoder*>(JSValue::decode(encodedThisValue));
|
||||
JSC::EnsureStillAliveScope thisArg = JSC::EnsureStillAliveScope(thisObject);
|
||||
|
||||
// Check for cached value and return if present
|
||||
if (JSValue cachedValue = thisObject->m_encoding.get())
|
||||
return JSValue::encode(cachedValue);
|
||||
|
||||
// Get value from Zig implementation
|
||||
JSC::JSValue result = JSC::JSValue::decode(
|
||||
TextDecoderPrototype__getEncoding(thisObject->wrapped(), globalObject)
|
||||
);
|
||||
RETURN_IF_EXCEPTION(throwScope, {});
|
||||
|
||||
// Store in cache for future access
|
||||
thisObject->m_encoding.set(vm, thisObject, result);
|
||||
RELEASE_AND_RETURN(throwScope, JSValue::encode(result));
|
||||
}
|
||||
```
|
||||
|
||||
### Zig Accessor Functions
|
||||
|
||||
For each cached property, the generator creates Zig accessor functions that allow Zig code to work with these GC-owned values:
|
||||
|
||||
```zig
|
||||
// External function declarations
|
||||
extern fn TextDecoderPrototype__encodingSetCachedValue(JSC.JSValue, *JSC.JSGlobalObject, JSC.JSValue) callconv(JSC.conv) void;
|
||||
extern fn TextDecoderPrototype__encodingGetCachedValue(JSC.JSValue) callconv(JSC.conv) JSC.JSValue;
|
||||
|
||||
/// `TextDecoder.encoding` setter
|
||||
/// This value will be visited by the garbage collector.
|
||||
pub fn encodingSetCached(thisValue: JSC.JSValue, globalObject: *JSC.JSGlobalObject, value: JSC.JSValue) void {
|
||||
JSC.markBinding(@src());
|
||||
TextDecoderPrototype__encodingSetCachedValue(thisValue, globalObject, value);
|
||||
}
|
||||
|
||||
/// `TextDecoder.encoding` getter
|
||||
/// This value will be visited by the garbage collector.
|
||||
pub fn encodingGetCached(thisValue: JSC.JSValue) ?JSC.JSValue {
|
||||
JSC.markBinding(@src());
|
||||
const result = TextDecoderPrototype__encodingGetCachedValue(thisValue);
|
||||
if (result == .zero)
|
||||
return null;
|
||||
|
||||
return result;
|
||||
}
|
||||
```
|
||||
|
||||
### Benefits of GC-Owned Values
|
||||
|
||||
This system provides several key benefits:
|
||||
|
||||
1. **Automatic Memory Management**: The JavaScriptCore GC tracks and manages these values
|
||||
2. **Proper Garbage Collection**: The WriteBarrier ensures values are properly visited during GC
|
||||
3. **Consistent Access**: Zig code can easily get/set these cached JS values
|
||||
4. **Performance**: Cached values avoid repeated computation or serialization
|
||||
|
||||
### Use Cases
|
||||
|
||||
GC-owned cached values are particularly useful for:
|
||||
|
||||
1. **Computed Properties**: Store expensive computation results
|
||||
2. **Lazily Created Objects**: Create objects only when needed, then cache them
|
||||
3. **References to Other Objects**: Store references to other JS objects that need GC tracking
|
||||
4. **Memoization**: Cache results based on input parameters
|
||||
|
||||
The WriteBarrier mechanism ensures that any JS values stored in this way are properly tracked by the garbage collector.
|
||||
|
||||
## Memory Management and Finalization
|
||||
|
||||
The binding system handles memory management across the JavaScript/Zig boundary:
|
||||
|
||||
1. **Object Creation**: JavaScript `new TextDecoder()` creates both a JS wrapper and a Zig struct
|
||||
2. **Reference Tracking**: JSC's GC tracks all JS references to the object
|
||||
3. **Finalization**: When the JS object is collected, the finalizer releases Zig resources
|
||||
|
||||
Bun uses a consistent pattern for resource cleanup:
|
||||
|
||||
```zig
|
||||
// Resource cleanup method - separate from finalization
|
||||
pub fn deinit(this: *TextDecoder) void {
|
||||
// Release resources like strings
|
||||
this._encoding.deref(); // String deref pattern
|
||||
|
||||
// Free any buffers
|
||||
if (this.buffer) |buffer| {
|
||||
bun.default_allocator.free(buffer);
|
||||
}
|
||||
}
|
||||
|
||||
// Called by the GC when object is collected
|
||||
pub fn finalize(this: *TextDecoder) void {
|
||||
JSC.markBinding(@src()); // For debugging
|
||||
this.deinit(); // Clean up resources
|
||||
bun.default_allocator.destroy(this); // Free the object itself
|
||||
}
|
||||
```
|
||||
|
||||
Some objects that hold references to other JS objects use `.deref()` instead:
|
||||
|
||||
```zig
|
||||
pub fn finalize(this: *SocketAddress) void {
|
||||
JSC.markBinding(@src());
|
||||
this._presentation.deref(); // Release references
|
||||
this.destroy();
|
||||
}
|
||||
```
|
||||
|
||||
## Error Handling with JSError
|
||||
|
||||
Bun uses `bun.JSError!JSValue` return type for proper error handling:
|
||||
|
||||
```zig
|
||||
pub fn decode(
|
||||
this: *TextDecoder,
|
||||
globalObject: *JSGlobalObject,
|
||||
callFrame: *JSC.CallFrame
|
||||
) bun.JSError!JSC.JSValue {
|
||||
// Throwing an error
|
||||
if (callFrame.argumentCount() < 1) {
|
||||
return globalObject.throw("Missing required argument", .{});
|
||||
}
|
||||
|
||||
// Or returning a success value
|
||||
return JSC.JSValue.jsString(globalObject, "Success!");
|
||||
}
|
||||
```
|
||||
|
||||
This pattern allows Zig functions to:
|
||||
1. Return JavaScript values on success
|
||||
2. Throw JavaScript exceptions on error
|
||||
3. Propagate errors automatically through the call stack
|
||||
|
||||
## Type Safety and Error Handling
|
||||
|
||||
The binding system includes robust error handling:
|
||||
|
||||
```cpp
|
||||
// Example of type checking in generated code
|
||||
JSTextDecoder* thisObject = jsDynamicCast<JSTextDecoder*>(callFrame->thisValue());
|
||||
if (UNLIKELY(!thisObject)) {
|
||||
scope.throwException(lexicalGlobalObject,
|
||||
Bun::createInvalidThisError(lexicalGlobalObject, callFrame->thisValue(), "TextDecoder"_s));
|
||||
return {};
|
||||
}
|
||||
```
|
||||
|
||||
## Prototypal Inheritance
|
||||
|
||||
The binding system creates proper JavaScript prototype chains:
|
||||
|
||||
1. **Constructor**: JSTextDecoderConstructor with standard .prototype property
|
||||
2. **Prototype**: JSTextDecoderPrototype with methods and properties
|
||||
3. **Instances**: Each JSTextDecoder instance with __proto__ pointing to prototype
|
||||
|
||||
This ensures JavaScript inheritance works as expected:
|
||||
|
||||
```cpp
|
||||
// From generated code
|
||||
void JSTextDecoderConstructor::finishCreation(VM& vm, JSC::JSGlobalObject* globalObject, JSTextDecoderPrototype* prototype)
|
||||
{
|
||||
Base::finishCreation(vm, 0, "TextDecoder"_s, PropertyAdditionMode::WithoutStructureTransition);
|
||||
|
||||
// Set up the prototype chain
|
||||
putDirectWithoutTransition(vm, vm.propertyNames->prototype, prototype, PropertyAttribute::DontEnum | PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly);
|
||||
ASSERT(inherits(info()));
|
||||
}
|
||||
```
|
||||
|
||||
## Performance Considerations
|
||||
|
||||
The binding system is optimized for performance:
|
||||
|
||||
1. **Direct Pointer Access**: JavaScript objects maintain a direct pointer to Zig objects
|
||||
2. **Property Caching**: WriteBarrier caching avoids repeated native calls for stable properties
|
||||
3. **Memory Management**: JSC garbage collection integrated with Zig memory management
|
||||
4. **Type Conversion**: Fast paths for common JavaScript/Zig type conversions
|
||||
|
||||
## Creating a New Class Binding
|
||||
|
||||
To create a new class binding in Bun:
|
||||
|
||||
1. **Define the class interface** in a `.classes.ts` file:
|
||||
```typescript
|
||||
define({
|
||||
name: "MyClass",
|
||||
constructor: true,
|
||||
finalize: true,
|
||||
proto: {
|
||||
myMethod: {
|
||||
args: 1,
|
||||
},
|
||||
myProperty: {
|
||||
getter: true,
|
||||
cache: true,
|
||||
}
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
2. **Implement the native functionality** in a `.zig` file:
|
||||
```zig
|
||||
pub const MyClass = struct {
|
||||
// State
|
||||
value: []const u8,
|
||||
|
||||
// Generated bindings
|
||||
pub usingnamespace JSC.Codegen.JSMyClass;
|
||||
pub usingnamespace bun.New(@This());
|
||||
|
||||
// Constructor
|
||||
pub fn constructor(
|
||||
globalObject: *JSGlobalObject,
|
||||
callFrame: *JSC.CallFrame,
|
||||
) bun.JSError!*MyClass {
|
||||
const arg = callFrame.argument(0);
|
||||
// Implementation
|
||||
}
|
||||
|
||||
// Method
|
||||
pub fn myMethod(
|
||||
this: *MyClass,
|
||||
globalObject: *JSGlobalObject,
|
||||
callFrame: *JSC.CallFrame,
|
||||
) bun.JSError!JSC.JSValue {
|
||||
// Implementation
|
||||
}
|
||||
|
||||
// Getter
|
||||
pub fn getMyProperty(this: *MyClass, globalObject: *JSGlobalObject) JSC.JSValue {
|
||||
return JSC.JSValue.jsString(globalObject, this.value);
|
||||
}
|
||||
|
||||
// Resource cleanup
|
||||
pub fn deinit(this: *MyClass) void {
|
||||
// Clean up resources
|
||||
}
|
||||
|
||||
pub fn finalize(this: *MyClass) void {
|
||||
this.deinit();
|
||||
bun.default_allocator.destroy(this);
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
3. **The binding generator** creates all necessary C++ and Zig glue code to connect JavaScript and Zig, including:
|
||||
- C++ class definitions
|
||||
- Method and property bindings
|
||||
- Memory management utilities
|
||||
- GC integration code
|
||||
|
||||
## Generated Code Structure
|
||||
|
||||
The binding generator produces several components:
|
||||
|
||||
### 1. C++ Classes
|
||||
|
||||
For each Zig class, the system generates:
|
||||
|
||||
- **JS<Class>**: Main wrapper that holds a pointer to the Zig object (`JSTextDecoder`)
|
||||
- **JS<Class>Prototype**: Contains methods and properties (`JSTextDecoderPrototype`)
|
||||
- **JS<Class>Constructor**: Implementation of the JavaScript constructor (`JSTextDecoderConstructor`)
|
||||
|
||||
### 2. C++ Methods and Properties
|
||||
|
||||
- **Method Callbacks**: `TextDecoderPrototype__decodeCallback`
|
||||
- **Property Getters/Setters**: `TextDecoderPrototype__encodingGetterWrap`
|
||||
- **Initialization Functions**: `finishCreation` methods for setting up the class
|
||||
|
||||
### 3. Zig Bindings
|
||||
|
||||
- **External Function Declarations**:
|
||||
```zig
|
||||
extern fn TextDecoderPrototype__decode(*TextDecoder, *JSC.JSGlobalObject, *JSC.CallFrame) callconv(JSC.conv) JSC.EncodedJSValue;
|
||||
```
|
||||
|
||||
- **Cached Value Accessors**:
|
||||
```zig
|
||||
pub fn encodingGetCached(thisValue: JSC.JSValue) ?JSC.JSValue { ... }
|
||||
pub fn encodingSetCached(thisValue: JSC.JSValue, globalObject: *JSC.JSGlobalObject, value: JSC.JSValue) void { ... }
|
||||
```
|
||||
|
||||
- **Constructor Helpers**:
|
||||
```zig
|
||||
pub fn create(globalObject: *JSC.JSGlobalObject) bun.JSError!JSC.JSValue { ... }
|
||||
```
|
||||
|
||||
### 4. GC Integration
|
||||
|
||||
- **Memory Cost Calculation**: `estimatedSize` method
|
||||
- **Child Visitor Methods**: `visitChildrenImpl` and `visitAdditionalChildren`
|
||||
- **Heap Analysis**: `analyzeHeap` for debugging memory issues
|
||||
|
||||
This architecture makes it possible to implement high-performance native functionality in Zig while exposing a clean, idiomatic JavaScript API to users.
|
||||
2
.github/pull_request_template.md
vendored
2
.github/pull_request_template.md
vendored
@@ -28,7 +28,7 @@ This adds a new flag --bail to bun test. When set, it will stop running tests af
|
||||
|
||||
- [ ] I checked the lifetime of memory allocated to verify it's (1) freed and (2) only freed when it should be
|
||||
- [ ] I included a test for the new code, or an existing test covers it
|
||||
- [ ] JSValue used outside outside of the stack is either wrapped in a JSC.Strong or is JSValueProtect'ed
|
||||
- [ ] JSValue used outside of the stack is either wrapped in a JSC.Strong or is JSValueProtect'ed
|
||||
- [ ] I wrote TypeScript/JavaScript tests and they pass locally (`bun-debug test test-file-name.test`)
|
||||
-->
|
||||
|
||||
|
||||
4
.github/workflows/clang-format.yml
vendored
4
.github/workflows/clang-format.yml
vendored
@@ -11,8 +11,8 @@ on:
|
||||
|
||||
env:
|
||||
BUN_VERSION: "1.2.0"
|
||||
LLVM_VERSION: "18.1.8"
|
||||
LLVM_VERSION_MAJOR: "18"
|
||||
LLVM_VERSION: "19.1.7"
|
||||
LLVM_VERSION_MAJOR: "19"
|
||||
|
||||
jobs:
|
||||
clang-format:
|
||||
|
||||
4
.github/workflows/clang-tidy.yml
vendored
4
.github/workflows/clang-tidy.yml
vendored
@@ -11,8 +11,8 @@ on:
|
||||
|
||||
env:
|
||||
BUN_VERSION: "1.2.0"
|
||||
LLVM_VERSION: "18.1.8"
|
||||
LLVM_VERSION_MAJOR: "18"
|
||||
LLVM_VERSION: "19.1.7"
|
||||
LLVM_VERSION_MAJOR: "19"
|
||||
|
||||
jobs:
|
||||
clang-tidy:
|
||||
|
||||
1
.github/workflows/run-lint.yml
vendored
1
.github/workflows/run-lint.yml
vendored
@@ -3,7 +3,6 @@ name: Lint
|
||||
permissions:
|
||||
contents: read
|
||||
env:
|
||||
LLVM_VERSION: 16
|
||||
BUN_VERSION: "1.2.0"
|
||||
|
||||
on:
|
||||
|
||||
2
.github/workflows/update-cares.yml
vendored
2
.github/workflows/update-cares.yml
vendored
@@ -89,4 +89,6 @@ jobs:
|
||||
|
||||
Updates c-ares to version ${{ steps.check-version.outputs.tag }}
|
||||
|
||||
Compare: https://github.com/c-ares/c-ares/compare/${{ steps.check-version.outputs.current }}...${{ steps.check-version.outputs.latest }}
|
||||
|
||||
Auto-updated by [this workflow](https://github.com/oven-sh/bun/actions/workflows/update-cares.yml)
|
||||
|
||||
2
.github/workflows/update-libarchive.yml
vendored
2
.github/workflows/update-libarchive.yml
vendored
@@ -89,4 +89,6 @@ jobs:
|
||||
|
||||
Updates libarchive to version ${{ steps.check-version.outputs.tag }}
|
||||
|
||||
Compare: https://github.com/libarchive/libarchive/compare/${{ steps.check-version.outputs.current }}...${{ steps.check-version.outputs.latest }}
|
||||
|
||||
Auto-updated by [this workflow](https://github.com/oven-sh/bun/actions/workflows/update-libarchive.yml)
|
||||
|
||||
2
.github/workflows/update-libdeflate.yml
vendored
2
.github/workflows/update-libdeflate.yml
vendored
@@ -89,4 +89,6 @@ jobs:
|
||||
|
||||
Updates libdeflate to version ${{ steps.check-version.outputs.tag }}
|
||||
|
||||
Compare: https://github.com/ebiggers/libdeflate/compare/${{ steps.check-version.outputs.current }}...${{ steps.check-version.outputs.latest }}
|
||||
|
||||
Auto-updated by [this workflow](https://github.com/oven-sh/bun/actions/workflows/update-libdeflate.yml)
|
||||
|
||||
2
.github/workflows/update-lolhtml.yml
vendored
2
.github/workflows/update-lolhtml.yml
vendored
@@ -89,4 +89,6 @@ jobs:
|
||||
|
||||
Updates lolhtml to version ${{ steps.check-version.outputs.tag }}
|
||||
|
||||
Compare: https://github.com/cloudflare/lol-html/compare/${{ steps.check-version.outputs.current }}...${{ steps.check-version.outputs.latest }}
|
||||
|
||||
Auto-updated by [this workflow](https://github.com/oven-sh/bun/actions/workflows/update-lolhtml.yml)
|
||||
|
||||
2
.github/workflows/update-lshpack.yml
vendored
2
.github/workflows/update-lshpack.yml
vendored
@@ -89,4 +89,6 @@ jobs:
|
||||
|
||||
Updates lshpack to version ${{ steps.check-version.outputs.tag }}
|
||||
|
||||
Compare: https://github.com/litespeedtech/ls-hpack/compare/${{ steps.check-version.outputs.current }}...${{ steps.check-version.outputs.latest }}
|
||||
|
||||
Auto-updated by [this workflow](https://github.com/oven-sh/bun/actions/workflows/update-lshpack.yml)
|
||||
|
||||
2
.github/workflows/update-sqlite3.yml
vendored
2
.github/workflows/update-sqlite3.yml
vendored
@@ -106,4 +106,6 @@ jobs:
|
||||
|
||||
Updates SQLite to version ${{ steps.check-version.outputs.latest }}
|
||||
|
||||
Compare: https://sqlite.org/src/vdiff?from=${{ steps.check-version.outputs.current }}&to=${{ steps.check-version.outputs.latest }}
|
||||
|
||||
Auto-updated by [this workflow](https://github.com/oven-sh/bun/actions/workflows/update-sqlite3.yml)
|
||||
|
||||
18
.lldbinit
18
.lldbinit
@@ -1,4 +1,16 @@
|
||||
# command script import vendor/zig/tools/lldb_pretty_printers.py
|
||||
command script import vendor/WebKit/Tools/lldb/lldb_webkit.py
|
||||
# Tell LLDB what to do when the debugged process receives SIGPWR: pass it through to the process
|
||||
# (-p), but do not stop the process (-s) or notify the user (-n).
|
||||
#
|
||||
# JSC's garbage collector sends this signal (as configured by Bun WebKit in
|
||||
# Thread::initializePlatformThreading() in ThreadingPOSIX.cpp) to the JS thread to suspend or resume
|
||||
# it. So stopping the process would just create noise when debugging any long-running script.
|
||||
process handle -p true -s false -n false SIGPWR
|
||||
|
||||
# type summary add --summary-string "${var} | inner=${var[0-30]}, source=${var[33-64]}, tag=${var[31-32]}" "unsigned long"
|
||||
command script import misctools/lldb/lldb_pretty_printers.py
|
||||
type category enable zig.lang
|
||||
type category enable zig.std
|
||||
|
||||
command script import misctools/lldb/lldb_webkit.py
|
||||
|
||||
command script delete btjs
|
||||
command alias btjs p {printf("gathering btjs trace...\n");printf("%s\n", (char*)dumpBtjsTrace())}
|
||||
47
.vscode/launch.json
generated
vendored
47
.vscode/launch.json
generated
vendored
@@ -22,7 +22,6 @@
|
||||
},
|
||||
"console": "internalConsole",
|
||||
// Don't pause when the GC runs while the debugger is open.
|
||||
"postRunCommands": ["command source '${workspaceFolder}/misctools/lldb/lldb_commands'"],
|
||||
},
|
||||
{
|
||||
"type": "lldb",
|
||||
@@ -38,7 +37,6 @@
|
||||
},
|
||||
"console": "internalConsole",
|
||||
// Don't pause when the GC runs while the debugger is open.
|
||||
"postRunCommands": ["command source '${workspaceFolder}/misctools/lldb/lldb_commands'"],
|
||||
},
|
||||
{
|
||||
"type": "lldb",
|
||||
@@ -60,7 +58,6 @@
|
||||
},
|
||||
"console": "internalConsole",
|
||||
// Don't pause when the GC runs while the debugger is open.
|
||||
"postRunCommands": ["command source '${workspaceFolder}/misctools/lldb/lldb_commands'"],
|
||||
},
|
||||
{
|
||||
"type": "lldb",
|
||||
@@ -76,7 +73,6 @@
|
||||
},
|
||||
"console": "internalConsole",
|
||||
// Don't pause when the GC runs while the debugger is open.
|
||||
"postRunCommands": ["command source '${workspaceFolder}/misctools/lldb/lldb_commands'"],
|
||||
},
|
||||
{
|
||||
"type": "lldb",
|
||||
@@ -92,7 +88,6 @@
|
||||
},
|
||||
"console": "internalConsole",
|
||||
// Don't pause when the GC runs while the debugger is open.
|
||||
"postRunCommands": ["command source '${workspaceFolder}/misctools/lldb/lldb_commands'"],
|
||||
},
|
||||
{
|
||||
"type": "lldb",
|
||||
@@ -108,7 +103,6 @@
|
||||
},
|
||||
"console": "internalConsole",
|
||||
// Don't pause when the GC runs while the debugger is open.
|
||||
"postRunCommands": ["command source '${workspaceFolder}/misctools/lldb/lldb_commands'"],
|
||||
},
|
||||
{
|
||||
"type": "lldb",
|
||||
@@ -125,7 +119,6 @@
|
||||
},
|
||||
"console": "internalConsole",
|
||||
// Don't pause when the GC runs while the debugger is open.
|
||||
"postRunCommands": ["command source '${workspaceFolder}/misctools/lldb/lldb_commands'"],
|
||||
"serverReadyAction": {
|
||||
"pattern": "https://debug.bun.sh/#localhost:([0-9]+)/",
|
||||
"uriFormat": "https://debug.bun.sh/#ws://localhost:%s/",
|
||||
@@ -147,7 +140,6 @@
|
||||
},
|
||||
"console": "internalConsole",
|
||||
// Don't pause when the GC runs while the debugger is open.
|
||||
"postRunCommands": ["command source '${workspaceFolder}/misctools/lldb/lldb_commands'"],
|
||||
"serverReadyAction": {
|
||||
"pattern": "https://debug.bun.sh/#localhost:([0-9]+)/",
|
||||
"uriFormat": "https://debug.bun.sh/#ws://localhost:%s/",
|
||||
@@ -169,7 +161,6 @@
|
||||
},
|
||||
"console": "internalConsole",
|
||||
// Don't pause when the GC runs while the debugger is open.
|
||||
"postRunCommands": ["command source '${workspaceFolder}/misctools/lldb/lldb_commands'"],
|
||||
},
|
||||
{
|
||||
"type": "lldb",
|
||||
@@ -188,7 +179,6 @@
|
||||
},
|
||||
"console": "internalConsole",
|
||||
// Don't pause when the GC runs while the debugger is open.
|
||||
"postRunCommands": ["command source '${workspaceFolder}/misctools/lldb/lldb_commands'"],
|
||||
},
|
||||
{
|
||||
"type": "lldb",
|
||||
@@ -203,7 +193,6 @@
|
||||
},
|
||||
"console": "internalConsole",
|
||||
// Don't pause when the GC runs while the debugger is open.
|
||||
"postRunCommands": ["command source '${workspaceFolder}/misctools/lldb/lldb_commands'"],
|
||||
},
|
||||
{
|
||||
"type": "lldb",
|
||||
@@ -221,7 +210,6 @@
|
||||
},
|
||||
"console": "internalConsole",
|
||||
// Don't pause when the GC runs while the debugger is open.
|
||||
"postRunCommands": ["command source '${workspaceFolder}/misctools/lldb/lldb_commands'"],
|
||||
},
|
||||
{
|
||||
"type": "lldb",
|
||||
@@ -236,7 +224,6 @@
|
||||
},
|
||||
"console": "internalConsole",
|
||||
// Don't pause when the GC runs while the debugger is open.
|
||||
"postRunCommands": ["command source '${workspaceFolder}/misctools/lldb/lldb_commands'"],
|
||||
},
|
||||
{
|
||||
"type": "lldb",
|
||||
@@ -253,7 +240,6 @@
|
||||
},
|
||||
"console": "internalConsole",
|
||||
// Don't pause when the GC runs while the debugger is open.
|
||||
"postRunCommands": ["command source '${workspaceFolder}/misctools/lldb/lldb_commands'"],
|
||||
"serverReadyAction": {
|
||||
"pattern": "https://debug.bun.sh/#localhost:([0-9]+)/",
|
||||
"uriFormat": "https://debug.bun.sh/#ws://localhost:%s/",
|
||||
@@ -275,7 +261,6 @@
|
||||
},
|
||||
"console": "internalConsole",
|
||||
// Don't pause when the GC runs while the debugger is open.
|
||||
"postRunCommands": ["command source '${workspaceFolder}/misctools/lldb/lldb_commands'"],
|
||||
"serverReadyAction": {
|
||||
"pattern": "https://debug.bun.sh/#localhost:([0-9]+)/",
|
||||
"uriFormat": "https://debug.bun.sh/#ws://localhost:%s/",
|
||||
@@ -297,7 +282,6 @@
|
||||
},
|
||||
"console": "internalConsole",
|
||||
// Don't pause when the GC runs while the debugger is open.
|
||||
"postRunCommands": ["command source '${workspaceFolder}/misctools/lldb/lldb_commands'"],
|
||||
},
|
||||
{
|
||||
"type": "lldb",
|
||||
@@ -313,7 +297,6 @@
|
||||
},
|
||||
"console": "internalConsole",
|
||||
// Don't pause when the GC runs while the debugger is open.
|
||||
"postRunCommands": ["command source '${workspaceFolder}/misctools/lldb/lldb_commands'"],
|
||||
},
|
||||
{
|
||||
"type": "lldb",
|
||||
@@ -329,7 +312,6 @@
|
||||
},
|
||||
"console": "internalConsole",
|
||||
// Don't pause when the GC runs while the debugger is open.
|
||||
"postRunCommands": ["command source '${workspaceFolder}/misctools/lldb/lldb_commands'"],
|
||||
},
|
||||
{
|
||||
"type": "lldb",
|
||||
@@ -345,7 +327,6 @@
|
||||
},
|
||||
"console": "internalConsole",
|
||||
// Don't pause when the GC runs while the debugger is open.
|
||||
"postRunCommands": ["command source '${workspaceFolder}/misctools/lldb/lldb_commands'"],
|
||||
},
|
||||
{
|
||||
"type": "lldb",
|
||||
@@ -361,7 +342,6 @@
|
||||
},
|
||||
"console": "internalConsole",
|
||||
// Don't pause when the GC runs while the debugger is open.
|
||||
"postRunCommands": ["command source '${workspaceFolder}/misctools/lldb/lldb_commands'"],
|
||||
},
|
||||
{
|
||||
"type": "lldb",
|
||||
@@ -378,7 +358,6 @@
|
||||
},
|
||||
"console": "internalConsole",
|
||||
// Don't pause when the GC runs while the debugger is open.
|
||||
"postRunCommands": ["command source '${workspaceFolder}/misctools/lldb/lldb_commands'"],
|
||||
"serverReadyAction": {
|
||||
"pattern": "https://debug.bun.sh/#localhost:([0-9]+)/",
|
||||
"uriFormat": "https://debug.bun.sh/#ws://localhost:%s/",
|
||||
@@ -400,7 +379,6 @@
|
||||
},
|
||||
"console": "internalConsole",
|
||||
// Don't pause when the GC runs while the debugger is open.
|
||||
"postRunCommands": ["command source '${workspaceFolder}/misctools/lldb/lldb_commands'"],
|
||||
"serverReadyAction": {
|
||||
"pattern": "https://debug.bun.sh/#localhost:([0-9]+)/",
|
||||
"uriFormat": "https://debug.bun.sh/#ws://localhost:%s/",
|
||||
@@ -421,7 +399,6 @@
|
||||
},
|
||||
"console": "internalConsole",
|
||||
// Don't pause when the GC runs while the debugger is open.
|
||||
"postRunCommands": ["command source '${workspaceFolder}/misctools/lldb/lldb_commands'"],
|
||||
},
|
||||
// bun test [*]
|
||||
{
|
||||
@@ -437,7 +414,6 @@
|
||||
},
|
||||
"console": "internalConsole",
|
||||
// Don't pause when the GC runs while the debugger is open.
|
||||
"postRunCommands": ["command source '${workspaceFolder}/misctools/lldb/lldb_commands'"],
|
||||
},
|
||||
{
|
||||
"type": "lldb",
|
||||
@@ -452,7 +428,6 @@
|
||||
},
|
||||
"console": "internalConsole",
|
||||
// Don't pause when the GC runs while the debugger is open.
|
||||
"postRunCommands": ["command source '${workspaceFolder}/misctools/lldb/lldb_commands'"],
|
||||
},
|
||||
{
|
||||
"type": "lldb",
|
||||
@@ -468,7 +443,6 @@
|
||||
},
|
||||
"console": "internalConsole",
|
||||
// Don't pause when the GC runs while the debugger is open.
|
||||
"postRunCommands": ["command source '${workspaceFolder}/misctools/lldb/lldb_commands'"],
|
||||
"serverReadyAction": {
|
||||
"pattern": "https://debug.bun.sh/#localhost:([0-9]+)/",
|
||||
"uriFormat": "https://debug.bun.sh/#ws://localhost:%s/",
|
||||
@@ -488,7 +462,6 @@
|
||||
},
|
||||
"console": "internalConsole",
|
||||
// Don't pause when the GC runs while the debugger is open.
|
||||
"postRunCommands": ["command source '${workspaceFolder}/misctools/lldb/lldb_commands'"],
|
||||
},
|
||||
{
|
||||
"type": "lldb",
|
||||
@@ -503,7 +476,6 @@
|
||||
},
|
||||
"console": "internalConsole",
|
||||
// Don't pause when the GC runs while the debugger is open.
|
||||
"postRunCommands": ["command source '${workspaceFolder}/misctools/lldb/lldb_commands'"],
|
||||
},
|
||||
// Windows: bun test [file]
|
||||
{
|
||||
@@ -1129,7 +1101,24 @@
|
||||
],
|
||||
"console": "internalConsole",
|
||||
// Don't pause when the GC runs while the debugger is open.
|
||||
"postRunCommands": ["command source '${workspaceFolder}/misctools/lldb/lldb_commands'"],
|
||||
},
|
||||
{
|
||||
"type": "bun",
|
||||
"name": "[JS] bun test [file]",
|
||||
"runtime": "${workspaceFolder}/build/debug/bun-debug",
|
||||
"runtimeArgs": ["test", "${file}"],
|
||||
"cwd": "${workspaceFolder}",
|
||||
"env": {
|
||||
"BUN_DEBUG_QUIET_LOGS": "1",
|
||||
"BUN_GARBAGE_COLLECTOR_LEVEL": "2",
|
||||
},
|
||||
},
|
||||
{
|
||||
"type": "midas-rr",
|
||||
"request": "attach",
|
||||
"name": "rr",
|
||||
"trace": "Off",
|
||||
"setupCommands": ["handle SIGPWR nostop noprint pass"],
|
||||
},
|
||||
],
|
||||
"inputs": [
|
||||
|
||||
2
.vscode/settings.json
vendored
2
.vscode/settings.json
vendored
@@ -35,8 +35,6 @@
|
||||
// "zig.zls.enableBuildOnSave": true,
|
||||
// "zig.buildOnSave": true,
|
||||
"zig.buildFilePath": "${workspaceFolder}/build.zig",
|
||||
"zig.path": "${workspaceFolder}/vendor/zig/zig.exe",
|
||||
"zig.zls.path": "${workspaceFolder}/vendor/zig/zls.exe",
|
||||
"zig.formattingProvider": "zls",
|
||||
"zig.zls.enableInlayHints": false,
|
||||
"[zig]": {
|
||||
|
||||
@@ -53,39 +53,39 @@ $ brew install bun
|
||||
|
||||
## Install LLVM
|
||||
|
||||
Bun requires LLVM 18 (`clang` is part of LLVM). This version requirement is to match WebKit (precompiled), as mismatching versions will cause memory allocation failures at runtime. In most cases, you can install LLVM through your system package manager:
|
||||
Bun requires LLVM 19 (`clang` is part of LLVM). This version requirement is to match WebKit (precompiled), as mismatching versions will cause memory allocation failures at runtime. In most cases, you can install LLVM through your system package manager:
|
||||
|
||||
{% codetabs group="os" %}
|
||||
|
||||
```bash#macOS (Homebrew)
|
||||
$ brew install llvm@18
|
||||
$ brew install llvm@19
|
||||
```
|
||||
|
||||
```bash#Ubuntu/Debian
|
||||
$ # LLVM has an automatic installation script that is compatible with all versions of Ubuntu
|
||||
$ wget https://apt.llvm.org/llvm.sh -O - | sudo bash -s -- 18 all
|
||||
$ wget https://apt.llvm.org/llvm.sh -O - | sudo bash -s -- 19 all
|
||||
```
|
||||
|
||||
```bash#Arch
|
||||
$ sudo pacman -S llvm clang18 lld
|
||||
$ sudo pacman -S llvm clang lld
|
||||
```
|
||||
|
||||
```bash#Fedora
|
||||
$ sudo dnf install llvm18 clang18 lld18-devel
|
||||
$ sudo dnf install llvm clang lld-devel
|
||||
```
|
||||
|
||||
```bash#openSUSE Tumbleweed
|
||||
$ sudo zypper install clang18 lld18 llvm18
|
||||
$ sudo zypper install clang19 lld19 llvm19
|
||||
```
|
||||
|
||||
{% /codetabs %}
|
||||
|
||||
If none of the above solutions apply, you will have to install it [manually](https://github.com/llvm/llvm-project/releases/tag/llvmorg-18.1.8).
|
||||
If none of the above solutions apply, you will have to install it [manually](https://github.com/llvm/llvm-project/releases/tag/llvmorg-19.1.7).
|
||||
|
||||
Make sure Clang/LLVM 18 is in your path:
|
||||
Make sure Clang/LLVM 19 is in your path:
|
||||
|
||||
```bash
|
||||
$ which clang-18
|
||||
$ which clang-19
|
||||
```
|
||||
|
||||
If not, run this to manually add it:
|
||||
@@ -94,13 +94,13 @@ If not, run this to manually add it:
|
||||
|
||||
```bash#macOS (Homebrew)
|
||||
# use fish_add_path if you're using fish
|
||||
# use path+="$(brew --prefix llvm@18)/bin" if you are using zsh
|
||||
$ export PATH="$(brew --prefix llvm@18)/bin:$PATH"
|
||||
# use path+="$(brew --prefix llvm@19)/bin" if you are using zsh
|
||||
$ export PATH="$(brew --prefix llvm@19)/bin:$PATH"
|
||||
```
|
||||
|
||||
```bash#Arch
|
||||
# use fish_add_path if you're using fish
|
||||
$ export PATH="$PATH:/usr/lib/llvm18/bin"
|
||||
$ export PATH="$PATH:/usr/lib/llvm19/bin"
|
||||
```
|
||||
|
||||
{% /codetabs %}
|
||||
@@ -134,6 +134,16 @@ We recommend adding `./build/debug` to your `$PATH` so that you can run `bun-deb
|
||||
$ bun-debug
|
||||
```
|
||||
|
||||
## Running debug builds
|
||||
|
||||
The `bd` package.json script compiles and runs a debug build of Bun, only printing the output of the build process if it fails.
|
||||
|
||||
```sh
|
||||
$ bun bd <args>
|
||||
$ bun bd test foo.test.ts
|
||||
$ bun bd ./foo.ts
|
||||
```
|
||||
|
||||
## Code generation scripts
|
||||
|
||||
Several code generation scripts are used during Bun's build process. These are run automatically when changes are made to certain files.
|
||||
@@ -205,18 +215,30 @@ WebKit is not cloned by default (to save time and disk space). To clone and buil
|
||||
# Clone WebKit into ./vendor/WebKit
|
||||
$ git clone https://github.com/oven-sh/WebKit vendor/WebKit
|
||||
|
||||
# Check out the commit hash specified in `set(WEBKIT_VERSION <commit_hash>)` in cmake/tools/SetupWebKit.cmake
|
||||
$ git -C vendor/WebKit checkout <commit_hash>
|
||||
|
||||
# Make a debug build of JSC. This will output build artifacts in ./vendor/WebKit/WebKitBuild/Debug
|
||||
# Optionally, you can use `make jsc` for a release build
|
||||
$ make jsc-debug
|
||||
$ make jsc-debug && rm vendor/WebKit/WebKitBuild/Debug/JavaScriptCore/DerivedSources/inspector/InspectorProtocolObjects.h
|
||||
|
||||
# Build bun with the local JSC build
|
||||
$ bun run build:local
|
||||
```
|
||||
|
||||
Using `bun run build:local` will build Bun in the `./build/debug-local` directory (instead of `./build/debug`), you'll have to change a couple of places to use this new directory:
|
||||
|
||||
- The first line in [`src/js/builtins.d.ts`](/src/js/builtins.d.ts)
|
||||
- The `CompilationDatabase` line in [`.clangd` config](/.clangd) should be `CompilationDatabase: build/debug-local`
|
||||
- In [`build.zig`](/build.zig), the `codegen_path` option should be `build/debug-local/codegen` (instead of `build/debug/codegen`)
|
||||
- In [`.vscode/launch.json`](/.vscode/launch.json), many configurations use `./build/debug/`, change them as you see fit
|
||||
|
||||
Note that the WebKit folder, including build artifacts, is 8GB+ in size.
|
||||
|
||||
If you are using a JSC debug build and using VScode, make sure to run the `C/C++: Select a Configuration` command to configure intellisense to find the debug headers.
|
||||
|
||||
Note that if you change make changes to our [WebKit fork](https://github.com/oven-sh/WebKit), you will also have to change [`SetupWebKit.cmake`](/cmake/tools/SetupWebKit.cmake) to point to the commit hash.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### 'span' file not found on Ubuntu
|
||||
@@ -238,7 +260,7 @@ The issue may manifest when initially running `bun setup` as Clang being unable
|
||||
```
|
||||
The C++ compiler
|
||||
|
||||
"/usr/bin/clang++-18"
|
||||
"/usr/bin/clang++-19"
|
||||
|
||||
is not able to compile a simple test program.
|
||||
```
|
||||
|
||||
22
Makefile
22
Makefile
@@ -91,9 +91,9 @@ ZIG ?= $(shell which zig 2>/dev/null || echo -e "error: Missing zig. Please make
|
||||
# This is easier to happen than you'd expect.
|
||||
# Using realpath here causes issues because clang uses clang++ as a symlink
|
||||
# so if that's resolved, it won't build for C++
|
||||
REAL_CC = $(shell which clang-18 2>/dev/null || which clang 2>/dev/null)
|
||||
REAL_CXX = $(shell which clang++-18 2>/dev/null || which clang++ 2>/dev/null)
|
||||
CLANG_FORMAT = $(shell which clang-format-18 2>/dev/null || which clang-format 2>/dev/null)
|
||||
REAL_CC = $(shell which clang-19 2>/dev/null || which clang 2>/dev/null)
|
||||
REAL_CXX = $(shell which clang++-19 2>/dev/null || which clang++ 2>/dev/null)
|
||||
CLANG_FORMAT = $(shell which clang-format-19 2>/dev/null || which clang-format 2>/dev/null)
|
||||
|
||||
CC = $(REAL_CC)
|
||||
CXX = $(REAL_CXX)
|
||||
@@ -117,14 +117,14 @@ CC_WITH_CCACHE = $(CCACHE_PATH) $(CC)
|
||||
ifeq ($(OS_NAME),darwin)
|
||||
# Find LLVM
|
||||
ifeq ($(wildcard $(LLVM_PREFIX)),)
|
||||
LLVM_PREFIX = $(shell brew --prefix llvm@18)
|
||||
LLVM_PREFIX = $(shell brew --prefix llvm@19)
|
||||
endif
|
||||
ifeq ($(wildcard $(LLVM_PREFIX)),)
|
||||
LLVM_PREFIX = $(shell brew --prefix llvm)
|
||||
endif
|
||||
ifeq ($(wildcard $(LLVM_PREFIX)),)
|
||||
# This is kinda ugly, but I can't find a better way to error :(
|
||||
LLVM_PREFIX = $(shell echo -e "error: Unable to find llvm. Please run 'brew install llvm@18' or set LLVM_PREFIX=/path/to/llvm")
|
||||
LLVM_PREFIX = $(shell echo -e "error: Unable to find llvm. Please run 'brew install llvm@19' or set LLVM_PREFIX=/path/to/llvm")
|
||||
endif
|
||||
|
||||
LDFLAGS += -L$(LLVM_PREFIX)/lib
|
||||
@@ -164,7 +164,7 @@ CMAKE_FLAGS_WITHOUT_RELEASE = -DCMAKE_C_COMPILER=$(CC) \
|
||||
-DCMAKE_OSX_DEPLOYMENT_TARGET=$(MIN_MACOS_VERSION) \
|
||||
$(CMAKE_CXX_COMPILER_LAUNCHER_FLAG) \
|
||||
-DCMAKE_AR=$(AR) \
|
||||
-DCMAKE_RANLIB=$(which llvm-18-ranlib 2>/dev/null || which llvm-ranlib 2>/dev/null) \
|
||||
-DCMAKE_RANLIB=$(which llvm-19-ranlib 2>/dev/null || which llvm-ranlib 2>/dev/null) \
|
||||
-DCMAKE_CXX_STANDARD=20 \
|
||||
-DCMAKE_C_STANDARD=17 \
|
||||
-DCMAKE_CXX_STANDARD_REQUIRED=ON \
|
||||
@@ -191,7 +191,7 @@ endif
|
||||
|
||||
ifeq ($(OS_NAME),linux)
|
||||
LIBICONV_PATH =
|
||||
AR = $(shell which llvm-ar-18 2>/dev/null || which llvm-ar 2>/dev/null || which ar 2>/dev/null)
|
||||
AR = $(shell which llvm-ar-19 2>/dev/null || which llvm-ar 2>/dev/null || which ar 2>/dev/null)
|
||||
endif
|
||||
|
||||
OPTIMIZATION_LEVEL=-O3 $(MARCH_NATIVE)
|
||||
@@ -255,7 +255,7 @@ DEFAULT_LINKER_FLAGS= -pthread -ldl
|
||||
endif
|
||||
ifeq ($(OS_NAME),darwin)
|
||||
_MIMALLOC_OBJECT_FILE = 0
|
||||
JSC_BUILD_STEPS += jsc-build-mac jsc-copy-headers
|
||||
JSC_BUILD_STEPS += jsc-build-mac
|
||||
JSC_BUILD_STEPS_DEBUG += jsc-build-mac-debug
|
||||
_MIMALLOC_FILE = libmimalloc.a
|
||||
_MIMALLOC_INPUT_PATH = libmimalloc.a
|
||||
@@ -286,7 +286,7 @@ STRIP=/usr/bin/strip
|
||||
endif
|
||||
|
||||
ifeq ($(OS_NAME),linux)
|
||||
STRIP=$(shell which llvm-strip 2>/dev/null || which llvm-strip-18 2>/dev/null || which strip 2>/dev/null || echo "Missing strip")
|
||||
STRIP=$(shell which llvm-strip 2>/dev/null || which llvm-strip-19 2>/dev/null || which strip 2>/dev/null || echo "Missing strip")
|
||||
endif
|
||||
|
||||
|
||||
@@ -674,7 +674,7 @@ endif
|
||||
.PHONY: assert-deps
|
||||
assert-deps:
|
||||
@echo "Checking if the required utilities are available..."
|
||||
@if [ $(CLANG_VERSION) -lt "18" ]; then echo -e "ERROR: clang version >=18 required, found: $(CLANG_VERSION). Install with:\n\n $(POSIX_PKG_MANAGER) install llvm@18"; exit 1; fi
|
||||
@if [ $(CLANG_VERSION) -lt "19" ]; then echo -e "ERROR: clang version >=19 required, found: $(CLANG_VERSION). Install with:\n\n $(POSIX_PKG_MANAGER) install llvm@19"; exit 1; fi
|
||||
@cmake --version >/dev/null 2>&1 || (echo -e "ERROR: cmake is required."; exit 1)
|
||||
@$(PYTHON) --version >/dev/null 2>&1 || (echo -e "ERROR: python is required."; exit 1)
|
||||
@$(ESBUILD) --version >/dev/null 2>&1 || (echo -e "ERROR: esbuild is required."; exit 1)
|
||||
@@ -924,7 +924,7 @@ bun-codesign-release-local-debug:
|
||||
|
||||
|
||||
.PHONY: jsc
|
||||
jsc: jsc-build jsc-copy-headers jsc-bindings
|
||||
jsc: jsc-build
|
||||
.PHONY: jsc-debug
|
||||
jsc-debug: jsc-build-debug
|
||||
.PHONY: jsc-build
|
||||
|
||||
44
bench/crypto/aes-gcm-throughput.mjs
Normal file
44
bench/crypto/aes-gcm-throughput.mjs
Normal file
@@ -0,0 +1,44 @@
|
||||
import { bench, run } from "../runner.mjs";
|
||||
import crypto from "node:crypto";
|
||||
import { Buffer } from "node:buffer";
|
||||
|
||||
const keylen = { "aes-128-gcm": 16, "aes-192-gcm": 24, "aes-256-gcm": 32 };
|
||||
const sizes = [4 * 1024, 1024 * 1024];
|
||||
const ciphers = ["aes-128-gcm", "aes-192-gcm", "aes-256-gcm"];
|
||||
|
||||
const messages = {};
|
||||
sizes.forEach(size => {
|
||||
messages[size] = Buffer.alloc(size, "b");
|
||||
});
|
||||
|
||||
const keys = {};
|
||||
ciphers.forEach(cipher => {
|
||||
keys[cipher] = crypto.randomBytes(keylen[cipher]);
|
||||
});
|
||||
|
||||
// Fixed IV and AAD
|
||||
const iv = crypto.randomBytes(12);
|
||||
const associate_data = Buffer.alloc(16, "z");
|
||||
|
||||
for (const cipher of ciphers) {
|
||||
for (const size of sizes) {
|
||||
const message = messages[size];
|
||||
const key = keys[cipher];
|
||||
|
||||
bench(`${cipher} ${size / 1024}KB`, () => {
|
||||
const alice = crypto.createCipheriv(cipher, key, iv);
|
||||
alice.setAAD(associate_data);
|
||||
const enc = alice.update(message);
|
||||
alice.final();
|
||||
const tag = alice.getAuthTag();
|
||||
|
||||
const bob = crypto.createDecipheriv(cipher, key, iv);
|
||||
bob.setAuthTag(tag);
|
||||
bob.setAAD(associate_data);
|
||||
bob.update(enc);
|
||||
bob.final();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
await run();
|
||||
53
bench/crypto/diffie-hellman.mjs
Normal file
53
bench/crypto/diffie-hellman.mjs
Normal file
@@ -0,0 +1,53 @@
|
||||
import crypto from "node:crypto";
|
||||
import { bench, run } from "../runner.mjs";
|
||||
|
||||
// Pre-generate DH params to avoid including setup in benchmarks
|
||||
const dhSize = 1024; // Reduced from 2048 for faster testing
|
||||
const dh = crypto.createDiffieHellman(dhSize);
|
||||
const dhPrime = dh.getPrime();
|
||||
const dhGenerator = dh.getGenerator();
|
||||
|
||||
// Classical Diffie-Hellman
|
||||
bench("DH - generateKeys", () => {
|
||||
const alice = crypto.createDiffieHellman(dhPrime, dhGenerator);
|
||||
return alice.generateKeys();
|
||||
});
|
||||
|
||||
bench("DH - computeSecret", () => {
|
||||
// Setup
|
||||
const alice = crypto.createDiffieHellman(dhPrime, dhGenerator);
|
||||
const aliceKey = alice.generateKeys();
|
||||
const bob = crypto.createDiffieHellman(dhPrime, dhGenerator);
|
||||
const bobKey = bob.generateKeys();
|
||||
|
||||
// Benchmark just the secret computation
|
||||
return alice.computeSecret(bobKey);
|
||||
});
|
||||
|
||||
// ECDH with prime256v1 (P-256)
|
||||
bench("ECDH-P256 - generateKeys", () => {
|
||||
const ecdh = crypto.createECDH("prime256v1");
|
||||
return ecdh.generateKeys();
|
||||
});
|
||||
|
||||
bench("ECDH-P256 - computeSecret", () => {
|
||||
// Setup
|
||||
const alice = crypto.createECDH("prime256v1");
|
||||
const aliceKey = alice.generateKeys();
|
||||
const bob = crypto.createECDH("prime256v1");
|
||||
const bobKey = bob.generateKeys();
|
||||
|
||||
// Benchmark just the secret computation
|
||||
return alice.computeSecret(bobKey);
|
||||
});
|
||||
|
||||
// ECDH with secp384r1 (P-384)
|
||||
bench("ECDH-P384 - computeSecret", () => {
|
||||
const alice = crypto.createECDH("secp384r1");
|
||||
const aliceKey = alice.generateKeys();
|
||||
const bob = crypto.createECDH("secp384r1");
|
||||
const bobKey = bob.generateKeys();
|
||||
return alice.computeSecret(bobKey);
|
||||
});
|
||||
|
||||
await run();
|
||||
44
bench/crypto/ecdh-convert-key.mjs
Normal file
44
bench/crypto/ecdh-convert-key.mjs
Normal file
@@ -0,0 +1,44 @@
|
||||
import crypto from "node:crypto";
|
||||
import { bench, run } from "../runner.mjs";
|
||||
|
||||
function generateTestKeyPairs() {
|
||||
const curves = crypto.getCurves();
|
||||
const keys = {};
|
||||
|
||||
for (const curve of curves) {
|
||||
const ecdh = crypto.createECDH(curve);
|
||||
ecdh.generateKeys();
|
||||
|
||||
keys[curve] = {
|
||||
compressed: ecdh.getPublicKey("hex", "compressed"),
|
||||
uncompressed: ecdh.getPublicKey("hex", "uncompressed"),
|
||||
instance: ecdh,
|
||||
};
|
||||
}
|
||||
|
||||
return keys;
|
||||
}
|
||||
|
||||
const testKeys = generateTestKeyPairs();
|
||||
|
||||
bench("ECDH key format - P256 compressed to uncompressed", () => {
|
||||
const publicKey = testKeys["prime256v1"].compressed;
|
||||
return crypto.ECDH.convertKey(publicKey, "prime256v1", "hex", "hex", "uncompressed");
|
||||
});
|
||||
|
||||
bench("ECDH key format - P256 uncompressed to compressed", () => {
|
||||
const publicKey = testKeys["prime256v1"].uncompressed;
|
||||
return crypto.ECDH.convertKey(publicKey, "prime256v1", "hex", "hex", "compressed");
|
||||
});
|
||||
|
||||
bench("ECDH key format - P384 compressed to uncompressed", () => {
|
||||
const publicKey = testKeys["secp384r1"].compressed;
|
||||
return crypto.ECDH.convertKey(publicKey, "secp384r1", "hex", "hex", "uncompressed");
|
||||
});
|
||||
|
||||
bench("ECDH key format - P384 uncompressed to compressed", () => {
|
||||
const publicKey = testKeys["secp384r1"].uncompressed;
|
||||
return crypto.ECDH.convertKey(publicKey, "secp384r1", "hex", "hex", "compressed");
|
||||
});
|
||||
|
||||
await run();
|
||||
50
bench/crypto/hkdf.mjs
Normal file
50
bench/crypto/hkdf.mjs
Normal file
@@ -0,0 +1,50 @@
|
||||
import crypto from "node:crypto";
|
||||
import { bench, run } from "../runner.mjs";
|
||||
|
||||
// Sample keys with different lengths
|
||||
const keys = {
|
||||
short: "secret",
|
||||
long: "this-is-a-much-longer-secret-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
|
||||
};
|
||||
|
||||
// Test parameters
|
||||
const salts = ["", "salt"];
|
||||
const infos = ["", "info"];
|
||||
const hashes = ["sha256", "sha512"];
|
||||
const sizes = [10, 1024];
|
||||
|
||||
// Benchmark sync HKDF
|
||||
for (const hash of hashes) {
|
||||
for (const keyName of Object.keys(keys)) {
|
||||
const key = keys[keyName];
|
||||
for (const size of sizes) {
|
||||
bench(`hkdfSync ${hash} ${keyName}-key ${size} bytes`, () => {
|
||||
return crypto.hkdfSync(hash, key, "salt", "info", size);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Benchmark different combinations of salt and info
|
||||
for (const salt of salts) {
|
||||
for (const info of infos) {
|
||||
bench(`hkdfSync sha256 with ${salt ? "salt" : "no-salt"} and ${info ? "info" : "no-info"}`, () => {
|
||||
return crypto.hkdfSync("sha256", "secret", salt, info, 64);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Benchmark async HKDF (using promises for cleaner benchmark)
|
||||
// Note: async benchmarks in Mitata require returning a Promise
|
||||
for (const hash of hashes) {
|
||||
bench(`hkdf ${hash} async`, async () => {
|
||||
return new Promise((resolve, reject) => {
|
||||
crypto.hkdf(hash, "secret", "salt", "info", 64, (err, derivedKey) => {
|
||||
if (err) reject(err);
|
||||
else resolve(derivedKey);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
await run();
|
||||
43
bench/crypto/primes.mjs
Normal file
43
bench/crypto/primes.mjs
Normal file
@@ -0,0 +1,43 @@
|
||||
import { checkPrime, checkPrimeSync, generatePrime, generatePrimeSync } from "node:crypto";
|
||||
import { bench, run } from "../runner.mjs";
|
||||
|
||||
const prime512 = generatePrimeSync(512);
|
||||
const prime2048 = generatePrimeSync(2048);
|
||||
|
||||
bench("checkPrimeSync 512", () => {
|
||||
return checkPrimeSync(prime512);
|
||||
});
|
||||
|
||||
bench("checkPrimeSync 2048", () => {
|
||||
return checkPrimeSync(prime2048);
|
||||
});
|
||||
|
||||
bench("checkPrime 512", async () => {
|
||||
const promises = Array.from({ length: 10 }, () => new Promise(resolve => checkPrime(prime512, resolve)));
|
||||
await Promise.all(promises);
|
||||
});
|
||||
|
||||
bench("checkPrime 2048", async () => {
|
||||
const promises = Array.from({ length: 10 }, () => new Promise(resolve => checkPrime(prime2048, resolve)));
|
||||
await Promise.all(promises);
|
||||
});
|
||||
|
||||
bench("generatePrimeSync 512", () => {
|
||||
return generatePrimeSync(512);
|
||||
});
|
||||
|
||||
bench("generatePrimeSync 2048", () => {
|
||||
return generatePrimeSync(2048);
|
||||
});
|
||||
|
||||
bench("generatePrime 512", async () => {
|
||||
const promises = Array.from({ length: 10 }, () => new Promise(resolve => generatePrime(512, resolve)));
|
||||
await Promise.all(promises);
|
||||
});
|
||||
|
||||
bench("generatePrime 2048", async () => {
|
||||
const promises = Array.from({ length: 10 }, () => new Promise(resolve => generatePrime(2048, resolve)));
|
||||
await Promise.all(promises);
|
||||
});
|
||||
|
||||
await run();
|
||||
50
bench/crypto/random.mjs
Normal file
50
bench/crypto/random.mjs
Normal file
@@ -0,0 +1,50 @@
|
||||
import crypto from "crypto";
|
||||
import { bench, run } from "../runner.mjs";
|
||||
|
||||
bench("randomInt - sync", () => {
|
||||
crypto.randomInt(1000);
|
||||
});
|
||||
|
||||
bench("randomInt - async", async () => {
|
||||
const { promise, resolve } = Promise.withResolvers();
|
||||
crypto.randomInt(1000, () => {
|
||||
resolve();
|
||||
});
|
||||
await promise;
|
||||
});
|
||||
|
||||
bench("randonBytes - 32", () => {
|
||||
crypto.randomBytes(32);
|
||||
});
|
||||
|
||||
bench("randomBytes - 256", () => {
|
||||
crypto.randomBytes(256);
|
||||
});
|
||||
|
||||
const buf = Buffer.alloc(256);
|
||||
|
||||
bench("randomFill - 32", async () => {
|
||||
const { promise, resolve } = Promise.withResolvers();
|
||||
crypto.randomFill(buf, 0, 32, () => {
|
||||
resolve();
|
||||
});
|
||||
await promise;
|
||||
});
|
||||
|
||||
bench("randomFill - 256", async () => {
|
||||
const { promise, resolve } = Promise.withResolvers();
|
||||
crypto.randomFill(buf, 0, 256, () => {
|
||||
resolve();
|
||||
});
|
||||
await promise;
|
||||
});
|
||||
|
||||
bench("randomFillSync - 32", () => {
|
||||
crypto.randomFillSync(buf, 0, 32);
|
||||
});
|
||||
|
||||
bench("randomFillSync - 256", () => {
|
||||
crypto.randomFillSync(buf, 0, 256);
|
||||
});
|
||||
|
||||
await run();
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
// Enable latest features
|
||||
"lib": ["ESNext", "DOM"],
|
||||
"lib": ["ESNext"],
|
||||
"target": "ESNext",
|
||||
"module": "ESNext",
|
||||
"moduleDetection": "force",
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
"eventemitter3": "^5.0.0",
|
||||
"execa": "^8.0.1",
|
||||
"fast-glob": "3.3.1",
|
||||
"fastify": "^5.0.0",
|
||||
"fdir": "^6.1.0",
|
||||
"mitata": "^1.0.25",
|
||||
"react": "^18.3.1",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
// Enable latest features
|
||||
"lib": ["ESNext", "DOM"],
|
||||
"lib": ["ESNext"],
|
||||
"target": "ESNext",
|
||||
"module": "ESNext",
|
||||
"moduleDetection": "force",
|
||||
|
||||
13
bench/snippets/express-hello.mjs
Normal file
13
bench/snippets/express-hello.mjs
Normal file
@@ -0,0 +1,13 @@
|
||||
import express from "express";
|
||||
|
||||
const app = express();
|
||||
const port = 3000;
|
||||
|
||||
var i = 0;
|
||||
app.get("/", (req, res) => {
|
||||
res.send("Hello World!" + i++);
|
||||
});
|
||||
|
||||
app.listen(port, () => {
|
||||
console.log(`Express app listening at http://localhost:${port}`);
|
||||
});
|
||||
20
bench/snippets/fastify.mjs
Normal file
20
bench/snippets/fastify.mjs
Normal file
@@ -0,0 +1,20 @@
|
||||
import Fastify from "fastify";
|
||||
|
||||
const fastify = Fastify({
|
||||
logger: false,
|
||||
});
|
||||
|
||||
fastify.get("/", async (request, reply) => {
|
||||
return { hello: "world" };
|
||||
});
|
||||
|
||||
const start = async () => {
|
||||
try {
|
||||
await fastify.listen({ port: 3000 });
|
||||
} catch (err) {
|
||||
fastify.log.error(err);
|
||||
process.exit(1);
|
||||
}
|
||||
};
|
||||
|
||||
start();
|
||||
37
build.zig
37
build.zig
@@ -19,17 +19,17 @@ const OperatingSystem = @import("src/env.zig").OperatingSystem;
|
||||
const pathRel = fs.path.relative;
|
||||
|
||||
/// Do not rename this constant. It is scanned by some scripts to determine which zig version to install.
|
||||
const recommended_zig_version = "0.14.0-dev.2987+183bb8b08";
|
||||
const recommended_zig_version = "0.14.0";
|
||||
|
||||
comptime {
|
||||
if (!std.mem.eql(u8, builtin.zig_version_string, recommended_zig_version)) {
|
||||
@compileError(
|
||||
"" ++
|
||||
"Bun requires Zig version " ++ recommended_zig_version ++ " (found " ++
|
||||
builtin.zig_version_string ++ "). This is " ++
|
||||
"automatically configured via Bun's CMake setup. You likely meant to run " ++
|
||||
"`bun setup`. If you are trying to upgrade the Zig compiler, " ++
|
||||
"run `./scripts/download-zig.sh master` or comment this message out.",
|
||||
"Bun requires Zig version " ++ recommended_zig_version ++ ", but you have " ++
|
||||
builtin.zig_version_string ++ ". This is automatically configured via Bun's " ++
|
||||
"CMake setup. You likely meant to run `bun run build`. If you are trying to " ++
|
||||
"upgrade the Zig compiler, edit ZIG_COMMIT in cmake/tools/SetupZig.cmake or " ++
|
||||
"comment this error out.",
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -319,7 +319,21 @@ pub fn build(b: *Build) !void {
|
||||
.{ .os = .linux, .arch = .aarch64 },
|
||||
.{ .os = .linux, .arch = .x86_64, .musl = true },
|
||||
.{ .os = .linux, .arch = .aarch64, .musl = true },
|
||||
});
|
||||
}, &.{ .Debug, .ReleaseFast });
|
||||
}
|
||||
|
||||
// zig build check-all-debug
|
||||
{
|
||||
const step = b.step("check-all-debug", "Check for semantic analysis errors on all supported platforms in debug mode");
|
||||
addMultiCheck(b, step, build_options, &.{
|
||||
.{ .os = .windows, .arch = .x86_64 },
|
||||
.{ .os = .mac, .arch = .x86_64 },
|
||||
.{ .os = .mac, .arch = .aarch64 },
|
||||
.{ .os = .linux, .arch = .x86_64 },
|
||||
.{ .os = .linux, .arch = .aarch64 },
|
||||
.{ .os = .linux, .arch = .x86_64, .musl = true },
|
||||
.{ .os = .linux, .arch = .aarch64, .musl = true },
|
||||
}, &.{.Debug});
|
||||
}
|
||||
|
||||
// zig build check-windows
|
||||
@@ -327,21 +341,21 @@ pub fn build(b: *Build) !void {
|
||||
const step = b.step("check-windows", "Check for semantic analysis errors on Windows");
|
||||
addMultiCheck(b, step, build_options, &.{
|
||||
.{ .os = .windows, .arch = .x86_64 },
|
||||
});
|
||||
}, &.{ .Debug, .ReleaseFast });
|
||||
}
|
||||
{
|
||||
const step = b.step("check-macos", "Check for semantic analysis errors on Windows");
|
||||
addMultiCheck(b, step, build_options, &.{
|
||||
.{ .os = .mac, .arch = .x86_64 },
|
||||
.{ .os = .mac, .arch = .aarch64 },
|
||||
});
|
||||
}, &.{ .Debug, .ReleaseFast });
|
||||
}
|
||||
{
|
||||
const step = b.step("check-linux", "Check for semantic analysis errors on Windows");
|
||||
addMultiCheck(b, step, build_options, &.{
|
||||
.{ .os = .linux, .arch = .x86_64 },
|
||||
.{ .os = .linux, .arch = .aarch64 },
|
||||
});
|
||||
}, &.{ .Debug, .ReleaseFast });
|
||||
}
|
||||
|
||||
// zig build translate-c-headers
|
||||
@@ -369,9 +383,10 @@ pub fn addMultiCheck(
|
||||
parent_step: *Step,
|
||||
root_build_options: BunBuildOptions,
|
||||
to_check: []const struct { os: OperatingSystem, arch: Arch, musl: bool = false },
|
||||
optimize: []const std.builtin.OptimizeMode,
|
||||
) void {
|
||||
for (to_check) |check| {
|
||||
for ([_]std.builtin.Mode{ .Debug, .ReleaseFast }) |mode| {
|
||||
for (optimize) |mode| {
|
||||
const check_target = b.resolveTargetQuery(.{
|
||||
.os_tag = OperatingSystem.stdOSTag(check.os),
|
||||
.cpu_arch = check.arch,
|
||||
|
||||
3
bun.lock
3
bun.lock
@@ -27,9 +27,10 @@
|
||||
},
|
||||
"packages/bun-types": {
|
||||
"name": "bun-types",
|
||||
"version": "1.2.5",
|
||||
"dependencies": {
|
||||
"@types/node": "*",
|
||||
"@types/ws": "~8.5.10",
|
||||
"@types/ws": "*",
|
||||
},
|
||||
"devDependencies": {
|
||||
"@biomejs/biome": "^1.5.3",
|
||||
|
||||
@@ -2,3 +2,7 @@
|
||||
# https://github.com/oven-sh/bun/issues/16289
|
||||
[test]
|
||||
preload = ["./test/js/node/harness.ts", "./test/preload.ts"]
|
||||
|
||||
[install]
|
||||
# Node.js never auto-installs modules.
|
||||
auto = "disable"
|
||||
|
||||
@@ -419,7 +419,15 @@ function(register_command)
|
||||
list(APPEND CMD_EFFECTIVE_OUTPUTS ${artifact})
|
||||
if(BUILDKITE)
|
||||
file(RELATIVE_PATH filename ${BUILD_PATH} ${artifact})
|
||||
list(APPEND CMD_COMMANDS COMMAND ${CMAKE_COMMAND} -E chdir ${BUILD_PATH} buildkite-agent artifact upload ${filename})
|
||||
if(filename STREQUAL "libbun-profile.a")
|
||||
# libbun-profile.a is now over 5gb in size, compress it first
|
||||
list(APPEND CMD_COMMANDS COMMAND ${CMAKE_COMMAND} -E chdir ${BUILD_PATH} rm -r ${BUILD_PATH}/codegen)
|
||||
list(APPEND CMD_COMMANDS COMMAND ${CMAKE_COMMAND} -E chdir ${BUILD_PATH} rm -r ${CACHE_PATH})
|
||||
list(APPEND CMD_COMMANDS COMMAND ${CMAKE_COMMAND} -E chdir ${BUILD_PATH} gzip -6 libbun-profile.a)
|
||||
list(APPEND CMD_COMMANDS COMMAND ${CMAKE_COMMAND} -E chdir ${BUILD_PATH} buildkite-agent artifact upload libbun-profile.a.gz)
|
||||
else()
|
||||
list(APPEND CMD_COMMANDS COMMAND ${CMAKE_COMMAND} -E chdir ${BUILD_PATH} buildkite-agent artifact upload ${filename})
|
||||
endif()
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ register_repository(
|
||||
REPOSITORY
|
||||
oven-sh/boringssl
|
||||
COMMIT
|
||||
914b005ef3ece44159dca0ffad74eb42a9f6679f
|
||||
7a5d984c69b0c34c4cbb56c6812eaa5b9bef485c
|
||||
)
|
||||
|
||||
register_cmake_command(
|
||||
|
||||
@@ -738,7 +738,7 @@ endif()
|
||||
# --- C/C++ Properties ---
|
||||
|
||||
set_target_properties(${bun} PROPERTIES
|
||||
CXX_STANDARD 20
|
||||
CXX_STANDARD 23
|
||||
CXX_STANDARD_REQUIRED YES
|
||||
CXX_EXTENSIONS YES
|
||||
CXX_VISIBILITY_PRESET hidden
|
||||
@@ -747,6 +747,18 @@ set_target_properties(${bun} PROPERTIES
|
||||
VISIBILITY_INLINES_HIDDEN YES
|
||||
)
|
||||
|
||||
if (NOT WIN32)
|
||||
# Enable precompiled headers
|
||||
# Only enable in these scenarios:
|
||||
# 1. NOT in CI, OR
|
||||
# 2. In CI AND BUN_CPP_ONLY is enabled
|
||||
if(NOT CI OR (CI AND BUN_CPP_ONLY))
|
||||
target_precompile_headers(${bun} PRIVATE
|
||||
"$<$<COMPILE_LANGUAGE:CXX>:${CWD}/src/bun.js/bindings/root.h>"
|
||||
)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# --- C/C++ Includes ---
|
||||
|
||||
if(WIN32)
|
||||
@@ -773,6 +785,10 @@ target_include_directories(${bun} PRIVATE
|
||||
${NODEJS_HEADERS_PATH}/include
|
||||
)
|
||||
|
||||
if(NOT WIN32)
|
||||
target_include_directories(${bun} PRIVATE ${CWD}/src/bun.js/bindings/libuv)
|
||||
endif()
|
||||
|
||||
if(LINUX)
|
||||
include(CheckIncludeFiles)
|
||||
check_include_files("sys/queue.h" HAVE_SYS_QUEUE_H)
|
||||
@@ -901,6 +917,10 @@ if(NOT WIN32)
|
||||
-Werror
|
||||
)
|
||||
endif()
|
||||
else()
|
||||
target_compile_options(${bun} PUBLIC
|
||||
-Wno-nullability-completeness
|
||||
)
|
||||
endif()
|
||||
|
||||
# --- Linker options ---
|
||||
@@ -943,28 +963,17 @@ endif()
|
||||
|
||||
if(LINUX)
|
||||
if(NOT ABI STREQUAL "musl")
|
||||
# on arm64
|
||||
if(CMAKE_SYSTEM_PROCESSOR MATCHES "arm|ARM|arm64|ARM64|aarch64|AARCH64")
|
||||
target_link_options(${bun} PUBLIC
|
||||
-Wl,--wrap=exp
|
||||
-Wl,--wrap=expf
|
||||
-Wl,--wrap=fcntl64
|
||||
-Wl,--wrap=log
|
||||
-Wl,--wrap=log2
|
||||
-Wl,--wrap=log2f
|
||||
-Wl,--wrap=logf
|
||||
-Wl,--wrap=pow
|
||||
-Wl,--wrap=powf
|
||||
)
|
||||
else()
|
||||
target_link_options(${bun} PUBLIC
|
||||
-Wl,--wrap=exp
|
||||
-Wl,--wrap=expf
|
||||
-Wl,--wrap=log2f
|
||||
-Wl,--wrap=logf
|
||||
-Wl,--wrap=powf
|
||||
)
|
||||
endif()
|
||||
target_link_options(${bun} PUBLIC
|
||||
-Wl,--wrap=exp
|
||||
-Wl,--wrap=expf
|
||||
-Wl,--wrap=fcntl64
|
||||
-Wl,--wrap=log
|
||||
-Wl,--wrap=log2
|
||||
-Wl,--wrap=log2f
|
||||
-Wl,--wrap=logf
|
||||
-Wl,--wrap=pow
|
||||
-Wl,--wrap=powf
|
||||
)
|
||||
endif()
|
||||
|
||||
if(NOT ABI STREQUAL "musl")
|
||||
|
||||
@@ -4,7 +4,7 @@ register_repository(
|
||||
REPOSITORY
|
||||
cloudflare/lol-html
|
||||
COMMIT
|
||||
4f8becea13a0021c8b71abd2dcc5899384973b66
|
||||
67f1d4ffd6b74db7e053fb129dcce620193c180d
|
||||
)
|
||||
|
||||
set(LOLHTML_CWD ${VENDOR_PATH}/lolhtml/c-api)
|
||||
|
||||
@@ -120,6 +120,9 @@ foreach(i RANGE ${BUILDKITE_JOBS_MAX_INDEX})
|
||||
endif()
|
||||
|
||||
if(BUILDKITE)
|
||||
if(BUILDKITE_ARTIFACT_PATH STREQUAL "libbun-profile.a")
|
||||
set(BUILDKITE_ARTIFACT_PATH libbun-profile.a.gz)
|
||||
endif()
|
||||
set(BUILDKITE_DOWNLOAD_COMMAND buildkite-agent artifact download ${BUILDKITE_ARTIFACT_PATH} . --build ${BUILDKITE_BUILD_UUID} --step ${BUILDKITE_JOB_ID})
|
||||
else()
|
||||
set(BUILDKITE_DOWNLOAD_COMMAND curl -L -o ${BUILDKITE_ARTIFACT_PATH} ${BUILDKITE_ARTIFACTS_URL}/${BUILDKITE_ARTIFACT_ID})
|
||||
@@ -135,6 +138,20 @@ foreach(i RANGE ${BUILDKITE_JOBS_MAX_INDEX})
|
||||
OUTPUT
|
||||
${BUILD_PATH}/${BUILDKITE_ARTIFACT_PATH}
|
||||
)
|
||||
if(BUILDKITE_ARTIFACT_PATH STREQUAL "libbun-profile.a.gz")
|
||||
add_custom_command(
|
||||
COMMENT
|
||||
"Unpacking libbun-profile.a.gz"
|
||||
VERBATIM COMMAND
|
||||
gunzip libbun-profile.a.gz
|
||||
WORKING_DIRECTORY
|
||||
${BUILD_PATH}
|
||||
OUTPUT
|
||||
${BUILD_PATH}/libbun-profile.a
|
||||
DEPENDS
|
||||
${BUILD_PATH}/libbun-profile.a.gz
|
||||
)
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
list(APPEND BUILDKITE_JOBS_MATCH ${BUILDKITE_JOB_NAME})
|
||||
|
||||
@@ -36,7 +36,8 @@ endif()
|
||||
string(REPLACE "\n" ";" GIT_CHANGED_SOURCES "${GIT_DIFF}")
|
||||
|
||||
if(CI)
|
||||
setx(GIT_CHANGED_SOURCES ${GIT_CHANGED_SOURCES})
|
||||
set(GIT_CHANGED_SOURCES "${GIT_CHANGED_SOURCES}")
|
||||
message(STATUS "Set GIT_CHANGED_SOURCES: ${GIT_CHANGED_SOURCES}")
|
||||
endif()
|
||||
|
||||
list(TRANSFORM GIT_CHANGED_SOURCES PREPEND ${CWD}/)
|
||||
|
||||
@@ -12,7 +12,7 @@ if(NOT ENABLE_LLVM)
|
||||
return()
|
||||
endif()
|
||||
|
||||
set(DEFAULT_LLVM_VERSION "18.1.8")
|
||||
set(DEFAULT_LLVM_VERSION "19.1.7")
|
||||
|
||||
optionx(LLVM_VERSION STRING "The version of LLVM to use" DEFAULT ${DEFAULT_LLVM_VERSION})
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ option(WEBKIT_VERSION "The version of WebKit to use")
|
||||
option(WEBKIT_LOCAL "If a local version of WebKit should be used instead of downloading")
|
||||
|
||||
if(NOT WEBKIT_VERSION)
|
||||
set(WEBKIT_VERSION 2be7f2f098210ce560f72ef95d93b008bc8eaaa1)
|
||||
set(WEBKIT_VERSION ef31d98a1370e01b7483cabcbe3593d055bea982)
|
||||
endif()
|
||||
|
||||
string(SUBSTRING ${WEBKIT_VERSION} 0 16 WEBKIT_VERSION_PREFIX)
|
||||
|
||||
@@ -20,7 +20,7 @@ else()
|
||||
unsupported(CMAKE_SYSTEM_NAME)
|
||||
endif()
|
||||
|
||||
set(ZIG_COMMIT "bb9d6ab2c0bbbf20cc24dad03e88f3b3ffdb7de7")
|
||||
set(ZIG_COMMIT "cd1995944508e4c946deb75bd70947d302e0db37")
|
||||
optionx(ZIG_TARGET STRING "The zig target to use" DEFAULT ${DEFAULT_ZIG_TARGET})
|
||||
|
||||
if(CMAKE_BUILD_TYPE STREQUAL "Release")
|
||||
|
||||
449
docs/api/cookie.md
Normal file
449
docs/api/cookie.md
Normal file
@@ -0,0 +1,449 @@
|
||||
Bun provides native APIs for working with HTTP cookies through `Bun.Cookie` and `Bun.CookieMap`. These APIs offer fast, easy-to-use methods for parsing, generating, and manipulating cookies in HTTP requests and responses.
|
||||
|
||||
## CookieMap class
|
||||
|
||||
`Bun.CookieMap` provides a Map-like interface for working with collections of cookies. It implements the `Iterable` interface, allowing you to use it with `for...of` loops and other iteration methods.
|
||||
|
||||
```ts
|
||||
// Empty cookie map
|
||||
const cookies = new Bun.CookieMap();
|
||||
|
||||
// From a cookie string
|
||||
const cookies1 = new Bun.CookieMap("name=value; foo=bar");
|
||||
|
||||
// From an object
|
||||
const cookies2 = new Bun.CookieMap({
|
||||
session: "abc123",
|
||||
theme: "dark",
|
||||
});
|
||||
|
||||
// From an array of name/value pairs
|
||||
const cookies3 = new Bun.CookieMap([
|
||||
["session", "abc123"],
|
||||
["theme", "dark"],
|
||||
]);
|
||||
```
|
||||
|
||||
### In HTTP servers
|
||||
|
||||
In Bun's HTTP server, the `cookies` property on the request object (in `routes`) is an instance of `CookieMap`:
|
||||
|
||||
```ts
|
||||
const server = Bun.serve({
|
||||
routes: {
|
||||
"/": req => {
|
||||
// Access request cookies
|
||||
const cookies = req.cookies;
|
||||
|
||||
// Get a specific cookie
|
||||
const sessionCookie = cookies.get("session");
|
||||
if (sessionCookie != null) {
|
||||
console.log(sessionCookie);
|
||||
}
|
||||
|
||||
// Check if a cookie exists
|
||||
if (cookies.has("theme")) {
|
||||
// ...
|
||||
}
|
||||
|
||||
// Set a cookie, it will be automatically applied to the response
|
||||
cookies.set("visited", "true");
|
||||
|
||||
return new Response("Hello");
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
console.log("Server listening at: " + server.url);
|
||||
```
|
||||
|
||||
### Methods
|
||||
|
||||
#### `get(name: string): string | null`
|
||||
|
||||
Retrieves a cookie by name. Returns `null` if the cookie doesn't exist.
|
||||
|
||||
```ts
|
||||
// Get by name
|
||||
const cookie = cookies.get("session");
|
||||
|
||||
if (cookie != null) {
|
||||
console.log(cookie);
|
||||
}
|
||||
```
|
||||
|
||||
#### `has(name: string): boolean`
|
||||
|
||||
Checks if a cookie with the given name exists.
|
||||
|
||||
```ts
|
||||
// Check if cookie exists
|
||||
if (cookies.has("session")) {
|
||||
// Cookie exists
|
||||
}
|
||||
```
|
||||
|
||||
#### `set(name: string, value: string): void`
|
||||
|
||||
#### `set(options: CookieInit): void`
|
||||
|
||||
#### `set(cookie: Cookie): void`
|
||||
|
||||
Adds or updates a cookie in the map. Cookies default to `{ path: "/", sameSite: "lax" }`.
|
||||
|
||||
```ts
|
||||
// Set by name and value
|
||||
cookies.set("session", "abc123");
|
||||
|
||||
// Set using options object
|
||||
cookies.set({
|
||||
name: "theme",
|
||||
value: "dark",
|
||||
maxAge: 3600,
|
||||
secure: true,
|
||||
});
|
||||
|
||||
// Set using Cookie instance
|
||||
const cookie = new Bun.Cookie("visited", "true");
|
||||
cookies.set(cookie);
|
||||
```
|
||||
|
||||
#### `delete(name: string): void`
|
||||
|
||||
#### `delete(options: CookieStoreDeleteOptions): void`
|
||||
|
||||
Removes a cookie from the map. When applied to a Response, this adds a cookie with an empty string value and an expiry date in the past. A cookie will only delete successfully on the browser if the domain and path is the same as it was when the cookie was created.
|
||||
|
||||
```ts
|
||||
// Delete by name using default domain and path.
|
||||
cookies.delete("session");
|
||||
|
||||
// Delete with domain/path options.
|
||||
cookies.delete({
|
||||
name: "session",
|
||||
domain: "example.com",
|
||||
path: "/admin",
|
||||
});
|
||||
```
|
||||
|
||||
#### `toJSON(): Record<string, string>`
|
||||
|
||||
Converts the cookie map to a serializable format.
|
||||
|
||||
```ts
|
||||
const json = cookies.toJSON();
|
||||
```
|
||||
|
||||
#### `toSetCookieHeaders(): string[]`
|
||||
|
||||
Returns an array of values for Set-Cookie headers that can be used to apply all cookie changes.
|
||||
|
||||
When using `Bun.serve()`, you don't need to call this method explicitly. Any changes made to the `req.cookies` map are automatically applied to the response headers. This method is primarily useful when working with other HTTP server implementations.
|
||||
|
||||
```js
|
||||
import { createServer } from "node:http";
|
||||
import { CookieMap } from "bun";
|
||||
|
||||
const server = createServer((req, res) => {
|
||||
const cookieHeader = req.headers.cookie || "";
|
||||
const cookies = new CookieMap(cookieHeader);
|
||||
|
||||
cookies.set("view-count", Number(cookies.get("view-count") || "0") + 1);
|
||||
cookies.delete("session");
|
||||
|
||||
res.writeHead(200, {
|
||||
"Content-Type": "text/plain",
|
||||
"Set-Cookie": cookies.toSetCookieHeaders(),
|
||||
});
|
||||
res.end(`Found ${cookies.size} cookies`);
|
||||
});
|
||||
|
||||
server.listen(3000, () => {
|
||||
console.log("Server running at http://localhost:3000/");
|
||||
});
|
||||
```
|
||||
|
||||
### Iteration
|
||||
|
||||
`CookieMap` provides several methods for iteration:
|
||||
|
||||
```ts
|
||||
// Iterate over [name, cookie] entries
|
||||
for (const [name, value] of cookies) {
|
||||
console.log(`${name}: ${value}`);
|
||||
}
|
||||
|
||||
// Using entries()
|
||||
for (const [name, value] of cookies.entries()) {
|
||||
console.log(`${name}: ${value}`);
|
||||
}
|
||||
|
||||
// Using keys()
|
||||
for (const name of cookies.keys()) {
|
||||
console.log(name);
|
||||
}
|
||||
|
||||
// Using values()
|
||||
for (const value of cookies.values()) {
|
||||
console.log(value);
|
||||
}
|
||||
|
||||
// Using forEach
|
||||
cookies.forEach((value, name) => {
|
||||
console.log(`${name}: ${value}`);
|
||||
});
|
||||
```
|
||||
|
||||
### Properties
|
||||
|
||||
#### `size: number`
|
||||
|
||||
Returns the number of cookies in the map.
|
||||
|
||||
```ts
|
||||
console.log(cookies.size); // Number of cookies
|
||||
```
|
||||
|
||||
## Cookie class
|
||||
|
||||
`Bun.Cookie` represents an HTTP cookie with its name, value, and attributes.
|
||||
|
||||
```ts
|
||||
import { Cookie } from "bun";
|
||||
|
||||
// Create a basic cookie
|
||||
const cookie = new Bun.Cookie("name", "value");
|
||||
|
||||
// Create a cookie with options
|
||||
const secureSessionCookie = new Bun.Cookie("session", "abc123", {
|
||||
domain: "example.com",
|
||||
path: "/admin",
|
||||
expires: new Date(Date.now() + 86400000), // 1 day
|
||||
httpOnly: true,
|
||||
secure: true,
|
||||
sameSite: "strict",
|
||||
});
|
||||
|
||||
// Parse from a cookie string
|
||||
const parsedCookie = new Bun.Cookie("name=value; Path=/; HttpOnly");
|
||||
|
||||
// Create from an options object
|
||||
const objCookie = new Bun.Cookie({
|
||||
name: "theme",
|
||||
value: "dark",
|
||||
maxAge: 3600,
|
||||
secure: true,
|
||||
});
|
||||
```
|
||||
|
||||
### Constructors
|
||||
|
||||
```ts
|
||||
// Basic constructor with name/value
|
||||
new Bun.Cookie(name: string, value: string);
|
||||
|
||||
// Constructor with name, value, and options
|
||||
new Bun.Cookie(name: string, value: string, options: CookieInit);
|
||||
|
||||
// Constructor from cookie string
|
||||
new Bun.Cookie(cookieString: string);
|
||||
|
||||
// Constructor from cookie object
|
||||
new Bun.Cookie(options: CookieInit);
|
||||
```
|
||||
|
||||
### Properties
|
||||
|
||||
```ts
|
||||
cookie.name; // string - Cookie name
|
||||
cookie.value; // string - Cookie value
|
||||
cookie.domain; // string | null - Domain scope (null if not specified)
|
||||
cookie.path; // string - URL path scope (defaults to "/")
|
||||
cookie.expires; // number | undefined - Expiration timestamp (ms since epoch)
|
||||
cookie.secure; // boolean - Require HTTPS
|
||||
cookie.sameSite; // "strict" | "lax" | "none" - SameSite setting
|
||||
cookie.partitioned; // boolean - Whether the cookie is partitioned (CHIPS)
|
||||
cookie.maxAge; // number | undefined - Max age in seconds
|
||||
cookie.httpOnly; // boolean - Accessible only via HTTP (not JavaScript)
|
||||
```
|
||||
|
||||
### Methods
|
||||
|
||||
#### `isExpired(): boolean`
|
||||
|
||||
Checks if the cookie has expired.
|
||||
|
||||
```ts
|
||||
// Expired cookie (Date in the past)
|
||||
const expiredCookie = new Bun.Cookie("name", "value", {
|
||||
expires: new Date(Date.now() - 1000),
|
||||
});
|
||||
console.log(expiredCookie.isExpired()); // true
|
||||
|
||||
// Valid cookie (Using maxAge instead of expires)
|
||||
const validCookie = new Bun.Cookie("name", "value", {
|
||||
maxAge: 3600, // 1 hour in seconds
|
||||
});
|
||||
console.log(validCookie.isExpired()); // false
|
||||
|
||||
// Session cookie (no expiration)
|
||||
const sessionCookie = new Bun.Cookie("name", "value");
|
||||
console.log(sessionCookie.isExpired()); // false
|
||||
```
|
||||
|
||||
#### `serialize(): string`
|
||||
|
||||
#### `toString(): string`
|
||||
|
||||
Returns a string representation of the cookie suitable for a `Set-Cookie` header.
|
||||
|
||||
```ts
|
||||
const cookie = new Bun.Cookie("session", "abc123", {
|
||||
domain: "example.com",
|
||||
path: "/admin",
|
||||
expires: new Date(Date.now() + 86400000),
|
||||
secure: true,
|
||||
httpOnly: true,
|
||||
sameSite: "strict",
|
||||
});
|
||||
|
||||
console.log(cookie.serialize());
|
||||
// => "session=abc123; Domain=example.com; Path=/admin; Expires=Sun, 19 Mar 2025 15:03:26 GMT; Secure; HttpOnly; SameSite=strict"
|
||||
console.log(cookie.toString());
|
||||
// => "session=abc123; Domain=example.com; Path=/admin; Expires=Sun, 19 Mar 2025 15:03:26 GMT; Secure; HttpOnly; SameSite=strict"
|
||||
```
|
||||
|
||||
#### `toJSON(): CookieInit`
|
||||
|
||||
Converts the cookie to a plain object suitable for JSON serialization.
|
||||
|
||||
```ts
|
||||
const cookie = new Bun.Cookie("session", "abc123", {
|
||||
secure: true,
|
||||
httpOnly: true,
|
||||
});
|
||||
|
||||
const json = cookie.toJSON();
|
||||
// => {
|
||||
// name: "session",
|
||||
// value: "abc123",
|
||||
// path: "/",
|
||||
// secure: true,
|
||||
// httpOnly: true,
|
||||
// sameSite: "lax",
|
||||
// partitioned: false
|
||||
// }
|
||||
|
||||
// Works with JSON.stringify
|
||||
const jsonString = JSON.stringify(cookie);
|
||||
```
|
||||
|
||||
### Static methods
|
||||
|
||||
#### `Cookie.parse(cookieString: string): Cookie`
|
||||
|
||||
Parses a cookie string into a `Cookie` instance.
|
||||
|
||||
```ts
|
||||
const cookie = Bun.Cookie.parse("name=value; Path=/; Secure; SameSite=Lax");
|
||||
|
||||
console.log(cookie.name); // "name"
|
||||
console.log(cookie.value); // "value"
|
||||
console.log(cookie.path); // "/"
|
||||
console.log(cookie.secure); // true
|
||||
console.log(cookie.sameSite); // "lax"
|
||||
```
|
||||
|
||||
#### `Cookie.from(name: string, value: string, options?: CookieInit): Cookie`
|
||||
|
||||
Factory method to create a cookie.
|
||||
|
||||
```ts
|
||||
const cookie = Bun.Cookie.from("session", "abc123", {
|
||||
httpOnly: true,
|
||||
secure: true,
|
||||
maxAge: 3600,
|
||||
});
|
||||
```
|
||||
|
||||
## Types
|
||||
|
||||
```ts
|
||||
interface CookieInit {
|
||||
name?: string;
|
||||
value?: string;
|
||||
domain?: string;
|
||||
/** Defaults to '/'. To allow the browser to set the path, use an empty string. */
|
||||
path?: string;
|
||||
expires?: number | Date | string;
|
||||
secure?: boolean;
|
||||
/** Defaults to `lax`. */
|
||||
sameSite?: CookieSameSite;
|
||||
httpOnly?: boolean;
|
||||
partitioned?: boolean;
|
||||
maxAge?: number;
|
||||
}
|
||||
|
||||
interface CookieStoreDeleteOptions {
|
||||
name: string;
|
||||
domain?: string | null;
|
||||
path?: string;
|
||||
}
|
||||
|
||||
interface CookieStoreGetOptions {
|
||||
name?: string;
|
||||
url?: string;
|
||||
}
|
||||
|
||||
type CookieSameSite = "strict" | "lax" | "none";
|
||||
|
||||
class Cookie {
|
||||
constructor(name: string, value: string, options?: CookieInit);
|
||||
constructor(cookieString: string);
|
||||
constructor(cookieObject?: CookieInit);
|
||||
|
||||
readonly name: string;
|
||||
value: string;
|
||||
domain?: string;
|
||||
path: string;
|
||||
expires?: Date;
|
||||
secure: boolean;
|
||||
sameSite: CookieSameSite;
|
||||
partitioned: boolean;
|
||||
maxAge?: number;
|
||||
httpOnly: boolean;
|
||||
|
||||
isExpired(): boolean;
|
||||
|
||||
serialize(): string;
|
||||
toString(): string;
|
||||
toJSON(): CookieInit;
|
||||
|
||||
static parse(cookieString: string): Cookie;
|
||||
static from(name: string, value: string, options?: CookieInit): Cookie;
|
||||
}
|
||||
|
||||
class CookieMap implements Iterable<[string, string]> {
|
||||
constructor(init?: string[][] | Record<string, string> | string);
|
||||
|
||||
get(name: string): string | null;
|
||||
|
||||
toSetCookieHeaders(): string[];
|
||||
|
||||
has(name: string): boolean;
|
||||
set(name: string, value: string, options?: CookieInit): void;
|
||||
set(options: CookieInit): void;
|
||||
delete(name: string): void;
|
||||
delete(options: CookieStoreDeleteOptions): void;
|
||||
delete(name: string, options: Omit<CookieStoreDeleteOptions, "name">): void;
|
||||
toJSON(): Record<string, string>;
|
||||
|
||||
readonly size: number;
|
||||
|
||||
entries(): IterableIterator<[string, string]>;
|
||||
keys(): IterableIterator<string>;
|
||||
values(): IterableIterator<string>;
|
||||
forEach(callback: (value: string, key: string, map: CookieMap) => void): void;
|
||||
[Symbol.iterator](): IterableIterator<[string, string]>;
|
||||
}
|
||||
```
|
||||
@@ -61,6 +61,7 @@ Routes in `Bun.serve()` receive a `BunRequest` (which extends [`Request`](https:
|
||||
// Simplified for brevity
|
||||
interface BunRequest<T extends string> extends Request {
|
||||
params: Record<T, string>;
|
||||
readonly cookies: CookieMap;
|
||||
}
|
||||
```
|
||||
|
||||
@@ -934,6 +935,83 @@ const server = Bun.serve({
|
||||
|
||||
Returns `null` for closed requests or Unix domain sockets.
|
||||
|
||||
## Working with Cookies
|
||||
|
||||
Bun provides a built-in API for working with cookies in HTTP requests and responses. The `BunRequest` object includes a `cookies` property that provides a `CookieMap` for easily accessing and manipulating cookies. When using `routes`, `Bun.serve()` automatically tracks `request.cookies.set` and applies them to the response.
|
||||
|
||||
### Reading cookies
|
||||
|
||||
Read cookies from incoming requests using the `cookies` property on the `BunRequest` object:
|
||||
|
||||
```ts
|
||||
Bun.serve({
|
||||
routes: {
|
||||
"/profile": req => {
|
||||
// Access cookies from the request
|
||||
const userId = req.cookies.get("user_id");
|
||||
const theme = req.cookies.get("theme") || "light";
|
||||
|
||||
return Response.json({
|
||||
userId,
|
||||
theme,
|
||||
message: "Profile page",
|
||||
});
|
||||
},
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
### Setting cookies
|
||||
|
||||
To set cookies, use the `set` method on the `CookieMap` from the `BunRequest` object.
|
||||
|
||||
```ts
|
||||
Bun.serve({
|
||||
routes: {
|
||||
"/login": req => {
|
||||
const cookies = req.cookies;
|
||||
|
||||
// Set a cookie with various options
|
||||
cookies.set("user_id", "12345", {
|
||||
maxAge: 60 * 60 * 24 * 7, // 1 week
|
||||
httpOnly: true,
|
||||
secure: true,
|
||||
path: "/",
|
||||
});
|
||||
|
||||
// Add a theme preference cookie
|
||||
cookies.set("theme", "dark");
|
||||
|
||||
// Modified cookies from the request are automatically applied to the response
|
||||
return new Response("Login successful");
|
||||
},
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
`Bun.serve()` automatically tracks modified cookies from the request and applies them to the response.
|
||||
|
||||
### Deleting cookies
|
||||
|
||||
To delete a cookie, use the `delete` method on the `request.cookies` (`CookieMap`) object:
|
||||
|
||||
```ts
|
||||
Bun.serve({
|
||||
routes: {
|
||||
"/logout": req => {
|
||||
// Delete the user_id cookie
|
||||
req.cookies.delete("user_id", {
|
||||
path: "/",
|
||||
});
|
||||
|
||||
return new Response("Logged out successfully");
|
||||
},
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
Deleted cookies become a `Set-Cookie` header on the response with the `maxAge` set to `0` and an empty `value`.
|
||||
|
||||
## Server Metrics
|
||||
|
||||
### server.pendingRequests and server.pendingWebSockets
|
||||
|
||||
@@ -12,5 +12,3 @@ Alternatively, use `process.dlopen`:
|
||||
let mod = { exports: {} };
|
||||
process.dlopen(mod, "./my-node-module.node");
|
||||
```
|
||||
|
||||
Bun polyfills the [`detect-libc`](https://npmjs.com/package/detect-libc) package, which is used by many Node-API modules to detect which `.node` binding to `require`.
|
||||
|
||||
@@ -715,7 +715,7 @@ await S3Client.delete("my-file.txt", credentials);
|
||||
await S3Client.unlink("my-file.txt", credentials);
|
||||
```
|
||||
|
||||
## s3:// protocol
|
||||
## `s3://` protocol
|
||||
|
||||
To make it easier to use the same code for local files and S3 files, the `s3://` protocol is supported in `fetch` and `Bun.file()`.
|
||||
|
||||
|
||||
@@ -77,6 +77,16 @@ console.log(text); // "const input = "hello world".repeat(400); ..."
|
||||
|
||||
---
|
||||
|
||||
- `ReadableStream`
|
||||
- Use a readable stream as input.
|
||||
|
||||
---
|
||||
|
||||
- `Blob`
|
||||
- Use a blob as input.
|
||||
|
||||
---
|
||||
|
||||
- `number`
|
||||
- Read from the file with a given file descriptor.
|
||||
|
||||
@@ -129,13 +139,13 @@ Configure the output stream by passing one of the following values to `stdout/st
|
||||
|
||||
---
|
||||
|
||||
- `Bun.file()`
|
||||
- Write to the specified file.
|
||||
- `"ignore"`
|
||||
- Discard the output.
|
||||
|
||||
---
|
||||
|
||||
- `null`
|
||||
- Write to `/dev/null`.
|
||||
- `Bun.file()`
|
||||
- Write to the specified file.
|
||||
|
||||
---
|
||||
|
||||
@@ -174,7 +184,8 @@ const proc = Bun.spawn(["bun", "--version"]);
|
||||
proc.kill();
|
||||
proc.killed; // true
|
||||
|
||||
proc.kill(); // specify an exit code
|
||||
proc.kill(15); // specify a signal code
|
||||
proc.kill("SIGTERM"); // specify a signal name
|
||||
```
|
||||
|
||||
The parent `bun` process will not terminate until all child processes have exited. Use `proc.unref()` to detach the child process from the parent.
|
||||
@@ -184,6 +195,64 @@ const proc = Bun.spawn(["bun", "--version"]);
|
||||
proc.unref();
|
||||
```
|
||||
|
||||
## Resource usage
|
||||
|
||||
You can get information about the process's resource usage after it has exited:
|
||||
|
||||
```ts
|
||||
const proc = Bun.spawn(["bun", "--version"]);
|
||||
await proc.exited;
|
||||
|
||||
const usage = proc.resourceUsage();
|
||||
console.log(`Max memory used: ${usage.maxRSS} bytes`);
|
||||
console.log(`CPU time (user): ${usage.cpuTime.user} µs`);
|
||||
console.log(`CPU time (system): ${usage.cpuTime.system} µs`);
|
||||
```
|
||||
|
||||
## Using AbortSignal
|
||||
|
||||
You can abort a subprocess using an `AbortSignal`:
|
||||
|
||||
```ts
|
||||
const controller = new AbortController();
|
||||
const { signal } = controller;
|
||||
|
||||
const proc = Bun.spawn({
|
||||
cmd: ["sleep", "100"],
|
||||
signal,
|
||||
});
|
||||
|
||||
// Later, to abort the process:
|
||||
controller.abort();
|
||||
```
|
||||
|
||||
## Using timeout and killSignal
|
||||
|
||||
You can set a timeout for a subprocess to automatically terminate after a specific duration:
|
||||
|
||||
```ts
|
||||
// Kill the process after 5 seconds
|
||||
const proc = Bun.spawn({
|
||||
cmd: ["sleep", "10"],
|
||||
timeout: 5000, // 5 seconds in milliseconds
|
||||
});
|
||||
|
||||
await proc.exited; // Will resolve after 5 seconds
|
||||
```
|
||||
|
||||
By default, timed-out processes are killed with the `SIGTERM` signal. You can specify a different signal with the `killSignal` option:
|
||||
|
||||
```ts
|
||||
// Kill the process with SIGKILL after 5 seconds
|
||||
const proc = Bun.spawn({
|
||||
cmd: ["sleep", "10"],
|
||||
timeout: 5000,
|
||||
killSignal: "SIGKILL", // Can be string name or signal number
|
||||
});
|
||||
```
|
||||
|
||||
The `killSignal` option also controls which signal is sent when an AbortSignal is aborted.
|
||||
|
||||
## Inter-process communication (IPC)
|
||||
|
||||
Bun supports direct inter-process communication channel between two `bun` processes. To receive messages from a spawned Bun subprocess, specify an `ipc` handler.
|
||||
@@ -233,11 +302,17 @@ process.send("Hello from child as string");
|
||||
process.send({ message: "Hello from child as object" });
|
||||
```
|
||||
|
||||
The `ipcMode` option controls the underlying communication format between the two processes:
|
||||
The `serialization` option controls the underlying communication format between the two processes:
|
||||
|
||||
- `advanced`: (default) Messages are serialized using the JSC `serialize` API, which supports cloning [everything `structuredClone` supports](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Structured_clone_algorithm). This does not support transferring ownership of objects.
|
||||
- `json`: Messages are serialized using `JSON.stringify` and `JSON.parse`, which does not support as many object types as `advanced` does.
|
||||
|
||||
To disconnect the IPC channel from the parent process, call:
|
||||
|
||||
```ts
|
||||
childProc.disconnect();
|
||||
```
|
||||
|
||||
### IPC between Bun & Node.js
|
||||
|
||||
To use IPC between a `bun` process and a Node.js process, set `serialization: "json"` in `Bun.spawn`. This is because Node.js and Bun use different JavaScript engines with different object serialization formats.
|
||||
@@ -310,7 +385,7 @@ spawnSync echo hi 1.47 ms/iter (1.14 ms … 2.64 ms) 1.57 ms 2.37 ms
|
||||
|
||||
## Reference
|
||||
|
||||
A simple reference of the Spawn API and types are shown below. The real types have complex generics to strongly type the `Subprocess` streams with the options passed to `Bun.spawn` and `Bun.spawnSync`. For full details, find these types as defined [bun.d.ts](https://github.com/oven-sh/bun/blob/main/packages/bun-types/bun.d.ts).
|
||||
A reference of the Spawn API and types are shown below. The real types have complex generics to strongly type the `Subprocess` streams with the options passed to `Bun.spawn` and `Bun.spawnSync`. For full details, find these types as defined [bun.d.ts](https://github.com/oven-sh/bun/blob/main/packages/bun-types/bun.d.ts).
|
||||
|
||||
```ts
|
||||
interface Bun {
|
||||
@@ -329,16 +404,25 @@ interface Bun {
|
||||
namespace SpawnOptions {
|
||||
interface OptionsObject {
|
||||
cwd?: string;
|
||||
env?: Record<string, string>;
|
||||
stdin?: SpawnOptions.Readable;
|
||||
stdout?: SpawnOptions.Writable;
|
||||
stderr?: SpawnOptions.Writable;
|
||||
onExit?: (
|
||||
proc: Subprocess,
|
||||
env?: Record<string, string | undefined>;
|
||||
stdio?: [Writable, Readable, Readable];
|
||||
stdin?: Writable;
|
||||
stdout?: Readable;
|
||||
stderr?: Readable;
|
||||
onExit?(
|
||||
subprocess: Subprocess,
|
||||
exitCode: number | null,
|
||||
signalCode: string | null,
|
||||
error: Error | null,
|
||||
) => void;
|
||||
signalCode: number | null,
|
||||
error?: ErrorLike,
|
||||
): void | Promise<void>;
|
||||
ipc?(message: any, subprocess: Subprocess): void;
|
||||
serialization?: "json" | "advanced";
|
||||
windowsHide?: boolean;
|
||||
windowsVerbatimArguments?: boolean;
|
||||
argv0?: string;
|
||||
signal?: AbortSignal;
|
||||
timeout?: number;
|
||||
killSignal?: string | number;
|
||||
}
|
||||
|
||||
type Readable =
|
||||
@@ -366,39 +450,62 @@ namespace SpawnOptions {
|
||||
| Request;
|
||||
}
|
||||
|
||||
interface Subprocess<Stdin, Stdout, Stderr> {
|
||||
interface Subprocess extends AsyncDisposable {
|
||||
readonly stdin: FileSink | number | undefined;
|
||||
readonly stdout: ReadableStream<Uint8Array> | number | undefined;
|
||||
readonly stderr: ReadableStream<Uint8Array> | number | undefined;
|
||||
readonly readable: ReadableStream<Uint8Array> | number | undefined;
|
||||
readonly pid: number;
|
||||
// the exact stream types here are derived from the generic parameters
|
||||
readonly stdin: number | ReadableStream | FileSink | undefined;
|
||||
readonly stdout: number | ReadableStream | undefined;
|
||||
readonly stderr: number | ReadableStream | undefined;
|
||||
|
||||
readonly exited: Promise<number>;
|
||||
|
||||
readonly exitCode: number | undefined;
|
||||
readonly signalCode: Signal | null;
|
||||
readonly exitCode: number | null;
|
||||
readonly signalCode: NodeJS.Signals | null;
|
||||
readonly killed: boolean;
|
||||
|
||||
kill(exitCode?: number | NodeJS.Signals): void;
|
||||
ref(): void;
|
||||
unref(): void;
|
||||
kill(code?: number): void;
|
||||
|
||||
send(message: any): void;
|
||||
disconnect(): void;
|
||||
resourceUsage(): ResourceUsage | undefined;
|
||||
}
|
||||
|
||||
interface SyncSubprocess<Stdout, Stderr> {
|
||||
readonly pid: number;
|
||||
readonly success: boolean;
|
||||
// the exact buffer types here are derived from the generic parameters
|
||||
readonly stdout: Buffer | undefined;
|
||||
readonly stderr: Buffer | undefined;
|
||||
interface SyncSubprocess {
|
||||
stdout: Buffer | undefined;
|
||||
stderr: Buffer | undefined;
|
||||
exitCode: number;
|
||||
success: boolean;
|
||||
resourceUsage: ResourceUsage;
|
||||
signalCode?: string;
|
||||
exitedDueToTimeout?: true;
|
||||
pid: number;
|
||||
}
|
||||
|
||||
type ReadableSubprocess = Subprocess<any, "pipe", "pipe">;
|
||||
type WritableSubprocess = Subprocess<"pipe", any, any>;
|
||||
type PipedSubprocess = Subprocess<"pipe", "pipe", "pipe">;
|
||||
type NullSubprocess = Subprocess<null, null, null>;
|
||||
interface ResourceUsage {
|
||||
contextSwitches: {
|
||||
voluntary: number;
|
||||
involuntary: number;
|
||||
};
|
||||
|
||||
type ReadableSyncSubprocess = SyncSubprocess<"pipe", "pipe">;
|
||||
type NullSyncSubprocess = SyncSubprocess<null, null>;
|
||||
cpuTime: {
|
||||
user: number;
|
||||
system: number;
|
||||
total: number;
|
||||
};
|
||||
maxRSS: number;
|
||||
|
||||
messages: {
|
||||
sent: number;
|
||||
received: number;
|
||||
};
|
||||
ops: {
|
||||
in: number;
|
||||
out: number;
|
||||
};
|
||||
shmSize: number;
|
||||
signalCount: number;
|
||||
swapCount: number;
|
||||
}
|
||||
|
||||
type Signal =
|
||||
| "SIGABRT"
|
||||
|
||||
@@ -240,7 +240,7 @@ const result = await sql.unsafe(
|
||||
|
||||
### Execute and Cancelling Queries
|
||||
|
||||
Bun's SQL is lazy that means its will only start executing when awaited or executed with `.execute()`.
|
||||
Bun's SQL is lazy, which means it will only start executing when awaited or executed with `.execute()`.
|
||||
You can cancel a query that is currently executing by calling the `cancel()` method on the query object.
|
||||
|
||||
```ts
|
||||
|
||||
@@ -11,7 +11,7 @@ Bun.listen({
|
||||
socket: {
|
||||
data(socket, data) {}, // message received from client
|
||||
open(socket) {}, // socket opened
|
||||
close(socket) {}, // socket closed
|
||||
close(socket, error) {}, // socket closed
|
||||
drain(socket) {}, // socket ready for more data
|
||||
error(socket, error) {}, // error handler
|
||||
},
|
||||
@@ -30,7 +30,7 @@ Bun.listen({
|
||||
open(socket) {},
|
||||
data(socket, data) {},
|
||||
drain(socket) {},
|
||||
close(socket) {},
|
||||
close(socket, error) {},
|
||||
error(socket, error) {},
|
||||
},
|
||||
});
|
||||
@@ -122,7 +122,7 @@ const socket = await Bun.connect({
|
||||
socket: {
|
||||
data(socket, data) {},
|
||||
open(socket) {},
|
||||
close(socket) {},
|
||||
close(socket, error) {},
|
||||
drain(socket) {},
|
||||
error(socket, error) {},
|
||||
|
||||
|
||||
@@ -15,8 +15,8 @@ Below is the full set of recommended `compilerOptions` for a Bun project. With t
|
||||
```jsonc
|
||||
{
|
||||
"compilerOptions": {
|
||||
// Enable latest features
|
||||
"lib": ["ESNext", "DOM"],
|
||||
// Environment setup & latest features
|
||||
"lib": ["ESNext"],
|
||||
"target": "ESNext",
|
||||
"module": "ESNext",
|
||||
"moduleDetection": "force",
|
||||
@@ -33,11 +33,12 @@ Below is the full set of recommended `compilerOptions` for a Bun project. With t
|
||||
"strict": true,
|
||||
"skipLibCheck": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"noUncheckedIndexedAccess": true,
|
||||
|
||||
// Some stricter flags
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true,
|
||||
"noPropertyAccessFromIndexSignature": true,
|
||||
// Some stricter flags (disabled by default)
|
||||
"noUnusedLocals": false,
|
||||
"noUnusedParameters": false,
|
||||
"noPropertyAccessFromIndexSignature": false,
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
15
docs/nav.ts
15
docs/nav.ts
@@ -355,24 +355,24 @@ export default {
|
||||
page("api/spawn", "Child processes", {
|
||||
description: `Spawn sync and async child processes with easily configurable input and output streams.`,
|
||||
}), // "`Bun.spawn`"),
|
||||
page("api/transpiler", "Transpiler", {
|
||||
description: `Bun exposes its internal transpiler as a pluggable API.`,
|
||||
}), // "`Bun.Transpiler`"),
|
||||
page("api/html-rewriter", "HTMLRewriter", {
|
||||
description: `Parse and transform HTML with Bun's native HTMLRewriter API, inspired by Cloudflare Workers.`,
|
||||
}), // "`HTMLRewriter`"),
|
||||
page("api/hashing", "Hashing", {
|
||||
description: `Native support for a range of fast hashing algorithms.`,
|
||||
}), // "`Bun.serve`"),
|
||||
page("api/console", "Console", {
|
||||
description: `Bun implements a Node.js-compatible \`console\` object with colorized output and deep pretty-printing.`,
|
||||
}), // "`Node-API`"),
|
||||
page("api/cookie", "Cookie", {
|
||||
description: "Bun's native Cookie API simplifies working with HTTP cookies.",
|
||||
}), // "`Node-API`"),
|
||||
page("api/ffi", "FFI", {
|
||||
description: `Call native code from JavaScript with Bun's foreign function interface (FFI) API.`,
|
||||
}), // "`bun:ffi`"),
|
||||
page("api/cc", "C Compiler", {
|
||||
description: `Build & run native C from JavaScript with Bun's native C compiler API`,
|
||||
}), // "`bun:ffi`"),
|
||||
page("api/html-rewriter", "HTMLRewriter", {
|
||||
description: `Parse and transform HTML with Bun's native HTMLRewriter API, inspired by Cloudflare Workers.`,
|
||||
}), // "`HTMLRewriter`"),
|
||||
page("api/test", "Testing", {
|
||||
description: `Bun's built-in test runner is fast and uses Jest-compatible syntax.`,
|
||||
}), // "`bun:test`"),
|
||||
@@ -398,6 +398,9 @@ export default {
|
||||
page("api/color", "Color", {
|
||||
description: `Bun's color function leverages Bun's CSS parser for parsing, normalizing, and converting colors from user input to a variety of output formats.`,
|
||||
}), // "`Color`"),
|
||||
page("api/transpiler", "Transpiler", {
|
||||
description: `Bun exposes its internal transpiler as a pluggable API.`,
|
||||
}), // "`Bun.Transpiler`"),
|
||||
|
||||
// divider("Dev Server"),
|
||||
// page("bun-dev", "Vanilla"),
|
||||
|
||||
@@ -60,7 +60,7 @@ Visual Studio can be installed graphically using the wizard or through WinGet:
|
||||
|
||||
After Visual Studio, you need the following:
|
||||
|
||||
- LLVM 18.1.8
|
||||
- LLVM 19.1.7
|
||||
- Go
|
||||
- Rust
|
||||
- NASM
|
||||
@@ -81,7 +81,7 @@ After Visual Studio, you need the following:
|
||||
> irm https://get.scoop.sh | iex
|
||||
> scoop install nodejs-lts go rust nasm ruby perl ccache
|
||||
# scoop seems to be buggy if you install llvm and the rest at the same time
|
||||
> scoop install llvm@18.1.8
|
||||
> scoop install llvm@19.1.7
|
||||
```
|
||||
|
||||
{% /codetabs %}
|
||||
|
||||
@@ -104,7 +104,7 @@ This page is updated regularly to reflect compatibility status of the latest ver
|
||||
|
||||
### [`node:crypto`](https://nodejs.org/api/crypto.html)
|
||||
|
||||
🟡 Missing `ECDH` `checkPrime` `checkPrimeSync` `generatePrime` `generatePrimeSync` `hkdf` `hkdfSync` `secureHeapUsed` `setEngine` `setFips`
|
||||
🟡 Missing `secureHeapUsed` `setEngine` `setFips`
|
||||
|
||||
Some methods are not optimized yet.
|
||||
|
||||
@@ -118,7 +118,7 @@ Some methods are not optimized yet.
|
||||
|
||||
### [`node:module`](https://nodejs.org/api/module.html)
|
||||
|
||||
🟡 Missing `runMain` `syncBuiltinESMExports`, `Module#load()`. Overriding `require.cache` is supported for ESM & CJS modules. `module._extensions`, `module._pathCache`, `module._cache` are no-ops. `module.register` is not implemented and we recommend using a [`Bun.plugin`](https://bun.sh/docs/runtime/plugins) in the meantime.
|
||||
🟡 Missing `syncBuiltinESMExports`, `Module#load()`. Overriding `require.cache` is supported for ESM & CJS modules. `module._extensions`, `module._pathCache`, `module._cache` are no-ops. `module.register` is not implemented and we recommend using a [`Bun.plugin`](https://bun.sh/docs/runtime/plugins) in the meantime.
|
||||
|
||||
### [`node:net`](https://nodejs.org/api/net.html)
|
||||
|
||||
@@ -174,7 +174,7 @@ Some methods are not optimized yet.
|
||||
|
||||
### [`node:test`](https://nodejs.org/api/test.html)
|
||||
|
||||
🔴 Not implemented. Use [`bun:test`](https://bun.sh/docs/cli/test) instead.
|
||||
🟡 Partly implemented. Missing mocks, snapshots, timers. Use [`bun:test`](https://bun.sh/docs/cli/test) instead.
|
||||
|
||||
### [`node:trace_events`](https://nodejs.org/api/tracing.html)
|
||||
|
||||
@@ -346,7 +346,7 @@ The table below lists all globals implemented by Node.js and Bun's current compa
|
||||
|
||||
### [`process`](https://nodejs.org/api/process.html)
|
||||
|
||||
🟡 Mostly implemented. `process.binding` (internal Node.js bindings some packages rely on) is partially implemented. `process.title` is a currently a no-op on macOS & Linux. `getActiveResourcesInfo` `setActiveResourcesInfo`, `getActiveResources` and `setSourceMapsEnabled` are stubs. Newer APIs like `process.loadEnvFile` and `process.getBuiltinModule` are not implemented yet.
|
||||
🟡 Mostly implemented. `process.binding` (internal Node.js bindings some packages rely on) is partially implemented. `process.title` is currently a no-op on macOS & Linux. `getActiveResourcesInfo` `setActiveResourcesInfo`, `getActiveResources` and `setSourceMapsEnabled` are stubs. Newer APIs like `process.loadEnvFile` and `process.getBuiltinModule` are not implemented yet.
|
||||
|
||||
### [`queueMicrotask()`](https://developer.mozilla.org/en-US/docs/Web/API/queueMicrotask)
|
||||
|
||||
@@ -378,8 +378,7 @@ The table below lists all globals implemented by Node.js and Bun's current compa
|
||||
|
||||
### [`require()`](https://nodejs.org/api/globals.html#require)
|
||||
|
||||
🟢 Fully implemented, including [`require.main`](https://nodejs.org/api/modules.html#requiremain), [`require.cache`](https://nodejs.org/api/modules.html#requirecache), [`require.resolve`](https://nodejs.org/api/modules.html#requireresolverequest-options). `require.extensions` is a stub.
|
||||
|
||||
🟢 Fully implemented, including [`require.main`](https://nodejs.org/api/modules.html#requiremain), [`require.cache`](https://nodejs.org/api/modules.html#requirecache), [`require.resolve`](https://nodejs.org/api/modules.html#requireresolverequest-options).
|
||||
### [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response)
|
||||
|
||||
🟢 Fully implemented.
|
||||
|
||||
@@ -329,7 +329,7 @@ await Bun.build({
|
||||
|
||||
{% callout %}
|
||||
|
||||
**NOTE**: Plugin lifcycle callbacks (`onStart()`, `onResolve()`, etc.) do not have the ability to modify the `build.config` object in the `setup()` function. If you want to mutate `build.config`, you must do so directly in the `setup()` function:
|
||||
**NOTE**: Plugin lifecycle callbacks (`onStart()`, `onResolve()`, etc.) do not have the ability to modify the `build.config` object in the `setup()` function. If you want to mutate `build.config`, you must do so directly in the `setup()` function:
|
||||
|
||||
```ts
|
||||
await Bun.build({
|
||||
@@ -400,7 +400,7 @@ type Loader = "js" | "jsx" | "ts" | "tsx" | "css" | "json" | "toml" | "object";
|
||||
|
||||
### Namespaces
|
||||
|
||||
`onLoad` and `onResolve` accept an optional `namespace` string. What is a namespaace?
|
||||
`onLoad` and `onResolve` accept an optional `namespace` string. What is a namespace?
|
||||
|
||||
Every module has a namespace. Namespaces are used to prefix the import in transpiled code; for instance, a loader with a `filter: /\.yaml$/` and `namespace: "yaml:"` will transform an import from `./myfile.yaml` into `yaml:./myfile.yaml`.
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ Bun supports things like top-level await, JSX, and extensioned `.ts` imports, wh
|
||||
```jsonc
|
||||
{
|
||||
"compilerOptions": {
|
||||
// Enable latest features
|
||||
// Environment setup & latest features
|
||||
"lib": ["ESNext"],
|
||||
"target": "ESNext",
|
||||
"module": "ESNext",
|
||||
@@ -35,12 +35,13 @@ Bun supports things like top-level await, JSX, and extensioned `.ts` imports, wh
|
||||
"strict": true,
|
||||
"skipLibCheck": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"noUncheckedIndexedAccess": true,
|
||||
|
||||
// Some stricter flags
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true,
|
||||
"noPropertyAccessFromIndexSignature": true
|
||||
}
|
||||
// Some stricter flags (disabled by default)
|
||||
"noUnusedLocals": false,
|
||||
"noUnusedParameters": false,
|
||||
"noPropertyAccessFromIndexSignature": false,
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
# Tell LLDB what to do when the debugged process receives SIGPWR: pass it through to the process
|
||||
# (-p), but do not stop the process (-s) or notify the user (-n).
|
||||
#
|
||||
# JSC's garbage collector sends this signal (as configured by Bun WebKit in
|
||||
# Thread::initializePlatformThreading() in ThreadingPOSIX.cpp) to the JS thread to suspend or resume
|
||||
# it. So stopping the process would just create noise when debugging any long-running script.
|
||||
process handle -p true -s false -n false SIGPWR
|
||||
|
||||
command script import misctools/lldb/lldb_pretty_printers.py
|
||||
type category enable zig.lang
|
||||
type category enable zig.std
|
||||
|
||||
command script import misctools/lldb/lldb_webkit.py
|
||||
@@ -329,7 +329,7 @@ def btjs(debugger, command, result, internal_dict):
|
||||
addressFormat = '#0{width}x'.format(width=target.GetAddressByteSize() * 2 + 2)
|
||||
process = target.GetProcess()
|
||||
thread = process.GetSelectedThread()
|
||||
jscModule = target.module["JavaScriptCore"]
|
||||
jscModule = target.module["JavaScriptCore"] or target.module["bun"] or target.module["bun-debug"]
|
||||
|
||||
if jscModule.FindSymbol("JSC::CallFrame::describeFrame").GetSize() or jscModule.FindSymbol("_ZN3JSC9CallFrame13describeFrameEv").GetSize():
|
||||
annotateJSFrames = True
|
||||
|
||||
@@ -29,6 +29,9 @@
|
||||
"test/js/**/*bad.js",
|
||||
"test/bundler/transpiler/decorators.test.ts", // uses `arguments` as decorator
|
||||
"test/bundler/native-plugin.test.ts", // parser doesn't handle import metadata
|
||||
"test/bundler/transpiler/with-statement-works.js", // parser doesn't allow `with` statement
|
||||
"test/js/node/module/extensions-fixture", // these files are not meant to be linted
|
||||
"test/cli/run/module-type-fixture",
|
||||
"test/bundler/transpiler/with-statement-works.js" // parser doesn't allow `with` statement
|
||||
],
|
||||
"overrides": [
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"private": true,
|
||||
"name": "bun",
|
||||
"version": "1.2.5",
|
||||
"version": "1.2.8",
|
||||
"workspaces": [
|
||||
"./packages/bun-types"
|
||||
],
|
||||
@@ -31,6 +31,7 @@
|
||||
},
|
||||
"scripts": {
|
||||
"build": "bun run build:debug",
|
||||
"bd": "(bun run --silent build:debug &> /tmp/bun.debug.build.log || (cat /tmp/bun.debug.build.log && rm -rf /tmp/bun.debug.build.log && exit 1)) && rm -f /tmp/bun.debug.build.log && ./build/debug/bun-debug",
|
||||
"build:debug": "bun ./scripts/build.mjs -GNinja -DCMAKE_BUILD_TYPE=Debug -B build/debug",
|
||||
"build:valgrind": "bun ./scripts/build.mjs -GNinja -DCMAKE_BUILD_TYPE=Debug -DENABLE_BASELINE=ON -ENABLE_VALGRIND=ON -B build/debug-valgrind",
|
||||
"build:release": "bun ./scripts/build.mjs -GNinja -DCMAKE_BUILD_TYPE=Release -B build/release",
|
||||
@@ -44,6 +45,7 @@
|
||||
"build:release:with_logs": "cmake . -DCMAKE_BUILD_TYPE=Release -DENABLE_LOGS=true -GNinja -Bbuild-release && ninja -Cbuild-release",
|
||||
"build:debug-zig-release": "cmake . -DCMAKE_BUILD_TYPE=Release -DZIG_OPTIMIZE=Debug -GNinja -Bbuild-debug-zig-release && ninja -Cbuild-debug-zig-release",
|
||||
"css-properties": "bun run src/css/properties/generate_properties.ts",
|
||||
"uv-posix-stubs": "bun run src/bun.js/bindings/libuv/generate_uv_posix_stubs.ts",
|
||||
"bump": "bun ./scripts/bump.ts",
|
||||
"typecheck": "tsc --noEmit && cd test && bun run typecheck",
|
||||
"fmt": "bun run prettier",
|
||||
|
||||
@@ -8,7 +8,7 @@ The official [Svelte](https://svelte.dev/) plugin for [Bun](https://bun.sh/).
|
||||
## Installation
|
||||
|
||||
```sh
|
||||
bun add -D bun-plugin-svelte
|
||||
$ bun add -D bun-plugin-svelte
|
||||
```
|
||||
|
||||
## Dev Server Usage
|
||||
@@ -16,52 +16,25 @@ bun add -D bun-plugin-svelte
|
||||
`bun-plugin-svelte` integrates with Bun's [Fullstack Dev Server](https://bun.sh/docs/bundler/fullstack), giving you
|
||||
HMR when developing your Svelte app.
|
||||
|
||||
```html
|
||||
<!-- index.html -->
|
||||
<html>
|
||||
<head>
|
||||
<script type="module" src="./index.ts"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
</body>
|
||||
</html>
|
||||
Start by registering it in your [bunfig.toml](https://bun.sh/docs/runtime/bunfig):
|
||||
|
||||
```toml
|
||||
[serve.static]
|
||||
plugins = ["bun-plugin-svelte"]
|
||||
```
|
||||
|
||||
```ts
|
||||
// index.ts
|
||||
Then start your dev server:
|
||||
|
||||
import { mount, unmount } from "svelte";
|
||||
import App from "./App.svelte";
|
||||
|
||||
// mount the application entrypoint to the DOM
|
||||
const root = document.getElementById("root")!;
|
||||
const app = mount(App, { target: root });
|
||||
```
|
||||
$ bun index.html
|
||||
```
|
||||
|
||||
```svelte
|
||||
<!-- App.svelte -->
|
||||
|
||||
<script lang="ts">
|
||||
// out-of-the-box typescript support
|
||||
let name: string = "Bun";
|
||||
</script>
|
||||
|
||||
<main class="app">
|
||||
<h1>Cookin up apps with {name}</h1>
|
||||
</main>
|
||||
|
||||
<style>
|
||||
h1 {
|
||||
color: #ff3e00;
|
||||
text-align: center;
|
||||
font-size: 2em;
|
||||
}
|
||||
</style>
|
||||
```
|
||||
See the [example](https://github.com/oven-sh/bun/tree/main/packages/bun-plugin-svelte/example) for a complete example.
|
||||
|
||||
## Bundler Usage
|
||||
|
||||
`bun-plugin-svelte` lets you bundle Svelte components with [`Bun.build`](https://bun.sh/docs/bundler).
|
||||
|
||||
```ts
|
||||
// build.ts
|
||||
// to use: bun run build.ts
|
||||
@@ -70,7 +43,7 @@ import { SveltePlugin } from "bun-plugin-svelte"; // NOTE: not published to npm
|
||||
Bun.build({
|
||||
entrypoints: ["src/index.ts"],
|
||||
outdir: "dist",
|
||||
target: "browser", // use "bun" or "node" to use Svelte components server-side
|
||||
target: "browser",
|
||||
sourcemap: true, // sourcemaps not yet supported
|
||||
plugins: [
|
||||
SveltePlugin({
|
||||
@@ -84,3 +57,13 @@ Bun.build({
|
||||
|
||||
`bun-plugin-svelte` does not yet support server-side imports (e.g. for SSR).
|
||||
This will be added in the near future.
|
||||
|
||||
## Not Yet Supported
|
||||
|
||||
Support for these features will be added in the near future
|
||||
|
||||
- Server-side imports/rendering
|
||||
- Source maps
|
||||
- CSS extensions (e.g. tailwind) in `<style>` blocks
|
||||
- TypeScript-specific features (e.g. enums and namespaces). If you're using
|
||||
TypeScript 5.8, consider enabling [`--erasableSyntaxOnly`](https://devblogs.microsoft.com/typescript/announcing-typescript-5-8-beta/#the---erasablesyntaxonly-option)
|
||||
|
||||
@@ -4,12 +4,12 @@
|
||||
"": {
|
||||
"name": "bun-plugin-svelte",
|
||||
"devDependencies": {
|
||||
"@threlte/core": "8.0.1",
|
||||
"bun-types": "canary",
|
||||
"svelte": "^5.20.4",
|
||||
},
|
||||
"peerDependencies": {
|
||||
"svelte": "^5",
|
||||
"typescript": "^5",
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -26,6 +26,8 @@
|
||||
|
||||
"@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.25", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ=="],
|
||||
|
||||
"@threlte/core": ["@threlte/core@8.0.1", "", { "dependencies": { "mitt": "^3.0.1" }, "peerDependencies": { "svelte": ">=5", "three": ">=0.155" } }, "sha512-vy1xRQppJFNmfPTeiRQue+KmYFsbPgVhwuYXRTvVrwPeD2oYz43gxUeOpe1FACeGKxrxZykeKJF5ebVvl7gBxw=="],
|
||||
|
||||
"@types/estree": ["@types/estree@1.0.6", "", {}, "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw=="],
|
||||
|
||||
"@types/node": ["@types/node@22.13.5", "", { "dependencies": { "undici-types": "~6.20.0" } }, "sha512-+lTU0PxZXn0Dr1NBtC7Y8cR21AJr87dLLU953CWA6pMxxv/UDc7jYAY90upcrie1nRcD6XNG5HOYEDtgW5TxAg=="],
|
||||
@@ -54,9 +56,11 @@
|
||||
|
||||
"magic-string": ["magic-string@0.30.17", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0" } }, "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA=="],
|
||||
|
||||
"mitt": ["mitt@3.0.1", "", {}, "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw=="],
|
||||
|
||||
"svelte": ["svelte@5.20.4", "", { "dependencies": { "@ampproject/remapping": "^2.3.0", "@jridgewell/sourcemap-codec": "^1.5.0", "@types/estree": "^1.0.5", "acorn": "^8.12.1", "acorn-typescript": "^1.4.13", "aria-query": "^5.3.1", "axobject-query": "^4.1.0", "clsx": "^2.1.1", "esm-env": "^1.2.1", "esrap": "^1.4.3", "is-reference": "^3.0.3", "locate-character": "^3.0.0", "magic-string": "^0.30.11", "zimmerframe": "^1.1.2" } }, "sha512-2Mo/AfObaw9zuD0u1JJ7sOVzRCGcpETEyDkLbtkcctWpCMCIyT0iz83xD8JT29SR7O4SgswuPRIDYReYF/607A=="],
|
||||
|
||||
"typescript": ["typescript@5.7.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw=="],
|
||||
"three": ["three@0.174.0", "", {}, "sha512-p+WG3W6Ov74alh3geCMkGK9NWuT62ee21cV3jEnun201zodVF4tCE5aZa2U122/mkLRmhJJUQmLLW1BH00uQJQ=="],
|
||||
|
||||
"undici-types": ["undici-types@6.20.0", "", {}, "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg=="],
|
||||
|
||||
|
||||
311
packages/bun-plugin-svelte/example/App.svelte
Normal file
311
packages/bun-plugin-svelte/example/App.svelte
Normal file
@@ -0,0 +1,311 @@
|
||||
<script lang="ts">
|
||||
import FeatureCard from "./FeatureCard.svelte";
|
||||
|
||||
const links = [
|
||||
{ text: "Bun Documentation", url: "https://bun.sh/docs" },
|
||||
{ text: "Svelte Documentation", url: "https://svelte.dev/docs" },
|
||||
{ text: "GitHub", url: "https://github.com/oven-sh/bun/tree/main/packages/bun-plugin-svelte" },
|
||||
];
|
||||
</script>
|
||||
|
||||
<main>
|
||||
<div class="hero">
|
||||
<div class="logo-container">
|
||||
<a href="https://bun.sh" class="bun-logo">
|
||||
<img
|
||||
src="https://github.com/user-attachments/assets/50282090-adfd-4ddb-9e27-c30753c6b161"
|
||||
alt="Bun Logo"
|
||||
height="42"
|
||||
/>
|
||||
</a>
|
||||
</div>
|
||||
<h1><span class="highlight">bun-plugin-svelte</span></h1>
|
||||
<p class="tagline">The official Svelte plugin for <a href="https://bun.sh" target="_blank">Bun</a></p>
|
||||
|
||||
<div class="cta-buttons">
|
||||
<a href="https://bun.sh/docs/bundler/html" class="button primary">🚀 Get Started</a>
|
||||
<a href="https://github.com/oven-sh/bun/tree/main/packages/bun-plugin-svelte/example" class="button secondary"
|
||||
>👀 View Examples</a
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<section class="usage">
|
||||
<h2>🏃➡️ Quick Start</h2>
|
||||
|
||||
<div class="flex-grid">
|
||||
<div>
|
||||
<h3>1. Install from <a href="https://npmjs.com/package/bun-plugin-svelte" target="_blank">NPM</a></h3>
|
||||
<pre><code class="language-bash">bun add -D bun-plugin-svelte</code></pre>
|
||||
</div>
|
||||
<div>
|
||||
<h3>2. Add it to your <a href="https://bun.sh/docs/runtime/bunfig" target="_blank">bunfig.toml</a></h3>
|
||||
<pre><code class="language-toml">
|
||||
[serve.static]
|
||||
plugins = ["bun-plugin-svelte"];
|
||||
</code></pre>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="features">
|
||||
<h2>✨ Features</h2>
|
||||
<div class="feature-grid">
|
||||
<FeatureCard title="🔥 HMR Support" link="https://bun.sh/docs/bundler/html">
|
||||
Integrates with Bun's Fullstack Dev Server for hot module replacement
|
||||
</FeatureCard>
|
||||
<FeatureCard title="📦 Bundling" link="https://bun.sh/docs/bundler">
|
||||
Bundle Svelte components with <a href="https://bun.sh/docs/bundler">Bun.build</a>
|
||||
</FeatureCard>
|
||||
</div>
|
||||
|
||||
<section class="resources">
|
||||
<h2>📖 Resources</h2>
|
||||
<ul class="resource-links">
|
||||
{#each links as link}
|
||||
<li><a href={link.url} target="_blank" rel="noopener noreferrer">{link.text}</a></li>
|
||||
{/each}
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<footer>
|
||||
<p>Made with ❤️ by the Bun team</p>
|
||||
</footer>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<style>
|
||||
:global(body) {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Open Sans",
|
||||
"Helvetica Neue", sans-serif;
|
||||
background-color: #f9f9f9;
|
||||
color: #333;
|
||||
}
|
||||
:global(a) {
|
||||
color: #ff3e00;
|
||||
text-decoration: none;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
:global(a::after) {
|
||||
content: "";
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 1px;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
background-color: #ff3e00;
|
||||
transform: scaleX(0);
|
||||
transform-origin: bottom right;
|
||||
transition: transform 0.3s ease-out;
|
||||
}
|
||||
|
||||
:global(a:hover::after) {
|
||||
transform: scaleX(1);
|
||||
transform-origin: bottom left;
|
||||
}
|
||||
|
||||
:global(a:visited) {
|
||||
color: #ff3e00;
|
||||
}
|
||||
|
||||
:global(pre > code.hljs) {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
main {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
padding: 2rem;
|
||||
}
|
||||
|
||||
.hero {
|
||||
text-align: center;
|
||||
padding: 3rem 1rem;
|
||||
margin-bottom: 2rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.logo-container {
|
||||
margin-bottom: 1.5rem;
|
||||
margin: auto 25%;
|
||||
}
|
||||
|
||||
.bun-logo {
|
||||
display: block;
|
||||
transition: transform 0.3s ease;
|
||||
}
|
||||
|
||||
.bun-logo:hover {
|
||||
transform: scale(1.05);
|
||||
}
|
||||
|
||||
.bun-logo img {
|
||||
max-width: 33vw;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 2.5rem;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.highlight {
|
||||
color: #ff3e00;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* Don't apply the underline effect to buttons and resource links */
|
||||
.button::after,
|
||||
.resource-links li a::after,
|
||||
.bun-logo::after {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.tagline {
|
||||
font-size: 1.2rem;
|
||||
color: #666;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.cta-buttons {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
gap: 1rem;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.button {
|
||||
display: inline-block;
|
||||
padding: 0.8rem 1.5rem;
|
||||
border-radius: 4px;
|
||||
text-decoration: none;
|
||||
font-weight: 600;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.button.primary {
|
||||
background-color: #ff3e00;
|
||||
color: white;
|
||||
box-shadow: 0 2px 10px rgba(255, 62, 0, 0.2);
|
||||
}
|
||||
|
||||
.button.primary:hover {
|
||||
background-color: #e63600;
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 4px 12px rgba(255, 62, 0, 0.3);
|
||||
}
|
||||
|
||||
.button.secondary {
|
||||
background-color: #f0f0f0;
|
||||
color: #333;
|
||||
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.button.secondary:hover {
|
||||
background-color: #e6e6e6;
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
|
||||
section {
|
||||
margin-bottom: 3rem;
|
||||
padding: 2rem;
|
||||
background-color: white;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 15px rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 1.8rem;
|
||||
margin-bottom: 1.5rem;
|
||||
color: #333;
|
||||
border-bottom: 2px solid #f0f0f0;
|
||||
padding-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.flex-grid {
|
||||
display: flex;
|
||||
gap: 1.5rem;
|
||||
}
|
||||
.flex-grid > div {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.feature-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
|
||||
gap: 1.5rem;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
pre {
|
||||
background-color: #f5f5f5;
|
||||
padding: 1rem;
|
||||
border-radius: 4px;
|
||||
overflow-x: auto;
|
||||
font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, monospace;
|
||||
font-size: 0.9rem;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
code {
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.resource-links {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.resource-links li a {
|
||||
display: inline-block;
|
||||
padding: 0.5rem 1rem;
|
||||
background-color: #f5f5f5;
|
||||
color: #333;
|
||||
text-decoration: none;
|
||||
border-radius: 4px;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.resource-links li a:hover {
|
||||
background-color: #ff3e00;
|
||||
color: white;
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
footer {
|
||||
text-align: center;
|
||||
padding: 2rem 0;
|
||||
color: #666;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.feature-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.cta-buttons {
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.button {
|
||||
width: 100%;
|
||||
max-width: 300px;
|
||||
margin-bottom: 0.5rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.resource-links {
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
28
packages/bun-plugin-svelte/example/FeatureCard.svelte
Normal file
28
packages/bun-plugin-svelte/example/FeatureCard.svelte
Normal file
@@ -0,0 +1,28 @@
|
||||
<script lang="ts">
|
||||
let { title, link, children } = $props();
|
||||
</script>
|
||||
|
||||
<div class="feature-card">
|
||||
<h3>
|
||||
<a href={link}>
|
||||
{title}
|
||||
</a>
|
||||
</h3>
|
||||
<p>
|
||||
{@render children()}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.feature-card {
|
||||
padding: 1rem;
|
||||
border-radius: 0.5rem;
|
||||
background-color: #fff;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
transition: transform 0.3s ease;
|
||||
}
|
||||
|
||||
.feature-card h3 {
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
</style>
|
||||
2
packages/bun-plugin-svelte/example/bunfig.toml
Normal file
2
packages/bun-plugin-svelte/example/bunfig.toml
Normal file
@@ -0,0 +1,2 @@
|
||||
[serve.static]
|
||||
plugins = ["bun-plugin-svelte"]
|
||||
25
packages/bun-plugin-svelte/example/index.html
Normal file
25
packages/bun-plugin-svelte/example/index.html
Normal file
@@ -0,0 +1,25 @@
|
||||
<html>
|
||||
<head>
|
||||
<script type="module" src="./index.ts"></script>
|
||||
|
||||
<link rel="prefetch" href="https://bun.sh/docs/bundler/plugins" />
|
||||
<link rel="preconnect" href="https://bun.sh" />
|
||||
<link rel="preconnect" href="https://github.com" />
|
||||
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@11.9.0/build/styles/default.min.css"
|
||||
/>
|
||||
<script src="https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@11.9.0/build/highlight.min.js"></script>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@11.9.0/build/languages/toml.min.js"></script>
|
||||
|
||||
<script>
|
||||
hljs.highlightAll();
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
</body>
|
||||
</html>
|
||||
29
packages/bun-plugin-svelte/example/index.ts
Normal file
29
packages/bun-plugin-svelte/example/index.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
import { mount, unmount } from "svelte";
|
||||
import App from "./App.svelte";
|
||||
|
||||
declare global {
|
||||
var didMount: boolean | undefined;
|
||||
var hljs: any;
|
||||
}
|
||||
|
||||
let app: Record<string, any> | undefined;
|
||||
|
||||
// mount the application entrypoint to the DOM on first load. On subsequent hot
|
||||
// updates, the app will be unmounted and re-mounted via the accept handler.
|
||||
|
||||
const root = document.getElementById("root")!;
|
||||
if (!globalThis.didMount) {
|
||||
app = mount(App, { target: root });
|
||||
}
|
||||
globalThis.didMount = true;
|
||||
|
||||
if (import.meta.hot) {
|
||||
import.meta.hot.accept(async () => {
|
||||
// avoid unmounting twice when another update gets accepted while outros are playing
|
||||
if (!app) return;
|
||||
const prevApp = app;
|
||||
app = undefined;
|
||||
await unmount(prevApp, { outro: true });
|
||||
app = mount(App, { target: root });
|
||||
});
|
||||
}
|
||||
@@ -1,6 +1,14 @@
|
||||
{
|
||||
"name": "bun-plugin-svelte",
|
||||
"version": "0.0.1",
|
||||
"version": "0.0.6",
|
||||
"description": "Official Svelte plugin for Bun",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/oven-sh/bun",
|
||||
"directory": "packages/bun-plugin-svelte"
|
||||
},
|
||||
"homepage": "https://bun.sh",
|
||||
"license": "MIT",
|
||||
"type": "module",
|
||||
"module": "src/index.ts",
|
||||
"index": "src/index.ts",
|
||||
@@ -8,16 +16,18 @@
|
||||
".": "./src/index.ts"
|
||||
},
|
||||
"scripts": {
|
||||
"example": "bun --config=./example/bunfig.toml example/index.html",
|
||||
"lint": "oxlint .",
|
||||
"fmt": "prettier --write .",
|
||||
"check:types": "tsc --noEmit",
|
||||
"build:types": "tsc --emitDeclarationOnly --declaration --declarationDir ./dist"
|
||||
},
|
||||
"devDependencies": {
|
||||
"bun-types": "canary",
|
||||
"svelte": "^5.20.4"
|
||||
"svelte": "^5.20.4",
|
||||
"@threlte/core": "8.0.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"typescript": "^5",
|
||||
"svelte": "^5"
|
||||
},
|
||||
"files": [
|
||||
|
||||
@@ -11,7 +11,11 @@ describe("SveltePlugin", () => {
|
||||
expect(() => SveltePlugin(undefined)).not.toThrow();
|
||||
});
|
||||
|
||||
it.each([null, 1, "hi", {}, "Client"])("throws if forceSide is not 'client' or 'server' (%p)", (forceSide: any) => {
|
||||
it.each([1, "hi", {}, "Client"])("throws if forceSide is not 'client' or 'server' (%p)", (forceSide: any) => {
|
||||
expect(() => SveltePlugin({ forceSide })).toThrow(TypeError);
|
||||
});
|
||||
|
||||
it.each([null, undefined])("forceSide may be nullish", (forceSide: any) => {
|
||||
expect(() => SveltePlugin({ forceSide })).not.toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,7 +1,13 @@
|
||||
import type { BunPlugin, BuildConfig, OnLoadResult } from "bun";
|
||||
import { basename } from "node:path";
|
||||
import { compile, compileModule } from "svelte/compiler";
|
||||
import { getBaseCompileOptions, validateOptions, type SvelteOptions, hash } from "./options";
|
||||
import {
|
||||
getBaseCompileOptions,
|
||||
validateOptions,
|
||||
type SvelteOptions,
|
||||
hash,
|
||||
getBaseModuleCompileOptions,
|
||||
} from "./options";
|
||||
|
||||
const kEmptyObject = Object.create(null);
|
||||
const virtualNamespace = "bun-svelte";
|
||||
@@ -23,38 +29,52 @@ function SveltePlugin(options: SvelteOptions = kEmptyObject as SvelteOptions): B
|
||||
return {
|
||||
name: "bun-plugin-svelte",
|
||||
setup(builder) {
|
||||
// resolve "svelte" export conditions
|
||||
//
|
||||
// FIXME: the dev server does not currently respect bundler configs; it
|
||||
// just passes a fake one to plugins and then never uses it. we need to to
|
||||
// update it to ~not~ do this.
|
||||
if (builder?.config) {
|
||||
var conditions = builder.config.conditions ?? [];
|
||||
if (typeof conditions === "string") {
|
||||
conditions = [conditions];
|
||||
}
|
||||
conditions.push("svelte");
|
||||
builder.config.conditions = conditions;
|
||||
}
|
||||
|
||||
const { config = kEmptyObject as Partial<BuildConfig> } = builder;
|
||||
const baseCompileOptions = getBaseCompileOptions(options ?? (kEmptyObject as Partial<SvelteOptions>), config);
|
||||
const baseModuleCompileOptions = getBaseModuleCompileOptions(
|
||||
options ?? (kEmptyObject as Partial<SvelteOptions>),
|
||||
config,
|
||||
);
|
||||
|
||||
const ts = new Bun.Transpiler({
|
||||
loader: "ts",
|
||||
target: config.target,
|
||||
});
|
||||
|
||||
builder
|
||||
.onLoad({ filter: /\.svelte(?:\.[tj]s)?$/ }, async args => {
|
||||
.onLoad({ filter: /\.svelte$/ }, async function onLoadSvelte(args) {
|
||||
const { path } = args;
|
||||
|
||||
var isModule = false;
|
||||
|
||||
switch (path.substring(path.length - 2)) {
|
||||
case "js":
|
||||
case "ts":
|
||||
isModule = true;
|
||||
break;
|
||||
}
|
||||
|
||||
const sourceText = await Bun.file(path).text();
|
||||
|
||||
const side =
|
||||
args && "side" in args // "side" only passed when run from dev server
|
||||
? (args as { side: "client" | "server" }).side
|
||||
: "server";
|
||||
const hmr = Boolean((args as { hmr?: boolean })["hmr"] ?? process.env.NODE_ENV !== "production");
|
||||
const generate = baseCompileOptions.generate ?? side;
|
||||
|
||||
const compileFn = isModule ? compileModule : compile;
|
||||
const result = compileFn(sourceText, {
|
||||
const hmr = Boolean((args as { hmr?: boolean })["hmr"] ?? process.env.NODE_ENV !== "production");
|
||||
const result = compile(sourceText, {
|
||||
...baseCompileOptions,
|
||||
generate,
|
||||
filename: args.path,
|
||||
hmr,
|
||||
});
|
||||
|
||||
var { js, css } = result;
|
||||
if (css?.code && generate != "server") {
|
||||
const uid = `${basename(path)}-${hash(path)}-style`.replaceAll(`"`, `'`);
|
||||
@@ -65,11 +85,37 @@ function SveltePlugin(options: SvelteOptions = kEmptyObject as SvelteOptions): B
|
||||
|
||||
return {
|
||||
contents: result.js.code,
|
||||
loader: "js",
|
||||
loader: "ts",
|
||||
} satisfies OnLoadResult;
|
||||
// TODO: allow plugins to return multiple results.
|
||||
// TODO: support layered sourcemaps
|
||||
})
|
||||
.onLoad({ filter: /\.svelte.[tj]s$/ }, async function onLoadSvelteModule(args) {
|
||||
const { path } = args;
|
||||
|
||||
const side =
|
||||
args && "side" in args // "side" only passed when run from dev server
|
||||
? (args as { side: "client" | "server" }).side
|
||||
: "server";
|
||||
const generate = baseModuleCompileOptions.generate ?? side;
|
||||
|
||||
var sourceText = await Bun.file(path).text();
|
||||
if (path.endsWith(".ts")) {
|
||||
sourceText = await ts.transform(sourceText);
|
||||
}
|
||||
const result = compileModule(sourceText, {
|
||||
...baseModuleCompileOptions,
|
||||
generate,
|
||||
filename: args.path,
|
||||
});
|
||||
|
||||
// NOTE: we assume js/ts modules won't have CSS blocks in them, so no
|
||||
// virtual modules get created.
|
||||
return {
|
||||
contents: result.js.code,
|
||||
loader: "js",
|
||||
};
|
||||
})
|
||||
.onResolve({ filter: /^bun-svelte:/ }, args => {
|
||||
return {
|
||||
path: args.path,
|
||||
|
||||
@@ -2,7 +2,7 @@ import { describe, beforeAll, it, expect } from "bun:test";
|
||||
import type { BuildConfig } from "bun";
|
||||
import type { CompileOptions } from "svelte/compiler";
|
||||
|
||||
import { getBaseCompileOptions, type SvelteOptions } from "./options";
|
||||
import { getBaseCompileOptions, validateOptions, type SvelteOptions } from "./options";
|
||||
|
||||
describe("getBaseCompileOptions", () => {
|
||||
describe("when no options are provided", () => {
|
||||
@@ -42,4 +42,13 @@ describe("getBaseCompileOptions", () => {
|
||||
);
|
||||
},
|
||||
);
|
||||
});
|
||||
}); // getBaseCompileOptions
|
||||
|
||||
describe("validateOptions(options)", () => {
|
||||
it.each(["", 1, null, undefined, true, false, Symbol("hi")])(
|
||||
"throws if options is not an object (%p)",
|
||||
(badOptions: any) => {
|
||||
expect(() => validateOptions(badOptions)).toThrow();
|
||||
},
|
||||
);
|
||||
}); // validateOptions
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import { strict as assert } from "node:assert";
|
||||
import type { BuildConfig } from "bun";
|
||||
import type { CompileOptions } from "svelte/compiler";
|
||||
import { type BuildConfig } from "bun";
|
||||
import type { CompileOptions, ModuleCompileOptions } from "svelte/compiler";
|
||||
|
||||
export interface SvelteOptions {
|
||||
type OverrideCompileOptions = Pick<CompileOptions, "customElement" | "runes" | "modernAst" | "namespace">;
|
||||
export interface SvelteOptions extends Pick<CompileOptions, "runes"> {
|
||||
/**
|
||||
* Force client-side or server-side generation.
|
||||
*
|
||||
@@ -20,6 +21,11 @@ export interface SvelteOptions {
|
||||
* Defaults to `true` when run via Bun's dev server, `false` otherwise.
|
||||
*/
|
||||
development?: boolean;
|
||||
|
||||
/**
|
||||
* Options to forward to the Svelte compiler.
|
||||
*/
|
||||
compilerOptions?: OverrideCompileOptions;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -27,15 +33,24 @@ export interface SvelteOptions {
|
||||
*/
|
||||
export function validateOptions(options: unknown): asserts options is SvelteOptions {
|
||||
assert(options && typeof options === "object", new TypeError("bun-svelte-plugin: options must be an object"));
|
||||
if ("forceSide" in options) {
|
||||
switch (options.forceSide) {
|
||||
const opts = options as Record<keyof SvelteOptions, unknown>;
|
||||
|
||||
if (opts.forceSide != null) {
|
||||
if (typeof opts.forceSide !== "string") {
|
||||
throw new TypeError("bun-svelte-plugin: forceSide must be a string, got " + typeof opts.forceSide);
|
||||
}
|
||||
switch (opts.forceSide) {
|
||||
case "client":
|
||||
case "server":
|
||||
break;
|
||||
default:
|
||||
throw new TypeError(
|
||||
`bun-svelte-plugin: forceSide must be either 'client' or 'server', got ${options.forceSide}`,
|
||||
);
|
||||
throw new TypeError(`bun-svelte-plugin: forceSide must be either 'client' or 'server', got ${opts.forceSide}`);
|
||||
}
|
||||
}
|
||||
|
||||
if (opts.compilerOptions) {
|
||||
if (typeof opts.compilerOptions !== "object") {
|
||||
throw new TypeError("bun-svelte-plugin: compilerOptions must be an object");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -44,8 +59,11 @@ export function validateOptions(options: unknown): asserts options is SvelteOpti
|
||||
* @internal
|
||||
*/
|
||||
export function getBaseCompileOptions(pluginOptions: SvelteOptions, config: Partial<BuildConfig>): CompileOptions {
|
||||
let { forceSide, development = false } = pluginOptions;
|
||||
const { minify = false, target } = config;
|
||||
let {
|
||||
development = false,
|
||||
compilerOptions: { customElement, runes, modernAst, namespace } = kEmptyObject as OverrideCompileOptions,
|
||||
} = pluginOptions;
|
||||
const { minify = false } = config;
|
||||
|
||||
const shouldMinify = Boolean(minify);
|
||||
const {
|
||||
@@ -60,6 +78,42 @@ export function getBaseCompileOptions(pluginOptions: SvelteOptions, config: Part
|
||||
identifiers: shouldMinify,
|
||||
};
|
||||
|
||||
const generate = generateSide(pluginOptions, config);
|
||||
|
||||
return {
|
||||
css: "external",
|
||||
generate,
|
||||
preserveWhitespace: !minifyWhitespace,
|
||||
preserveComments: !shouldMinify,
|
||||
dev: development,
|
||||
customElement,
|
||||
runes,
|
||||
modernAst,
|
||||
namespace,
|
||||
cssHash({ css }) {
|
||||
// same prime number seed used by svelte/compiler.
|
||||
// TODO: ensure this provides enough entropy
|
||||
return `svelte-${hash(css)}`;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export function getBaseModuleCompileOptions(
|
||||
pluginOptions: SvelteOptions,
|
||||
config: Partial<BuildConfig>,
|
||||
): ModuleCompileOptions {
|
||||
const { development = false } = pluginOptions;
|
||||
const generate = generateSide(pluginOptions, config);
|
||||
return {
|
||||
dev: development,
|
||||
generate,
|
||||
};
|
||||
}
|
||||
|
||||
function generateSide(pluginOptions: SvelteOptions, config: Partial<BuildConfig>) {
|
||||
let { forceSide } = pluginOptions;
|
||||
const { target } = config;
|
||||
|
||||
if (forceSide == null && typeof target === "string") {
|
||||
switch (target) {
|
||||
case "browser":
|
||||
@@ -73,19 +127,8 @@ export function getBaseCompileOptions(pluginOptions: SvelteOptions, config: Part
|
||||
// warn? throw?
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
css: "external",
|
||||
generate: forceSide,
|
||||
preserveWhitespace: !minifyWhitespace,
|
||||
preserveComments: !shouldMinify,
|
||||
dev: development,
|
||||
cssHash({ css }) {
|
||||
// same prime number seed used by svelte/compiler.
|
||||
// TODO: ensure this provides enough entropy
|
||||
return `svelte-${hash(css)}`;
|
||||
},
|
||||
};
|
||||
return forceSide;
|
||||
}
|
||||
|
||||
export const hash = (content: string): string => Bun.hash(content, 5381).toString(36);
|
||||
const kEmptyObject = Object.create(null);
|
||||
|
||||
17
packages/bun-plugin-svelte/test/fixtures/svelte-export-condition.svelte
vendored
Normal file
17
packages/bun-plugin-svelte/test/fixtures/svelte-export-condition.svelte
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
<script>
|
||||
import { Canvas } from "@threlte/core";
|
||||
let name = "Bun";
|
||||
</script>
|
||||
|
||||
<main class="app">
|
||||
<h1>Cookin up apps with {name}</h1>
|
||||
<Canvas />
|
||||
</main>
|
||||
|
||||
<style>
|
||||
h1 {
|
||||
color: #ff3e00;
|
||||
text-align: center;
|
||||
font-size: 2em;
|
||||
}
|
||||
</style>
|
||||
15
packages/bun-plugin-svelte/test/fixtures/todo-cjs.svelte.ts
vendored
Normal file
15
packages/bun-plugin-svelte/test/fixtures/todo-cjs.svelte.ts
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
class Todo {
|
||||
title: string | undefined = $state();
|
||||
done: boolean = $state(false);
|
||||
createdAt: Date = $state(new Date());
|
||||
|
||||
constructor(title: string) {
|
||||
this.title = title;
|
||||
}
|
||||
|
||||
public toggle(): void {
|
||||
this.done = !this.done;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Todo;
|
||||
13
packages/bun-plugin-svelte/test/fixtures/todo.svelte.ts
vendored
Normal file
13
packages/bun-plugin-svelte/test/fixtures/todo.svelte.ts
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
export class Todo {
|
||||
title: string | undefined = $state();
|
||||
done: boolean = $state(false);
|
||||
createdAt: Date = $state(new Date());
|
||||
|
||||
constructor(title: string) {
|
||||
this.title = title;
|
||||
}
|
||||
|
||||
public toggle(): void {
|
||||
this.done = !this.done;
|
||||
}
|
||||
}
|
||||
25
packages/bun-plugin-svelte/test/fixtures/with-cjs.svelte
vendored
Normal file
25
packages/bun-plugin-svelte/test/fixtures/with-cjs.svelte
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
<script lang="ts">
|
||||
const Todo = require("./todo-cjs.svelte.ts");
|
||||
|
||||
let name = "World";
|
||||
let todo: Todo = $state(new Todo("Hello World!"));
|
||||
</script>
|
||||
|
||||
<main class="app">
|
||||
<h1>Hello {todo.title}!</h1>
|
||||
<!-- clicking calls toggle -->
|
||||
<input type="checkbox" bind:checked={todo.done} />
|
||||
<button onclick={todo.toggle}>Toggle</button>
|
||||
</main>
|
||||
|
||||
<style>
|
||||
h1 {
|
||||
color: #ff3e00;
|
||||
text-align: center;
|
||||
font-size: 2em;
|
||||
font-weight: 100;
|
||||
}
|
||||
.app {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
</style>
|
||||
25
packages/bun-plugin-svelte/test/fixtures/with-modules.svelte
vendored
Normal file
25
packages/bun-plugin-svelte/test/fixtures/with-modules.svelte
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
<script lang="ts">
|
||||
import { Todo } from "./todo.svelte";
|
||||
|
||||
let name = "World";
|
||||
let todo: Todo = $state(new Todo("Hello World!"));
|
||||
</script>
|
||||
|
||||
<main class="app">
|
||||
<h1>Hello {todo.title}!</h1>
|
||||
<!-- clicking calls toggle -->
|
||||
<input type="checkbox" bind:checked={todo.done} />
|
||||
<button onclick={todo.toggle}>Toggle</button>
|
||||
</main>
|
||||
|
||||
<style>
|
||||
h1 {
|
||||
color: #ff3e00;
|
||||
text-align: center;
|
||||
font-size: 2em;
|
||||
font-weight: 100;
|
||||
}
|
||||
.app {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
</style>
|
||||
@@ -4,6 +4,7 @@ import fs from "node:fs";
|
||||
import os from "node:os";
|
||||
import { render } from "svelte/server";
|
||||
import { SveltePlugin } from "../src";
|
||||
import type { BuildOutput } from "bun";
|
||||
|
||||
const fixturePath = (...segs: string[]) => path.join(import.meta.dirname, "fixtures", ...segs);
|
||||
|
||||
@@ -23,13 +24,81 @@ afterAll(() => {
|
||||
}
|
||||
});
|
||||
|
||||
it("hello world component", async () => {
|
||||
const res = await Bun.build({
|
||||
entrypoints: [fixturePath("foo.svelte")],
|
||||
outdir,
|
||||
plugins: [SveltePlugin()],
|
||||
describe("given a hello world component", () => {
|
||||
const entrypoints = [fixturePath("foo.svelte")];
|
||||
it("when no options are provided, builds successfully", async () => {
|
||||
const res = await Bun.build({
|
||||
entrypoints,
|
||||
outdir,
|
||||
plugins: [SveltePlugin()],
|
||||
});
|
||||
expect(res.success).toBeTrue();
|
||||
});
|
||||
|
||||
describe("when a custom element is provided", () => {
|
||||
let res: BuildOutput;
|
||||
|
||||
beforeAll(async () => {
|
||||
res = await Bun.build({
|
||||
entrypoints,
|
||||
outdir,
|
||||
plugins: [SveltePlugin({ compilerOptions: { customElement: true } })],
|
||||
});
|
||||
});
|
||||
|
||||
it("builds successfully", () => {
|
||||
expect(res.success).toBeTrue();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("when importing `.svelte.ts` files with ESM", () => {
|
||||
let res: BuildOutput;
|
||||
|
||||
beforeAll(async () => {
|
||||
res = await Bun.build({
|
||||
entrypoints: [fixturePath("with-modules.svelte")],
|
||||
outdir,
|
||||
plugins: [SveltePlugin()],
|
||||
});
|
||||
});
|
||||
|
||||
it("builds successfully", () => {
|
||||
expect(res.success).toBeTrue();
|
||||
});
|
||||
|
||||
it(`handles "svelte" export condition`, async () => {
|
||||
const res = await Bun.build({
|
||||
entrypoints: [fixturePath("svelte-export-condition.svelte")],
|
||||
outdir,
|
||||
plugins: [SveltePlugin()],
|
||||
});
|
||||
expect(res.success).toBeTrue();
|
||||
});
|
||||
});
|
||||
|
||||
describe("when importing `.svelte.ts` files with CJS", () => {
|
||||
let res: BuildOutput;
|
||||
|
||||
beforeAll(async () => {
|
||||
res = await Bun.build({
|
||||
entrypoints: [fixturePath("with-cjs.svelte")],
|
||||
outdir,
|
||||
plugins: [SveltePlugin()],
|
||||
});
|
||||
});
|
||||
|
||||
it("builds successfully", () => {
|
||||
expect(res.success).toBeTrue();
|
||||
});
|
||||
|
||||
it("does not double-wrap the module with function(module, exports, __filename, __dirname)", async () => {
|
||||
const ts = res.outputs.find(output => output.loader === "ts");
|
||||
expect(ts).toBeDefined();
|
||||
const code = await ts!.text();
|
||||
expect(code).toContain("require_todo_cjs_svelte");
|
||||
expect(code).toContain("var require_todo_cjs_svelte = __commonJS((exports, module) => {\n");
|
||||
});
|
||||
expect(res.success).toBeTrue();
|
||||
});
|
||||
|
||||
describe("Bun.build", () => {
|
||||
|
||||
@@ -20,7 +20,7 @@ That's it! VS Code and TypeScript automatically load `@types/*` packages into yo
|
||||
|
||||
# Contributing
|
||||
|
||||
The `@types/bun` package is a shim that loads `bun-types`. The `bun-types` package lives in the Bun repo under `packages/bun-types`. It is generated via [./scripts/bundle.ts](./scripts/bundle.ts).
|
||||
The `@types/bun` package is a shim that loads `bun-types`. The `bun-types` package lives in the Bun repo under `packages/bun-types`.
|
||||
|
||||
To add a new file, add it under `packages/bun-types`. Then add a [triple-slash directive](https://www.typescriptlang.org/docs/handbook/triple-slash-directives.html) pointing to it inside [./index.d.ts](./index.d.ts).
|
||||
|
||||
@@ -28,8 +28,6 @@ To add a new file, add it under `packages/bun-types`. Then add a [triple-slash d
|
||||
+ /// <reference path="./newfile.d.ts" />
|
||||
```
|
||||
|
||||
[`./bundle.ts`](./bundle.ts) merges the types in this folder into a single file. To run it:
|
||||
|
||||
```bash
|
||||
bun build
|
||||
```
|
||||
|
||||
114
packages/bun-types/authoring.md
Normal file
114
packages/bun-types/authoring.md
Normal file
@@ -0,0 +1,114 @@
|
||||
# Authoring @types/bun
|
||||
|
||||
These declarations define the `'bun'` module, the `Bun` global variable, and lots of other global declarations like extending the `fetch` interface.
|
||||
|
||||
## The `'bun'` Module
|
||||
|
||||
The `Bun` global variable and the `'bun'` module types are defined with one syntax. It supports declaring both types/interfaces and runtime values:
|
||||
|
||||
```typescript
|
||||
declare module "bun" {
|
||||
// Your types go here
|
||||
interface MyInterface {
|
||||
// ...
|
||||
}
|
||||
|
||||
type MyType = string | number;
|
||||
|
||||
function myFunction(): void;
|
||||
}
|
||||
```
|
||||
|
||||
You can write as many `declare module "bun"` declarations as you need. Symbols will be accessible from other files inside of the declaration, and they will all be merged when the types are evaluated.
|
||||
|
||||
You can consume these declarations in two ways:
|
||||
|
||||
1. Importing it from `'bun'`:
|
||||
|
||||
```typescript
|
||||
import { type MyInterface, type MyType, myFunction } from "bun";
|
||||
|
||||
const myInterface: MyInterface = {};
|
||||
const myType: MyType = "cool";
|
||||
myFunction();
|
||||
```
|
||||
|
||||
2. Using the global `Bun` object:
|
||||
|
||||
```typescript
|
||||
const myInterface: Bun.MyInterface = {};
|
||||
const myType: Bun.MyType = "cool";
|
||||
Bun.myFunction();
|
||||
```
|
||||
|
||||
Consuming them inside the ambient declarations is also easy:
|
||||
|
||||
```ts
|
||||
// These are equivalent
|
||||
type A = import("bun").MyType;
|
||||
type A = Bun.MyType;
|
||||
```
|
||||
|
||||
## File Structure
|
||||
|
||||
Types are organized across multiple `.d.ts` files in the `packages/bun-types` directory:
|
||||
|
||||
- `index.d.ts` - The main entry point that references all other type files
|
||||
- `bun.d.ts` - Core Bun APIs and types
|
||||
- `globals.d.ts` - Global type declarations
|
||||
- `test.d.ts` - Testing-related types
|
||||
- `sqlite.d.ts` - SQLite-related types
|
||||
- ...etc. You can make more files
|
||||
|
||||
Note: The order of references in `index.d.ts` is important - `bun.ns.d.ts` must be referenced last to ensure the `Bun` global gets defined properly.
|
||||
|
||||
### Best Practices
|
||||
|
||||
1. **Type Safety**
|
||||
|
||||
- Please use strict types instead of `any` where possible
|
||||
- Leverage TypeScript's type system features (generics, unions, etc.)
|
||||
- Document complex types with JSDoc comments
|
||||
|
||||
2. **Compatibility**
|
||||
|
||||
- Use `Bun.__internal.UseLibDomIfAvailable<LibDomName extends string, OurType>` for types that might conflict with lib.dom.d.ts (see [`./fetch.d.ts`](./fetch.d.ts) for a real example)
|
||||
- `@types/node` often expects variables to always be defined (this was the biggest cause of most of the conflicts in the past!), so we use the `UseLibDomIfAvailable` type to make sure we don't overwrite `lib.dom.d.ts` but still provide Bun types while simultaneously declaring the variable exists (for Node to work) in the cases that we can.
|
||||
|
||||
3. **Documentation**
|
||||
- Add JSDoc comments for public APIs
|
||||
- Include examples in documentation when helpful
|
||||
- Document default values and important behaviors
|
||||
|
||||
### Internal Types
|
||||
|
||||
For internal types that shouldn't be exposed to users, use the `__internal` namespace:
|
||||
|
||||
```typescript
|
||||
declare module "bun" {
|
||||
namespace __internal {
|
||||
interface MyInternalType {
|
||||
// ...
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The internal namespace is mostly used for declaring things that shouldn't be globally accessible on the `bun` namespace, but are still used in public apis. You can see a pretty good example of that in the [`./fetch.d.ts`](./fetch.d.ts) file.
|
||||
|
||||
## Testing Types
|
||||
|
||||
We test our type definitions using a special test file at `fixture/index.ts`. This file contains TypeScript code that exercises our type definitions, but is never actually executed - it's only used to verify that the types work correctly.
|
||||
|
||||
The test file is type-checked in two different environments:
|
||||
|
||||
1. With `lib.dom.d.ts` included - This simulates usage in a browser environment where DOM types are available
|
||||
2. Without `lib.dom.d.ts` - This simulates usage in a server-like environment without DOM types
|
||||
|
||||
Your type definitions must work properly in both environments. This ensures that Bun's types are compatible regardless of whether DOM types are present or not.
|
||||
|
||||
For example, if you're adding types for a new API, you should just add code to `fixture/index.ts` that uses your new API. Doesn't need to work at runtime (e.g. you can fake api keys for example), it's just checking that the types are correct.
|
||||
|
||||
## Questions
|
||||
|
||||
Feel free to open an issue or speak to any of the more TypeScript-focused team members if you need help authoring types or fixing type tests.
|
||||
2952
packages/bun-types/bun.d.ts
vendored
2952
packages/bun-types/bun.d.ts
vendored
File diff suppressed because it is too large
Load Diff
7
packages/bun-types/bun.ns.d.ts
vendored
Normal file
7
packages/bun-types/bun.ns.d.ts
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
import * as BunModule from "bun";
|
||||
|
||||
declare global {
|
||||
export import Bun = BunModule;
|
||||
}
|
||||
|
||||
export {};
|
||||
30
packages/bun-types/deprecated.d.ts
vendored
30
packages/bun-types/deprecated.d.ts
vendored
@@ -1,4 +1,19 @@
|
||||
declare module "bun" {
|
||||
interface BunMessageEvent<T> {
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
initMessageEvent(
|
||||
type: string,
|
||||
bubbles?: boolean,
|
||||
cancelable?: boolean,
|
||||
data?: any,
|
||||
origin?: string,
|
||||
lastEventId?: string,
|
||||
source?: null,
|
||||
): void;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Renamed to `ErrorLike`
|
||||
*/
|
||||
@@ -38,21 +53,6 @@ declare namespace NodeJS {
|
||||
}
|
||||
}
|
||||
|
||||
declare namespace Bun {
|
||||
interface MessageEvent {
|
||||
/** @deprecated */
|
||||
initMessageEvent(
|
||||
type: string,
|
||||
bubbles?: boolean,
|
||||
cancelable?: boolean,
|
||||
data?: any,
|
||||
origin?: string,
|
||||
lastEventId?: string,
|
||||
source?: null,
|
||||
): void;
|
||||
}
|
||||
}
|
||||
|
||||
interface CustomEvent<T = any> {
|
||||
/** @deprecated */
|
||||
initCustomEvent(type: string, bubbles?: boolean, cancelable?: boolean, detail?: T): void;
|
||||
|
||||
292
packages/bun-types/devserver.d.ts
vendored
292
packages/bun-types/devserver.d.ts
vendored
@@ -1,133 +1,189 @@
|
||||
export {};
|
||||
declare module "bun" {
|
||||
type HMREventNames =
|
||||
| "beforeUpdate"
|
||||
| "afterUpdate"
|
||||
| "beforeFullReload"
|
||||
| "beforePrune"
|
||||
| "invalidate"
|
||||
| "error"
|
||||
| "ws:disconnect"
|
||||
| "ws:connect";
|
||||
|
||||
declare global {
|
||||
interface ImportMeta {
|
||||
/**
|
||||
* The event names for the dev server
|
||||
*/
|
||||
type HMREvent = `bun:${HMREventNames}` | (string & {});
|
||||
}
|
||||
|
||||
interface ImportMeta {
|
||||
/**
|
||||
* Hot module replacement APIs. This value is `undefined` in production and
|
||||
* can be used in an `if` statement to check if HMR APIs are available
|
||||
*
|
||||
* ```ts
|
||||
* if (import.meta.hot) {
|
||||
* // HMR APIs are available
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* However, this check is usually not needed as Bun will dead-code-eliminate
|
||||
* calls to all of the HMR APIs in production builds.
|
||||
*
|
||||
* https://bun.sh/docs/bundler/hmr
|
||||
*/
|
||||
hot: {
|
||||
/**
|
||||
* Hot module replacement APIs. This value is `undefined` in production and
|
||||
* can be used in an `if` statement to check if HMR APIs are available
|
||||
* `import.meta.hot.data` maintains state between module instances during
|
||||
* hot replacement, enabling data transfer from previous to new versions.
|
||||
* When `import.meta.hot.data` is written to, Bun will mark this module as
|
||||
* capable of self-accepting (equivalent of calling `accept()`).
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* if (import.meta.hot) {
|
||||
* // HMR APIs are available
|
||||
* }
|
||||
* const root = import.meta.hot.data.root ??= createRoot(elem);
|
||||
* root.render(<App />); // re-use an existing root
|
||||
* ```
|
||||
*
|
||||
* However, this check is usually not needed as Bun will dead-code-eliminate
|
||||
* calls to all of the HMR APIs in production builds.
|
||||
* In production, `data` is inlined to be `{}`. This is handy because Bun
|
||||
* knows it can minify `{}.prop ??= value` into `value` in production.
|
||||
*
|
||||
*
|
||||
* https://bun.sh/docs/bundler/hmr
|
||||
*/
|
||||
hot: {
|
||||
/**
|
||||
* `import.meta.hot.data` maintains state between module instances during
|
||||
* hot replacement, enabling data transfer from previous to new versions.
|
||||
* When `import.meta.hot.data` is written to, Bun will mark this module as
|
||||
* capable of self-accepting (equivalent of calling `accept()`).
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* const root = import.meta.hot.data.root ??= createRoot(elem);
|
||||
* root.render(<App />); // re-use an existing root
|
||||
* ```
|
||||
*
|
||||
* In production, `data` is inlined to be `{}`. This is handy because Bun
|
||||
* knows it can minify `{}.prop ??= value` into `value` in production.
|
||||
*
|
||||
*
|
||||
*/
|
||||
data: any;
|
||||
data: any;
|
||||
|
||||
/**
|
||||
* Indicate that this module can be replaced simply by re-evaluating the
|
||||
* file. After a hot update, importers of this module will be
|
||||
* automatically patched.
|
||||
*
|
||||
* When `import.meta.hot.accept` is not used, the page will reload when
|
||||
* the file updates, and a console message shows which files were checked.
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* import { getCount } from "./foo";
|
||||
*
|
||||
* console.log("count is ", getCount());
|
||||
*
|
||||
* import.meta.hot.accept();
|
||||
* ```
|
||||
*/
|
||||
accept(): void;
|
||||
/**
|
||||
* Indicate that this module can be replaced simply by re-evaluating the
|
||||
* file. After a hot update, importers of this module will be
|
||||
* automatically patched.
|
||||
*
|
||||
* When `import.meta.hot.accept` is not used, the page will reload when
|
||||
* the file updates, and a console message shows which files were checked.
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* import { getCount } from "./foo";
|
||||
*
|
||||
* console.log("count is ", getCount());
|
||||
*
|
||||
* import.meta.hot.accept();
|
||||
* ```
|
||||
*/
|
||||
accept(): void;
|
||||
|
||||
/**
|
||||
* Indicate that this module can be replaced by evaluating the new module,
|
||||
* and then calling the callback with the new module. In this mode, the
|
||||
* importers do not get patched. This is to match Vite, which is unable
|
||||
* to patch their import statements. Prefer using `import.meta.hot.accept()`
|
||||
* without an argument as it usually makes your code easier to understand.
|
||||
*
|
||||
* When `import.meta.hot.accept` is not used, the page will reload when
|
||||
* the file updates, and a console message shows which files were checked.
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* export const count = 0;
|
||||
*
|
||||
* import.meta.hot.accept((newModule) => {
|
||||
* if (newModule) {
|
||||
* // newModule is undefined when SyntaxError happened
|
||||
* console.log('updated: count is now ', newModule.count)
|
||||
* }
|
||||
* });
|
||||
* ```
|
||||
*
|
||||
* In production, calls to this are dead-code-eliminated.
|
||||
*/
|
||||
accept(cb: (newModule: any | undefined) => void): void;
|
||||
/**
|
||||
* Indicate that this module can be replaced by evaluating the new module,
|
||||
* and then calling the callback with the new module. In this mode, the
|
||||
* importers do not get patched. This is to match Vite, which is unable
|
||||
* to patch their import statements. Prefer using `import.meta.hot.accept()`
|
||||
* without an argument as it usually makes your code easier to understand.
|
||||
*
|
||||
* When `import.meta.hot.accept` is not used, the page will reload when
|
||||
* the file updates, and a console message shows which files were checked.
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* export const count = 0;
|
||||
*
|
||||
* import.meta.hot.accept((newModule) => {
|
||||
* if (newModule) {
|
||||
* // newModule is undefined when SyntaxError happened
|
||||
* console.log('updated: count is now ', newModule.count)
|
||||
* }
|
||||
* });
|
||||
* ```
|
||||
*
|
||||
* In production, calls to this are dead-code-eliminated.
|
||||
*/
|
||||
accept(cb: (newModule: any | undefined) => void): void;
|
||||
|
||||
/**
|
||||
* Indicate that a dependency's module can be accepted. When the dependency
|
||||
* is updated, the callback will be called with the new module.
|
||||
*
|
||||
* When `import.meta.hot.accept` is not used, the page will reload when
|
||||
* the file updates, and a console message shows which files were checked.
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* import.meta.hot.accept('./foo', (newModule) => {
|
||||
* if (newModule) {
|
||||
* // newModule is undefined when SyntaxError happened
|
||||
* console.log('updated: count is now ', newModule.count)
|
||||
* }
|
||||
* });
|
||||
* ```
|
||||
*/
|
||||
accept(specifier: string, callback: (newModule: any) => void): void;
|
||||
/**
|
||||
* Indicate that a dependency's module can be accepted. When the dependency
|
||||
* is updated, the callback will be called with the new module.
|
||||
*
|
||||
* When `import.meta.hot.accept` is not used, the page will reload when
|
||||
* the file updates, and a console message shows which files were checked.
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* import.meta.hot.accept('./foo', (newModule) => {
|
||||
* if (newModule) {
|
||||
* // newModule is undefined when SyntaxError happened
|
||||
* console.log('updated: count is now ', newModule.count)
|
||||
* }
|
||||
* });
|
||||
* ```
|
||||
*/
|
||||
accept(specifier: string, callback: (newModule: any) => void): void;
|
||||
|
||||
/**
|
||||
* Indicate that a dependency's module can be accepted. This variant
|
||||
* accepts an array of dependencies, where the callback will receive
|
||||
* the one updated module, and `undefined` for the rest.
|
||||
*
|
||||
* When `import.meta.hot.accept` is not used, the page will reload when
|
||||
* the file updates, and a console message shows which files were checked.
|
||||
*/
|
||||
accept(specifiers: string[], callback: (newModules: (any | undefined)[]) => void): void;
|
||||
/**
|
||||
* Indicate that a dependency's module can be accepted. This variant
|
||||
* accepts an array of dependencies, where the callback will receive
|
||||
* the one updated module, and `undefined` for the rest.
|
||||
*
|
||||
* When `import.meta.hot.accept` is not used, the page will reload when
|
||||
* the file updates, and a console message shows which files were checked.
|
||||
*/
|
||||
accept(specifiers: string[], callback: (newModules: (any | undefined)[]) => void): void;
|
||||
|
||||
/**
|
||||
* Attach an on-dispose callback. This is called:
|
||||
* - Just before the module is replaced with another copy (before the next is loaded)
|
||||
* - After the module is detached (removing all imports to this module)
|
||||
*
|
||||
* This callback is not called on route navigation or when the browser tab closes.
|
||||
*
|
||||
* Returning a promise will delay module replacement until the module is
|
||||
* disposed. All dispose callbacks are called in parallel.
|
||||
*/
|
||||
dispose(cb: (data: any) => void | Promise<void>): void;
|
||||
/**
|
||||
* Attach an on-dispose callback. This is called:
|
||||
* - Just before the module is replaced with another copy (before the next is loaded)
|
||||
* - After the module is detached (removing all imports to this module)
|
||||
*
|
||||
* This callback is not called on route navigation or when the browser tab closes.
|
||||
*
|
||||
* Returning a promise will delay module replacement until the module is
|
||||
* disposed. All dispose callbacks are called in parallel.
|
||||
*/
|
||||
dispose(cb: (data: any) => void | Promise<void>): void;
|
||||
|
||||
/**
|
||||
* No-op
|
||||
* @deprecated
|
||||
*/
|
||||
decline(): void;
|
||||
};
|
||||
}
|
||||
/**
|
||||
* No-op
|
||||
* @deprecated
|
||||
*/
|
||||
decline(): void;
|
||||
|
||||
// NOTE TO CONTRIBUTORS ////////////////////////////////////////
|
||||
// Callback is currently never called for `.prune()` //
|
||||
// so the types are commented out until we support it. //
|
||||
////////////////////////////////////////////////////////////////
|
||||
// /**
|
||||
// * Attach a callback that is called when the module is removed from the module graph.
|
||||
// *
|
||||
// * This can be used to clean up resources that were created when the module was loaded.
|
||||
// * Unlike `import.meta.hot.dispose()`, this pairs much better with `accept` and `data` to manage stateful resources.
|
||||
// *
|
||||
// * @example
|
||||
// * ```ts
|
||||
// * export const ws = (import.meta.hot.data.ws ??= new WebSocket(location.origin));
|
||||
// *
|
||||
// * import.meta.hot.prune(() => {
|
||||
// * ws.close();
|
||||
// * });
|
||||
// * ```
|
||||
// */
|
||||
// prune(callback: () => void): void;
|
||||
|
||||
/**
|
||||
* Listen for an event from the dev server
|
||||
*
|
||||
* For compatibility with Vite, event names are also available via vite:* prefix instead of bun:*.
|
||||
*
|
||||
* https://bun.sh/docs/bundler/hmr#import-meta-hot-on-and-off
|
||||
* @param event The event to listen to
|
||||
* @param callback The callback to call when the event is emitted
|
||||
*/
|
||||
on(event: Bun.HMREvent, callback: () => void): void;
|
||||
|
||||
/**
|
||||
* Stop listening for an event from the dev server
|
||||
*
|
||||
* For compatibility with Vite, event names are also available via vite:* prefix instead of bun:*.
|
||||
*
|
||||
* https://bun.sh/docs/bundler/hmr#import-meta-hot-on-and-off
|
||||
* @param event The event to stop listening to
|
||||
* @param callback The callback to stop listening to
|
||||
*/
|
||||
off(event: Bun.HMREvent, callback: () => void): void;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -19,13 +19,13 @@ declare module "*/bun.lock" {
|
||||
}
|
||||
|
||||
declare module "*.html" {
|
||||
// In Bun v1.2, we might change this to Bun.HTMLBundle
|
||||
// In Bun v1.2, this might change to Bun.HTMLBundle
|
||||
var contents: any;
|
||||
export = contents;
|
||||
}
|
||||
|
||||
declare module "*.svg" {
|
||||
// Bun 1.2.3 added support for frontend dev server
|
||||
var contents: `${string}.svg`;
|
||||
export = contents;
|
||||
var content: `${string}.svg`;
|
||||
export = content;
|
||||
}
|
||||
223
packages/bun-types/fetch.d.ts
vendored
223
packages/bun-types/fetch.d.ts
vendored
@@ -1,161 +1,72 @@
|
||||
interface Headers {
|
||||
/**
|
||||
* Convert {@link Headers} to a plain JavaScript object.
|
||||
*
|
||||
* About 10x faster than `Object.fromEntries(headers.entries())`
|
||||
*
|
||||
* Called when you run `JSON.stringify(headers)`
|
||||
*
|
||||
* Does not preserve insertion order. Well-known header names are lowercased. Other header names are left as-is.
|
||||
*/
|
||||
toJSON(): Record<string, string>;
|
||||
/**
|
||||
* Get the total number of headers
|
||||
*/
|
||||
readonly count: number;
|
||||
/**
|
||||
* Get all headers matching the name
|
||||
*
|
||||
* Only supports `"Set-Cookie"`. All other headers are empty arrays.
|
||||
*
|
||||
* @param name - The header name to get
|
||||
*
|
||||
* @returns An array of header values
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* const headers = new Headers();
|
||||
* headers.append("Set-Cookie", "foo=bar");
|
||||
* headers.append("Set-Cookie", "baz=qux");
|
||||
* headers.getAll("Set-Cookie"); // ["foo=bar", "baz=qux"]
|
||||
* ```
|
||||
*/
|
||||
getAll(name: "set-cookie" | "Set-Cookie"): string[];
|
||||
}
|
||||
/*
|
||||
|
||||
var Headers: {
|
||||
prototype: Headers;
|
||||
new (init?: Bun.HeadersInit): Headers;
|
||||
};
|
||||
This file does not declare any global types.
|
||||
|
||||
interface Request {
|
||||
headers: Headers;
|
||||
}
|
||||
That should only happen in [./globals.d.ts](./globals.d.ts)
|
||||
so that our documentation generator can pick it up, as it
|
||||
expects all globals to be declared in one file.
|
||||
|
||||
var Request: {
|
||||
prototype: Request;
|
||||
new (requestInfo: string, requestInit?: RequestInit): Request;
|
||||
new (requestInfo: RequestInit & { url: string }): Request;
|
||||
new (requestInfo: Request, requestInit?: RequestInit): Request;
|
||||
};
|
||||
|
||||
var Response: {
|
||||
new (body?: Bun.BodyInit | null | undefined, init?: Bun.ResponseInit | undefined): Response;
|
||||
/**
|
||||
* Create a new {@link Response} with a JSON body
|
||||
*
|
||||
* @param body - The body of the response
|
||||
* @param options - options to pass to the response
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* ```ts
|
||||
* const response = Response.json({hi: "there"});
|
||||
* console.assert(
|
||||
* await response.text(),
|
||||
* `{"hi":"there"}`
|
||||
* );
|
||||
* ```
|
||||
* -------
|
||||
*
|
||||
* This is syntactic sugar for:
|
||||
* ```js
|
||||
* new Response(JSON.stringify(body), {headers: { "Content-Type": "application/json" }})
|
||||
* ```
|
||||
* @link https://github.com/whatwg/fetch/issues/1389
|
||||
*/
|
||||
json(body?: any, options?: Bun.ResponseInit | number): Response;
|
||||
|
||||
/**
|
||||
* Create a new {@link Response} that redirects to url
|
||||
*
|
||||
* @param url - the URL to redirect to
|
||||
* @param status - the HTTP status code to use for the redirect
|
||||
*/
|
||||
// tslint:disable-next-line:unified-signatures
|
||||
redirect(url: string, status?: number): Response;
|
||||
|
||||
/**
|
||||
* Create a new {@link Response} that redirects to url
|
||||
*
|
||||
* @param url - the URL to redirect to
|
||||
* @param options - options to pass to the response
|
||||
*/
|
||||
// tslint:disable-next-line:unified-signatures
|
||||
redirect(url: string, options?: Bun.ResponseInit): Response;
|
||||
|
||||
/**
|
||||
* Create a new {@link Response} that has a network error
|
||||
*/
|
||||
error(): Response;
|
||||
};
|
||||
|
||||
type _BunTLSOptions = import("bun").TLSOptions;
|
||||
interface BunFetchRequestInitTLS extends _BunTLSOptions {
|
||||
/**
|
||||
* Custom function to check the server identity
|
||||
* @param hostname - The hostname of the server
|
||||
* @param cert - The certificate of the server
|
||||
* @returns An error if the server is unauthorized, otherwise undefined
|
||||
*/
|
||||
checkServerIdentity?: NonNullable<import("node:tls").ConnectionOptions["checkServerIdentity"]>;
|
||||
}
|
||||
|
||||
/**
|
||||
* BunFetchRequestInit represents additional options that Bun supports in `fetch()` only.
|
||||
*
|
||||
* Bun extends the `fetch` API with some additional options, except
|
||||
* this interface is not quite a `RequestInit`, because they won't work
|
||||
* if passed to `new Request()`. This is why it's a separate type.
|
||||
*/
|
||||
interface BunFetchRequestInit extends RequestInit {
|
||||
/**
|
||||
* Override the default TLS options
|
||||
*/
|
||||
tls?: BunFetchRequestInitTLS;
|
||||
|
||||
declare module "bun" {
|
||||
type HeadersInit = string[][] | Record<string, string | ReadonlyArray<string>> | Headers;
|
||||
type BodyInit = ReadableStream | Bun.XMLHttpRequestBodyInit | URLSearchParams | AsyncGenerator<Uint8Array>;
|
||||
|
||||
namespace __internal {
|
||||
type LibOrFallbackHeaders = LibDomIsLoaded extends true ? {} : import("undici-types").Headers;
|
||||
type LibOrFallbackRequest = LibDomIsLoaded extends true ? {} : import("undici-types").Request;
|
||||
type LibOrFallbackResponse = LibDomIsLoaded extends true ? {} : import("undici-types").Response;
|
||||
type LibOrFallbackResponseInit = LibDomIsLoaded extends true ? {} : import("undici-types").ResponseInit;
|
||||
type LibOrFallbackRequestInit = LibDomIsLoaded extends true
|
||||
? {}
|
||||
: Omit<import("undici-types").RequestInit, "body" | "headers"> & {
|
||||
body?: Bun.BodyInit | null | undefined;
|
||||
headers?: Bun.HeadersInit;
|
||||
};
|
||||
|
||||
interface BunHeadersOverride extends LibOrFallbackHeaders {
|
||||
/**
|
||||
* Convert {@link Headers} to a plain JavaScript object.
|
||||
*
|
||||
* About 10x faster than `Object.fromEntries(headers.entries())`
|
||||
*
|
||||
* Called when you run `JSON.stringify(headers)`
|
||||
*
|
||||
* Does not preserve insertion order. Well-known header names are lowercased. Other header names are left as-is.
|
||||
*/
|
||||
toJSON(): Record<string, string>;
|
||||
|
||||
/**
|
||||
* Get the total number of headers
|
||||
*/
|
||||
readonly count: number;
|
||||
|
||||
/**
|
||||
* Get all headers matching the name
|
||||
*
|
||||
* Only supports `"Set-Cookie"`. All other headers are empty arrays.
|
||||
*
|
||||
* @param name - The header name to get
|
||||
*
|
||||
* @returns An array of header values
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* const headers = new Headers();
|
||||
* headers.append("Set-Cookie", "foo=bar");
|
||||
* headers.append("Set-Cookie", "baz=qux");
|
||||
* headers.getAll("Set-Cookie"); // ["foo=bar", "baz=qux"]
|
||||
* ```
|
||||
*/
|
||||
getAll(name: "set-cookie" | "Set-Cookie"): string[];
|
||||
}
|
||||
|
||||
interface BunRequestOverride extends LibOrFallbackRequest {
|
||||
headers: BunHeadersOverride;
|
||||
}
|
||||
|
||||
interface BunResponseOverride extends LibOrFallbackResponse {
|
||||
headers: BunHeadersOverride;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var fetch: {
|
||||
/**
|
||||
* Send a HTTP(s) request
|
||||
*
|
||||
* @param request Request object
|
||||
* @param init A structured value that contains settings for the fetch() request.
|
||||
*
|
||||
* @returns A promise that resolves to {@link Response} object.
|
||||
*/
|
||||
(request: Request, init?: BunFetchRequestInit): Promise<Response>;
|
||||
|
||||
/**
|
||||
* Send a HTTP(s) request
|
||||
*
|
||||
* @param url URL string
|
||||
* @param init A structured value that contains settings for the fetch() request.
|
||||
*
|
||||
* @returns A promise that resolves to {@link Response} object.
|
||||
*/
|
||||
(url: string | URL | Request, init?: BunFetchRequestInit): Promise<Response>;
|
||||
|
||||
(input: string | URL | globalThis.Request, init?: BunFetchRequestInit): Promise<Response>;
|
||||
|
||||
/**
|
||||
* Start the DNS resolution, TCP connection, and TLS handshake for a request
|
||||
* before the request is actually sent.
|
||||
*
|
||||
* This can reduce the latency of a request when you know there's some
|
||||
* long-running task that will delay the request starting.
|
||||
*
|
||||
* This is a bun-specific API and is not part of the Fetch API specification.
|
||||
*/
|
||||
preconnect(url: string | URL): void;
|
||||
};
|
||||
|
||||
27
packages/bun-types/ffi.d.ts
vendored
27
packages/bun-types/ffi.d.ts
vendored
@@ -13,6 +13,8 @@
|
||||
* that convert JavaScript types to C types and back. Internally,
|
||||
* bun uses [tinycc](https://github.com/TinyCC/tinycc), so a big thanks
|
||||
* goes to Fabrice Bellard and TinyCC maintainers for making this possible.
|
||||
*
|
||||
* @category FFI
|
||||
*/
|
||||
declare module "bun:ffi" {
|
||||
enum FFIType {
|
||||
@@ -543,14 +545,6 @@ declare module "bun:ffi" {
|
||||
|
||||
type Symbols = Readonly<Record<string, FFIFunction>>;
|
||||
|
||||
// /**
|
||||
// * Compile a callback function
|
||||
// *
|
||||
// * Returns a function pointer
|
||||
// *
|
||||
// */
|
||||
// export function callback(ffi: FFIFunction, cb: Function): number;
|
||||
|
||||
interface Library<Fns extends Symbols> {
|
||||
symbols: ConvertFns<Fns>;
|
||||
|
||||
@@ -608,6 +602,8 @@ declare module "bun:ffi" {
|
||||
* that convert JavaScript types to C types and back. Internally,
|
||||
* bun uses [tinycc](https://github.com/TinyCC/tinycc), so a big thanks
|
||||
* goes to Fabrice Bellard and TinyCC maintainers for making this possible.
|
||||
*
|
||||
* @category FFI
|
||||
*/
|
||||
function dlopen<Fns extends Record<string, FFIFunction>>(
|
||||
name: string | import("bun").BunFile | URL,
|
||||
@@ -626,9 +622,9 @@ declare module "bun:ffi" {
|
||||
* JavaScript:
|
||||
* ```js
|
||||
* import { cc } from "bun:ffi";
|
||||
* import hello from "./hello.c" with {type: "file"};
|
||||
* import source from "./hello.c" with {type: "file"};
|
||||
* const {symbols: {hello}} = cc({
|
||||
* source: hello,
|
||||
* source,
|
||||
* symbols: {
|
||||
* hello: {
|
||||
* returns: "cstring",
|
||||
@@ -681,8 +677,9 @@ declare module "bun:ffi" {
|
||||
* @example
|
||||
* ```js
|
||||
* import { cc } from "bun:ffi";
|
||||
* import source from "./hello.c" with {type: "file"};
|
||||
* const {symbols: {hello}} = cc({
|
||||
* source: hello,
|
||||
* source,
|
||||
* define: {
|
||||
* "NDEBUG": "1",
|
||||
* },
|
||||
@@ -707,8 +704,9 @@ declare module "bun:ffi" {
|
||||
* @example
|
||||
* ```js
|
||||
* import { cc } from "bun:ffi";
|
||||
* import source from "./hello.c" with {type: "file"};
|
||||
* const {symbols: {hello}} = cc({
|
||||
* source: hello,
|
||||
* source,
|
||||
* flags: ["-framework CoreFoundation", "-framework Security"],
|
||||
* symbols: {
|
||||
* hello: {
|
||||
@@ -1024,6 +1022,8 @@ declare module "bun:ffi" {
|
||||
* // Do something with rawPtr
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @category FFI
|
||||
*/
|
||||
function ptr(view: NodeJS.TypedArray | ArrayBufferLike | DataView, byteOffset?: number): Pointer;
|
||||
|
||||
@@ -1048,8 +1048,9 @@ declare module "bun:ffi" {
|
||||
* 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!
|
||||
*
|
||||
* @category FFI
|
||||
*/
|
||||
|
||||
class CString extends String {
|
||||
/**
|
||||
* Get a string from a UTF-8 encoded C string
|
||||
|
||||
3361
packages/bun-types/globals.d.ts
vendored
3361
packages/bun-types/globals.d.ts
vendored
File diff suppressed because it is too large
Load Diff
2
packages/bun-types/html-rewriter.d.ts
vendored
2
packages/bun-types/html-rewriter.d.ts
vendored
@@ -147,6 +147,8 @@ declare namespace HTMLRewriterTypes {
|
||||
* });
|
||||
* rewriter.transform(await fetch("https://remix.run"));
|
||||
* ```
|
||||
*
|
||||
* @category HTML Manipulation
|
||||
*/
|
||||
declare class HTMLRewriter {
|
||||
constructor();
|
||||
|
||||
24
packages/bun-types/index.d.ts
vendored
24
packages/bun-types/index.d.ts
vendored
@@ -1,24 +1,26 @@
|
||||
// Project: https://github.com/oven-sh/bun
|
||||
// Definitions by: Jarred Sumner <https://github.com/Jarred-Sumner>
|
||||
// Definitions by: Bun Contributors <https://github.com/oven-sh/bun/graphs/contributors>
|
||||
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
|
||||
|
||||
/// <reference lib="esnext" />
|
||||
/// <reference types="ws" />
|
||||
/// <reference types="node" />
|
||||
|
||||
// contributors: uncomment this to detect conflicts with lib.dom.d.ts
|
||||
// /// <reference lib="dom" />
|
||||
|
||||
/// <reference path="./bun.d.ts" />
|
||||
/// <reference path="./globals.d.ts" />
|
||||
/// <reference path="./s3.d.ts" />
|
||||
/// <reference path="./fetch.d.ts" />
|
||||
/// <reference path="./overrides.d.ts" />
|
||||
/// <reference path="./bun.d.ts" />
|
||||
/// <reference path="./extensions.d.ts" />
|
||||
/// <reference path="./devserver.d.ts" />
|
||||
/// <reference path="./ffi.d.ts" />
|
||||
/// <reference path="./test.d.ts" />
|
||||
/// <reference path="./html-rewriter.d.ts" />
|
||||
/// <reference path="./jsc.d.ts" />
|
||||
/// <reference path="./sqlite.d.ts" />
|
||||
/// <reference path="./test.d.ts" />
|
||||
/// <reference path="./wasm.d.ts" />
|
||||
/// <reference path="./overrides.d.ts" />
|
||||
/// <reference path="./deprecated.d.ts" />
|
||||
/// <reference path="./ambient.d.ts" />
|
||||
/// <reference path="./devserver.d.ts" />
|
||||
|
||||
/// <reference path="./bun.ns.d.ts" />
|
||||
|
||||
// @ts-ignore Must disable this so it doesn't conflict with the DOM onmessage type, but still
|
||||
// allows us to declare our own globals that Node's types can "see" and not conflict with
|
||||
declare var onmessage: never;
|
||||
|
||||
71
packages/bun-types/overrides.d.ts
vendored
71
packages/bun-types/overrides.d.ts
vendored
@@ -1,18 +1,71 @@
|
||||
export {};
|
||||
|
||||
import type { BunFile, Env, PathLike } from "bun";
|
||||
|
||||
declare global {
|
||||
namespace NodeJS {
|
||||
interface Process {
|
||||
readonly version: string;
|
||||
browser: boolean;
|
||||
|
||||
/**
|
||||
* Whether you are using Bun
|
||||
*/
|
||||
isBun: true;
|
||||
|
||||
/**
|
||||
* The current git sha of Bun
|
||||
*/
|
||||
revision: string;
|
||||
|
||||
reallyExit(code?: number): never;
|
||||
dlopen(module: { exports: any }, filename: string, flags?: number): void;
|
||||
_exiting: boolean;
|
||||
noDeprecation: boolean;
|
||||
|
||||
binding(m: "constants"): {
|
||||
os: typeof import("node:os").constants;
|
||||
fs: typeof import("node:fs").constants;
|
||||
crypto: typeof import("node:crypto").constants;
|
||||
zlib: typeof import("node:zlib").constants;
|
||||
trace: {
|
||||
TRACE_EVENT_PHASE_BEGIN: number;
|
||||
TRACE_EVENT_PHASE_END: number;
|
||||
TRACE_EVENT_PHASE_COMPLETE: number;
|
||||
TRACE_EVENT_PHASE_INSTANT: number;
|
||||
TRACE_EVENT_PHASE_ASYNC_BEGIN: number;
|
||||
TRACE_EVENT_PHASE_ASYNC_STEP_INTO: number;
|
||||
TRACE_EVENT_PHASE_ASYNC_STEP_PAST: number;
|
||||
TRACE_EVENT_PHASE_ASYNC_END: number;
|
||||
TRACE_EVENT_PHASE_NESTABLE_ASYNC_BEGIN: number;
|
||||
TRACE_EVENT_PHASE_NESTABLE_ASYNC_END: number;
|
||||
TRACE_EVENT_PHASE_NESTABLE_ASYNC_INSTANT: number;
|
||||
TRACE_EVENT_PHASE_FLOW_BEGIN: number;
|
||||
TRACE_EVENT_PHASE_FLOW_STEP: number;
|
||||
TRACE_EVENT_PHASE_FLOW_END: number;
|
||||
TRACE_EVENT_PHASE_METADATA: number;
|
||||
TRACE_EVENT_PHASE_COUNTER: number;
|
||||
TRACE_EVENT_PHASE_SAMPLE: number;
|
||||
TRACE_EVENT_PHASE_CREATE_OBJECT: number;
|
||||
TRACE_EVENT_PHASE_SNAPSHOT_OBJECT: number;
|
||||
TRACE_EVENT_PHASE_DELETE_OBJECT: number;
|
||||
TRACE_EVENT_PHASE_MEMORY_DUMP: number;
|
||||
TRACE_EVENT_PHASE_MARK: number;
|
||||
TRACE_EVENT_PHASE_CLOCK_SYNC: number;
|
||||
TRACE_EVENT_PHASE_ENTER_CONTEXT: number;
|
||||
TRACE_EVENT_PHASE_LEAVE_CONTEXT: number;
|
||||
TRACE_EVENT_PHASE_LINK_IDS: number;
|
||||
};
|
||||
};
|
||||
binding(m: string): object;
|
||||
}
|
||||
|
||||
interface ProcessVersions extends Dict<string> {
|
||||
bun: string;
|
||||
}
|
||||
interface ProcessEnv extends Env {}
|
||||
}
|
||||
}
|
||||
|
||||
declare module "fs/promises" {
|
||||
function exists(path: PathLike): Promise<boolean>;
|
||||
function exists(path: Bun.PathLike): Promise<boolean>;
|
||||
}
|
||||
|
||||
declare module "tls" {
|
||||
@@ -22,7 +75,7 @@ declare module "tls" {
|
||||
* the well-known CAs curated by Mozilla. Mozilla's CAs are completely
|
||||
* replaced when CAs are explicitly specified using this option.
|
||||
*/
|
||||
ca?: string | Buffer | NodeJS.TypedArray | BunFile | Array<string | Buffer | BunFile> | undefined;
|
||||
ca?: string | Buffer | NodeJS.TypedArray | Bun.BunFile | Array<string | Buffer | Bun.BunFile> | undefined;
|
||||
/**
|
||||
* Cert chains in PEM format. One cert chain should be provided per
|
||||
* private key. Each cert chain should consist of the PEM formatted
|
||||
@@ -38,8 +91,8 @@ declare module "tls" {
|
||||
| string
|
||||
| Buffer
|
||||
| NodeJS.TypedArray
|
||||
| BunFile
|
||||
| Array<string | Buffer | NodeJS.TypedArray | BunFile>
|
||||
| Bun.BunFile
|
||||
| Array<string | Buffer | NodeJS.TypedArray | Bun.BunFile>
|
||||
| undefined;
|
||||
/**
|
||||
* Private keys in PEM format. PEM allows the option of private keys
|
||||
@@ -54,9 +107,9 @@ declare module "tls" {
|
||||
key?:
|
||||
| string
|
||||
| Buffer
|
||||
| BunFile
|
||||
| Bun.BunFile
|
||||
| NodeJS.TypedArray
|
||||
| Array<string | Buffer | BunFile | NodeJS.TypedArray | KeyObject>
|
||||
| Array<string | Buffer | Bun.BunFile | NodeJS.TypedArray | KeyObject>
|
||||
| undefined;
|
||||
}
|
||||
|
||||
|
||||
@@ -9,14 +9,14 @@
|
||||
"directory": "packages/bun-types"
|
||||
},
|
||||
"files": [
|
||||
"*.d.ts",
|
||||
"./*.d.ts",
|
||||
"docs/**/*.md",
|
||||
"docs/*.md"
|
||||
],
|
||||
"homepage": "https://bun.sh",
|
||||
"dependencies": {
|
||||
"@types/node": "*",
|
||||
"@types/ws": "~8.5.10"
|
||||
"@types/ws": "*"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@biomejs/biome": "^1.5.3",
|
||||
@@ -27,7 +27,7 @@
|
||||
"scripts": {
|
||||
"prebuild": "echo $(pwd)",
|
||||
"copy-docs": "rm -rf docs && cp -rL ../../docs/ ./docs && find ./docs -type f -name '*.md' -exec sed -i 's/\\$BUN_LATEST_VERSION/'\"${BUN_VERSION#bun-v}\"'/g' {} +",
|
||||
"build": "bun run copy-docs && bun scripts/build.ts && bun run fmt",
|
||||
"build": "bun run copy-docs && bun scripts/build.ts",
|
||||
"test": "tsc",
|
||||
"fmt": "echo $(which biome) && biome format --write ."
|
||||
},
|
||||
|
||||
831
packages/bun-types/s3.d.ts
vendored
Normal file
831
packages/bun-types/s3.d.ts
vendored
Normal file
@@ -0,0 +1,831 @@
|
||||
declare module "bun" {
|
||||
/**
|
||||
* Fast incremental writer for files and pipes.
|
||||
*
|
||||
* This uses the same interface as {@link ArrayBufferSink}, but writes to a file or pipe.
|
||||
*/
|
||||
interface FileSink {
|
||||
/**
|
||||
* Write a chunk of data to the file.
|
||||
*
|
||||
* If the file descriptor is not writable yet, the data is buffered.
|
||||
*/
|
||||
write(chunk: string | ArrayBufferView | ArrayBuffer | SharedArrayBuffer): number;
|
||||
/**
|
||||
* Flush the internal buffer, committing the data to disk or the pipe.
|
||||
*/
|
||||
flush(): number | Promise<number>;
|
||||
/**
|
||||
* Close the file descriptor. This also flushes the internal buffer.
|
||||
*/
|
||||
end(error?: Error): number | Promise<number>;
|
||||
|
||||
start(options?: {
|
||||
/**
|
||||
* Preallocate an internal buffer of this size
|
||||
* This can significantly improve performance when the chunk size is small
|
||||
*/
|
||||
highWaterMark?: number;
|
||||
}): void;
|
||||
|
||||
/**
|
||||
* For FIFOs & pipes, this lets you decide whether Bun's process should
|
||||
* remain alive until the pipe is closed.
|
||||
*
|
||||
* By default, it is automatically managed. While the stream is open, the
|
||||
* process remains alive and once the other end hangs up or the stream
|
||||
* closes, the process exits.
|
||||
*
|
||||
* If you previously called {@link unref}, you can call this again to re-enable automatic management.
|
||||
*
|
||||
* Internally, it will reference count the number of times this is called. By default, that number is 1
|
||||
*
|
||||
* If the file is not a FIFO or pipe, {@link ref} and {@link unref} do
|
||||
* nothing. If the pipe is already closed, this does nothing.
|
||||
*/
|
||||
ref(): void;
|
||||
|
||||
/**
|
||||
* For FIFOs & pipes, this lets you decide whether Bun's process should
|
||||
* remain alive until the pipe is closed.
|
||||
*
|
||||
* If you want to allow Bun's process to terminate while the stream is open,
|
||||
* call this.
|
||||
*
|
||||
* If the file is not a FIFO or pipe, {@link ref} and {@link unref} do
|
||||
* nothing. If the pipe is already closed, this does nothing.
|
||||
*/
|
||||
unref(): void;
|
||||
}
|
||||
|
||||
interface NetworkSink extends FileSink {
|
||||
/**
|
||||
* Write a chunk of data to the network.
|
||||
*
|
||||
* If the network is not writable yet, the data is buffered.
|
||||
*/
|
||||
write(chunk: string | ArrayBufferView | ArrayBuffer | SharedArrayBuffer): number;
|
||||
/**
|
||||
* Flush the internal buffer, committing the data to the network.
|
||||
*/
|
||||
flush(): number | Promise<number>;
|
||||
/**
|
||||
* Finish the upload. This also flushes the internal buffer.
|
||||
*/
|
||||
end(error?: Error): number | Promise<number>;
|
||||
|
||||
/**
|
||||
* Get the stat of the file.
|
||||
*/
|
||||
stat(): Promise<import("node:fs").Stats>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configuration options for S3 operations
|
||||
*/
|
||||
interface S3Options extends BlobPropertyBag {
|
||||
/**
|
||||
* The Access Control List (ACL) policy for the file.
|
||||
* Controls who can access the file and what permissions they have.
|
||||
*
|
||||
* @example
|
||||
* // Setting public read access
|
||||
* const file = s3("public-file.txt", {
|
||||
* acl: "public-read",
|
||||
* bucket: "my-bucket"
|
||||
* });
|
||||
*
|
||||
* @example
|
||||
* // Using with presigned URLs
|
||||
* const url = file.presign({
|
||||
* acl: "public-read",
|
||||
* expiresIn: 3600
|
||||
* });
|
||||
*/
|
||||
acl?:
|
||||
| "private"
|
||||
| "public-read"
|
||||
| "public-read-write"
|
||||
| "aws-exec-read"
|
||||
| "authenticated-read"
|
||||
| "bucket-owner-read"
|
||||
| "bucket-owner-full-control"
|
||||
| "log-delivery-write";
|
||||
|
||||
/**
|
||||
* The S3 bucket name. Can be set via `S3_BUCKET` or `AWS_BUCKET` environment variables.
|
||||
*
|
||||
* @example
|
||||
* // Using explicit bucket
|
||||
* const file = s3("my-file.txt", { bucket: "my-bucket" });
|
||||
*
|
||||
* @example
|
||||
* // Using environment variables
|
||||
* // With S3_BUCKET=my-bucket in .env
|
||||
* const file = s3("my-file.txt");
|
||||
*/
|
||||
bucket?: string;
|
||||
|
||||
/**
|
||||
* The AWS region. Can be set via `S3_REGION` or `AWS_REGION` environment variables.
|
||||
*
|
||||
* @example
|
||||
* const file = s3("my-file.txt", {
|
||||
* bucket: "my-bucket",
|
||||
* region: "us-west-2"
|
||||
* });
|
||||
*/
|
||||
region?: string;
|
||||
|
||||
/**
|
||||
* The access key ID for authentication.
|
||||
* Can be set via `S3_ACCESS_KEY_ID` or `AWS_ACCESS_KEY_ID` environment variables.
|
||||
*/
|
||||
accessKeyId?: string;
|
||||
|
||||
/**
|
||||
* The secret access key for authentication.
|
||||
* Can be set via `S3_SECRET_ACCESS_KEY` or `AWS_SECRET_ACCESS_KEY` environment variables.
|
||||
*/
|
||||
secretAccessKey?: string;
|
||||
|
||||
/**
|
||||
* Optional session token for temporary credentials.
|
||||
* Can be set via `S3_SESSION_TOKEN` or `AWS_SESSION_TOKEN` environment variables.
|
||||
*
|
||||
* @example
|
||||
* // Using temporary credentials
|
||||
* const file = s3("my-file.txt", {
|
||||
* accessKeyId: tempAccessKey,
|
||||
* secretAccessKey: tempSecretKey,
|
||||
* sessionToken: tempSessionToken
|
||||
* });
|
||||
*/
|
||||
sessionToken?: string;
|
||||
|
||||
/**
|
||||
* The S3-compatible service endpoint URL.
|
||||
* Can be set via `S3_ENDPOINT` or `AWS_ENDPOINT` environment variables.
|
||||
*
|
||||
* @example
|
||||
* // AWS S3
|
||||
* const file = s3("my-file.txt", {
|
||||
* endpoint: "https://s3.us-east-1.amazonaws.com"
|
||||
* });
|
||||
*
|
||||
* @example
|
||||
* // Cloudflare R2
|
||||
* const file = s3("my-file.txt", {
|
||||
* endpoint: "https://<account-id>.r2.cloudflarestorage.com"
|
||||
* });
|
||||
*
|
||||
* @example
|
||||
* // DigitalOcean Spaces
|
||||
* const file = s3("my-file.txt", {
|
||||
* endpoint: "https://<region>.digitaloceanspaces.com"
|
||||
* });
|
||||
*
|
||||
* @example
|
||||
* // MinIO (local development)
|
||||
* const file = s3("my-file.txt", {
|
||||
* endpoint: "http://localhost:9000"
|
||||
* });
|
||||
*/
|
||||
endpoint?: string;
|
||||
|
||||
/**
|
||||
* Use virtual hosted style endpoint. default to false, when true if `endpoint` is informed it will ignore the `bucket`
|
||||
*
|
||||
* @example
|
||||
* // Using virtual hosted style
|
||||
* const file = s3("my-file.txt", {
|
||||
* virtualHostedStyle: true,
|
||||
* endpoint: "https://my-bucket.s3.us-east-1.amazonaws.com"
|
||||
* });
|
||||
*/
|
||||
virtualHostedStyle?: boolean;
|
||||
|
||||
/**
|
||||
* The size of each part in multipart uploads (in bytes).
|
||||
* - Minimum: 5 MiB
|
||||
* - Maximum: 5120 MiB
|
||||
* - Default: 5 MiB
|
||||
*
|
||||
* @example
|
||||
* // Configuring multipart uploads
|
||||
* const file = s3("large-file.dat", {
|
||||
* partSize: 10 * 1024 * 1024, // 10 MiB parts
|
||||
* queueSize: 4 // Upload 4 parts in parallel
|
||||
* });
|
||||
*
|
||||
* const writer = file.writer();
|
||||
* // ... write large file in chunks
|
||||
*/
|
||||
partSize?: number;
|
||||
|
||||
/**
|
||||
* Number of parts to upload in parallel for multipart uploads.
|
||||
* - Default: 5
|
||||
* - Maximum: 255
|
||||
*
|
||||
* Increasing this value can improve upload speeds for large files
|
||||
* but will use more memory.
|
||||
*/
|
||||
queueSize?: number;
|
||||
|
||||
/**
|
||||
* Number of retry attempts for failed uploads.
|
||||
* - Default: 3
|
||||
* - Maximum: 255
|
||||
*
|
||||
* @example
|
||||
* // Setting retry attempts
|
||||
* const file = s3("my-file.txt", {
|
||||
* retry: 5 // Retry failed uploads up to 5 times
|
||||
* });
|
||||
*/
|
||||
retry?: number;
|
||||
|
||||
/**
|
||||
* The Content-Type of the file.
|
||||
* Automatically set based on file extension when possible.
|
||||
*
|
||||
* @example
|
||||
* // Setting explicit content type
|
||||
* const file = s3("data.bin", {
|
||||
* type: "application/octet-stream"
|
||||
* });
|
||||
*/
|
||||
type?: string;
|
||||
|
||||
/**
|
||||
* By default, Amazon S3 uses the STANDARD Storage Class to store newly created objects.
|
||||
*
|
||||
* @example
|
||||
* // Setting explicit Storage class
|
||||
* const file = s3("my-file.json", {
|
||||
* storageClass: "STANDARD_IA"
|
||||
* });
|
||||
*/
|
||||
storageClass?:
|
||||
| "STANDARD"
|
||||
| "DEEP_ARCHIVE"
|
||||
| "EXPRESS_ONEZONE"
|
||||
| "GLACIER"
|
||||
| "GLACIER_IR"
|
||||
| "INTELLIGENT_TIERING"
|
||||
| "ONEZONE_IA"
|
||||
| "OUTPOSTS"
|
||||
| "REDUCED_REDUNDANCY"
|
||||
| "SNOW"
|
||||
| "STANDARD_IA";
|
||||
|
||||
/**
|
||||
* @deprecated The size of the internal buffer in bytes. Defaults to 5 MiB. use `partSize` and `queueSize` instead.
|
||||
*/
|
||||
highWaterMark?: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Options for generating presigned URLs
|
||||
*/
|
||||
interface S3FilePresignOptions extends S3Options {
|
||||
/**
|
||||
* Number of seconds until the presigned URL expires.
|
||||
* - Default: 86400 (1 day)
|
||||
*
|
||||
* @example
|
||||
* // Short-lived URL
|
||||
* const url = file.presign({
|
||||
* expiresIn: 3600 // 1 hour
|
||||
* });
|
||||
*
|
||||
* @example
|
||||
* // Long-lived public URL
|
||||
* const url = file.presign({
|
||||
* expiresIn: 7 * 24 * 60 * 60, // 7 days
|
||||
* acl: "public-read"
|
||||
* });
|
||||
*/
|
||||
expiresIn?: number;
|
||||
|
||||
/**
|
||||
* The HTTP method allowed for the presigned URL.
|
||||
*
|
||||
* @example
|
||||
* // GET URL for downloads
|
||||
* const downloadUrl = file.presign({
|
||||
* method: "GET",
|
||||
* expiresIn: 3600
|
||||
* });
|
||||
*
|
||||
* @example
|
||||
* // PUT URL for uploads
|
||||
* const uploadUrl = file.presign({
|
||||
* method: "PUT",
|
||||
* expiresIn: 3600,
|
||||
* type: "application/json"
|
||||
* });
|
||||
*/
|
||||
method?: "GET" | "POST" | "PUT" | "DELETE" | "HEAD";
|
||||
}
|
||||
|
||||
interface S3Stats {
|
||||
size: number;
|
||||
lastModified: Date;
|
||||
etag: string;
|
||||
type: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a file in an S3-compatible storage service.
|
||||
* Extends the Blob interface for compatibility with web APIs.
|
||||
*
|
||||
* @category Cloud Storage
|
||||
*/
|
||||
interface S3File extends Blob {
|
||||
/**
|
||||
* The size of the file in bytes.
|
||||
* This is a Promise because it requires a network request to determine the size.
|
||||
*
|
||||
* @example
|
||||
* // Getting file size
|
||||
* const size = await file.size;
|
||||
* console.log(`File size: ${size} bytes`);
|
||||
*
|
||||
* @example
|
||||
* // Check if file is larger than 1MB
|
||||
* if (await file.size > 1024 * 1024) {
|
||||
* console.log("Large file detected");
|
||||
* }
|
||||
*/
|
||||
/**
|
||||
* TODO: figure out how to get the typescript types to not error for this property.
|
||||
*/
|
||||
// size: Promise<number>;
|
||||
|
||||
/**
|
||||
* Creates a new S3File representing a slice of the original file.
|
||||
* Uses HTTP Range headers for efficient partial downloads.
|
||||
*
|
||||
* @param begin - Starting byte offset
|
||||
* @param end - Ending byte offset (exclusive)
|
||||
* @param contentType - Optional MIME type for the slice
|
||||
* @returns A new S3File representing the specified range
|
||||
*
|
||||
* @example
|
||||
* // Reading file header
|
||||
* const header = file.slice(0, 1024);
|
||||
* const headerText = await header.text();
|
||||
*
|
||||
* @example
|
||||
* // Reading with content type
|
||||
* const jsonSlice = file.slice(1024, 2048, "application/json");
|
||||
* const data = await jsonSlice.json();
|
||||
*
|
||||
* @example
|
||||
* // Reading from offset to end
|
||||
* const remainder = file.slice(1024);
|
||||
* const content = await remainder.text();
|
||||
*/
|
||||
slice(begin?: number, end?: number, contentType?: string): S3File;
|
||||
slice(begin?: number, contentType?: string): S3File;
|
||||
slice(contentType?: string): S3File;
|
||||
|
||||
/**
|
||||
* Creates a writable stream for uploading data.
|
||||
* Suitable for large files as it uses multipart upload.
|
||||
*
|
||||
* @param options - Configuration for the upload
|
||||
* @returns A NetworkSink for writing data
|
||||
*
|
||||
* @example
|
||||
* // Basic streaming write
|
||||
* const writer = file.writer({
|
||||
* type: "application/json"
|
||||
* });
|
||||
* writer.write('{"hello": ');
|
||||
* writer.write('"world"}');
|
||||
* await writer.end();
|
||||
*
|
||||
* @example
|
||||
* // Optimized large file upload
|
||||
* const writer = file.writer({
|
||||
* partSize: 10 * 1024 * 1024, // 10MB parts
|
||||
* queueSize: 4, // Upload 4 parts in parallel
|
||||
* retry: 3 // Retry failed parts
|
||||
* });
|
||||
*
|
||||
* // Write large chunks of data efficiently
|
||||
* for (const chunk of largeDataChunks) {
|
||||
* writer.write(chunk);
|
||||
* }
|
||||
* await writer.end();
|
||||
*
|
||||
* @example
|
||||
* // Error handling
|
||||
* const writer = file.writer();
|
||||
* try {
|
||||
* writer.write(data);
|
||||
* await writer.end();
|
||||
* } catch (err) {
|
||||
* console.error('Upload failed:', err);
|
||||
* // Writer will automatically abort multipart upload on error
|
||||
* }
|
||||
*/
|
||||
writer(options?: S3Options): NetworkSink;
|
||||
|
||||
/**
|
||||
* Gets a readable stream of the file's content.
|
||||
* Useful for processing large files without loading them entirely into memory.
|
||||
*
|
||||
* @returns A ReadableStream for the file content
|
||||
*
|
||||
* @example
|
||||
* // Basic streaming read
|
||||
* const stream = file.stream();
|
||||
* for await (const chunk of stream) {
|
||||
* console.log('Received chunk:', chunk);
|
||||
* }
|
||||
*
|
||||
* @example
|
||||
* // Piping to response
|
||||
* const stream = file.stream();
|
||||
* return new Response(stream, {
|
||||
* headers: { 'Content-Type': file.type }
|
||||
* });
|
||||
*
|
||||
* @example
|
||||
* // Processing large files
|
||||
* const stream = file.stream();
|
||||
* const textDecoder = new TextDecoder();
|
||||
* for await (const chunk of stream) {
|
||||
* const text = textDecoder.decode(chunk);
|
||||
* // Process text chunk by chunk
|
||||
* }
|
||||
*/
|
||||
readonly readable: ReadableStream;
|
||||
stream(): ReadableStream;
|
||||
|
||||
/**
|
||||
* The name or path of the file in the bucket.
|
||||
*
|
||||
* @example
|
||||
* const file = s3("folder/image.jpg");
|
||||
* console.log(file.name); // "folder/image.jpg"
|
||||
*/
|
||||
readonly name?: string;
|
||||
|
||||
/**
|
||||
* The bucket name containing the file.
|
||||
*
|
||||
* @example
|
||||
* const file = s3("s3://my-bucket/file.txt");
|
||||
* console.log(file.bucket); // "my-bucket"
|
||||
*/
|
||||
readonly bucket?: string;
|
||||
|
||||
/**
|
||||
* Checks if the file exists in S3.
|
||||
* Uses HTTP HEAD request to efficiently check existence without downloading.
|
||||
*
|
||||
* @returns Promise resolving to true if file exists, false otherwise
|
||||
*
|
||||
* @example
|
||||
* // Basic existence check
|
||||
* if (await file.exists()) {
|
||||
* console.log("File exists in S3");
|
||||
* }
|
||||
*
|
||||
* @example
|
||||
* // With error handling
|
||||
* try {
|
||||
* const exists = await file.exists();
|
||||
* if (!exists) {
|
||||
* console.log("File not found");
|
||||
* }
|
||||
* } catch (err) {
|
||||
* console.error("Error checking file:", err);
|
||||
* }
|
||||
*/
|
||||
exists(): Promise<boolean>;
|
||||
|
||||
/**
|
||||
* Uploads data to S3.
|
||||
* Supports various input types and automatically handles large files.
|
||||
*
|
||||
* @param data - The data to upload
|
||||
* @param options - Upload configuration options
|
||||
* @returns Promise resolving to number of bytes written
|
||||
*
|
||||
* @example
|
||||
* // Writing string data
|
||||
* await file.write("Hello World", {
|
||||
* type: "text/plain"
|
||||
* });
|
||||
*
|
||||
* @example
|
||||
* // Writing JSON
|
||||
* const data = { hello: "world" };
|
||||
* await file.write(JSON.stringify(data), {
|
||||
* type: "application/json"
|
||||
* });
|
||||
*
|
||||
* @example
|
||||
* // Writing from Response
|
||||
* const response = await fetch("https://example.com/data");
|
||||
* await file.write(response);
|
||||
*
|
||||
* @example
|
||||
* // Writing with ACL
|
||||
* await file.write(data, {
|
||||
* acl: "public-read",
|
||||
* type: "application/octet-stream"
|
||||
* });
|
||||
*/
|
||||
write(
|
||||
data: string | ArrayBufferView | ArrayBuffer | SharedArrayBuffer | Request | Response | BunFile | S3File | Blob,
|
||||
options?: S3Options,
|
||||
): Promise<number>;
|
||||
|
||||
/**
|
||||
* Generates a presigned URL for the file.
|
||||
* Allows temporary access to the file without exposing credentials.
|
||||
*
|
||||
* @param options - Configuration for the presigned URL
|
||||
* @returns Presigned URL string
|
||||
*
|
||||
* @example
|
||||
* // Basic download URL
|
||||
* const url = file.presign({
|
||||
* expiresIn: 3600 // 1 hour
|
||||
* });
|
||||
*
|
||||
* @example
|
||||
* // Upload URL with specific content type
|
||||
* const uploadUrl = file.presign({
|
||||
* method: "PUT",
|
||||
* expiresIn: 3600,
|
||||
* type: "image/jpeg",
|
||||
* acl: "public-read"
|
||||
* });
|
||||
*
|
||||
* @example
|
||||
* // URL with custom permissions
|
||||
* const url = file.presign({
|
||||
* method: "GET",
|
||||
* expiresIn: 7 * 24 * 60 * 60, // 7 days
|
||||
* acl: "public-read"
|
||||
* });
|
||||
*/
|
||||
presign(options?: S3FilePresignOptions): string;
|
||||
|
||||
/**
|
||||
* Deletes the file from S3.
|
||||
*
|
||||
* @returns Promise that resolves when deletion is complete
|
||||
*
|
||||
* @example
|
||||
* // Basic deletion
|
||||
* await file.delete();
|
||||
*
|
||||
* @example
|
||||
* // With error handling
|
||||
* try {
|
||||
* await file.delete();
|
||||
* console.log("File deleted successfully");
|
||||
* } catch (err) {
|
||||
* console.error("Failed to delete file:", err);
|
||||
* }
|
||||
*/
|
||||
delete(): Promise<void>;
|
||||
|
||||
/**
|
||||
* Alias for delete() method.
|
||||
* Provided for compatibility with Node.js fs API naming.
|
||||
*
|
||||
* @example
|
||||
* await file.unlink();
|
||||
*/
|
||||
unlink: S3File["delete"];
|
||||
|
||||
/**
|
||||
* Get the stat of a file in an S3-compatible storage service.
|
||||
*
|
||||
* @returns Promise resolving to S3Stat
|
||||
*/
|
||||
stat(): Promise<S3Stats>;
|
||||
}
|
||||
|
||||
/**
|
||||
* A configured S3 bucket instance for managing files.
|
||||
* The instance is callable to create S3File instances and provides methods
|
||||
* for common operations.
|
||||
*
|
||||
* @example
|
||||
* // Basic bucket setup
|
||||
* const bucket = new S3Client({
|
||||
* bucket: "my-bucket",
|
||||
* accessKeyId: "key",
|
||||
* secretAccessKey: "secret"
|
||||
* });
|
||||
*
|
||||
* // Get file instance
|
||||
* const file = bucket("image.jpg");
|
||||
*
|
||||
* // Common operations
|
||||
* await bucket.write("data.json", JSON.stringify({hello: "world"}));
|
||||
* const url = bucket.presign("file.pdf");
|
||||
* await bucket.unlink("old.txt");
|
||||
*
|
||||
* @category Cloud Storage
|
||||
*/
|
||||
class S3Client {
|
||||
prototype: S3Client;
|
||||
/**
|
||||
* Create a new instance of an S3 bucket so that credentials can be managed
|
||||
* from a single instance instead of being passed to every method.
|
||||
*
|
||||
* @param options The default options to use for the S3 client. Can be
|
||||
* overriden by passing options to the methods.
|
||||
*
|
||||
* ## Keep S3 credentials in a single instance
|
||||
*
|
||||
* @example
|
||||
* const bucket = new Bun.S3Client({
|
||||
* accessKeyId: "your-access-key",
|
||||
* secretAccessKey: "your-secret-key",
|
||||
* bucket: "my-bucket",
|
||||
* endpoint: "https://s3.us-east-1.amazonaws.com",
|
||||
* sessionToken: "your-session-token",
|
||||
* });
|
||||
*
|
||||
* // S3Client is callable, so you can do this:
|
||||
* const file = bucket.file("my-file.txt");
|
||||
*
|
||||
* // or this:
|
||||
* await file.write("Hello Bun!");
|
||||
* await file.text();
|
||||
*
|
||||
* // To delete the file:
|
||||
* await bucket.delete("my-file.txt");
|
||||
*
|
||||
* // To write a file without returning the instance:
|
||||
* await bucket.write("my-file.txt", "Hello Bun!");
|
||||
*
|
||||
*/
|
||||
constructor(options?: S3Options);
|
||||
|
||||
/**
|
||||
* Creates an S3File instance for the given path.
|
||||
*
|
||||
* @example
|
||||
* const file = bucket.file("image.jpg");
|
||||
* await file.write(imageData);
|
||||
* const configFile = bucket("config.json", {
|
||||
* type: "application/json",
|
||||
* acl: "private"
|
||||
* });
|
||||
*/
|
||||
file(path: string, options?: S3Options): S3File;
|
||||
|
||||
/**
|
||||
* Writes data directly to a path in the bucket.
|
||||
* Supports strings, buffers, streams, and web API types.
|
||||
*
|
||||
* @example
|
||||
* // Write string
|
||||
* await bucket.write("hello.txt", "Hello World");
|
||||
*
|
||||
* // Write JSON with type
|
||||
* await bucket.write(
|
||||
* "data.json",
|
||||
* JSON.stringify({hello: "world"}),
|
||||
* {type: "application/json"}
|
||||
* );
|
||||
*
|
||||
* // Write from fetch
|
||||
* const res = await fetch("https://example.com/data");
|
||||
* await bucket.write("data.bin", res);
|
||||
*
|
||||
* // Write with ACL
|
||||
* await bucket.write("public.html", html, {
|
||||
* acl: "public-read",
|
||||
* type: "text/html"
|
||||
* });
|
||||
*/
|
||||
write(
|
||||
path: string,
|
||||
data:
|
||||
| string
|
||||
| ArrayBufferView
|
||||
| ArrayBuffer
|
||||
| SharedArrayBuffer
|
||||
| Request
|
||||
| Response
|
||||
| BunFile
|
||||
| S3File
|
||||
| Blob
|
||||
| File,
|
||||
options?: S3Options,
|
||||
): Promise<number>;
|
||||
|
||||
/**
|
||||
* Generate a presigned URL for temporary access to a file.
|
||||
* Useful for generating upload/download URLs without exposing credentials.
|
||||
*
|
||||
* @example
|
||||
* // Download URL
|
||||
* const downloadUrl = bucket.presign("file.pdf", {
|
||||
* expiresIn: 3600 // 1 hour
|
||||
* });
|
||||
*
|
||||
* // Upload URL
|
||||
* const uploadUrl = bucket.presign("uploads/image.jpg", {
|
||||
* method: "PUT",
|
||||
* expiresIn: 3600,
|
||||
* type: "image/jpeg",
|
||||
* acl: "public-read"
|
||||
* });
|
||||
*
|
||||
* // Long-lived public URL
|
||||
* const publicUrl = bucket.presign("public/doc.pdf", {
|
||||
* expiresIn: 7 * 24 * 60 * 60, // 7 days
|
||||
* acl: "public-read"
|
||||
* });
|
||||
*/
|
||||
presign(path: string, options?: S3FilePresignOptions): string;
|
||||
|
||||
/**
|
||||
* Delete a file from the bucket.
|
||||
*
|
||||
* @example
|
||||
* // Simple delete
|
||||
* await bucket.unlink("old-file.txt");
|
||||
*
|
||||
* // With error handling
|
||||
* try {
|
||||
* await bucket.unlink("file.dat");
|
||||
* console.log("File deleted");
|
||||
* } catch (err) {
|
||||
* console.error("Delete failed:", err);
|
||||
* }
|
||||
*/
|
||||
unlink(path: string, options?: S3Options): Promise<void>;
|
||||
delete: S3Client["unlink"];
|
||||
|
||||
/**
|
||||
* Get the size of a file in bytes.
|
||||
* Uses HEAD request to efficiently get size.
|
||||
*
|
||||
* @example
|
||||
* // Get size
|
||||
* const bytes = await bucket.size("video.mp4");
|
||||
* console.log(`Size: ${bytes} bytes`);
|
||||
*
|
||||
* // Check if file is large
|
||||
* if (await bucket.size("data.zip") > 100 * 1024 * 1024) {
|
||||
* console.log("File is larger than 100MB");
|
||||
* }
|
||||
*/
|
||||
size(path: string, options?: S3Options): Promise<number>;
|
||||
|
||||
/**
|
||||
* Check if a file exists in the bucket.
|
||||
* Uses HEAD request to check existence.
|
||||
*
|
||||
* @example
|
||||
* // Check existence
|
||||
* if (await bucket.exists("config.json")) {
|
||||
* const file = bucket("config.json");
|
||||
* const config = await file.json();
|
||||
* }
|
||||
*
|
||||
* // With error handling
|
||||
* try {
|
||||
* if (!await bucket.exists("required.txt")) {
|
||||
* throw new Error("Required file missing");
|
||||
* }
|
||||
* } catch (err) {
|
||||
* console.error("Check failed:", err);
|
||||
* }
|
||||
*/
|
||||
exists(path: string, options?: S3Options): Promise<boolean>;
|
||||
/**
|
||||
* Get the stat of a file in an S3-compatible storage service.
|
||||
*
|
||||
* @param path The path to the file.
|
||||
* @param options The options to use for the S3 client.
|
||||
*/
|
||||
stat(path: string, options?: S3Options): Promise<S3Stats>;
|
||||
}
|
||||
|
||||
/**
|
||||
* A default instance of S3Client
|
||||
*
|
||||
* Pulls credentials from environment variables. Use `new Bun.S3Client()` if you need to explicitly set credentials.
|
||||
*
|
||||
* @category Cloud Storage
|
||||
*/
|
||||
var s3: S3Client;
|
||||
}
|
||||
77
packages/bun-types/sqlite.d.ts
vendored
77
packages/bun-types/sqlite.d.ts
vendored
@@ -58,6 +58,8 @@ declare module "bun:sqlite" {
|
||||
* ```ts
|
||||
* const db = new Database("mydb.sqlite", {readonly: true});
|
||||
* ```
|
||||
*
|
||||
* @category Database
|
||||
*/
|
||||
constructor(
|
||||
filename?: string,
|
||||
@@ -477,6 +479,79 @@ declare module "bun:sqlite" {
|
||||
*/
|
||||
static deserialize(serialized: NodeJS.TypedArray | ArrayBufferLike, isReadOnly?: boolean): Database;
|
||||
|
||||
/**
|
||||
* Load a serialized SQLite3 database. This version enables you to specify
|
||||
* additional options such as `strict` to put the database into strict mode.
|
||||
*
|
||||
* Internally, this calls `sqlite3_deserialize`.
|
||||
*
|
||||
* @param serialized Data to load
|
||||
* @returns `Database` instance
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* test("supports serialize/deserialize", () => {
|
||||
* const db = Database.open(":memory:");
|
||||
* db.exec("CREATE TABLE test (id INTEGER PRIMARY KEY, name TEXT)");
|
||||
* db.exec('INSERT INTO test (name) VALUES ("Hello")');
|
||||
* db.exec('INSERT INTO test (name) VALUES ("World")');
|
||||
*
|
||||
* const input = db.serialize();
|
||||
* const db2 = Database.deserialize(input, { strict: true });
|
||||
*
|
||||
* const stmt = db2.prepare("SELECT * FROM test");
|
||||
* expect(JSON.stringify(stmt.get())).toBe(
|
||||
* JSON.stringify({
|
||||
* id: 1,
|
||||
* name: "Hello",
|
||||
* }),
|
||||
* );
|
||||
*
|
||||
* expect(JSON.stringify(stmt.all())).toBe(
|
||||
* JSON.stringify([
|
||||
* {
|
||||
* id: 1,
|
||||
* name: "Hello",
|
||||
* },
|
||||
* {
|
||||
* id: 2,
|
||||
* name: "World",
|
||||
* },
|
||||
* ]),
|
||||
* );
|
||||
* db2.exec("insert into test (name) values ($foo)", { foo: "baz" });
|
||||
* expect(JSON.stringify(stmt.all())).toBe(
|
||||
* JSON.stringify([
|
||||
* {
|
||||
* id: 1,
|
||||
* name: "Hello",
|
||||
* },
|
||||
* {
|
||||
* id: 2,
|
||||
* name: "World",
|
||||
* },
|
||||
* {
|
||||
* id: 3,
|
||||
* name: "baz",
|
||||
* },
|
||||
* ]),
|
||||
* );
|
||||
*
|
||||
* const db3 = Database.deserialize(input, { readonly: true, strict: true });
|
||||
* try {
|
||||
* db3.exec("insert into test (name) values ($foo)", { foo: "baz" });
|
||||
* throw new Error("Expected error");
|
||||
* } catch (e) {
|
||||
* expect(e.message).toBe("attempt to write a readonly database");
|
||||
* }
|
||||
* });
|
||||
* ```
|
||||
*/
|
||||
static deserialize(
|
||||
serialized: NodeJS.TypedArray | ArrayBufferLike,
|
||||
options?: { readonly?: boolean; strict?: boolean; safeIntegers?: boolean },
|
||||
): Database;
|
||||
|
||||
/**
|
||||
* See `sqlite3_file_control` for more information.
|
||||
* @link https://www.sqlite.org/c3ref/file_control.html
|
||||
@@ -494,6 +569,8 @@ declare module "bun:sqlite" {
|
||||
*
|
||||
* This is returned by {@link Database.prepare} and {@link Database.query}.
|
||||
*
|
||||
* @category Database
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* const stmt = db.prepare("SELECT * FROM foo WHERE bar = ?");
|
||||
|
||||
186
packages/bun-types/test.d.ts
vendored
186
packages/bun-types/test.d.ts
vendored
@@ -1,4 +1,5 @@
|
||||
/**
|
||||
* Bun's fast, Jest-compatible testing framework.
|
||||
*
|
||||
* To run tests, run `bun test`
|
||||
*
|
||||
@@ -12,10 +13,15 @@
|
||||
* ```bash
|
||||
* $ bun test <filename>
|
||||
* ```
|
||||
*
|
||||
* @see [Documentation](https://bun.sh/docs/cli/test)
|
||||
*/
|
||||
declare module "bun:test" {
|
||||
/**
|
||||
* -- Mocks --
|
||||
*
|
||||
* @see [Bun Docs - Mocks](https://bun.sh/docs/test/mocks)
|
||||
* @category Testing
|
||||
*/
|
||||
export type Mock<T extends (...args: any[]) => any> = JestMock.Mock<T>;
|
||||
|
||||
@@ -90,10 +96,45 @@ declare module "bun:test" {
|
||||
export function setSystemTime(now?: Date | number): ThisType<void>;
|
||||
|
||||
interface Jest {
|
||||
/**
|
||||
* Restores all mocks and replaced properties back to their original value.
|
||||
* Equivalent to calling {@link JestMock.MockInstance.mockRestore
|
||||
* .mockRestore()} on every mocked function and {@link mock.restore
|
||||
* .restore()} on every replaced property.
|
||||
*/
|
||||
restoreAllMocks(): void;
|
||||
/**
|
||||
* Clears the `mock.calls`, `mock.instances`, `mock.contexts` and
|
||||
* `mock.results` properties of all mocks. Equivalent to calling
|
||||
* {@link JestMock.MockInstance.mockClear .mockClear()} on every mocked
|
||||
* function.
|
||||
*
|
||||
* @see {@link JestMock.MockFunctionState}
|
||||
*/
|
||||
clearAllMocks(): void;
|
||||
/**
|
||||
* Returns a new, unused {@link Mock mock function}. Optionally takes a mock
|
||||
* implementation.
|
||||
*
|
||||
* @see [Bun Docs - Mocks](https://bun.sh/docs/test/mocks)
|
||||
*/
|
||||
fn<T extends (...args: any[]) => any>(func?: T): Mock<T>;
|
||||
/**
|
||||
* Set the current system time used by fake timers.
|
||||
*
|
||||
* Simulates a user changing the system clock while your program is running.
|
||||
* It affects the current time but it does not in itself cause e.g. timers
|
||||
* to fire; they will fire exactly as they would have done without the call
|
||||
* to {@link jest.setSystemTime()}.
|
||||
*/
|
||||
setSystemTime(now?: number | Date): void;
|
||||
/**
|
||||
* Set the default timeout interval (in milliseconds) for all tests and
|
||||
* before/after hooks in the test file.
|
||||
*
|
||||
* This only affects the test file from which this function is called. The
|
||||
* default timeout interval is 5 seconds if this method is not called.
|
||||
*/
|
||||
setTimeout(milliseconds: number): void;
|
||||
}
|
||||
export const jest: Jest;
|
||||
@@ -144,11 +185,49 @@ declare module "bun:test" {
|
||||
type SpiedSetter<T> = JestMock.SpiedSetter<T>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@link Mock} that tracks calls to `obj.methodOrPropertyValue`
|
||||
* without replacing it.
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* import { test, expect, spyOn } from "bun:test";
|
||||
*
|
||||
* const ringo = {
|
||||
* name: "Ringo",
|
||||
* sayHi() {
|
||||
* console.log(`Hello I'm ${this.name}`);
|
||||
* },
|
||||
* };
|
||||
*
|
||||
* const spy = spyOn(ringo, "sayHi");
|
||||
*
|
||||
* test("spyon", () => {
|
||||
* expect(spy).toHaveBeenCalledTimes(0);
|
||||
* ringo.sayHi();
|
||||
* expect(spy).toHaveBeenCalledTimes(1);
|
||||
* });
|
||||
* ```
|
||||
*
|
||||
* @param obj The object to spy on
|
||||
* @param methodOrPropertyValue the method or property value on `obj` to track
|
||||
* calls on.
|
||||
*
|
||||
* @returns a new {@link Mock}, using `obj.methodOrPropertyValue` as it's
|
||||
* implementation.
|
||||
*
|
||||
* @see [Bun Docs - `.spyOn()`](https://bun.sh/docs/test/mocks#spyon)
|
||||
* @see jest.fn
|
||||
*/
|
||||
export function spyOn<T extends object, K extends keyof T>(
|
||||
obj: T,
|
||||
methodOrPropertyValue: K,
|
||||
): Mock<T[K] extends (...args: any[]) => any ? T[K] : never>;
|
||||
|
||||
interface FunctionLike {
|
||||
readonly name: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Describes a group of related tests.
|
||||
*
|
||||
@@ -164,11 +243,9 @@ declare module "bun:test" {
|
||||
*
|
||||
* @param label the label for the tests
|
||||
* @param fn the function that defines the tests
|
||||
*
|
||||
* @category Testing
|
||||
*/
|
||||
|
||||
interface FunctionLike {
|
||||
readonly name: string;
|
||||
}
|
||||
export interface Describe {
|
||||
(fn: () => void): void;
|
||||
|
||||
@@ -185,13 +262,21 @@ declare module "bun:test" {
|
||||
*
|
||||
* @param label the label for the tests
|
||||
* @param fn the function that defines the tests
|
||||
*
|
||||
* @see {@link skipIf} to conditionally skip tests
|
||||
*/
|
||||
skip(label: string, fn: () => void): void;
|
||||
/**
|
||||
* Marks this group of tests as to be written or to be fixed.
|
||||
*
|
||||
* Tests marked as `todo` will only be run when the `--todo` flag is
|
||||
* passed. When writing tests for code that runs but doesn't work yet,
|
||||
* consider using {@link test.failing}.
|
||||
*
|
||||
* @param label the label for the tests
|
||||
* @param fn the function that defines the tests
|
||||
*
|
||||
* @see {@link todoIf} to conditionally mark tests as `todo`
|
||||
*/
|
||||
todo(label: string, fn?: () => void): void;
|
||||
/**
|
||||
@@ -301,8 +386,9 @@ declare module "bun:test" {
|
||||
*/
|
||||
export function afterEach(fn: (() => void | Promise<unknown>) | ((done: (err?: unknown) => void) => void)): void;
|
||||
/**
|
||||
* Sets the default timeout for all tests in the current file. If a test specifies a timeout, it will
|
||||
* override this value. The default timeout is 5000ms (5 seconds).
|
||||
* Sets the default timeout for all tests in the current file. If a test
|
||||
* specifies a timeout, it will override this value. The default timeout is
|
||||
* 5000ms (5 seconds).
|
||||
*
|
||||
* @param milliseconds the number of milliseconds for the default timeout
|
||||
*/
|
||||
@@ -326,7 +412,8 @@ declare module "bun:test" {
|
||||
*/
|
||||
retry?: number;
|
||||
/**
|
||||
* Sets the number of times to repeat the test, regardless of whether it passed or failed.
|
||||
* Sets the number of times to repeat the test, regardless of whether it
|
||||
* passed or failed.
|
||||
*
|
||||
* @default 0
|
||||
*/
|
||||
@@ -352,6 +439,9 @@ declare module "bun:test" {
|
||||
* @param label the label for the test
|
||||
* @param fn the test function
|
||||
* @param options the test timeout or options
|
||||
*
|
||||
* @category Testing
|
||||
* @see [Bun Docs - Writing Tests](https://bun.sh/docs/test/writing)
|
||||
*/
|
||||
export interface Test {
|
||||
(
|
||||
@@ -410,25 +500,50 @@ declare module "bun:test" {
|
||||
/**
|
||||
* Marks this test as failing.
|
||||
*
|
||||
* Use `test.failing` when you are writing a test and expecting it to fail.
|
||||
* Use {@link test.failing} when you are writing a test and expecting it to fail.
|
||||
* These tests will behave the other way normal tests do. If failing test
|
||||
* will throw any errors then it will pass. If it does not throw it will
|
||||
* fail.
|
||||
* fail. This is handy for writing tests for code with known bugs, when
|
||||
* you want to make sure tests are updated when those bugs get fixed.
|
||||
*
|
||||
* `test.failing` is very similar to {@link test.todo} except that it always
|
||||
* {@link test.failing} is similar to {@link test.todo} except that it always
|
||||
* runs, regardless of the `--todo` flag.
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* import { it, expect } from 'bun:test';
|
||||
*
|
||||
* function add(a, b) {
|
||||
* // TODO: fully implement
|
||||
* return a;
|
||||
* }
|
||||
*
|
||||
* it('add(0, 0) == 0', () => {
|
||||
* expect(add(0, 0)).toBe(0);
|
||||
* });
|
||||
*
|
||||
* // FIXME: not working yet
|
||||
* it.failing('add(1, 1) == 2', () => {
|
||||
* expect(add(1, 1)).toBe(2);
|
||||
* });
|
||||
* ```
|
||||
*
|
||||
* @param label the label for the test
|
||||
* @param fn the test function
|
||||
* @param options the test timeout or options
|
||||
* @param options A timeout (in milliseconds) or options object.
|
||||
*/
|
||||
failing(label: string, fn?: (() => void | Promise<unknown>) | ((done: (err?: unknown) => void) => void)): void;
|
||||
failing(
|
||||
label: string,
|
||||
fn?: (() => void | Promise<unknown>) | ((done: (err?: unknown) => void) => void),
|
||||
options?: number | TestOptions,
|
||||
): void;
|
||||
/**
|
||||
* Runs this test, if `condition` is true.
|
||||
*
|
||||
* This is the opposite of `test.skipIf()`.
|
||||
* This is the opposite of {@link test.skipIf `test.skipIf()`}.
|
||||
*
|
||||
* @param condition if the test should run
|
||||
* @returns a test function with the same signature as {@link test}
|
||||
*/
|
||||
if(
|
||||
condition: boolean,
|
||||
@@ -440,7 +555,10 @@ declare module "bun:test" {
|
||||
/**
|
||||
* Skips this test, if `condition` is true.
|
||||
*
|
||||
* This is the opposite of {@link test.if `test.if()`}.
|
||||
*
|
||||
* @param condition if the test should be skipped
|
||||
* @returns a test function with the same signature as {@link test}
|
||||
*/
|
||||
skipIf(
|
||||
condition: boolean,
|
||||
@@ -453,6 +571,7 @@ declare module "bun:test" {
|
||||
* Marks this test as to be written or to be fixed, if `condition` is true.
|
||||
*
|
||||
* @param condition if the test should be marked TODO
|
||||
* @returns a test function with the same signature as {@link test}
|
||||
*/
|
||||
todoIf(
|
||||
condition: boolean,
|
||||
@@ -601,12 +720,45 @@ declare module "bun:test" {
|
||||
unreachable(msg?: string | Error): never;
|
||||
|
||||
/**
|
||||
* Ensures that an assertion is made
|
||||
* Ensures that at least one assertion is made during a test.
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* test('Callback is called', async () => {
|
||||
* expect.hasAssertions();
|
||||
* const cb = (state: any) => expect(state).toHaveProperty('ready');
|
||||
* await loadState(cb);
|
||||
* });
|
||||
* ```
|
||||
*
|
||||
* @see {@link assertions} to ensure a specific number of assertions are
|
||||
* made.
|
||||
*/
|
||||
hasAssertions(): void;
|
||||
|
||||
/**
|
||||
* Ensures that a specific number of assertions are made
|
||||
* Ensures that a specific number of assertions are made during a test.
|
||||
*
|
||||
* This is often useful when testing asynchronous code, in order to make
|
||||
* sure that assertions in a callback actually got called.
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* test('doAsync calls both callbacks', () => {
|
||||
* expect.assertions(2);
|
||||
* function callback1(data) {
|
||||
* expect(data).toBeTruthy();
|
||||
* }
|
||||
* function callback2(data) {
|
||||
* expect(data).toBeTruthy();
|
||||
* }
|
||||
*
|
||||
* doAsync(callback1, callback2);
|
||||
* });
|
||||
* ```
|
||||
*
|
||||
* @see {@link hasAssertions} to ensure at least one assertion is made,
|
||||
* without specifying a number.
|
||||
*/
|
||||
assertions(neededAssertions: number): void;
|
||||
}
|
||||
@@ -1778,10 +1930,6 @@ declare module "bun:test" {
|
||||
type MatcherContext = MatcherUtils & MatcherState;
|
||||
}
|
||||
|
||||
declare module "test" {
|
||||
export type * from "bun:test";
|
||||
}
|
||||
|
||||
declare namespace JestMock {
|
||||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
Bun.spawn(["echo", '"hi"']);
|
||||
performance;
|
||||
@@ -1,23 +0,0 @@
|
||||
new ReadableStream({
|
||||
start(controller) {
|
||||
controller.enqueue("hello");
|
||||
controller.enqueue("world");
|
||||
controller.close();
|
||||
},
|
||||
});
|
||||
|
||||
// this will have type errors when lib.dom.d.ts is present
|
||||
// afaik this isn't fixable
|
||||
new ReadableStream({
|
||||
type: "direct",
|
||||
pull(controller) {
|
||||
// eslint-disable-next-line
|
||||
controller.write("hello");
|
||||
// eslint-disable-next-line
|
||||
controller.write("world");
|
||||
controller.close();
|
||||
},
|
||||
cancel() {
|
||||
// called if stream.cancel() is called
|
||||
},
|
||||
});
|
||||
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"skipLibCheck": true,
|
||||
"declaration": true,
|
||||
"emitDeclarationOnly": true,
|
||||
"noEmit": false,
|
||||
"declarationDir": "out"
|
||||
},
|
||||
"include": ["**/*.ts"],
|
||||
"exclude": ["dist", "node_modules"]
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"skipLibCheck": true,
|
||||
"declaration": true,
|
||||
"emitDeclarationOnly": true,
|
||||
"noEmit": false,
|
||||
"declarationDir": "out"
|
||||
},
|
||||
"include": ["**/*.ts"],
|
||||
"exclude": ["dist", "node_modules"]
|
||||
}
|
||||
|
||||
433
packages/bun-types/wasm.d.ts
vendored
433
packages/bun-types/wasm.d.ts
vendored
@@ -1,270 +1,193 @@
|
||||
export {};
|
||||
|
||||
type _Global<T extends Bun.WebAssembly.ValueType = Bun.WebAssembly.ValueType> = typeof globalThis extends {
|
||||
onerror: any;
|
||||
WebAssembly: { Global: infer T };
|
||||
}
|
||||
? T
|
||||
: Bun.WebAssembly.Global<T>;
|
||||
|
||||
type _CompileError = typeof globalThis extends {
|
||||
onerror: any;
|
||||
WebAssembly: { CompileError: infer T };
|
||||
}
|
||||
? T
|
||||
: Bun.WebAssembly.CompileError;
|
||||
|
||||
type _LinkError = typeof globalThis extends {
|
||||
onerror: any;
|
||||
WebAssembly: { LinkError: infer T };
|
||||
}
|
||||
? T
|
||||
: Bun.WebAssembly.LinkError;
|
||||
|
||||
type _RuntimeError = typeof globalThis extends {
|
||||
onerror: any;
|
||||
WebAssembly: { RuntimeError: infer T };
|
||||
}
|
||||
? T
|
||||
: Bun.WebAssembly.RuntimeError;
|
||||
|
||||
type _Memory = typeof globalThis extends {
|
||||
onerror: any;
|
||||
WebAssembly: { Memory: infer T };
|
||||
}
|
||||
? T
|
||||
: Bun.WebAssembly.Memory;
|
||||
|
||||
type _Instance = typeof globalThis extends {
|
||||
onerror: any;
|
||||
WebAssembly: { Instance: infer T };
|
||||
}
|
||||
? T
|
||||
: Bun.WebAssembly.Instance;
|
||||
|
||||
type _Module = typeof globalThis extends {
|
||||
onerror: any;
|
||||
WebAssembly: { Module: infer T };
|
||||
}
|
||||
? T
|
||||
: Bun.WebAssembly.Module;
|
||||
|
||||
type _Table = typeof globalThis extends {
|
||||
onerror: any;
|
||||
WebAssembly: { Table: infer T };
|
||||
}
|
||||
? T
|
||||
: Bun.WebAssembly.Table;
|
||||
|
||||
declare global {
|
||||
namespace Bun {
|
||||
namespace WebAssembly {
|
||||
type ImportExportKind = "function" | "global" | "memory" | "table";
|
||||
type TableKind = "anyfunc" | "externref";
|
||||
// eslint-disable-next-line @typescript-eslint/ban-types
|
||||
type ExportValue = Function | Global | WebAssembly.Memory | WebAssembly.Table;
|
||||
type Exports = Record<string, ExportValue>;
|
||||
type ImportValue = ExportValue | number;
|
||||
type Imports = Record<string, ModuleImports>;
|
||||
type ModuleImports = Record<string, ImportValue>;
|
||||
|
||||
interface ValueTypeMap {
|
||||
// eslint-disable-next-line @typescript-eslint/ban-types
|
||||
anyfunc: Function;
|
||||
externref: any;
|
||||
f32: number;
|
||||
f64: number;
|
||||
i32: number;
|
||||
i64: bigint;
|
||||
v128: never;
|
||||
}
|
||||
|
||||
type ValueType = keyof ValueTypeMap;
|
||||
|
||||
interface GlobalDescriptor<T extends ValueType = ValueType> {
|
||||
mutable?: boolean;
|
||||
value: T;
|
||||
}
|
||||
|
||||
interface Global<T extends ValueType = ValueType> {
|
||||
// <T extends ValueType = ValueType> {
|
||||
/** [MDN Reference](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/WebAssembly/Global/value) */
|
||||
value: ValueTypeMap[T];
|
||||
/** [MDN Reference](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/WebAssembly/Global/valueOf) */
|
||||
valueOf(): ValueTypeMap[T];
|
||||
}
|
||||
|
||||
interface CompileError extends Error {}
|
||||
|
||||
interface LinkError extends Error {}
|
||||
|
||||
interface RuntimeError extends Error {}
|
||||
|
||||
/** [MDN Reference](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/WebAssembly/Instance) */
|
||||
interface Instance {
|
||||
/** [MDN Reference](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/WebAssembly/Instance/exports) */
|
||||
readonly exports: Exports;
|
||||
}
|
||||
|
||||
/** [MDN Reference](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/WebAssembly/Memory) */
|
||||
interface Memory {
|
||||
/** [MDN Reference](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/WebAssembly/Memory/buffer) */
|
||||
readonly buffer: ArrayBuffer;
|
||||
/** [MDN Reference](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/WebAssembly/Memory/grow) */
|
||||
grow(delta: number): number;
|
||||
}
|
||||
|
||||
/** [MDN Reference](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/WebAssembly/Module) */
|
||||
interface Module {}
|
||||
|
||||
/** [MDN Reference](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/WebAssembly/Table) */
|
||||
interface Table {
|
||||
/** [MDN Reference](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/WebAssembly/Table/length) */
|
||||
readonly length: number;
|
||||
/** [MDN Reference](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/WebAssembly/Table/get) */
|
||||
get(index: number): any;
|
||||
/** [MDN Reference](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/WebAssembly/Table/grow) */
|
||||
grow(delta: number, value?: any): number;
|
||||
/** [MDN Reference](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/WebAssembly/Table/set) */
|
||||
set(index: number, value?: any): void;
|
||||
}
|
||||
|
||||
interface MemoryDescriptor {
|
||||
initial: number;
|
||||
maximum?: number;
|
||||
shared?: boolean;
|
||||
}
|
||||
|
||||
interface ModuleExportDescriptor {
|
||||
kind: ImportExportKind;
|
||||
name: string;
|
||||
}
|
||||
|
||||
interface ModuleImportDescriptor {
|
||||
kind: ImportExportKind;
|
||||
module: string;
|
||||
name: string;
|
||||
}
|
||||
|
||||
interface TableDescriptor {
|
||||
element: TableKind;
|
||||
initial: number;
|
||||
maximum?: number;
|
||||
}
|
||||
|
||||
interface WebAssemblyInstantiatedSource {
|
||||
instance: Instance;
|
||||
module: Module;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
declare module "bun" {
|
||||
namespace WebAssembly {
|
||||
interface ValueTypeMap extends Bun.WebAssembly.ValueTypeMap {}
|
||||
interface GlobalDescriptor<T extends keyof ValueTypeMap = keyof ValueTypeMap>
|
||||
extends Bun.WebAssembly.GlobalDescriptor<T> {}
|
||||
interface MemoryDescriptor extends Bun.WebAssembly.MemoryDescriptor {}
|
||||
interface ModuleExportDescriptor extends Bun.WebAssembly.ModuleExportDescriptor {}
|
||||
interface ModuleImportDescriptor extends Bun.WebAssembly.ModuleImportDescriptor {}
|
||||
interface TableDescriptor extends Bun.WebAssembly.TableDescriptor {}
|
||||
interface WebAssemblyInstantiatedSource extends Bun.WebAssembly.WebAssemblyInstantiatedSource {}
|
||||
type ImportExportKind = "function" | "global" | "memory" | "table";
|
||||
type TableKind = "anyfunc" | "externref";
|
||||
type ExportValue = Function | Global | WebAssembly.Memory | WebAssembly.Table;
|
||||
type Exports = Record<string, ExportValue>;
|
||||
type ImportValue = ExportValue | number;
|
||||
type Imports = Record<string, ModuleImports>;
|
||||
type ModuleImports = Record<string, ImportValue>;
|
||||
|
||||
interface LinkError extends _LinkError {}
|
||||
var LinkError: {
|
||||
prototype: LinkError;
|
||||
new (message?: string): LinkError;
|
||||
(message?: string): LinkError;
|
||||
};
|
||||
|
||||
interface CompileError extends _CompileError {}
|
||||
var CompileError: typeof globalThis extends {
|
||||
onerror: any;
|
||||
WebAssembly: { CompileError: infer T };
|
||||
interface ValueTypeMap {
|
||||
anyfunc: Function;
|
||||
externref: any;
|
||||
f32: number;
|
||||
f64: number;
|
||||
i32: number;
|
||||
i64: bigint;
|
||||
v128: never;
|
||||
}
|
||||
? T
|
||||
: {
|
||||
prototype: CompileError;
|
||||
new (message?: string): CompileError;
|
||||
(message?: string): CompileError;
|
||||
};
|
||||
|
||||
interface RuntimeError extends _RuntimeError {}
|
||||
var RuntimeError: {
|
||||
prototype: RuntimeError;
|
||||
new (message?: string): RuntimeError;
|
||||
(message?: string): RuntimeError;
|
||||
};
|
||||
type ValueType = keyof ValueTypeMap;
|
||||
|
||||
interface Global<T extends keyof ValueTypeMap = keyof ValueTypeMap> extends _Global<T> {}
|
||||
var Global: typeof globalThis extends {
|
||||
onerror: any;
|
||||
WebAssembly: { Global: infer T };
|
||||
interface GlobalDescriptor<T extends ValueType = ValueType> {
|
||||
mutable?: boolean;
|
||||
value: T;
|
||||
}
|
||||
? T
|
||||
: {
|
||||
prototype: Global;
|
||||
new <T extends Bun.WebAssembly.ValueType = Bun.WebAssembly.ValueType>(
|
||||
descriptor: GlobalDescriptor<T>,
|
||||
v?: ValueTypeMap[T],
|
||||
): Global<T>;
|
||||
};
|
||||
|
||||
interface Instance extends _Instance {}
|
||||
var Instance: typeof globalThis extends {
|
||||
onerror: any;
|
||||
WebAssembly: { Instance: infer T };
|
||||
interface Global<T extends ValueType = ValueType> {
|
||||
// <T extends ValueType = ValueType> {
|
||||
/** [MDN Reference](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/WebAssembly/Global/value) */
|
||||
value: ValueTypeMap[T];
|
||||
/** [MDN Reference](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/WebAssembly/Global/valueOf) */
|
||||
valueOf(): ValueTypeMap[T];
|
||||
}
|
||||
? T
|
||||
: {
|
||||
prototype: Instance;
|
||||
new (module: Module, importObject?: Bun.WebAssembly.Imports): Instance;
|
||||
};
|
||||
|
||||
interface Memory extends _Memory {}
|
||||
var Memory: {
|
||||
prototype: Memory;
|
||||
new (descriptor: MemoryDescriptor): Memory;
|
||||
};
|
||||
interface CompileError extends Error {}
|
||||
|
||||
interface Module extends _Module {}
|
||||
var Module: typeof globalThis extends {
|
||||
onerror: any;
|
||||
WebAssembly: { Module: infer T };
|
||||
interface LinkError extends Error {}
|
||||
|
||||
interface RuntimeError extends Error {}
|
||||
|
||||
/** [MDN Reference](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/WebAssembly/Instance) */
|
||||
interface Instance {
|
||||
/** [MDN Reference](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/WebAssembly/Instance/exports) */
|
||||
readonly exports: Exports;
|
||||
}
|
||||
? T
|
||||
: {
|
||||
prototype: Module;
|
||||
new (bytes: Bun.BufferSource): Module;
|
||||
/** [MDN Reference](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/WebAssembly/Module/customSections) */
|
||||
customSections(moduleObject: Module, sectionName: string): ArrayBuffer[];
|
||||
/** [MDN Reference](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/WebAssembly/Module/exports) */
|
||||
exports(moduleObject: Module): ModuleExportDescriptor[];
|
||||
/** [MDN Reference](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/WebAssembly/Module/imports) */
|
||||
imports(moduleObject: Module): ModuleImportDescriptor[];
|
||||
};
|
||||
|
||||
interface Table extends _Table {}
|
||||
var Table: {
|
||||
prototype: Table;
|
||||
new (descriptor: TableDescriptor, value?: any): Table;
|
||||
};
|
||||
/** [MDN Reference](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/WebAssembly/Memory) */
|
||||
interface Memory {
|
||||
/** [MDN Reference](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/WebAssembly/Memory/buffer) */
|
||||
readonly buffer: ArrayBuffer;
|
||||
/** [MDN Reference](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/WebAssembly/Memory/grow) */
|
||||
grow(delta: number): number;
|
||||
}
|
||||
|
||||
/** [MDN Reference](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/WebAssembly/compile) */
|
||||
function compile(bytes: Bun.BufferSource): Promise<Module>;
|
||||
/** [MDN Reference](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/WebAssembly/compileStreaming) */
|
||||
function compileStreaming(source: Response | PromiseLike<Response>): Promise<Module>;
|
||||
/** [MDN Reference](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/WebAssembly/instantiate) */
|
||||
function instantiate(
|
||||
bytes: Bun.BufferSource,
|
||||
importObject?: Bun.WebAssembly.Imports,
|
||||
): Promise<WebAssemblyInstantiatedSource>;
|
||||
function instantiate(moduleObject: Module, importObject?: Bun.WebAssembly.Imports): Promise<Instance>;
|
||||
/** [MDN Reference](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/WebAssembly/instantiateStreaming) */
|
||||
function instantiateStreaming(
|
||||
source: Response | PromiseLike<Response>,
|
||||
importObject?: Bun.WebAssembly.Imports,
|
||||
): Promise<WebAssemblyInstantiatedSource>;
|
||||
/** [MDN Reference](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/WebAssembly/validate) */
|
||||
function validate(bytes: Bun.BufferSource): boolean;
|
||||
/** [MDN Reference](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/WebAssembly/Module) */
|
||||
interface Module {}
|
||||
|
||||
/** [MDN Reference](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/WebAssembly/Table) */
|
||||
interface Table {
|
||||
/** [MDN Reference](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/WebAssembly/Table/length) */
|
||||
readonly length: number;
|
||||
/** [MDN Reference](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/WebAssembly/Table/get) */
|
||||
get(index: number): any;
|
||||
/** [MDN Reference](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/WebAssembly/Table/grow) */
|
||||
grow(delta: number, value?: any): number;
|
||||
/** [MDN Reference](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/WebAssembly/Table/set) */
|
||||
set(index: number, value?: any): void;
|
||||
}
|
||||
|
||||
interface MemoryDescriptor {
|
||||
initial: number;
|
||||
maximum?: number;
|
||||
shared?: boolean;
|
||||
}
|
||||
|
||||
interface ModuleExportDescriptor {
|
||||
kind: ImportExportKind;
|
||||
name: string;
|
||||
}
|
||||
|
||||
interface ModuleImportDescriptor {
|
||||
kind: ImportExportKind;
|
||||
module: string;
|
||||
name: string;
|
||||
}
|
||||
|
||||
interface TableDescriptor {
|
||||
element: TableKind;
|
||||
initial: number;
|
||||
maximum?: number;
|
||||
}
|
||||
|
||||
interface WebAssemblyInstantiatedSource {
|
||||
instance: Instance;
|
||||
module: Module;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
declare namespace WebAssembly {
|
||||
interface ValueTypeMap extends Bun.WebAssembly.ValueTypeMap {}
|
||||
interface GlobalDescriptor<T extends keyof ValueTypeMap = keyof ValueTypeMap>
|
||||
extends Bun.WebAssembly.GlobalDescriptor<T> {}
|
||||
interface MemoryDescriptor extends Bun.WebAssembly.MemoryDescriptor {}
|
||||
interface ModuleExportDescriptor extends Bun.WebAssembly.ModuleExportDescriptor {}
|
||||
interface ModuleImportDescriptor extends Bun.WebAssembly.ModuleImportDescriptor {}
|
||||
interface TableDescriptor extends Bun.WebAssembly.TableDescriptor {}
|
||||
interface WebAssemblyInstantiatedSource extends Bun.WebAssembly.WebAssemblyInstantiatedSource {}
|
||||
|
||||
interface LinkError extends Bun.WebAssembly.LinkError {}
|
||||
var LinkError: {
|
||||
prototype: LinkError;
|
||||
new (message?: string): LinkError;
|
||||
(message?: string): LinkError;
|
||||
};
|
||||
|
||||
interface CompileError extends Bun.WebAssembly.CompileError {}
|
||||
var CompileError: {
|
||||
prototype: CompileError;
|
||||
new (message?: string): CompileError;
|
||||
(message?: string): CompileError;
|
||||
};
|
||||
|
||||
interface RuntimeError extends Bun.WebAssembly.RuntimeError {}
|
||||
var RuntimeError: {
|
||||
prototype: RuntimeError;
|
||||
new (message?: string): RuntimeError;
|
||||
(message?: string): RuntimeError;
|
||||
};
|
||||
|
||||
interface Global<T extends keyof ValueTypeMap = keyof ValueTypeMap> extends Bun.WebAssembly.Global<T> {}
|
||||
var Global: {
|
||||
prototype: Global;
|
||||
new <T extends Bun.WebAssembly.ValueType = Bun.WebAssembly.ValueType>(
|
||||
descriptor: GlobalDescriptor<T>,
|
||||
v?: ValueTypeMap[T],
|
||||
): Global<T>;
|
||||
};
|
||||
|
||||
interface Instance extends Bun.WebAssembly.Instance {}
|
||||
var Instance: {
|
||||
prototype: Instance;
|
||||
new (module: Module, importObject?: Bun.WebAssembly.Imports): Instance;
|
||||
};
|
||||
|
||||
interface Memory extends Bun.WebAssembly.Memory {}
|
||||
var Memory: {
|
||||
prototype: Memory;
|
||||
new (descriptor: MemoryDescriptor): Memory;
|
||||
};
|
||||
|
||||
interface Module extends Bun.WebAssembly.Module {}
|
||||
var Module: Bun.__internal.UseLibDomIfAvailable<
|
||||
"WebAssembly",
|
||||
{
|
||||
Module: {
|
||||
prototype: Module;
|
||||
new (bytes: Bun.BufferSource): Module;
|
||||
/** [MDN Reference](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/WebAssembly/Module/customSections) */
|
||||
customSections(moduleObject: Module, sectionName: string): ArrayBuffer[];
|
||||
/** [MDN Reference](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/WebAssembly/Module/exports) */
|
||||
exports(moduleObject: Module): ModuleExportDescriptor[];
|
||||
/** [MDN Reference](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/WebAssembly/Module/imports) */
|
||||
imports(moduleObject: Module): ModuleImportDescriptor[];
|
||||
};
|
||||
}
|
||||
>["Module"];
|
||||
|
||||
interface Table extends Bun.WebAssembly.Table {}
|
||||
var Table: {
|
||||
prototype: Table;
|
||||
new (descriptor: TableDescriptor, value?: any): Table;
|
||||
};
|
||||
|
||||
/** [MDN Reference](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/WebAssembly/compile) */
|
||||
function compile(bytes: Bun.BufferSource): Promise<Module>;
|
||||
/** [MDN Reference](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/WebAssembly/compileStreaming) */
|
||||
function compileStreaming(source: Response | PromiseLike<Response>): Promise<Module>;
|
||||
/** [MDN Reference](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/WebAssembly/instantiate) */
|
||||
function instantiate(
|
||||
bytes: Bun.BufferSource,
|
||||
importObject?: Bun.WebAssembly.Imports,
|
||||
): Promise<WebAssemblyInstantiatedSource>;
|
||||
function instantiate(moduleObject: Module, importObject?: Bun.WebAssembly.Imports): Promise<Instance>;
|
||||
/** [MDN Reference](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/WebAssembly/instantiateStreaming) */
|
||||
function instantiateStreaming(
|
||||
source: Response | PromiseLike<Response>,
|
||||
importObject?: Bun.WebAssembly.Imports,
|
||||
): Promise<WebAssemblyInstantiatedSource>;
|
||||
/** [MDN Reference](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/WebAssembly/validate) */
|
||||
function validate(bytes: Bun.BufferSource): boolean;
|
||||
}
|
||||
|
||||
@@ -72,7 +72,7 @@ void us_socket_context_close(int ssl, struct us_socket_context_t *context) {
|
||||
while (ls) {
|
||||
struct us_listen_socket_t *nextLS = (struct us_listen_socket_t *) ls->s.next;
|
||||
us_listen_socket_close(ssl, ls);
|
||||
|
||||
|
||||
ls = nextLS;
|
||||
}
|
||||
|
||||
@@ -310,7 +310,7 @@ struct us_bun_verify_error_t us_socket_verify_error(int ssl, struct us_socket_t
|
||||
}
|
||||
#endif
|
||||
|
||||
return (struct us_bun_verify_error_t) { .error = 0, .code = NULL, .reason = NULL };
|
||||
return (struct us_bun_verify_error_t) { .error = 0, .code = NULL, .reason = NULL };
|
||||
}
|
||||
|
||||
void us_internal_socket_context_free(int ssl, struct us_socket_context_t *context) {
|
||||
@@ -337,7 +337,7 @@ void us_socket_context_ref(int ssl, struct us_socket_context_t *context) {
|
||||
}
|
||||
void us_socket_context_unref(int ssl, struct us_socket_context_t *context) {
|
||||
uint32_t ref_count = context->ref_count;
|
||||
context->ref_count--;
|
||||
context->ref_count--;
|
||||
if (ref_count == 1) {
|
||||
us_internal_socket_context_free(ssl, context);
|
||||
}
|
||||
@@ -520,7 +520,7 @@ void *us_socket_context_connect(int ssl, struct us_socket_context_t *context, co
|
||||
}
|
||||
|
||||
struct us_connecting_socket_t *c = us_calloc(1, sizeof(struct us_connecting_socket_t) + socket_ext_size);
|
||||
c->socket_ext_size = socket_ext_size;
|
||||
c->socket_ext_size = socket_ext_size;
|
||||
c->options = options;
|
||||
c->ssl = ssl > 0;
|
||||
c->timeout = 255;
|
||||
@@ -641,9 +641,9 @@ void us_internal_socket_after_open(struct us_socket_t *s, int error) {
|
||||
|
||||
/* Emit error, close without emitting on_close */
|
||||
|
||||
/* There are two possible states here:
|
||||
1. It's a us_connecting_socket_t*. DNS resolution failed, or a connection failed.
|
||||
2. It's a us_socket_t*
|
||||
/* There are two possible states here:
|
||||
1. It's a us_connecting_socket_t*. DNS resolution failed, or a connection failed.
|
||||
2. It's a us_socket_t*
|
||||
|
||||
We differentiate between these two cases by checking if the connect_state is null.
|
||||
*/
|
||||
@@ -887,7 +887,7 @@ void us_socket_context_on_connect_error(int ssl, struct us_socket_context_t *con
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
context->on_connect_error = on_connect_error;
|
||||
}
|
||||
|
||||
@@ -898,7 +898,7 @@ void us_socket_context_on_socket_connect_error(int ssl, struct us_socket_context
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
context->on_socket_connect_error = on_connect_error;
|
||||
}
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user