Compare commits

..

2 Commits

Author SHA1 Message Date
Dylan Conway
302b1e2b85 update 2025-09-30 19:50:11 -07:00
Dylan Conway
a58e4c0c01 force heap allocators 2025-09-30 19:40:31 -07:00
2028 changed files with 55033 additions and 125017 deletions

19
.aikido
View File

@@ -1,19 +0,0 @@
exclude:
paths:
- test
- scripts
- bench
- packages/bun-lambda
- packages/bun-release
- packages/bun-wasm
- packages/bun-vscode
- packages/bun-plugin-yaml
- packages/bun-plugin-svelte
- packages/bun-native-plugin-rs
- packages/bun-native-bundler-plugin-api
- packages/bun-inspector-protocol
- packages/bun-inspector-frontend
- packages/bun-error
- packages/bun-debug-adapter-protocol
- packages/bun-build-mdx-rs
- packages/@types/bun

View File

@@ -48,14 +48,6 @@ RUN --mount=type=tmpfs,target=/tmp \
wget -O /tmp/cmake.sh "$cmake_url" && \
sh /tmp/cmake.sh --skip-license --prefix=/usr
RUN --mount=type=tmpfs,target=/tmp \
sccache_version="0.12.0" && \
arch=$(uname -m) && \
sccache_url="https://github.com/mozilla/sccache/releases/download/v${sccache_version}/sccache-v${sccache_version}-${arch}-unknown-linux-musl.tar.gz" && \
wget -O /tmp/sccache.tar.gz "$sccache_url" && \
tar -xzf /tmp/sccache.tar.gz -C /tmp && \
install -m755 /tmp/sccache-v${sccache_version}-${arch}-unknown-linux-musl/sccache /usr/local/bin
RUN update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-13 130 \
--slave /usr/bin/g++ g++ /usr/bin/g++-13 \
--slave /usr/bin/gcc-ar gcc-ar /usr/bin/gcc-ar-13 \
@@ -118,14 +110,14 @@ ARG BUILDKITE_AGENT_TAGS
# Install Rust nightly
RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y \
RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y \
&& export PATH=$HOME/.cargo/bin:$PATH \
&& rustup install nightly \
&& rustup default nightly
RUN ARCH=$(if [ "$TARGETARCH" = "arm64" ]; then echo "arm64"; else echo "amd64"; fi) && \
echo "Downloading buildkite" && \
echo "Downloading buildkite" && \
curl -fsSL "https://github.com/buildkite/agent/releases/download/v3.87.0/buildkite-agent-linux-${ARCH}-3.87.0.tar.gz" -o /tmp/buildkite-agent.tar.gz && \
mkdir -p /tmp/buildkite-agent && \
tar -xzf /tmp/buildkite-agent.tar.gz -C /tmp/buildkite-agent && \
@@ -133,20 +125,6 @@ RUN ARCH=$(if [ "$TARGETARCH" = "arm64" ]; then echo "arm64"; else echo "amd64";
RUN mkdir -p /var/cache/buildkite-agent /var/log/buildkite-agent /var/run/buildkite-agent /etc/buildkite-agent /var/lib/buildkite-agent/cache/bun
# The following is necessary to configure buildkite to use a stable
# checkout directory. sccache hashes absolute paths into its cache keys,
# so if buildkite uses a different checkout path each time (which it does
# by default), sccache will be useless.
RUN mkdir -p -m 755 /var/lib/buildkite-agent/hooks && \
cat <<'EOF' > /var/lib/buildkite-agent/hooks/environment
#!/bin/sh
set -efu
export BUILDKITE_BUILD_CHECKOUT_PATH=/var/lib/buildkite-agent/build
EOF
RUN chmod 744 /var/lib/buildkite-agent/hooks/environment
COPY ../*/agent.mjs /var/bun/scripts/
ENV BUN_INSTALL_CACHE=/var/lib/buildkite-agent/cache/bun
@@ -169,7 +147,7 @@ COPY . /workspace/bun
# Install Rust nightly
RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y \
RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y \
&& export PATH=$HOME/.cargo/bin:$PATH \
&& rustup install nightly \
&& rustup default nightly
@@ -183,4 +161,4 @@ RUN --mount=type=tmpfs,target=/workspace/bun/build \
ls -la \
&& bun run build:release \
&& mkdir -p /target \
&& cp -r /workspace/bun/build/release/bun /target/bun
&& cp -r /workspace/bun/build/release/bun /target/bun

View File

@@ -16,7 +16,6 @@ import {
getEmoji,
getEnv,
getLastSuccessfulBuild,
getSecret,
isBuildkite,
isBuildManual,
isFork,
@@ -31,7 +30,7 @@ import {
} from "../scripts/utils.mjs";
/**
* @typedef {"linux" | "darwin" | "windows" | "freebsd"} Os
* @typedef {"linux" | "darwin" | "windows"} Os
* @typedef {"aarch64" | "x64"} Arch
* @typedef {"musl"} Abi
* @typedef {"debian" | "ubuntu" | "alpine" | "amazonlinux"} Distro
@@ -109,12 +108,11 @@ const buildPlatforms = [
{ 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: "x64", profile: "asan", distro: "amazonlinux", release: "2023", features: ["docker"] },
{ os: "linux", arch: "aarch64", abi: "musl", distro: "alpine", release: "3.22" },
{ os: "linux", arch: "x64", abi: "musl", distro: "alpine", release: "3.22" },
{ os: "linux", arch: "x64", abi: "musl", baseline: true, distro: "alpine", release: "3.22" },
{ 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" },
{ os: "freebsd", arch: "x64", release: "14.3" },
];
/**
@@ -125,19 +123,19 @@ const testPlatforms = [
{ os: "darwin", arch: "aarch64", release: "13", tier: "previous" },
{ os: "darwin", arch: "x64", release: "14", tier: "latest" },
{ os: "darwin", arch: "x64", release: "13", tier: "previous" },
{ os: "linux", arch: "aarch64", distro: "debian", release: "13", tier: "latest" },
{ os: "linux", arch: "x64", distro: "debian", release: "13", tier: "latest" },
{ os: "linux", arch: "x64", baseline: true, distro: "debian", release: "13", tier: "latest" },
{ os: "linux", arch: "x64", profile: "asan", distro: "debian", release: "13", tier: "latest" },
{ os: "linux", arch: "aarch64", distro: "debian", release: "12", tier: "latest" },
{ os: "linux", arch: "x64", distro: "debian", release: "12", tier: "latest" },
{ os: "linux", arch: "x64", baseline: true, distro: "debian", release: "12", tier: "latest" },
{ os: "linux", arch: "x64", profile: "asan", distro: "debian", release: "12", tier: "latest" },
{ os: "linux", arch: "aarch64", distro: "ubuntu", release: "25.04", tier: "latest" },
{ os: "linux", arch: "aarch64", distro: "ubuntu", release: "24.04", tier: "latest" },
{ os: "linux", arch: "x64", distro: "ubuntu", release: "25.04", tier: "latest" },
{ os: "linux", arch: "x64", distro: "ubuntu", release: "24.04", tier: "latest" },
{ os: "linux", arch: "x64", baseline: true, distro: "ubuntu", release: "25.04", tier: "latest" },
{ os: "linux", arch: "x64", baseline: true, distro: "ubuntu", release: "24.04", tier: "latest" },
{ os: "linux", arch: "aarch64", abi: "musl", distro: "alpine", release: "3.22", tier: "latest" },
{ os: "linux", arch: "x64", abi: "musl", distro: "alpine", release: "3.22", tier: "latest" },
{ os: "linux", arch: "x64", abi: "musl", baseline: true, distro: "alpine", release: "3.22", 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" },
];
@@ -225,7 +223,7 @@ function getImageName(platform, options) {
* @param {number} [limit]
* @link https://buildkite.com/docs/pipelines/command-step#retry-attributes
*/
function getRetry() {
function getRetry(limit = 0) {
return {
manual: {
permit_on_passed: true,
@@ -294,7 +292,7 @@ function getEc2Agent(platform, options, ec2Options) {
* @returns {string}
*/
function getCppAgent(platform, options) {
const { os, arch } = platform;
const { os, arch, distro } = platform;
if (os === "darwin") {
return {
@@ -315,7 +313,7 @@ function getCppAgent(platform, options) {
* @returns {string}
*/
function getLinkBunAgent(platform, options) {
const { os, arch } = platform;
const { os, arch, distro } = platform;
if (os === "darwin") {
return {
@@ -345,7 +343,7 @@ function getZigPlatform() {
arch: "aarch64",
abi: "musl",
distro: "alpine",
release: "3.22",
release: "3.21",
};
}
@@ -354,7 +352,14 @@ function getZigPlatform() {
* @param {PipelineOptions} options
* @returns {Agent}
*/
function getZigAgent(_platform, options) {
function getZigAgent(platform, options) {
const { arch } = platform;
// Uncomment to restore to using macOS on-prem for Zig.
// return {
// queue: "build-zig",
// };
return getEc2Agent(getZigPlatform(), options, {
instanceType: "r8g.large",
});
@@ -456,6 +461,23 @@ function getBuildCommand(target, options, label) {
return `bun run build:${buildProfile}`;
}
/**
* @param {Platform} platform
* @param {PipelineOptions} options
* @returns {Step}
*/
function getBuildVendorStep(platform, options) {
return {
key: `${getTargetKey(platform)}-build-vendor`,
label: `${getTargetLabel(platform)} - build-vendor`,
agents: getCppAgent(platform, options),
retry: getRetry(),
cancel_on_build_failing: isMergeQueue(),
env: getBuildEnv(platform, options),
command: `${getBuildCommand(platform, options)} --target dependencies`,
};
}
/**
* @param {Platform} platform
* @param {PipelineOptions} options
@@ -505,9 +527,9 @@ function getBuildZigStep(platform, options) {
const toolchain = getBuildToolchain(platform);
return {
key: `${getTargetKey(platform)}-build-zig`,
retry: getRetry(),
label: `${getTargetLabel(platform)} - build-zig`,
agents: getZigAgent(platform, options),
retry: getRetry(),
cancel_on_build_failing: isMergeQueue(),
env: getBuildEnv(platform, options),
command: `${getBuildCommand(platform, options)} --target bun-zig --toolchain ${toolchain}`,
@@ -557,6 +579,7 @@ function getBuildBunStep(platform, options) {
/**
* @typedef {Object} TestOptions
* @property {string} [buildId]
* @property {boolean} [unifiedTests]
* @property {string[]} [testFiles]
* @property {boolean} [dryRun]
*/
@@ -569,7 +592,7 @@ function getBuildBunStep(platform, options) {
*/
function getTestBunStep(platform, options, testOptions = {}) {
const { os, profile } = platform;
const { buildId, testFiles } = testOptions;
const { buildId, unifiedTests, testFiles } = testOptions;
const args = [`--step=${getTargetKey(platform)}-build-bun`];
if (buildId) {
@@ -591,7 +614,7 @@ function getTestBunStep(platform, options, testOptions = {}) {
agents: getTestAgent(platform, options),
retry: getRetry(),
cancel_on_build_failing: isMergeQueue(),
parallelism: os === "darwin" ? 2 : 10,
parallelism: unifiedTests ? undefined : os === "darwin" ? 2 : 10,
timeout_in_minutes: profile === "asan" || os === "windows" ? 45 : 30,
env: {
ASAN_OPTIONS: "allow_user_segv_handler=1:disable_coredump=0:detect_leaks=0",
@@ -660,7 +683,7 @@ function getReleaseStep(buildPlatforms, options) {
agents: {
queue: "test-darwin",
},
depends_on: buildPlatforms.filter(p => p.os !== "freebsd").map(platform => `${getTargetKey(platform)}-build-bun`),
depends_on: buildPlatforms.map(platform => `${getTargetKey(platform)}-build-bun`),
env: {
CANARY: revision,
},
@@ -773,6 +796,8 @@ function getBenchmarkStep() {
* @property {Platform[]} [buildPlatforms]
* @property {Platform[]} [testPlatforms]
* @property {string[]} [testFiles]
* @property {boolean} [unifiedBuilds]
* @property {boolean} [unifiedTests]
*/
/**
@@ -943,6 +968,22 @@ function getOptionsStep() {
default: "false",
options: booleanOptions,
},
{
key: "unified-builds",
select: "Do you want to build each platform in a single step?",
hint: "If true, builds will not be split into separate steps (this will likely slow down the build)",
required: false,
default: "false",
options: booleanOptions,
},
{
key: "unified-tests",
select: "Do you want to run tests in a single step?",
hint: "If true, tests will not be split into separate steps (this will be very slow)",
required: false,
default: "false",
options: booleanOptions,
},
],
};
}
@@ -1008,6 +1049,8 @@ async function getPipelineOptions() {
buildImages: parseBoolean(options["build-images"]),
publishImages: parseBoolean(options["publish-images"]),
testFiles: parseArray(options["test-files"]),
unifiedBuilds: parseBoolean(options["unified-builds"]),
unifiedTests: parseBoolean(options["unified-tests"]),
buildPlatforms: buildPlatformKeys?.length
? buildPlatformKeys.flatMap(key => buildProfiles.map(profile => ({ ...buildPlatformsMap.get(key), profile })))
: Array.from(buildPlatformsMap.values()),
@@ -1073,7 +1116,7 @@ async function getPipeline(options = {}) {
const imagePlatforms = new Map(
buildImages || publishImages
? [...buildPlatforms, ...testPlatforms]
.filter(({ os }) => os !== "darwin")
.filter(({ os }) => os === "linux" || os === "windows")
.map(platform => [getImageKey(platform), platform])
: [],
);
@@ -1089,7 +1132,7 @@ async function getPipeline(options = {}) {
});
}
let { skipBuilds, forceBuilds, dryRun } = options;
let { skipBuilds, forceBuilds, unifiedBuilds, dryRun } = options;
dryRun = dryRun || !!buildImages;
/** @type {string | undefined} */
@@ -1107,13 +1150,10 @@ async function getPipeline(options = {}) {
const includeASAN = !isMainBranch();
if (!buildId) {
let relevantBuildPlatforms = includeASAN
const relevantBuildPlatforms = includeASAN
? buildPlatforms
: buildPlatforms.filter(({ profile }) => profile !== "asan");
// run build-image but no build-bun yet
relevantBuildPlatforms = relevantBuildPlatforms.filter(({ os }) => os !== "freebsd");
steps.push(
...relevantBuildPlatforms.map(target => {
const imageKey = getImageKey(target);
@@ -1123,16 +1163,13 @@ async function getPipeline(options = {}) {
dependsOn.push(`${imageKey}-build-image`);
}
const steps = [];
steps.push(getBuildCppStep(target, options));
steps.push(getBuildZigStep(target, options));
steps.push(getLinkBunStep(target, options));
return getStepWithDependsOn(
{
key: getTargetKey(target),
group: getTargetLabel(target),
steps,
steps: unifiedBuilds
? [getBuildBunStep(target, options)]
: [getBuildCppStep(target, options), getBuildZigStep(target, options), getLinkBunStep(target, options)],
},
...dependsOn,
);
@@ -1141,13 +1178,13 @@ async function getPipeline(options = {}) {
}
if (!isMainBranch()) {
const { skipTests, forceTests, testFiles } = options;
const { skipTests, forceTests, unifiedTests, testFiles } = options;
if (!skipTests || forceTests) {
steps.push(
...testPlatforms.map(target => ({
key: getTargetKey(target),
group: getTargetLabel(target),
steps: [getTestBunStep(target, options, { testFiles, buildId })],
steps: [getTestBunStep(target, options, { unifiedTests, testFiles, buildId })],
})),
);
}
@@ -1190,43 +1227,6 @@ async function main() {
console.log("Generated options:", options);
}
startGroup("Querying GitHub for files...");
if (options && isBuildkite && !isMainBranch()) {
/** @type {string[]} */
let allFiles = [];
/** @type {string[]} */
let newFiles = [];
let prFileCount = 0;
try {
console.log("on buildkite: collecting new files from PR");
const per_page = 50;
const { BUILDKITE_PULL_REQUEST } = process.env;
for (let i = 1; i <= 10; i++) {
const res = await fetch(
`https://api.github.com/repos/oven-sh/bun/pulls/${BUILDKITE_PULL_REQUEST}/files?per_page=${per_page}&page=${i}`,
{ headers: { Authorization: `Bearer ${getSecret("GITHUB_TOKEN")}` } },
);
const doc = await res.json();
console.log(`-> page ${i}, found ${doc.length} items`);
if (doc.length === 0) break;
for (const { filename, status } of doc) {
prFileCount += 1;
allFiles.push(filename);
if (status !== "added") continue;
newFiles.push(filename);
}
if (doc.length < per_page) break;
}
console.log(`- PR ${BUILDKITE_PULL_REQUEST}, ${prFileCount} files, ${newFiles.length} new files`);
} catch (e) {
console.error(e);
}
if (allFiles.every(filename => filename.startsWith("docs/"))) {
console.log(`- PR is only docs, skipping tests!`);
return;
}
}
startGroup("Generating pipeline...");
const pipeline = await getPipeline(options);
if (!pipeline) {

View File

@@ -1,43 +0,0 @@
---
allowed-tools: Bash(gh issue view:*), Bash(gh search:*), Bash(gh issue list:*), Bash(gh api:*), Bash(gh issue comment:*)
description: Find duplicate GitHub issues
---
# Issue deduplication command
Find up to 3 likely duplicate issues for a given GitHub issue.
To do this, follow these steps precisely:
1. Use an agent to check if the GitHub issue (a) is closed, (b) does not need to be deduped (eg. because it is broad product feedback without a specific solution, or positive feedback), or (c) already has a duplicate detection comment (check for the exact HTML marker `<!-- dedupe-bot:marker -->` in the issue comments - ignore other bot comments). If so, do not proceed.
2. Use an agent to view a GitHub issue, and ask the agent to return a summary of the issue
3. Then, launch 5 parallel agents to search GitHub for duplicates of this issue, using diverse keywords and search approaches, using the summary from Step 2. **IMPORTANT**: Always scope searches with `repo:owner/repo` to constrain results to the current repository only.
4. Next, feed the results from Steps 2 and 3 into another agent, so that it can filter out false positives, that are likely not actually duplicates of the original issue. If there are no duplicates remaining, do not proceed.
5. Finally, comment back on the issue with a list of up to three duplicate issues (or zero, if there are no likely duplicates)
Notes (be sure to tell this to your agents, too):
- Use `gh` to interact with GitHub, rather than web fetch
- Do not use other tools, beyond `gh` (eg. don't use other MCP servers, file edit, etc.)
- Make a todo list first
- Always scope searches with `repo:owner/repo` to prevent cross-repo false positives
- For your comment, follow the following format precisely (assuming for this example that you found 3 suspected duplicates):
---
Found 3 possible duplicate issues:
1. <link to issue>
2. <link to issue>
3. <link to issue>
This issue will be automatically closed as a duplicate in 3 days.
- If your issue is a duplicate, please close it and 👍 the existing issue instead
- To prevent auto-closure, add a comment or 👎 this comment
🤖 Generated with [Claude Code](https://claude.ai/code)
<!-- dedupe-bot:marker -->
---

View File

@@ -1,88 +0,0 @@
#!/usr/bin/env bun
import { extname } from "path";
import { spawnSync } from "child_process";
const input = await Bun.stdin.json();
const toolName = input.tool_name;
const toolInput = input.tool_input || {};
const filePath = toolInput.file_path;
// Only process Write, Edit, and MultiEdit tools
if (!["Write", "Edit", "MultiEdit"].includes(toolName)) {
process.exit(0);
}
const ext = extname(filePath);
// Only format known files
if (!filePath) {
process.exit(0);
}
function formatZigFile() {
try {
// Format the Zig file
const result = spawnSync("vendor/zig/zig.exe", ["fmt", filePath], {
cwd: process.env.CLAUDE_PROJECT_DIR || process.cwd(),
encoding: "utf-8",
});
if (result.error) {
console.error(`Failed to format ${filePath}: ${result.error.message}`);
process.exit(0);
}
if (result.status !== 0) {
console.error(`zig fmt failed for ${filePath}:`);
if (result.stderr) {
console.error(result.stderr);
}
process.exit(0);
}
} catch (error) {}
}
function formatTypeScriptFile() {
try {
// Format the TypeScript file
const result = spawnSync(
"./node_modules/.bin/prettier",
["--plugin=prettier-plugin-organize-imports", "--config", ".prettierrc", "--write", filePath],
{
cwd: process.env.CLAUDE_PROJECT_DIR || process.cwd(),
encoding: "utf-8",
},
);
} catch (error) {}
}
if (ext === ".zig") {
formatZigFile();
} else if (
[
".cjs",
".css",
".html",
".js",
".json",
".jsonc",
".jsx",
".less",
".mjs",
".pcss",
".postcss",
".sass",
".scss",
".styl",
".stylus",
".toml",
".ts",
".tsx",
".yaml",
].includes(ext)
) {
formatTypeScriptFile();
}
process.exit(0);

View File

@@ -1,207 +0,0 @@
#!/usr/bin/env bun
import { basename, extname } from "path";
const input = await Bun.stdin.json();
const toolName = input.tool_name;
const toolInput = input.tool_input || {};
const command = toolInput.command || "";
const timeout = toolInput.timeout;
const cwd = input.cwd || "";
// Get environment variables from the hook context
// Note: We check process.env directly as env vars are inherited
let useSystemBun = process.env.USE_SYSTEM_BUN;
if (toolName !== "Bash" || !command) {
process.exit(0);
}
function denyWithReason(reason) {
const output = {
hookSpecificOutput: {
hookEventName: "PreToolUse",
permissionDecision: "deny",
permissionDecisionReason: reason,
},
};
console.log(JSON.stringify(output));
process.exit(0);
}
// Parse the command to extract argv0 and positional args
let tokens;
try {
// Simple shell parsing - split on spaces but respect quotes (both single and double)
tokens = command.match(/(?:[^\s"']+|"[^"]*"|'[^']*')+/g)?.map(t => t.replace(/^['"]|['"]$/g, "")) || [];
} catch {
process.exit(0);
}
if (tokens.length === 0) {
process.exit(0);
}
// Strip inline environment variable assignments (e.g., FOO=1 bun test)
const inlineEnv = new Map();
let commandStart = 0;
while (
commandStart < tokens.length &&
/^[A-Za-z_][A-Za-z0-9_]*=/.test(tokens[commandStart]) &&
!tokens[commandStart].includes("/")
) {
const [name, value = ""] = tokens[commandStart].split("=", 2);
inlineEnv.set(name, value);
commandStart++;
}
if (commandStart >= tokens.length) {
process.exit(0);
}
tokens = tokens.slice(commandStart);
useSystemBun = inlineEnv.get("USE_SYSTEM_BUN") ?? useSystemBun;
// Get the executable name (argv0)
const argv0 = basename(tokens[0], extname(tokens[0]));
// Check if it's zig or zig.exe
if (argv0 === "zig") {
// Filter out flags (starting with -) to get positional arguments
const positionalArgs = tokens.slice(1).filter(arg => !arg.startsWith("-"));
// Check if the positional args contain "build" followed by "obj"
if (positionalArgs.length >= 2 && positionalArgs[0] === "build" && positionalArgs[1] === "obj") {
denyWithReason("error: Use `bun bd` to build Bun and wait patiently");
}
}
// Check if argv0 is timeout and the command is "bun bd"
if (argv0 === "timeout") {
// Find the actual command after timeout and its arguments
const timeoutArgEndIndex = tokens.slice(1).findIndex(t => !t.startsWith("-") && !/^\d/.test(t));
if (timeoutArgEndIndex === -1) {
process.exit(0);
}
const actualCommandIndex = timeoutArgEndIndex + 1;
if (actualCommandIndex >= tokens.length) {
process.exit(0);
}
const actualCommand = basename(tokens[actualCommandIndex]);
const restArgs = tokens.slice(actualCommandIndex + 1);
// Check if it's "bun bd" or "bun-debug bd" without other positional args
if (actualCommand === "bun" || actualCommand.includes("bun-debug")) {
// Claude is a sneaky fucker
let positionalArgs = restArgs.filter(arg => !arg.startsWith("-"));
const redirectStderrToStdoutIndex = positionalArgs.findIndex(arg => arg === "2>&1");
if (redirectStderrToStdoutIndex !== -1) {
positionalArgs.splice(redirectStderrToStdoutIndex, 1);
}
const redirectStdoutToStderrIndex = positionalArgs.findIndex(arg => arg === "1>&2");
if (redirectStdoutToStderrIndex !== -1) {
positionalArgs.splice(redirectStdoutToStderrIndex, 1);
}
const redirectToFileIndex = positionalArgs.findIndex(arg => arg === ">");
if (redirectToFileIndex !== -1) {
positionalArgs.splice(redirectToFileIndex, 2);
}
const redirectToFileAppendIndex = positionalArgs.findIndex(arg => arg === ">>");
if (redirectToFileAppendIndex !== -1) {
positionalArgs.splice(redirectToFileAppendIndex, 2);
}
const redirectTOFileInlineIndex = positionalArgs.findIndex(arg => arg.startsWith(">"));
if (redirectTOFileInlineIndex !== -1) {
positionalArgs.splice(redirectTOFileInlineIndex, 1);
}
const pipeIndex = positionalArgs.findIndex(arg => arg === "|");
if (pipeIndex !== -1) {
positionalArgs = positionalArgs.slice(0, pipeIndex);
}
positionalArgs = positionalArgs.map(arg => arg.trim()).filter(Boolean);
if (positionalArgs.length === 1 && positionalArgs[0] === "bd") {
denyWithReason("error: Run `bun bd` without a timeout");
}
}
}
// Check if command is "bun .* test" or "bun-debug test" with -u/--update-snapshots AND -t/--test-name-pattern
if (argv0 === "bun" || argv0.includes("bun-debug")) {
const allArgs = tokens.slice(1);
// Check if "test" is in positional args or "bd" followed by "test"
const positionalArgs = allArgs.filter(arg => !arg.startsWith("-"));
const hasTest = positionalArgs.includes("test") || (positionalArgs[0] === "bd" && positionalArgs[1] === "test");
if (hasTest) {
const hasUpdateSnapshots = allArgs.some(arg => arg === "-u" || arg === "--update-snapshots");
const hasTestNamePattern = allArgs.some(arg => arg === "-t" || arg === "--test-name-pattern");
if (hasUpdateSnapshots && hasTestNamePattern) {
denyWithReason("error: Cannot use -u/--update-snapshots with -t/--test-name-pattern");
}
}
}
// Check if timeout option is set for "bun bd" command
if (timeout !== undefined && (argv0 === "bun" || argv0.includes("bun-debug"))) {
const positionalArgs = tokens.slice(1).filter(arg => !arg.startsWith("-"));
if (positionalArgs.length === 1 && positionalArgs[0] === "bd") {
denyWithReason("error: Run `bun bd` without a timeout");
}
}
// Check if running "bun test <file>" without USE_SYSTEM_BUN=1
if ((argv0 === "bun" || argv0.includes("bun-debug")) && useSystemBun !== "1") {
const allArgs = tokens.slice(1);
const positionalArgs = allArgs.filter(arg => !arg.startsWith("-"));
// Check if it's "test" (not "bd test")
if (positionalArgs.length >= 1 && positionalArgs[0] === "test" && positionalArgs[0] !== "bd") {
denyWithReason(
"error: In development, use `bun bd test <file>` to test your changes. If you meant to use a release version, set USE_SYSTEM_BUN=1",
);
}
}
// Check if running "bun bd test" from bun repo root or test folder without a file path
if (argv0 === "bun" || argv0.includes("bun-debug")) {
const allArgs = tokens.slice(1);
const positionalArgs = allArgs.filter(arg => !arg.startsWith("-"));
// Check if it's "bd test"
if (positionalArgs.length >= 2 && positionalArgs[0] === "bd" && positionalArgs[1] === "test") {
// Check if cwd is the bun repo root or test folder
const isBunRepoRoot = cwd === "/workspace/bun" || cwd.endsWith("/bun");
const isTestFolder = cwd.endsWith("/bun/test");
if (isBunRepoRoot || isTestFolder) {
// Check if there's a file path argument (looks like a path: contains / or has test extension)
const hasFilePath = positionalArgs
.slice(2)
.some(
arg =>
arg.includes("/") ||
arg.endsWith(".test.ts") ||
arg.endsWith(".test.js") ||
arg.endsWith(".test.tsx") ||
arg.endsWith(".test.jsx"),
);
if (!hasFilePath) {
denyWithReason(
"error: `bun bd test` from repo root or test folder will run all tests. Use `bun bd test <path>` with a specific test file.",
);
}
}
}
}
// Allow the command to proceed
process.exit(0);

View File

@@ -1,26 +0,0 @@
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/pre-bash-zig-build.js"
}
]
}
],
"PostToolUse": [
{
"matcher": "Write|Edit|MultiEdit",
"hooks": [
{
"type": "command",
"command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/post-edit-zig-format.js"
}
]
}
]
}
}

View File

@@ -1,143 +0,0 @@
language: en-US
reviews:
profile: assertive
request_changes_workflow: false
high_level_summary: false
high_level_summary_placeholder: "@coderabbitai summary"
high_level_summary_in_walkthrough: true
auto_title_placeholder: "@coderabbitai"
review_status: false
commit_status: false
fail_commit_status: false
collapse_walkthrough: false
changed_files_summary: true
sequence_diagrams: false
estimate_code_review_effort: false
assess_linked_issues: true
related_issues: true
related_prs: true
suggested_labels: false
suggested_reviewers: true
in_progress_fortune: false
poem: false
abort_on_close: true
path_filters:
- "!test/js/node/test/"
auto_review:
enabled: true
auto_incremental_review: true
drafts: false
finishing_touches:
docstrings:
enabled: false
unit_tests:
enabled: false
pre_merge_checks:
docstrings:
mode: off
title:
mode: warning
description:
mode: warning
issue_assessment:
mode: warning
tools:
shellcheck:
enabled: true
ruff:
enabled: true
markdownlint:
enabled: true
github-checks:
enabled: true
timeout_ms: 90000
languagetool:
enabled: true
enabled_only: false
level: default
biome:
enabled: true
hadolint:
enabled: true
swiftlint:
enabled: true
phpstan:
enabled: true
level: default
phpmd:
enabled: true
phpcs:
enabled: true
golangci-lint:
enabled: true
yamllint:
enabled: true
gitleaks:
enabled: true
checkov:
enabled: true
detekt:
enabled: true
eslint:
enabled: true
flake8:
enabled: true
rubocop:
enabled: true
buf:
enabled: true
regal:
enabled: true
actionlint:
enabled: true
pmd:
enabled: true
clang:
enabled: true
cppcheck:
enabled: true
semgrep:
enabled: true
circleci:
enabled: true
clippy:
enabled: true
sqlfluff:
enabled: true
prismaLint:
enabled: true
pylint:
enabled: true
oxc:
enabled: true
shopifyThemeCheck:
enabled: true
luacheck:
enabled: true
brakeman:
enabled: true
dotenvLint:
enabled: true
htmlhint:
enabled: true
checkmake:
enabled: true
osvScanner:
enabled: true
chat:
auto_reply: true
knowledge_base:
opt_out: false
code_guidelines:
enabled: true
filePatterns:
- "**/.cursor/rules/*.mdc"
- "**/CLAUDE.md"

View File

@@ -30,7 +30,7 @@ bun bd <file> <...args>
Debug logs look like this:
```zig
const log = bun.Output.scoped(.${SCOPE}, .hidden);
const log = bun.Output.scoped(.${SCOPE}, false);
// ...later
log("MY DEBUG LOG", .{})

1
.gitattributes vendored
View File

@@ -16,7 +16,6 @@
*.map text eol=lf whitespace=blank-at-eol,-blank-at-eof,-space-before-tab,tab-in-indent,tabwidth=2
*.md text eol=lf whitespace=blank-at-eol,-blank-at-eof,-space-before-tab,tab-in-indent,tabwidth=2
*.mdc text eol=lf whitespace=blank-at-eol,-blank-at-eof,-space-before-tab,tab-in-indent,tabwidth=2
*.mdx text eol=lf whitespace=blank-at-eol,-blank-at-eof,-space-before-tab,tab-in-indent,tabwidth=2
*.mjs text eol=lf whitespace=blank-at-eol,-blank-at-eof,-space-before-tab,tab-in-indent,tabwidth=2
*.mts text eol=lf whitespace=blank-at-eol,-blank-at-eof,-space-before-tab,tab-in-indent,tabwidth=2

View File

@@ -1,29 +0,0 @@
name: Auto-close duplicate issues
on:
schedule:
- cron: "0 9 * * *"
workflow_dispatch:
jobs:
auto-close-duplicates:
runs-on: ubuntu-latest
timeout-minutes: 10
concurrency:
group: auto-close-duplicates-${{ github.repository }}
cancel-in-progress: true
permissions:
contents: read
issues: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Setup Bun
uses: ./.github/actions/setup-bun
- name: Auto-close duplicate issues
run: bun run scripts/auto-close-duplicates.ts
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GITHUB_REPOSITORY: ${{ github.repository }}

View File

@@ -1,34 +0,0 @@
name: Claude Issue Dedupe
on:
issues:
types: [opened]
workflow_dispatch:
inputs:
issue_number:
description: 'Issue number to process for duplicate detection'
required: true
type: string
jobs:
claude-dedupe-issues:
runs-on: ubuntu-latest
timeout-minutes: 10
concurrency:
group: claude-dedupe-issues-${{ github.event.issue.number || inputs.issue_number }}
cancel-in-progress: true
permissions:
contents: read
issues: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Run Claude Code slash command
uses: anthropics/claude-code-base-action@beta
with:
prompt: "/dedupe ${{ github.repository }}/issues/${{ github.event.issue.number || inputs.issue_number }}"
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
claude_args: "--model claude-sonnet-4-5-20250929"
claude_env: |
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}

67
.github/workflows/claude.yml vendored Normal file
View File

@@ -0,0 +1,67 @@
name: Claude Code
on:
issue_comment:
types: [created]
pull_request_review_comment:
types: [created]
issues:
types: [opened, assigned]
pull_request_review:
types: [submitted]
jobs:
claude:
if: |
github.repository == 'oven-sh/bun' &&
(
(github.event_name == 'issue_comment' && (github.event.comment.author_association == 'MEMBER' || github.event.comment.author_association == 'OWNER' || github.event.comment.author_association == 'COLLABORATOR')) ||
(github.event_name == 'pull_request_review_comment' && (github.event.comment.author_association == 'MEMBER' || github.event.comment.author_association == 'OWNER' || github.event.comment.author_association == 'COLLABORATOR')) ||
(github.event_name == 'pull_request_review' && (github.event.review.author_association == 'MEMBER' || github.event.review.author_association == 'OWNER' || github.event.review.author_association == 'COLLABORATOR')) ||
(github.event_name == 'issues' && (github.event.issue.author_association == 'MEMBER' || github.event.issue.author_association == 'OWNER' || github.event.issue.author_association == 'COLLABORATOR'))
) &&
(github.event_name == 'issue_comment' && contains(github.event.comment.body, '@claude')) ||
(github.event_name == 'pull_request_review_comment' && contains(github.event.comment.body, '@claude')) ||
(github.event_name == 'pull_request_review' && contains(github.event.review.body, '@claude')) ||
(github.event_name == 'issues' && (contains(github.event.issue.body, '@claude') || contains(github.event.issue.title, '@claude')))
runs-on: claude
env:
IS_SANDBOX: 1
container:
image: localhost:5000/claude-bun:latest
options: --privileged --user 1000:1000
permissions:
contents: read
id-token: write
steps:
- name: Checkout repository
working-directory: /workspace/bun
run: |
git config --global user.email "claude-bot@bun.sh" && \
git config --global user.name "Claude Bot" && \
git config --global url."git@github.com:".insteadOf "https://github.com/" && \
git config --global url."git@github.com:".insteadOf "http://github.com/" && \
git config --global --add safe.directory /workspace/bun && \
git config --global push.default current && \
git config --global pull.rebase true && \
git config --global init.defaultBranch main && \
git config --global core.editor "vim" && \
git config --global color.ui auto && \
git config --global fetch.prune true && \
git config --global diff.colorMoved zebra && \
git config --global merge.conflictStyle diff3 && \
git config --global rerere.enabled true && \
git config --global core.autocrlf input
git fetch origin ${{ github.event.pull_request.head.sha }}
git checkout ${{ github.event.pull_request.head.ref }}
git reset --hard origin/${{ github.event.pull_request.head.ref }}
- name: Run Claude Code
id: claude
# TODO: switch this out once they merge their v1
uses: km-anthropic/claude-code-action@v1-dev
with:
timeout_minutes: "180"
claude_args: |
--dangerously-skip-permissions
--system-prompt "You are working on the Bun codebase"
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}

24
.github/workflows/docs.yml vendored Normal file
View File

@@ -0,0 +1,24 @@
name: Docs
on:
push:
paths:
- "docs/**"
- "packages/bun-types/**.d.ts"
- "CONTRIBUTING.md"
- "src/cli/install.sh"
- "src/cli/install.ps1"
branches:
- main
jobs:
deploy:
name: Deploy
runs-on: ubuntu-latest
if: ${{ github.repository_owner == 'oven-sh' }}
steps:
# redeploy Vercel site when a file in `docs` changes
# using VERCEL_DEPLOY_HOOK environment variable
- name: Trigger Webhook
run: |
curl -v ${{ secrets.VERCEL_DEPLOY_HOOK }}

View File

@@ -9,7 +9,7 @@ on:
pull_request:
merge_group:
env:
BUN_VERSION: "1.3.2"
BUN_VERSION: "1.2.20"
LLVM_VERSION: "19.1.7"
LLVM_VERSION_MAJOR: "19"

View File

@@ -142,8 +142,8 @@ jobs:
uses: actions/github-script@v7
with:
script: |
const closeAction = ${{ fromJson(steps.add-labels.outputs.close-action) }};
const closeAction = JSON.parse('${{ steps.add-labels.outputs.close-action }}');
// Comment with the reason
await github.rest.issues.createComment({
owner: context.repo.owner,
@@ -151,7 +151,7 @@ jobs:
issue_number: context.issue.number,
body: closeAction.comment
});
// Close the issue
await github.rest.issues.update({
owner: context.repo.owner,

19
.github/workflows/typos.yml vendored Normal file
View File

@@ -0,0 +1,19 @@
name: Typos
on:
push:
branches:
- main
pull_request:
branches:
- main
jobs:
docs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Spellcheck
uses: crate-ci/typos@v1.29.4
with:
files: docs/**/*

View File

@@ -70,7 +70,24 @@ jobs:
- name: Update SQLite if needed
if: success() && steps.check-version.outputs.current_num < steps.check-version.outputs.latest_num
run: |
./scripts/update-sqlite-amalgamation.sh ${{ steps.check-version.outputs.latest_num }} ${{ steps.check-version.outputs.latest_year }}
set -euo pipefail
TEMP_DIR=$(mktemp -d)
cd $TEMP_DIR
echo "Downloading from: https://sqlite.org/${{ steps.check-version.outputs.latest_year }}/sqlite-amalgamation-${{ steps.check-version.outputs.latest_num }}.zip"
# Download and extract latest version
wget "https://sqlite.org/${{ steps.check-version.outputs.latest_year }}/sqlite-amalgamation-${{ steps.check-version.outputs.latest_num }}.zip"
unzip "sqlite-amalgamation-${{ steps.check-version.outputs.latest_num }}.zip"
cd "sqlite-amalgamation-${{ steps.check-version.outputs.latest_num }}"
# Add header comment and copy files
echo "// clang-format off" > $GITHUB_WORKSPACE/src/bun.js/bindings/sqlite/sqlite3.c
cat sqlite3.c >> $GITHUB_WORKSPACE/src/bun.js/bindings/sqlite/sqlite3.c
echo "// clang-format off" > $GITHUB_WORKSPACE/src/bun.js/bindings/sqlite/sqlite3_local.h
cat sqlite3.h >> $GITHUB_WORKSPACE/src/bun.js/bindings/sqlite/sqlite3_local.h
- name: Create Pull Request
if: success() && steps.check-version.outputs.current_num < steps.check-version.outputs.latest_num

4
.gitignore vendored
View File

@@ -1,9 +1,7 @@
.claude/settings.local.json
.DS_Store
.env
.envrc
.eslintcache
.gdb_history
.idea
.next
.ninja_deps
@@ -191,4 +189,4 @@ scratch*.{js,ts,tsx,cjs,mjs}
scripts/lldb-inline
# We regenerate these in all the build scripts
cmake/sources/*.txt
cmake/sources/*.txt

View File

@@ -7,8 +7,4 @@ src/react-refresh.js
*.min.js
test/snippets
test/js/node/test
test/napi/node-napi-tests
bun.lock
# the output codeblocks need to stay minified
docs/bundler/minifier.mdx

View File

@@ -19,12 +19,6 @@
"options": {
"printWidth": 80
}
},
{
"files": ["src/codegen/bindgenv2/**/*.ts", "*.bindv2.ts"],
"options": {
"printWidth": 100
}
}
]
}

16
.vscode/settings.json vendored
View File

@@ -27,22 +27,18 @@
"git.ignoreLimitWarning": true,
// Zig
// "zig.initialSetupDone": true,
// "zig.buildOption": "build",
"zig.initialSetupDone": true,
"zig.buildOption": "build",
"zig.zls.zigLibPath": "${workspaceFolder}/vendor/zig/lib",
"zig.buildOnSaveArgs": [
"-Dgenerated-code=./build/debug/codegen",
"--watch",
"-fincremental"
],
// "zig.zls.buildOnSaveStep": "check",
"zig.buildArgs": ["-Dgenerated-code=./build/debug/codegen", "--watch", "-fincremental"],
"zig.zls.buildOnSaveStep": "check",
// "zig.zls.enableBuildOnSave": true,
// "zig.buildOnSave": true,
// "zig.buildFilePath": "${workspaceFolder}/build.zig",
"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.zls.enableInlayHints": false,
"[zig]": {
"editor.tabSize": 4,
"editor.useTabStops": false,

112
CLAUDE.md
View File

@@ -23,51 +23,28 @@ Tip: Bun is already installed and in $PATH. The `bd` subcommand is a package.jso
### Test Organization
If a test is for a specific numbered GitHub Issue, it should be placed in `test/regression/issue/${issueNumber}.test.ts`. Ensure the issue number is **REAL** and not a placeholder!
If no valid issue number is provided, find the best existing file to modify instead, such as;
- `test/js/bun/` - Bun-specific API tests (http, crypto, ffi, shell, etc.)
- `test/js/node/` - Node.js compatibility tests
- `test/js/web/` - Web API tests (fetch, WebSocket, streams, etc.)
- `test/cli/` - CLI command tests (install, run, test, etc.)
- `test/bundler/` - Bundler and transpiler tests. Use `itBundled` helper.
- `test/regression/issue/` - Regression tests (create one per bug fix)
- `test/bundler/` - Bundler and transpiler tests
- `test/integration/` - End-to-end integration tests
- `test/napi/` - N-API compatibility tests
- `test/v8/` - V8 C++ API compatibility tests
### Writing Tests
Tests use Bun's Jest-compatible test runner with proper test fixtures.
- For **single-file tests**, prefer `-e` over `tempDir`.
- For **multi-file tests**, prefer `tempDir` and `Bun.spawn`.
Tests use Bun's Jest-compatible test runner with proper test fixtures:
```typescript
import { test, expect } from "bun:test";
import { bunEnv, bunExe, normalizeBunSnapshot, tempDir } from "harness";
test("(single-file test) my feature", async () => {
await using proc = Bun.spawn({
cmd: [bunExe(), "-e", "console.log('Hello, world!')"],
env: bunEnv,
});
const [stdout, stderr, exitCode] = await Promise.all([
proc.stdout.text(),
proc.stderr.text(),
proc.exited,
]);
expect(normalizeBunSnapshot(stdout)).toMatchInlineSnapshot(`"Hello, world!"`);
expect(exitCode).toBe(0);
});
test("(multi-file test) my feature", async () => {
test("my feature", async () => {
// Create temp directory with test files
using dir = tempDir("test-prefix", {
"index.js": `import { foo } from "./foo.ts"; foo();`,
"foo.ts": `export function foo() { console.log("foo"); }`,
"index.js": `console.log("hello");`,
});
// Spawn Bun process
@@ -84,21 +61,15 @@ test("(multi-file test) my feature", async () => {
proc.exited,
]);
expect(exitCode).toBe(0);
// Prefer snapshot tests over expect(stdout).toBe("hello\n");
expect(normalizeBunSnapshot(stdout, dir)).toMatchInlineSnapshot(`"hello"`);
// Assert the exit code last. This gives you a more useful error message on test failure.
expect(exitCode).toBe(0);
});
```
- Always use `port: 0`. Do not hardcode ports. Do not use your own random port number function.
- Use `normalizeBunSnapshot` to normalize snapshot output of the test.
- NEVER write tests that check for no "panic" or "uncaught exception" or similar in the test output. That is NOT a valid test.
- Use `tempDir` from `"harness"` to create a temporary directory. **Do not** use `tmpdirSync` or `fs.mkdtempSync` to create temporary directories.
- When spawning processes, tests should expect(stdout).toBe(...) BEFORE expect(exitCode).toBe(0). This gives you a more useful error message on test failure.
- **CRITICAL**: Do not write flaky tests. Do not use `setTimeout` in tests. Instead, `await` the condition to be met. You are not testing the TIME PASSING, you are testing the CONDITION.
- **CRITICAL**: Verify your test fails with `USE_SYSTEM_BUN=1 bun test <file>` and passes with `bun bd test <file>`. Your test is NOT VALID if it passes with `USE_SYSTEM_BUN=1`.
## Code Architecture
@@ -107,7 +78,7 @@ test("(multi-file test) my feature", async () => {
- **Zig code** (`src/*.zig`): Core runtime, JavaScript bindings, package manager
- **C++ code** (`src/bun.js/bindings/*.cpp`): JavaScriptCore bindings, Web APIs
- **TypeScript** (`src/js/`): Built-in JavaScript modules with special syntax (see JavaScript Modules section)
- **Generated code**: Many files are auto-generated from `.classes.ts` and other sources. Bun will automatically rebuild these files when you make changes to them.
- **Generated code**: Many files are auto-generated from `.classes.ts` and other sources
### Core Source Organization
@@ -172,6 +143,19 @@ When implementing JavaScript classes in C++:
3. Add iso subspaces for classes with C++ fields
4. Cache structures in ZigGlobalObject
## Development Workflow
### Code Formatting
- `bun run prettier` - Format JS/TS files
- `bun run zig-format` - Format Zig files
- `bun run clang-format` - Format C++ files
### Watching for Changes
- `bun run watch` - Incremental Zig compilation with error checking
- `bun run watch-windows` - Windows-specific watch mode
### Code Generation
Code generation happens automatically as part of the build process. The main scripts are:
@@ -193,6 +177,47 @@ Built-in JavaScript modules use special syntax and are organized as:
- `internal/` - Internal modules not exposed to users
- `builtins/` - Core JavaScript builtins (streams, console, etc.)
### Special Syntax in Built-in Modules
1. **`$` prefix** - Access to private properties and JSC intrinsics:
```js
const arr = $Array.from(...); // Private global
map.$set(...); // Private method
const arr2 = $newArrayWithSize(5); // JSC intrinsic
```
2. **`require()`** - Must use string literals, resolved at compile time:
```js
const fs = require("fs"); // Directly loads by numeric ID
```
3. **Debug helpers**:
- `$debug()` - Like console.log but stripped in release builds
- `$assert()` - Assertions stripped in release builds
- `if($debug) {}` - Check if debug env var is set
4. **Platform detection**: `process.platform` and `process.arch` are inlined and dead-code eliminated
5. **Export syntax**: Use `export default` which gets converted to a return statement:
```js
export default {
readFile,
writeFile,
};
```
Note: These are NOT ES modules. The preprocessor converts `$` to `@` (JSC's actual syntax) and handles the special functions.
## CI
Bun uses BuildKite for CI. To get the status of a PR, you can use the following command:
```bash
bun ci
```
## Important Development Notes
1. **Never use `bun test` or `bun <file>` directly** - always use `bun bd test` or `bun bd <command>`. `bun bd` compiles & runs the debug build.
@@ -204,8 +229,19 @@ Built-in JavaScript modules use special syntax and are organized as:
7. **Avoid shell commands** - Don't use `find` or `grep` in tests; use Bun's Glob and built-in tools
8. **Memory management** - In Zig code, be careful with allocators and use defer for cleanup
9. **Cross-platform** - Run `bun run zig:check-all` to compile the Zig code on all platforms when making platform-specific changes
10. **Debug builds** - Use `BUN_DEBUG_QUIET_LOGS=1` to disable debug logging, or `BUN_DEBUG_<scopeName>=1` to enable specific `Output.scoped(.${scopeName}, .visible)`s
10. **Debug builds** - Use `BUN_DEBUG_QUIET_LOGS=1` to disable debug logging, or `BUN_DEBUG_<scope>=1` to enable specific scopes
11. **Be humble & honest** - NEVER overstate what you got done or what actually works in commits, PRs or in messages to the user.
12. **Branch names must start with `claude/`** - This is a requirement for the CI to work.
**ONLY** push up changes after running `bun bd test <file>` and ensuring your tests pass.
## Key APIs and Features
### Bun-Specific APIs
- **Bun.serve()** - High-performance HTTP server
- **Bun.spawn()** - Process spawning with better performance than Node.js
- **Bun.file()** - Fast file I/O operations
- **Bun.write()** - Unified API for writing to files, stdout, etc.
- **Bun.$ (Shell)** - Cross-platform shell scripting
- **Bun.SQLite** - Native SQLite integration
- **Bun.FFI** - Call native libraries from JavaScript
- **Bun.Glob** - Fast file pattern matching

View File

@@ -24,6 +24,7 @@ if(CMAKE_HOST_APPLE)
include(SetupMacSDK)
endif()
include(SetupLLVM)
include(SetupCcache)
# --- Project ---
@@ -47,16 +48,6 @@ include(SetupEsbuild)
include(SetupZig)
include(SetupRust)
find_program(SCCACHE_PROGRAM sccache)
if(SCCACHE_PROGRAM AND NOT DEFINED ENV{NO_SCCACHE})
include(SetupSccache)
else()
find_program(CCACHE_PROGRAM ccache)
if(CCACHE_PROGRAM)
include(SetupCcache)
endif()
endif()
# Generate dependency versions header
include(GenerateDependencyVersions)

View File

@@ -2,40 +2,26 @@ Configuring a development environment for Bun can take 10-30 minutes depending o
If you are using Windows, please refer to [this guide](https://bun.com/docs/project/building-windows)
## Using Nix (Alternative)
A Nix flake is provided as an alternative to manual dependency installation:
```bash
nix develop
# or explicitly use the pure shell
# nix develop .#pure
export CMAKE_SYSTEM_PROCESSOR=$(uname -m)
bun bd
```
This provides all dependencies in an isolated, reproducible environment without requiring sudo.
## Install Dependencies (Manual)
## Install Dependencies
Using your system's package manager, install Bun's dependencies:
{% codetabs group="os" %}
```bash#macOS (Homebrew)
$ brew install automake cmake coreutils gnu-sed go icu4c libiconv libtool ninja pkg-config rust ruby sccache
$ brew install automake ccache cmake coreutils gnu-sed go icu4c libiconv libtool ninja pkg-config rust ruby
```
```bash#Ubuntu/Debian
$ sudo apt install curl wget lsb-release software-properties-common cargo cmake git golang libtool ninja-build pkg-config rustc ruby-full xz-utils
$ sudo apt install curl wget lsb-release software-properties-common cargo ccache cmake git golang libtool ninja-build pkg-config rustc ruby-full xz-utils
```
```bash#Arch
$ sudo pacman -S base-devel cmake git go libiconv libtool make ninja pkg-config python rust sed unzip ruby
$ sudo pacman -S base-devel ccache cmake git go libiconv libtool make ninja pkg-config python rust sed unzip ruby
```
```bash#Fedora
$ sudo dnf install cargo clang19 llvm19 lld19 cmake git golang libtool ninja-build pkg-config rustc ruby libatomic-static libstdc++-static sed unzip which libicu-devel 'perl(Math::BigInt)'
$ sudo dnf install cargo clang19 llvm19 lld19 ccache cmake git golang libtool ninja-build pkg-config rustc ruby libatomic-static libstdc++-static sed unzip which libicu-devel 'perl(Math::BigInt)'
```
```bash#openSUSE Tumbleweed
@@ -65,44 +51,6 @@ $ brew install bun
{% /codetabs %}
### Optional: Install `sccache`
sccache is used to cache compilation artifacts, significantly speeding up builds. It must be installed with S3 support:
```bash
# For macOS
$ brew install sccache
# For Linux. Note that the version in your package manager may not have S3 support.
$ cargo install sccache --features=s3
```
This will install `sccache` with S3 support. Our build scripts will automatically detect and use `sccache` with our shared S3 cache. **Note**: Not all versions of `sccache` are compiled with S3 support, hence we recommend installing it via `cargo`.
#### Registering AWS Credentials for `sccache` (Core Developers Only)
Core developers have write access to the shared S3 cache. To enable write access, you must log in with AWS credentials. The easiest way to do this is to use the [`aws` CLI](https://aws.amazon.com/cli/) and invoke [`aws configure` to provide your AWS security info](https://docs.aws.amazon.com/cli/latest/reference/configure/).
The `cmake` scripts should automatically detect your AWS credentials from the environment or the `~/.aws/credentials` file.
<details>
<summary>Logging in to the `aws` CLI</summary>
1. Install the AWS CLI by following [the official guide](https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html).
2. Log in to your AWS account console. A team member should provide you with your credentials.
3. Click your name in the top right > Security credentials.
4. Scroll to "Access keys" and create a new access key.
5. Run `aws configure` in your terminal and provide the access key ID and secret access key when prompted.
</details>
<details>
<summary>Common Issues You May Encounter</summary>
- To confirm that the cache is being used, you can use the `sccache --show-stats` command right after a build. This will expose very useful statistics, including cache hits/misses.
- If you have multiple AWS profiles configured, ensure that the correct profile is set in the `AWS_PROFILE` environment variable.
- `sccache` follows a server-client model. If you run into weird issues where `sccache` refuses to use S3, even though you have AWS credentials configured, try killing any running `sccache` servers with `sccache --stop-server` and then re-running the build.
</details>
## Install LLVM
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:
@@ -201,7 +149,7 @@ Bun generally takes about 2.5 minutes to compile a debug build when there are Zi
- Batch up your changes
- Ensure zls is running with incremental watching for LSP errors (if you use VSCode and install Zig and run `bun run build` once to download Zig, this should just work)
- Prefer using the debugger ("CodeLLDB" in VSCode) to step through the code.
- Use debug logs. `BUN_DEBUG_<scope>=1` will enable debug logging for the corresponding `Output.scoped(.<scope>, .hidden)` logs. You can also set `BUN_DEBUG_QUIET_LOGS=1` to disable all debug logging that isn't explicitly enabled. To dump debug logs into a file, `BUN_DEBUG=<path-to-file>.log`. Debug logs are aggressively removed in release builds.
- Use debug logs. `BUN_DEBUG_<scope>=1` will enable debug logging for the corresponding `Output.scoped(.<scope>, false)` logs. You can also set `BUN_DEBUG_QUIET_LOGS=1` to disable all debug logging that isn't explicitly enabled. To dump debug lgos into a file, `BUN_DEBUG=<path-to-file>.log`. Debug logs are aggressively removed in release builds.
- src/js/\*\*.ts changes are pretty much instant to rebuild. C++ changes are a bit slower, but still much faster than the Zig code (Zig is one compilation unit, C++ is many).
## Code generation scripts
@@ -369,6 +317,15 @@ $ bun run build -DUSE_STATIC_LIBATOMIC=OFF
The built version of Bun may not work on other systems if compiled this way.
### ccache conflicts with building TinyCC on macOS
If you run into issues with `ccache` when building TinyCC, try reinstalling ccache
```bash
brew uninstall ccache
brew install ccache
```
## Using bun-debug
- Disable logging: `BUN_DEBUG_QUIET_LOGS=1 bun-debug ...` (to disable all debug logging)

2
LATEST
View File

@@ -1 +1 @@
1.3.3
1.2.23

View File

@@ -54,7 +54,7 @@ Bun supports Linux (x64 & arm64), macOS (x64 & Apple Silicon) and Windows (x64).
curl -fsSL https://bun.com/install | bash
# on windows
powershell -c "irm bun.sh/install.ps1 | iex"
powershell -c "irm bun.com/install.ps1 | iex"
# with npm
npm install -g bun
@@ -104,13 +104,13 @@ bun upgrade --canary
- [File types (Loaders)](https://bun.com/docs/runtime/loaders)
- [TypeScript](https://bun.com/docs/runtime/typescript)
- [JSX](https://bun.com/docs/runtime/jsx)
- [Environment variables](https://bun.com/docs/runtime/environment-variables)
- [Environment variables](https://bun.com/docs/runtime/env)
- [Bun APIs](https://bun.com/docs/runtime/bun-apis)
- [Web APIs](https://bun.com/docs/runtime/web-apis)
- [Node.js compatibility](https://bun.com/docs/runtime/nodejs-compat)
- [Node.js compatibility](https://bun.com/docs/runtime/nodejs-apis)
- [Single-file executable](https://bun.com/docs/bundler/executables)
- [Plugins](https://bun.com/docs/runtime/plugins)
- [Watch mode / Hot Reloading](https://bun.com/docs/runtime/watch-mode)
- [Watch mode / Hot Reloading](https://bun.com/docs/runtime/hot)
- [Module resolution](https://bun.com/docs/runtime/modules)
- [Auto-install](https://bun.com/docs/runtime/autoimport)
- [bunfig.toml](https://bun.com/docs/runtime/bunfig)
@@ -230,7 +230,7 @@ bun upgrade --canary
- Ecosystem
- [Use React and JSX](https://bun.com/guides/ecosystem/react)
- [Use Gel with Bun](https://bun.com/guides/ecosystem/gel)
- [Use EdgeDB with Bun](https://bun.com/guides/ecosystem/edgedb)
- [Use Prisma with Bun](https://bun.com/guides/ecosystem/prisma)
- [Add Sentry to a Bun app](https://bun.com/guides/ecosystem/sentry)
- [Create a Discord bot](https://bun.com/guides/ecosystem/discordjs)

View File

@@ -1,29 +1,40 @@
# Create T3 App
# `install` benchmark
This is a [T3 Stack](https://create.t3.gg/) project bootstrapped with `create-t3-app`.
Requires [`hyperfine`](https://github.com/sharkdp/hyperfine). The goal of this benchmark is to compare installation performance of Bun with other package managers _when caches are hot_.
## What's next? How do I make an app with this?
### With lockfile, online mode
We try to keep this project as simple as possible, so you can start with just the scaffolding we set up for you, and add additional things later when they become necessary.
To run the benchmark with the standard "install" command for each package manager:
If you are not familiar with the different technologies used in this project, please refer to the respective docs. If you still are in the wind, please join our [Discord](https://t3.gg/discord) and ask for help.
```sh
$ hyperfine --prepare 'rm -rf node_modules' --warmup 1 --runs 3 'bun install' 'pnpm install' 'yarn' 'npm install'
```
- [Next.js](https://nextjs.org)
- [NextAuth.js](https://next-auth.js.org)
- [Prisma](https://prisma.io)
- [Drizzle](https://orm.drizzle.team)
- [Tailwind CSS](https://tailwindcss.com)
- [tRPC](https://trpc.io)
### With lockfile, offline mode
## Learn More
Even though all packages are cached, some tools may hit the npm API during the version resolution step. (This is not the same as re-downloading a package.) To entirely avoid network calls, the other package managers require `--prefer-offline/--offline` flag. To run the benchmark using "offline" mode:
To learn more about the [T3 Stack](https://create.t3.gg/), take a look at the following resources:
```sh
$ hyperfine --prepare 'rm -rf node_modules' --runs 1 'bun install' 'pnpm install --prefer-offline' 'yarn --offline' 'npm install --prefer-offline'
```
- [Documentation](https://create.t3.gg/)
- [Learn the T3 Stack](https://create.t3.gg/en/faq#what-learning-resources-are-currently-available) — Check out these awesome tutorials
### Without lockfile, offline mode
You can check out the [create-t3-app GitHub repository](https://github.com/t3-oss/create-t3-app) — your feedback and contributions are welcome!
To run the benchmark with offline mode but without lockfiles:
## How do I deploy this?
```sh
$ hyperfine --prepare 'rm -rf node_modules' --warmup 1 'rm bun.lock && bun install' 'rm pnpm-lock.yaml && pnpm install --prefer-offline' 'rm yarn.lock && yarn --offline' 'rm package-lock.json && npm install --prefer-offline'
```
Follow our deployment guides for [Vercel](https://create.t3.gg/en/deployment/vercel), [Netlify](https://create.t3.gg/en/deployment/netlify) and [Docker](https://create.t3.gg/en/deployment/docker) for more information.
##
To check that the app is working as expected:
```
$ bun run dev
$ npm run dev
$ yarn dev
$ pnpm dev
```
Then visit [http://localhost:3000](http://localhost:3000).

View File

@@ -0,0 +1,18 @@
/**
* By default, Remix will handle hydrating your app on the client for you.
* You are free to delete this file if you'd like to, but if you ever want it revealed again, you can run `npx remix reveal` ✨
* For more information, see https://remix.run/docs/en/main/file-conventions/entry.client
*/
import { RemixBrowser } from "@remix-run/react";
import { startTransition, StrictMode } from "react";
import { hydrateRoot } from "react-dom/client";
startTransition(() => {
hydrateRoot(
document,
<StrictMode>
<RemixBrowser />
</StrictMode>,
);
});

View File

@@ -0,0 +1,101 @@
/**
* By default, Remix will handle generating the HTTP Response for you.
* You are free to delete this file if you'd like to, but if you ever want it revealed again, you can run `npx remix reveal` ✨
* For more information, see https://remix.run/docs/en/main/file-conventions/entry.server
*/
import type { EntryContext } from "@remix-run/node";
import { Response } from "@remix-run/node";
import { RemixServer } from "@remix-run/react";
import isbot from "isbot";
import { PassThrough } from "node:stream";
import { renderToPipeableStream } from "react-dom/server";
const ABORT_DELAY = 5_000;
export default function handleRequest(
request: Request,
responseStatusCode: number,
responseHeaders: Headers,
remixContext: EntryContext,
) {
return isbot(request.headers.get("user-agent"))
? handleBotRequest(request, responseStatusCode, responseHeaders, remixContext)
: handleBrowserRequest(request, responseStatusCode, responseHeaders, remixContext);
}
function handleBotRequest(
request: Request,
responseStatusCode: number,
responseHeaders: Headers,
remixContext: EntryContext,
) {
return new Promise((resolve, reject) => {
const { pipe, abort } = renderToPipeableStream(
<RemixServer context={remixContext} url={request.url} abortDelay={ABORT_DELAY} />,
{
onAllReady() {
const body = new PassThrough();
responseHeaders.set("Content-Type", "text/html");
resolve(
new Response(body, {
headers: responseHeaders,
status: responseStatusCode,
}),
);
pipe(body);
},
onShellError(error: unknown) {
reject(error);
},
onError(error: unknown) {
responseStatusCode = 500;
console.error(error);
},
},
);
setTimeout(abort, ABORT_DELAY);
});
}
function handleBrowserRequest(
request: Request,
responseStatusCode: number,
responseHeaders: Headers,
remixContext: EntryContext,
) {
return new Promise((resolve, reject) => {
const { pipe, abort } = renderToPipeableStream(
<RemixServer context={remixContext} url={request.url} abortDelay={ABORT_DELAY} />,
{
onShellReady() {
const body = new PassThrough();
responseHeaders.set("Content-Type", "text/html");
resolve(
new Response(body, {
headers: responseHeaders,
status: responseStatusCode,
}),
);
pipe(body);
},
onShellError(error: unknown) {
reject(error);
},
onError(error: unknown) {
console.error(error);
responseStatusCode = 500;
},
},
);
setTimeout(abort, ABORT_DELAY);
});
}

View File

@@ -0,0 +1,20 @@
import { Links, LiveReload, Meta, Outlet, Scripts, ScrollRestoration } from "@remix-run/react";
export default function App() {
return (
<html lang="en">
<head>
<meta charSet="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
<Meta />
<Links />
</head>
<body>
<Outlet />
<ScrollRestoration />
<Scripts />
<LiveReload />
</body>
</html>
);
}

View File

@@ -0,0 +1,30 @@
import type { V2_MetaFunction } from "@remix-run/node";
export const meta: V2_MetaFunction = () => {
return [{ title: "New Remix App" }];
};
export default function Index() {
return (
<div style={{ fontFamily: "system-ui, sans-serif", lineHeight: "1.4" }}>
<h1>Welcome to Remix</h1>
<ul>
<li>
<a target="_blank" href="https://remix.run/tutorials/blog" rel="noreferrer">
15m Quickstart Blog Tutorial
</a>
</li>
<li>
<a target="_blank" href="https://remix.run/tutorials/jokes" rel="noreferrer">
Deep Dive Jokes App Tutorial
</a>
</li>
<li>
<a target="_blank" href="https://remix.run/docs" rel="noreferrer">
Remix Docs
</a>
</li>
</ul>
</div>
);
}

View File

@@ -1,488 +0,0 @@
{
"lockfileVersion": 1,
"workspaces": {
"": {
"name": "installbench",
"dependencies": {
"@auth/drizzle-adapter": "^1.7.2",
"@t3-oss/env-nextjs": "^0.12.0",
"@tanstack/react-query": "^5.69.0",
"@trpc/client": "^11.0.0",
"@trpc/react-query": "^11.0.0",
"@trpc/server": "^11.0.0",
"drizzle-orm": "^0.41.0",
"esbuild": "^0.25.11",
"next": "^15.2.3",
"next-auth": "5.0.0-beta.25",
"postgres": "^3.4.4",
"react": "^19.0.0",
"react-dom": "^19.0.0",
"server-only": "^0.0.1",
"superjson": "^2.2.1",
"zod": "^3.24.2",
},
"devDependencies": {
"@biomejs/biome": "1.9.4",
"@tailwindcss/postcss": "^4.0.15",
"@types/node": "^20.14.10",
"@types/react": "^19.0.0",
"@types/react-dom": "^19.0.0",
"drizzle-kit": "^0.30.5",
"postcss": "^8.5.3",
"tailwindcss": "^4.0.15",
"typescript": "^5.8.2",
},
},
},
"packages": {
"@alloc/quick-lru": ["@alloc/quick-lru@5.2.0", "", {}, "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw=="],
"@auth/core": ["@auth/core@0.41.1", "", { "dependencies": { "@panva/hkdf": "1.2.1", "jose": "6.1.0", "oauth4webapi": "3.8.2", "preact": "10.24.3", "preact-render-to-string": "6.5.11" } }, "sha512-t9cJ2zNYAdWMacGRMT6+r4xr1uybIdmYa49calBPeTqwgAFPV/88ac9TEvCR85pvATiSPt8VaNf+Gt24JIT/uw=="],
"@auth/drizzle-adapter": ["@auth/drizzle-adapter@1.11.1", "", { "dependencies": { "@auth/core": "0.41.1" } }, "sha512-cQTvDZqsyF7RPhDm/B6SvqdVP9EzQhy3oM4Muu7fjjmSYFLbSR203E6dH631ZHSKDn2b4WZkfMnjPDzRsPSAeA=="],
"@biomejs/biome": ["@biomejs/biome@1.9.4", "", { "optionalDependencies": { "@biomejs/cli-darwin-arm64": "1.9.4", "@biomejs/cli-darwin-x64": "1.9.4", "@biomejs/cli-linux-arm64": "1.9.4", "@biomejs/cli-linux-arm64-musl": "1.9.4", "@biomejs/cli-linux-x64": "1.9.4", "@biomejs/cli-linux-x64-musl": "1.9.4", "@biomejs/cli-win32-arm64": "1.9.4", "@biomejs/cli-win32-x64": "1.9.4" }, "bin": { "biome": "bin/biome" } }, "sha512-1rkd7G70+o9KkTn5KLmDYXihGoTaIGO9PIIN2ZB7UJxFrWw04CZHPYiMRjYsaDvVV7hP1dYNRLxSANLaBFGpog=="],
"@biomejs/cli-darwin-arm64": ["@biomejs/cli-darwin-arm64@1.9.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-bFBsPWrNvkdKrNCYeAp+xo2HecOGPAy9WyNyB/jKnnedgzl4W4Hb9ZMzYNbf8dMCGmUdSavlYHiR01QaYR58cw=="],
"@biomejs/cli-darwin-x64": ["@biomejs/cli-darwin-x64@1.9.4", "", { "os": "darwin", "cpu": "x64" }, "sha512-ngYBh/+bEedqkSevPVhLP4QfVPCpb+4BBe2p7Xs32dBgs7rh9nY2AIYUL6BgLw1JVXV8GlpKmb/hNiuIxfPfZg=="],
"@biomejs/cli-linux-arm64": ["@biomejs/cli-linux-arm64@1.9.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-fJIW0+LYujdjUgJJuwesP4EjIBl/N/TcOX3IvIHJQNsAqvV2CHIogsmA94BPG6jZATS4Hi+xv4SkBBQSt1N4/g=="],
"@biomejs/cli-linux-arm64-musl": ["@biomejs/cli-linux-arm64-musl@1.9.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-v665Ct9WCRjGa8+kTr0CzApU0+XXtRgwmzIf1SeKSGAv+2scAlW6JR5PMFo6FzqqZ64Po79cKODKf3/AAmECqA=="],
"@biomejs/cli-linux-x64": ["@biomejs/cli-linux-x64@1.9.4", "", { "os": "linux", "cpu": "x64" }, "sha512-lRCJv/Vi3Vlwmbd6K+oQ0KhLHMAysN8lXoCI7XeHlxaajk06u7G+UsFSO01NAs5iYuWKmVZjmiOzJ0OJmGsMwg=="],
"@biomejs/cli-linux-x64-musl": ["@biomejs/cli-linux-x64-musl@1.9.4", "", { "os": "linux", "cpu": "x64" }, "sha512-gEhi/jSBhZ2m6wjV530Yy8+fNqG8PAinM3oV7CyO+6c3CEh16Eizm21uHVsyVBEB6RIM8JHIl6AGYCv6Q6Q9Tg=="],
"@biomejs/cli-win32-arm64": ["@biomejs/cli-win32-arm64@1.9.4", "", { "os": "win32", "cpu": "arm64" }, "sha512-tlbhLk+WXZmgwoIKwHIHEBZUwxml7bRJgk0X2sPyNR3S93cdRq6XulAZRQJ17FYGGzWne0fgrXBKpl7l4M87Hg=="],
"@biomejs/cli-win32-x64": ["@biomejs/cli-win32-x64@1.9.4", "", { "os": "win32", "cpu": "x64" }, "sha512-8Y5wMhVIPaWe6jw2H+KlEm4wP/f7EW3810ZLmDlrEEy5KvBsb9ECEfu/kMWD484ijfQ8+nIi0giMgu9g1UAuuA=="],
"@drizzle-team/brocli": ["@drizzle-team/brocli@0.10.2", "", {}, "sha512-z33Il7l5dKjUgGULTqBsQBQwckHh5AbIuxhdsIxDDiZAzBOrZO6q9ogcWC65kU382AfynTfgNumVcNIjuIua6w=="],
"@emnapi/runtime": ["@emnapi/runtime@1.6.0", "", { "dependencies": { "tslib": "2.8.1" } }, "sha512-obtUmAHTMjll499P+D9A3axeJFlhdjOWdKUNs/U6QIGT7V5RjcUW1xToAzjvmgTSQhDbYn/NwfTRoJcQ2rNBxA=="],
"@esbuild-kit/core-utils": ["@esbuild-kit/core-utils@3.3.2", "", { "dependencies": { "esbuild": "0.18.20", "source-map-support": "0.5.21" } }, "sha512-sPRAnw9CdSsRmEtnsl2WXWdyquogVpB3yZ3dgwJfe8zrOzTsV7cJvmwrKVa+0ma5BoiGJ+BoqkMvawbayKUsqQ=="],
"@esbuild-kit/esm-loader": ["@esbuild-kit/esm-loader@2.6.5", "", { "dependencies": { "@esbuild-kit/core-utils": "3.3.2", "get-tsconfig": "4.13.0" } }, "sha512-FxEMIkJKnodyA1OaCUoEvbYRkoZlLZ4d/eXFu9Fh8CbBBgP5EmZxrfTRyN0qpXZ4vOvqnE5YdRdcrmUUXuU+dA=="],
"@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.25.11", "", { "os": "aix", "cpu": "ppc64" }, "sha512-Xt1dOL13m8u0WE8iplx9Ibbm+hFAO0GsU2P34UNoDGvZYkY8ifSiy6Zuc1lYxfG7svWE2fzqCUmFp5HCn51gJg=="],
"@esbuild/android-arm": ["@esbuild/android-arm@0.25.11", "", { "os": "android", "cpu": "arm" }, "sha512-uoa7dU+Dt3HYsethkJ1k6Z9YdcHjTrSb5NUy66ZfZaSV8hEYGD5ZHbEMXnqLFlbBflLsl89Zke7CAdDJ4JI+Gg=="],
"@esbuild/android-arm64": ["@esbuild/android-arm64@0.25.11", "", { "os": "android", "cpu": "arm64" }, "sha512-9slpyFBc4FPPz48+f6jyiXOx/Y4v34TUeDDXJpZqAWQn/08lKGeD8aDp9TMn9jDz2CiEuHwfhRmGBvpnd/PWIQ=="],
"@esbuild/android-x64": ["@esbuild/android-x64@0.25.11", "", { "os": "android", "cpu": "x64" }, "sha512-Sgiab4xBjPU1QoPEIqS3Xx+R2lezu0LKIEcYe6pftr56PqPygbB7+szVnzoShbx64MUupqoE0KyRlN7gezbl8g=="],
"@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.25.11", "", { "os": "darwin", "cpu": "arm64" }, "sha512-VekY0PBCukppoQrycFxUqkCojnTQhdec0vevUL/EDOCnXd9LKWqD/bHwMPzigIJXPhC59Vd1WFIL57SKs2mg4w=="],
"@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.25.11", "", { "os": "darwin", "cpu": "x64" }, "sha512-+hfp3yfBalNEpTGp9loYgbknjR695HkqtY3d3/JjSRUyPg/xd6q+mQqIb5qdywnDxRZykIHs3axEqU6l1+oWEQ=="],
"@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.25.11", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-CmKjrnayyTJF2eVuO//uSjl/K3KsMIeYeyN7FyDBjsR3lnSJHaXlVoAK8DZa7lXWChbuOk7NjAc7ygAwrnPBhA=="],
"@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.25.11", "", { "os": "freebsd", "cpu": "x64" }, "sha512-Dyq+5oscTJvMaYPvW3x3FLpi2+gSZTCE/1ffdwuM6G1ARang/mb3jvjxs0mw6n3Lsw84ocfo9CrNMqc5lTfGOw=="],
"@esbuild/linux-arm": ["@esbuild/linux-arm@0.25.11", "", { "os": "linux", "cpu": "arm" }, "sha512-TBMv6B4kCfrGJ8cUPo7vd6NECZH/8hPpBHHlYI3qzoYFvWu2AdTvZNuU/7hsbKWqu/COU7NIK12dHAAqBLLXgw=="],
"@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.25.11", "", { "os": "linux", "cpu": "arm64" }, "sha512-Qr8AzcplUhGvdyUF08A1kHU3Vr2O88xxP0Tm8GcdVOUm25XYcMPp2YqSVHbLuXzYQMf9Bh/iKx7YPqECs6ffLA=="],
"@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.25.11", "", { "os": "linux", "cpu": "ia32" }, "sha512-TmnJg8BMGPehs5JKrCLqyWTVAvielc615jbkOirATQvWWB1NMXY77oLMzsUjRLa0+ngecEmDGqt5jiDC6bfvOw=="],
"@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.25.11", "", { "os": "linux", "cpu": "none" }, "sha512-DIGXL2+gvDaXlaq8xruNXUJdT5tF+SBbJQKbWy/0J7OhU8gOHOzKmGIlfTTl6nHaCOoipxQbuJi7O++ldrxgMw=="],
"@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.25.11", "", { "os": "linux", "cpu": "none" }, "sha512-Osx1nALUJu4pU43o9OyjSCXokFkFbyzjXb6VhGIJZQ5JZi8ylCQ9/LFagolPsHtgw6himDSyb5ETSfmp4rpiKQ=="],
"@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.25.11", "", { "os": "linux", "cpu": "ppc64" }, "sha512-nbLFgsQQEsBa8XSgSTSlrnBSrpoWh7ioFDUmwo158gIm5NNP+17IYmNWzaIzWmgCxq56vfr34xGkOcZ7jX6CPw=="],
"@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.25.11", "", { "os": "linux", "cpu": "none" }, "sha512-HfyAmqZi9uBAbgKYP1yGuI7tSREXwIb438q0nqvlpxAOs3XnZ8RsisRfmVsgV486NdjD7Mw2UrFSw51lzUk1ww=="],
"@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.25.11", "", { "os": "linux", "cpu": "s390x" }, "sha512-HjLqVgSSYnVXRisyfmzsH6mXqyvj0SA7pG5g+9W7ESgwA70AXYNpfKBqh1KbTxmQVaYxpzA/SvlB9oclGPbApw=="],
"@esbuild/linux-x64": ["@esbuild/linux-x64@0.25.11", "", { "os": "linux", "cpu": "x64" }, "sha512-HSFAT4+WYjIhrHxKBwGmOOSpphjYkcswF449j6EjsjbinTZbp8PJtjsVK1XFJStdzXdy/jaddAep2FGY+wyFAQ=="],
"@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.25.11", "", { "os": "none", "cpu": "arm64" }, "sha512-hr9Oxj1Fa4r04dNpWr3P8QKVVsjQhqrMSUzZzf+LZcYjZNqhA3IAfPQdEh1FLVUJSiu6sgAwp3OmwBfbFgG2Xg=="],
"@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.25.11", "", { "os": "none", "cpu": "x64" }, "sha512-u7tKA+qbzBydyj0vgpu+5h5AeudxOAGncb8N6C9Kh1N4n7wU1Xw1JDApsRjpShRpXRQlJLb9wY28ELpwdPcZ7A=="],
"@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.25.11", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-Qq6YHhayieor3DxFOoYM1q0q1uMFYb7cSpLD2qzDSvK1NAvqFi8Xgivv0cFC6J+hWVw2teCYltyy9/m/14ryHg=="],
"@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.25.11", "", { "os": "openbsd", "cpu": "x64" }, "sha512-CN+7c++kkbrckTOz5hrehxWN7uIhFFlmS/hqziSFVWpAzpWrQoAG4chH+nN3Be+Kzv/uuo7zhX716x3Sn2Jduw=="],
"@esbuild/openharmony-arm64": ["@esbuild/openharmony-arm64@0.25.11", "", { "os": "none", "cpu": "arm64" }, "sha512-rOREuNIQgaiR+9QuNkbkxubbp8MSO9rONmwP5nKncnWJ9v5jQ4JxFnLu4zDSRPf3x4u+2VN4pM4RdyIzDty/wQ=="],
"@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.25.11", "", { "os": "sunos", "cpu": "x64" }, "sha512-nq2xdYaWxyg9DcIyXkZhcYulC6pQ2FuCgem3LI92IwMgIZ69KHeY8T4Y88pcwoLIjbed8n36CyKoYRDygNSGhA=="],
"@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.25.11", "", { "os": "win32", "cpu": "arm64" }, "sha512-3XxECOWJq1qMZ3MN8srCJ/QfoLpL+VaxD/WfNRm1O3B4+AZ/BnLVgFbUV3eiRYDMXetciH16dwPbbHqwe1uU0Q=="],
"@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.25.11", "", { "os": "win32", "cpu": "ia32" }, "sha512-3ukss6gb9XZ8TlRyJlgLn17ecsK4NSQTmdIXRASVsiS2sQ6zPPZklNJT5GR5tE/MUarymmy8kCEf5xPCNCqVOA=="],
"@esbuild/win32-x64": ["@esbuild/win32-x64@0.25.11", "", { "os": "win32", "cpu": "x64" }, "sha512-D7Hpz6A2L4hzsRpPaCYkQnGOotdUpDzSGRIv9I+1ITdHROSFUWW95ZPZWQmGka1Fg7W3zFJowyn9WGwMJ0+KPA=="],
"@img/colour": ["@img/colour@1.0.0", "", {}, "sha512-A5P/LfWGFSl6nsckYtjw9da+19jB8hkJ6ACTGcDfEJ0aE+l2n2El7dsVM7UVHZQ9s2lmYMWlrS21YLy2IR1LUw=="],
"@img/sharp-darwin-arm64": ["@img/sharp-darwin-arm64@0.34.4", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-arm64": "1.2.3" }, "os": "darwin", "cpu": "arm64" }, "sha512-sitdlPzDVyvmINUdJle3TNHl+AG9QcwiAMsXmccqsCOMZNIdW2/7S26w0LyU8euiLVzFBL3dXPwVCq/ODnf2vA=="],
"@img/sharp-darwin-x64": ["@img/sharp-darwin-x64@0.34.4", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-x64": "1.2.3" }, "os": "darwin", "cpu": "x64" }, "sha512-rZheupWIoa3+SOdF/IcUe1ah4ZDpKBGWcsPX6MT0lYniH9micvIU7HQkYTfrx5Xi8u+YqwLtxC/3vl8TQN6rMg=="],
"@img/sharp-libvips-darwin-arm64": ["@img/sharp-libvips-darwin-arm64@1.2.3", "", { "os": "darwin", "cpu": "arm64" }, "sha512-QzWAKo7kpHxbuHqUC28DZ9pIKpSi2ts2OJnoIGI26+HMgq92ZZ4vk8iJd4XsxN+tYfNJxzH6W62X5eTcsBymHw=="],
"@img/sharp-libvips-darwin-x64": ["@img/sharp-libvips-darwin-x64@1.2.3", "", { "os": "darwin", "cpu": "x64" }, "sha512-Ju+g2xn1E2AKO6YBhxjj+ACcsPQRHT0bhpglxcEf+3uyPY+/gL8veniKoo96335ZaPo03bdDXMv0t+BBFAbmRA=="],
"@img/sharp-libvips-linux-arm": ["@img/sharp-libvips-linux-arm@1.2.3", "", { "os": "linux", "cpu": "arm" }, "sha512-x1uE93lyP6wEwGvgAIV0gP6zmaL/a0tGzJs/BIDDG0zeBhMnuUPm7ptxGhUbcGs4okDJrk4nxgrmxpib9g6HpA=="],
"@img/sharp-libvips-linux-arm64": ["@img/sharp-libvips-linux-arm64@1.2.3", "", { "os": "linux", "cpu": "arm64" }, "sha512-I4RxkXU90cpufazhGPyVujYwfIm9Nk1QDEmiIsaPwdnm013F7RIceaCc87kAH+oUB1ezqEvC6ga4m7MSlqsJvQ=="],
"@img/sharp-libvips-linux-ppc64": ["@img/sharp-libvips-linux-ppc64@1.2.3", "", { "os": "linux", "cpu": "ppc64" }, "sha512-Y2T7IsQvJLMCBM+pmPbM3bKT/yYJvVtLJGfCs4Sp95SjvnFIjynbjzsa7dY1fRJX45FTSfDksbTp6AGWudiyCg=="],
"@img/sharp-libvips-linux-s390x": ["@img/sharp-libvips-linux-s390x@1.2.3", "", { "os": "linux", "cpu": "s390x" }, "sha512-RgWrs/gVU7f+K7P+KeHFaBAJlNkD1nIZuVXdQv6S+fNA6syCcoboNjsV2Pou7zNlVdNQoQUpQTk8SWDHUA3y/w=="],
"@img/sharp-libvips-linux-x64": ["@img/sharp-libvips-linux-x64@1.2.3", "", { "os": "linux", "cpu": "x64" }, "sha512-3JU7LmR85K6bBiRzSUc/Ff9JBVIFVvq6bomKE0e63UXGeRw2HPVEjoJke1Yx+iU4rL7/7kUjES4dZ/81Qjhyxg=="],
"@img/sharp-libvips-linuxmusl-arm64": ["@img/sharp-libvips-linuxmusl-arm64@1.2.3", "", { "os": "linux", "cpu": "arm64" }, "sha512-F9q83RZ8yaCwENw1GieztSfj5msz7GGykG/BA+MOUefvER69K/ubgFHNeSyUu64amHIYKGDs4sRCMzXVj8sEyw=="],
"@img/sharp-libvips-linuxmusl-x64": ["@img/sharp-libvips-linuxmusl-x64@1.2.3", "", { "os": "linux", "cpu": "x64" }, "sha512-U5PUY5jbc45ANM6tSJpsgqmBF/VsL6LnxJmIf11kB7J5DctHgqm0SkuXzVWtIY90GnJxKnC/JT251TDnk1fu/g=="],
"@img/sharp-linux-arm": ["@img/sharp-linux-arm@0.34.4", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm": "1.2.3" }, "os": "linux", "cpu": "arm" }, "sha512-Xyam4mlqM0KkTHYVSuc6wXRmM7LGN0P12li03jAnZ3EJWZqj83+hi8Y9UxZUbxsgsK1qOEwg7O0Bc0LjqQVtxA=="],
"@img/sharp-linux-arm64": ["@img/sharp-linux-arm64@0.34.4", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm64": "1.2.3" }, "os": "linux", "cpu": "arm64" }, "sha512-YXU1F/mN/Wu786tl72CyJjP/Ngl8mGHN1hST4BGl+hiW5jhCnV2uRVTNOcaYPs73NeT/H8Upm3y9582JVuZHrQ=="],
"@img/sharp-linux-ppc64": ["@img/sharp-linux-ppc64@0.34.4", "", { "optionalDependencies": { "@img/sharp-libvips-linux-ppc64": "1.2.3" }, "os": "linux", "cpu": "ppc64" }, "sha512-F4PDtF4Cy8L8hXA2p3TO6s4aDt93v+LKmpcYFLAVdkkD3hSxZzee0rh6/+94FpAynsuMpLX5h+LRsSG3rIciUQ=="],
"@img/sharp-linux-s390x": ["@img/sharp-linux-s390x@0.34.4", "", { "optionalDependencies": { "@img/sharp-libvips-linux-s390x": "1.2.3" }, "os": "linux", "cpu": "s390x" }, "sha512-qVrZKE9Bsnzy+myf7lFKvng6bQzhNUAYcVORq2P7bDlvmF6u2sCmK2KyEQEBdYk+u3T01pVsPrkj943T1aJAsw=="],
"@img/sharp-linux-x64": ["@img/sharp-linux-x64@0.34.4", "", { "optionalDependencies": { "@img/sharp-libvips-linux-x64": "1.2.3" }, "os": "linux", "cpu": "x64" }, "sha512-ZfGtcp2xS51iG79c6Vhw9CWqQC8l2Ot8dygxoDoIQPTat/Ov3qAa8qpxSrtAEAJW+UjTXc4yxCjNfxm4h6Xm2A=="],
"@img/sharp-linuxmusl-arm64": ["@img/sharp-linuxmusl-arm64@0.34.4", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-arm64": "1.2.3" }, "os": "linux", "cpu": "arm64" }, "sha512-8hDVvW9eu4yHWnjaOOR8kHVrew1iIX+MUgwxSuH2XyYeNRtLUe4VNioSqbNkB7ZYQJj9rUTT4PyRscyk2PXFKA=="],
"@img/sharp-linuxmusl-x64": ["@img/sharp-linuxmusl-x64@0.34.4", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-x64": "1.2.3" }, "os": "linux", "cpu": "x64" }, "sha512-lU0aA5L8QTlfKjpDCEFOZsTYGn3AEiO6db8W5aQDxj0nQkVrZWmN3ZP9sYKWJdtq3PWPhUNlqehWyXpYDcI9Sg=="],
"@img/sharp-wasm32": ["@img/sharp-wasm32@0.34.4", "", { "dependencies": { "@emnapi/runtime": "1.6.0" }, "cpu": "none" }, "sha512-33QL6ZO/qpRyG7woB/HUALz28WnTMI2W1jgX3Nu2bypqLIKx/QKMILLJzJjI+SIbvXdG9fUnmrxR7vbi1sTBeA=="],
"@img/sharp-win32-arm64": ["@img/sharp-win32-arm64@0.34.4", "", { "os": "win32", "cpu": "arm64" }, "sha512-2Q250do/5WXTwxW3zjsEuMSv5sUU4Tq9VThWKlU2EYLm4MB7ZeMwF+SFJutldYODXF6jzc6YEOC+VfX0SZQPqA=="],
"@img/sharp-win32-ia32": ["@img/sharp-win32-ia32@0.34.4", "", { "os": "win32", "cpu": "ia32" }, "sha512-3ZeLue5V82dT92CNL6rsal6I2weKw1cYu+rGKm8fOCCtJTR2gYeUfY3FqUnIJsMUPIH68oS5jmZ0NiJ508YpEw=="],
"@img/sharp-win32-x64": ["@img/sharp-win32-x64@0.34.4", "", { "os": "win32", "cpu": "x64" }, "sha512-xIyj4wpYs8J18sVN3mSQjwrw7fKUqRw+Z5rnHNCy5fYTxigBz81u5mOMPmFumwjcn8+ld1ppptMBCLic1nz6ig=="],
"@jridgewell/gen-mapping": ["@jridgewell/gen-mapping@0.3.13", "", { "dependencies": { "@jridgewell/sourcemap-codec": "1.5.5", "@jridgewell/trace-mapping": "0.3.31" } }, "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA=="],
"@jridgewell/remapping": ["@jridgewell/remapping@2.3.5", "", { "dependencies": { "@jridgewell/gen-mapping": "0.3.13", "@jridgewell/trace-mapping": "0.3.31" } }, "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ=="],
"@jridgewell/resolve-uri": ["@jridgewell/resolve-uri@3.1.2", "", {}, "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw=="],
"@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.5", "", {}, "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og=="],
"@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.31", "", { "dependencies": { "@jridgewell/resolve-uri": "3.1.2", "@jridgewell/sourcemap-codec": "1.5.5" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="],
"@next/env": ["@next/env@15.5.6", "", {}, "sha512-3qBGRW+sCGzgbpc5TS1a0p7eNxnOarGVQhZxfvTdnV0gFI61lX7QNtQ4V1TSREctXzYn5NetbUsLvyqwLFJM6Q=="],
"@next/swc-darwin-arm64": ["@next/swc-darwin-arm64@15.5.6", "", { "os": "darwin", "cpu": "arm64" }, "sha512-ES3nRz7N+L5Umz4KoGfZ4XX6gwHplwPhioVRc25+QNsDa7RtUF/z8wJcbuQ2Tffm5RZwuN2A063eapoJ1u4nPg=="],
"@next/swc-darwin-x64": ["@next/swc-darwin-x64@15.5.6", "", { "os": "darwin", "cpu": "x64" }, "sha512-JIGcytAyk9LQp2/nuVZPAtj8uaJ/zZhsKOASTjxDug0SPU9LAM3wy6nPU735M1OqacR4U20LHVF5v5Wnl9ptTA=="],
"@next/swc-linux-arm64-gnu": ["@next/swc-linux-arm64-gnu@15.5.6", "", { "os": "linux", "cpu": "arm64" }, "sha512-qvz4SVKQ0P3/Im9zcS2RmfFL/UCQnsJKJwQSkissbngnB/12c6bZTCB0gHTexz1s6d/mD0+egPKXAIRFVS7hQg=="],
"@next/swc-linux-arm64-musl": ["@next/swc-linux-arm64-musl@15.5.6", "", { "os": "linux", "cpu": "arm64" }, "sha512-FsbGVw3SJz1hZlvnWD+T6GFgV9/NYDeLTNQB2MXoPN5u9VA9OEDy6fJEfePfsUKAhJufFbZLgp0cPxMuV6SV0w=="],
"@next/swc-linux-x64-gnu": ["@next/swc-linux-x64-gnu@15.5.6", "", { "os": "linux", "cpu": "x64" }, "sha512-3QnHGFWlnvAgyxFxt2Ny8PTpXtQD7kVEeaFat5oPAHHI192WKYB+VIKZijtHLGdBBvc16tiAkPTDmQNOQ0dyrA=="],
"@next/swc-linux-x64-musl": ["@next/swc-linux-x64-musl@15.5.6", "", { "os": "linux", "cpu": "x64" }, "sha512-OsGX148sL+TqMK9YFaPFPoIaJKbFJJxFzkXZljIgA9hjMjdruKht6xDCEv1HLtlLNfkx3c5w2GLKhj7veBQizQ=="],
"@next/swc-win32-arm64-msvc": ["@next/swc-win32-arm64-msvc@15.5.6", "", { "os": "win32", "cpu": "arm64" }, "sha512-ONOMrqWxdzXDJNh2n60H6gGyKed42Ieu6UTVPZteXpuKbLZTH4G4eBMsr5qWgOBA+s7F+uB4OJbZnrkEDnZ5Fg=="],
"@next/swc-win32-x64-msvc": ["@next/swc-win32-x64-msvc@15.5.6", "", { "os": "win32", "cpu": "x64" }, "sha512-pxK4VIjFRx1MY92UycLOOw7dTdvccWsNETQ0kDHkBlcFH1GrTLUjSiHU1ohrznnux6TqRHgv5oflhfIWZwVROQ=="],
"@panva/hkdf": ["@panva/hkdf@1.2.1", "", {}, "sha512-6oclG6Y3PiDFcoyk8srjLfVKyMfVCKJ27JwNPViuXziFpmdz+MZnZN/aKY0JGXgYuO/VghU0jcOAZgWXZ1Dmrw=="],
"@petamoriken/float16": ["@petamoriken/float16@3.9.3", "", {}, "sha512-8awtpHXCx/bNpFt4mt2xdkgtgVvKqty8VbjHI/WWWQuEw+KLzFot3f4+LkQY9YmOtq7A5GdOnqoIC8Pdygjk2g=="],
"@swc/helpers": ["@swc/helpers@0.5.15", "", { "dependencies": { "tslib": "2.8.1" } }, "sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g=="],
"@t3-oss/env-core": ["@t3-oss/env-core@0.12.0", "", { "optionalDependencies": { "typescript": "5.9.3", "zod": "3.25.76" } }, "sha512-lOPj8d9nJJTt81mMuN9GMk8x5veOt7q9m11OSnCBJhwp1QrL/qR+M8Y467ULBSm9SunosryWNbmQQbgoiMgcdw=="],
"@t3-oss/env-nextjs": ["@t3-oss/env-nextjs@0.12.0", "", { "dependencies": { "@t3-oss/env-core": "0.12.0" }, "optionalDependencies": { "typescript": "5.9.3", "zod": "3.25.76" } }, "sha512-rFnvYk1049RnNVUPvY8iQ55AuQh1Rr+qZzQBh3t++RttCGK4COpXGNxS4+45afuQq02lu+QAOy/5955aU8hRKw=="],
"@tailwindcss/node": ["@tailwindcss/node@4.1.16", "", { "dependencies": { "@jridgewell/remapping": "2.3.5", "enhanced-resolve": "5.18.3", "jiti": "2.6.1", "lightningcss": "1.30.2", "magic-string": "0.30.21", "source-map-js": "1.2.1", "tailwindcss": "4.1.16" } }, "sha512-BX5iaSsloNuvKNHRN3k2RcCuTEgASTo77mofW0vmeHkfrDWaoFAFvNHpEgtu0eqyypcyiBkDWzSMxJhp3AUVcw=="],
"@tailwindcss/oxide": ["@tailwindcss/oxide@4.1.16", "", { "optionalDependencies": { "@tailwindcss/oxide-android-arm64": "4.1.16", "@tailwindcss/oxide-darwin-arm64": "4.1.16", "@tailwindcss/oxide-darwin-x64": "4.1.16", "@tailwindcss/oxide-freebsd-x64": "4.1.16", "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.16", "@tailwindcss/oxide-linux-arm64-gnu": "4.1.16", "@tailwindcss/oxide-linux-arm64-musl": "4.1.16", "@tailwindcss/oxide-linux-x64-gnu": "4.1.16", "@tailwindcss/oxide-linux-x64-musl": "4.1.16", "@tailwindcss/oxide-wasm32-wasi": "4.1.16", "@tailwindcss/oxide-win32-arm64-msvc": "4.1.16", "@tailwindcss/oxide-win32-x64-msvc": "4.1.16" } }, "sha512-2OSv52FRuhdlgyOQqgtQHuCgXnS8nFSYRp2tJ+4WZXKgTxqPy7SMSls8c3mPT5pkZ17SBToGM5LHEJBO7miEdg=="],
"@tailwindcss/oxide-android-arm64": ["@tailwindcss/oxide-android-arm64@4.1.16", "", { "os": "android", "cpu": "arm64" }, "sha512-8+ctzkjHgwDJ5caq9IqRSgsP70xhdhJvm+oueS/yhD5ixLhqTw9fSL1OurzMUhBwE5zK26FXLCz2f/RtkISqHA=="],
"@tailwindcss/oxide-darwin-arm64": ["@tailwindcss/oxide-darwin-arm64@4.1.16", "", { "os": "darwin", "cpu": "arm64" }, "sha512-C3oZy5042v2FOALBZtY0JTDnGNdS6w7DxL/odvSny17ORUnaRKhyTse8xYi3yKGyfnTUOdavRCdmc8QqJYwFKA=="],
"@tailwindcss/oxide-darwin-x64": ["@tailwindcss/oxide-darwin-x64@4.1.16", "", { "os": "darwin", "cpu": "x64" }, "sha512-vjrl/1Ub9+JwU6BP0emgipGjowzYZMjbWCDqwA2Z4vCa+HBSpP4v6U2ddejcHsolsYxwL5r4bPNoamlV0xDdLg=="],
"@tailwindcss/oxide-freebsd-x64": ["@tailwindcss/oxide-freebsd-x64@4.1.16", "", { "os": "freebsd", "cpu": "x64" }, "sha512-TSMpPYpQLm+aR1wW5rKuUuEruc/oOX3C7H0BTnPDn7W/eMw8W+MRMpiypKMkXZfwH8wqPIRKppuZoedTtNj2tg=="],
"@tailwindcss/oxide-linux-arm-gnueabihf": ["@tailwindcss/oxide-linux-arm-gnueabihf@4.1.16", "", { "os": "linux", "cpu": "arm" }, "sha512-p0GGfRg/w0sdsFKBjMYvvKIiKy/LNWLWgV/plR4lUgrsxFAoQBFrXkZ4C0w8IOXfslB9vHK/JGASWD2IefIpvw=="],
"@tailwindcss/oxide-linux-arm64-gnu": ["@tailwindcss/oxide-linux-arm64-gnu@4.1.16", "", { "os": "linux", "cpu": "arm64" }, "sha512-DoixyMmTNO19rwRPdqviTrG1rYzpxgyYJl8RgQvdAQUzxC1ToLRqtNJpU/ATURSKgIg6uerPw2feW0aS8SNr/w=="],
"@tailwindcss/oxide-linux-arm64-musl": ["@tailwindcss/oxide-linux-arm64-musl@4.1.16", "", { "os": "linux", "cpu": "arm64" }, "sha512-H81UXMa9hJhWhaAUca6bU2wm5RRFpuHImrwXBUvPbYb+3jo32I9VIwpOX6hms0fPmA6f2pGVlybO6qU8pF4fzQ=="],
"@tailwindcss/oxide-linux-x64-gnu": ["@tailwindcss/oxide-linux-x64-gnu@4.1.16", "", { "os": "linux", "cpu": "x64" }, "sha512-ZGHQxDtFC2/ruo7t99Qo2TTIvOERULPl5l0K1g0oK6b5PGqjYMga+FcY1wIUnrUxY56h28FxybtDEla+ICOyew=="],
"@tailwindcss/oxide-linux-x64-musl": ["@tailwindcss/oxide-linux-x64-musl@4.1.16", "", { "os": "linux", "cpu": "x64" }, "sha512-Oi1tAaa0rcKf1Og9MzKeINZzMLPbhxvm7rno5/zuP1WYmpiG0bEHq4AcRUiG2165/WUzvxkW4XDYCscZWbTLZw=="],
"@tailwindcss/oxide-wasm32-wasi": ["@tailwindcss/oxide-wasm32-wasi@4.1.16", "", { "cpu": "none" }, "sha512-B01u/b8LteGRwucIBmCQ07FVXLzImWESAIMcUU6nvFt/tYsQ6IHz8DmZ5KtvmwxD+iTYBtM1xwoGXswnlu9v0Q=="],
"@tailwindcss/oxide-win32-arm64-msvc": ["@tailwindcss/oxide-win32-arm64-msvc@4.1.16", "", { "os": "win32", "cpu": "arm64" }, "sha512-zX+Q8sSkGj6HKRTMJXuPvOcP8XfYON24zJBRPlszcH1Np7xuHXhWn8qfFjIujVzvH3BHU+16jBXwgpl20i+v9A=="],
"@tailwindcss/oxide-win32-x64-msvc": ["@tailwindcss/oxide-win32-x64-msvc@4.1.16", "", { "os": "win32", "cpu": "x64" }, "sha512-m5dDFJUEejbFqP+UXVstd4W/wnxA4F61q8SoL+mqTypId2T2ZpuxosNSgowiCnLp2+Z+rivdU0AqpfgiD7yCBg=="],
"@tailwindcss/postcss": ["@tailwindcss/postcss@4.1.16", "", { "dependencies": { "@alloc/quick-lru": "5.2.0", "@tailwindcss/node": "4.1.16", "@tailwindcss/oxide": "4.1.16", "postcss": "8.5.6", "tailwindcss": "4.1.16" } }, "sha512-Qn3SFGPXYQMKR/UtqS+dqvPrzEeBZHrFA92maT4zijCVggdsXnDBMsPFJo1eArX3J+O+Gi+8pV4PkqjLCNBk3A=="],
"@tanstack/query-core": ["@tanstack/query-core@5.90.5", "", {}, "sha512-wLamYp7FaDq6ZnNehypKI5fNvxHPfTYylE0m/ZpuuzJfJqhR5Pxg9gvGBHZx4n7J+V5Rg5mZxHHTlv25Zt5u+w=="],
"@tanstack/react-query": ["@tanstack/react-query@5.90.5", "", { "dependencies": { "@tanstack/query-core": "5.90.5" }, "peerDependencies": { "react": "19.2.0" } }, "sha512-pN+8UWpxZkEJ/Rnnj2v2Sxpx1WFlaa9L6a4UO89p6tTQbeo+m0MS8oYDjbggrR8QcTyjKoYWKS3xJQGr3ExT8Q=="],
"@trpc/client": ["@trpc/client@11.7.1", "", { "peerDependencies": { "@trpc/server": "11.7.1", "typescript": "5.9.3" } }, "sha512-uOnAjElKI892/U6aQMcBHYs3x7mme3Cvv1F87ytBL56rBvs7+DyK7r43zgaXKf13+GtPEI6ex5xjVUfyDW8XcQ=="],
"@trpc/react-query": ["@trpc/react-query@11.7.1", "", { "peerDependencies": { "@tanstack/react-query": "5.90.5", "@trpc/client": "11.7.1", "@trpc/server": "11.7.1", "react": "19.2.0", "react-dom": "19.2.0", "typescript": "5.9.3" } }, "sha512-dEHDjIqSTzO8nLlCbtiFBMBwhbSkK1QP7aYVo3nP3sYBna0b+iCtrPXdxVPCSopr9/aIqDTEh+dMRZa7yBgjfQ=="],
"@trpc/server": ["@trpc/server@11.7.1", "", { "peerDependencies": { "typescript": "5.9.3" } }, "sha512-N3U8LNLIP4g9C7LJ/sLkjuPHwqlvE3bnspzC4DEFVdvx2+usbn70P80E3wj5cjOTLhmhRiwJCSXhlB+MHfGeCw=="],
"@types/cookie": ["@types/cookie@0.6.0", "", {}, "sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA=="],
"@types/node": ["@types/node@20.19.24", "", { "dependencies": { "undici-types": "6.21.0" } }, "sha512-FE5u0ezmi6y9OZEzlJfg37mqqf6ZDSF2V/NLjUyGrR9uTZ7Sb9F7bLNZ03S4XVUNRWGA7Ck4c1kK+YnuWjl+DA=="],
"@types/react": ["@types/react@19.2.2", "", { "dependencies": { "csstype": "3.1.3" } }, "sha512-6mDvHUFSjyT2B2yeNx2nUgMxh9LtOWvkhIU3uePn2I2oyNymUAX1NIsdgviM4CH+JSrp2D2hsMvJOkxY+0wNRA=="],
"@types/react-dom": ["@types/react-dom@19.2.2", "", { "peerDependencies": { "@types/react": "19.2.2" } }, "sha512-9KQPoO6mZCi7jcIStSnlOWn2nEF3mNmyr3rIAsGnAbQKYbRLyqmeSc39EVgtxXVia+LMT8j3knZLAZAh+xLmrw=="],
"buffer-from": ["buffer-from@1.1.2", "", {}, "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ=="],
"caniuse-lite": ["caniuse-lite@1.0.30001752", "", {}, "sha512-vKUk7beoukxE47P5gcVNKkDRzXdVofotshHwfR9vmpeFKxmI5PBpgOMC18LUJUA/DvJ70Y7RveasIBraqsyO/g=="],
"client-only": ["client-only@0.0.1", "", {}, "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA=="],
"cookie": ["cookie@0.7.1", "", {}, "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w=="],
"copy-anything": ["copy-anything@4.0.5", "", { "dependencies": { "is-what": "5.5.0" } }, "sha512-7Vv6asjS4gMOuILabD3l739tsaxFQmC+a7pLZm02zyvs8p977bL3zEgq3yDk5rn9B0PbYgIv++jmHcuUab4RhA=="],
"csstype": ["csstype@3.1.3", "", {}, "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="],
"debug": ["debug@4.4.3", "", { "dependencies": { "ms": "2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="],
"detect-libc": ["detect-libc@2.1.2", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="],
"drizzle-kit": ["drizzle-kit@0.30.6", "", { "dependencies": { "@drizzle-team/brocli": "0.10.2", "@esbuild-kit/esm-loader": "2.6.5", "esbuild": "0.19.12", "esbuild-register": "3.6.0", "gel": "2.1.1" }, "bin": { "drizzle-kit": "bin.cjs" } }, "sha512-U4wWit0fyZuGuP7iNmRleQyK2V8wCuv57vf5l3MnG4z4fzNTjY/U13M8owyQ5RavqvqxBifWORaR3wIUzlN64g=="],
"drizzle-orm": ["drizzle-orm@0.41.0", "", { "optionalDependencies": { "gel": "2.1.1", "postgres": "3.4.7" } }, "sha512-7A4ZxhHk9gdlXmTdPj/lREtP+3u8KvZ4yEN6MYVxBzZGex5Wtdc+CWSbu7btgF6TB0N+MNPrvW7RKBbxJchs/Q=="],
"enhanced-resolve": ["enhanced-resolve@5.18.3", "", { "dependencies": { "graceful-fs": "4.2.11", "tapable": "2.3.0" } }, "sha512-d4lC8xfavMeBjzGr2vECC3fsGXziXZQyJxD868h2M/mBI3PwAuODxAkLkq5HYuvrPYcUtiLzsTo8U3PgX3Ocww=="],
"env-paths": ["env-paths@3.0.0", "", {}, "sha512-dtJUTepzMW3Lm/NPxRf3wP4642UWhjL2sQxc+ym2YMj1m/H2zDNQOlezafzkHwn6sMstjHTwG6iQQsctDW/b1A=="],
"esbuild": ["esbuild@0.25.11", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.11", "@esbuild/android-arm": "0.25.11", "@esbuild/android-arm64": "0.25.11", "@esbuild/android-x64": "0.25.11", "@esbuild/darwin-arm64": "0.25.11", "@esbuild/darwin-x64": "0.25.11", "@esbuild/freebsd-arm64": "0.25.11", "@esbuild/freebsd-x64": "0.25.11", "@esbuild/linux-arm": "0.25.11", "@esbuild/linux-arm64": "0.25.11", "@esbuild/linux-ia32": "0.25.11", "@esbuild/linux-loong64": "0.25.11", "@esbuild/linux-mips64el": "0.25.11", "@esbuild/linux-ppc64": "0.25.11", "@esbuild/linux-riscv64": "0.25.11", "@esbuild/linux-s390x": "0.25.11", "@esbuild/linux-x64": "0.25.11", "@esbuild/netbsd-arm64": "0.25.11", "@esbuild/netbsd-x64": "0.25.11", "@esbuild/openbsd-arm64": "0.25.11", "@esbuild/openbsd-x64": "0.25.11", "@esbuild/openharmony-arm64": "0.25.11", "@esbuild/sunos-x64": "0.25.11", "@esbuild/win32-arm64": "0.25.11", "@esbuild/win32-ia32": "0.25.11", "@esbuild/win32-x64": "0.25.11" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-KohQwyzrKTQmhXDW1PjCv3Tyspn9n5GcY2RTDqeORIdIJY8yKIF7sTSopFmn/wpMPW4rdPXI0UE5LJLuq3bx0Q=="],
"esbuild-register": ["esbuild-register@3.6.0", "", { "dependencies": { "debug": "4.4.3" }, "peerDependencies": { "esbuild": "0.19.12" } }, "sha512-H2/S7Pm8a9CL1uhp9OvjwrBh5Pvx0H8qVOxNu8Wed9Y7qv56MPtq+GGM8RJpq6glYJn9Wspr8uw7l55uyinNeg=="],
"gel": ["gel@2.1.1", "", { "dependencies": { "@petamoriken/float16": "3.9.3", "debug": "4.4.3", "env-paths": "3.0.0", "semver": "7.7.3", "shell-quote": "1.8.3", "which": "4.0.0" }, "bin": { "gel": "dist/cli.mjs" } }, "sha512-Newg9X7mRYskoBjSw70l1YnJ/ZGbq64VPyR821H5WVkTGpHG2O0mQILxCeUhxdYERLFY9B4tUyKLyf3uMTjtKw=="],
"get-tsconfig": ["get-tsconfig@4.13.0", "", { "dependencies": { "resolve-pkg-maps": "1.0.0" } }, "sha512-1VKTZJCwBrvbd+Wn3AOgQP/2Av+TfTCOlE4AcRJE72W1ksZXbAx8PPBR9RzgTeSPzlPMHrbANMH3LbltH73wxQ=="],
"graceful-fs": ["graceful-fs@4.2.11", "", {}, "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="],
"is-what": ["is-what@5.5.0", "", {}, "sha512-oG7cgbmg5kLYae2N5IVd3jm2s+vldjxJzK1pcu9LfpGuQ93MQSzo0okvRna+7y5ifrD+20FE8FvjusyGaz14fw=="],
"isexe": ["isexe@3.1.1", "", {}, "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ=="],
"jiti": ["jiti@2.6.1", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ=="],
"jose": ["jose@6.1.0", "", {}, "sha512-TTQJyoEoKcC1lscpVDCSsVgYzUDg/0Bt3WE//WiTPK6uOCQC2KZS4MpugbMWt/zyjkopgZoXhZuCi00gLudfUA=="],
"lightningcss": ["lightningcss@1.30.2", "", { "dependencies": { "detect-libc": "2.1.2" }, "optionalDependencies": { "lightningcss-android-arm64": "1.30.2", "lightningcss-darwin-arm64": "1.30.2", "lightningcss-darwin-x64": "1.30.2", "lightningcss-freebsd-x64": "1.30.2", "lightningcss-linux-arm-gnueabihf": "1.30.2", "lightningcss-linux-arm64-gnu": "1.30.2", "lightningcss-linux-arm64-musl": "1.30.2", "lightningcss-linux-x64-gnu": "1.30.2", "lightningcss-linux-x64-musl": "1.30.2", "lightningcss-win32-arm64-msvc": "1.30.2", "lightningcss-win32-x64-msvc": "1.30.2" } }, "sha512-utfs7Pr5uJyyvDETitgsaqSyjCb2qNRAtuqUeWIAKztsOYdcACf2KtARYXg2pSvhkt+9NfoaNY7fxjl6nuMjIQ=="],
"lightningcss-android-arm64": ["lightningcss-android-arm64@1.30.2", "", { "os": "android", "cpu": "arm64" }, "sha512-BH9sEdOCahSgmkVhBLeU7Hc9DWeZ1Eb6wNS6Da8igvUwAe0sqROHddIlvU06q3WyXVEOYDZ6ykBZQnjTbmo4+A=="],
"lightningcss-darwin-arm64": ["lightningcss-darwin-arm64@1.30.2", "", { "os": "darwin", "cpu": "arm64" }, "sha512-ylTcDJBN3Hp21TdhRT5zBOIi73P6/W0qwvlFEk22fkdXchtNTOU4Qc37SkzV+EKYxLouZ6M4LG9NfZ1qkhhBWA=="],
"lightningcss-darwin-x64": ["lightningcss-darwin-x64@1.30.2", "", { "os": "darwin", "cpu": "x64" }, "sha512-oBZgKchomuDYxr7ilwLcyms6BCyLn0z8J0+ZZmfpjwg9fRVZIR5/GMXd7r9RH94iDhld3UmSjBM6nXWM2TfZTQ=="],
"lightningcss-freebsd-x64": ["lightningcss-freebsd-x64@1.30.2", "", { "os": "freebsd", "cpu": "x64" }, "sha512-c2bH6xTrf4BDpK8MoGG4Bd6zAMZDAXS569UxCAGcA7IKbHNMlhGQ89eRmvpIUGfKWNVdbhSbkQaWhEoMGmGslA=="],
"lightningcss-linux-arm-gnueabihf": ["lightningcss-linux-arm-gnueabihf@1.30.2", "", { "os": "linux", "cpu": "arm" }, "sha512-eVdpxh4wYcm0PofJIZVuYuLiqBIakQ9uFZmipf6LF/HRj5Bgm0eb3qL/mr1smyXIS1twwOxNWndd8z0E374hiA=="],
"lightningcss-linux-arm64-gnu": ["lightningcss-linux-arm64-gnu@1.30.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-UK65WJAbwIJbiBFXpxrbTNArtfuznvxAJw4Q2ZGlU8kPeDIWEX1dg3rn2veBVUylA2Ezg89ktszWbaQnxD/e3A=="],
"lightningcss-linux-arm64-musl": ["lightningcss-linux-arm64-musl@1.30.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-5Vh9dGeblpTxWHpOx8iauV02popZDsCYMPIgiuw97OJ5uaDsL86cnqSFs5LZkG3ghHoX5isLgWzMs+eD1YzrnA=="],
"lightningcss-linux-x64-gnu": ["lightningcss-linux-x64-gnu@1.30.2", "", { "os": "linux", "cpu": "x64" }, "sha512-Cfd46gdmj1vQ+lR6VRTTadNHu6ALuw2pKR9lYq4FnhvgBc4zWY1EtZcAc6EffShbb1MFrIPfLDXD6Xprbnni4w=="],
"lightningcss-linux-x64-musl": ["lightningcss-linux-x64-musl@1.30.2", "", { "os": "linux", "cpu": "x64" }, "sha512-XJaLUUFXb6/QG2lGIW6aIk6jKdtjtcffUT0NKvIqhSBY3hh9Ch+1LCeH80dR9q9LBjG3ewbDjnumefsLsP6aiA=="],
"lightningcss-win32-arm64-msvc": ["lightningcss-win32-arm64-msvc@1.30.2", "", { "os": "win32", "cpu": "arm64" }, "sha512-FZn+vaj7zLv//D/192WFFVA0RgHawIcHqLX9xuWiQt7P0PtdFEVaxgF9rjM/IRYHQXNnk61/H/gb2Ei+kUQ4xQ=="],
"lightningcss-win32-x64-msvc": ["lightningcss-win32-x64-msvc@1.30.2", "", { "os": "win32", "cpu": "x64" }, "sha512-5g1yc73p+iAkid5phb4oVFMB45417DkRevRbt/El/gKXJk4jid+vPFF/AXbxn05Aky8PapwzZrdJShv5C0avjw=="],
"magic-string": ["magic-string@0.30.21", "", { "dependencies": { "@jridgewell/sourcemap-codec": "1.5.5" } }, "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ=="],
"ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="],
"nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="],
"next": ["next@15.5.6", "", { "dependencies": { "@next/env": "15.5.6", "@swc/helpers": "0.5.15", "caniuse-lite": "1.0.30001752", "postcss": "8.4.31", "styled-jsx": "5.1.6" }, "optionalDependencies": { "@next/swc-darwin-arm64": "15.5.6", "@next/swc-darwin-x64": "15.5.6", "@next/swc-linux-arm64-gnu": "15.5.6", "@next/swc-linux-arm64-musl": "15.5.6", "@next/swc-linux-x64-gnu": "15.5.6", "@next/swc-linux-x64-musl": "15.5.6", "@next/swc-win32-arm64-msvc": "15.5.6", "@next/swc-win32-x64-msvc": "15.5.6", "sharp": "0.34.4" }, "peerDependencies": { "react": "19.2.0", "react-dom": "19.2.0" }, "bin": { "next": "dist/bin/next" } }, "sha512-zTxsnI3LQo3c9HSdSf91O1jMNsEzIXDShXd4wVdg9y5shwLqBXi4ZtUUJyB86KGVSJLZx0PFONvO54aheGX8QQ=="],
"next-auth": ["next-auth@5.0.0-beta.25", "", { "dependencies": { "@auth/core": "0.37.2" }, "peerDependencies": { "next": "15.5.6", "react": "19.2.0" } }, "sha512-2dJJw1sHQl2qxCrRk+KTQbeH+izFbGFPuJj5eGgBZFYyiYYtvlrBeUw1E/OJJxTRjuxbSYGnCTkUIRsIIW0bog=="],
"oauth4webapi": ["oauth4webapi@3.8.2", "", {}, "sha512-FzZZ+bht5X0FKe7Mwz3DAVAmlH1BV5blSak/lHMBKz0/EBMhX6B10GlQYI51+oRp8ObJaX0g6pXrAxZh5s8rjw=="],
"picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="],
"postcss": ["postcss@8.5.6", "", { "dependencies": { "nanoid": "3.3.11", "picocolors": "1.1.1", "source-map-js": "1.2.1" } }, "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg=="],
"postgres": ["postgres@3.4.7", "", {}, "sha512-Jtc2612XINuBjIl/QTWsV5UvE8UHuNblcO3vVADSrKsrc6RqGX6lOW1cEo3CM2v0XG4Nat8nI+YM7/f26VxXLw=="],
"preact": ["preact@10.24.3", "", {}, "sha512-Z2dPnBnMUfyQfSQ+GBdsGa16hz35YmLmtTLhM169uW944hYL6xzTYkJjC07j+Wosz733pMWx0fgON3JNw1jJQA=="],
"preact-render-to-string": ["preact-render-to-string@6.5.11", "", { "peerDependencies": { "preact": "10.24.3" } }, "sha512-ubnauqoGczeGISiOh6RjX0/cdaF8v/oDXIjO85XALCQjwQP+SB4RDXXtvZ6yTYSjG+PC1QRP2AhPgCEsM2EvUw=="],
"pretty-format": ["pretty-format@3.8.0", "", {}, "sha512-WuxUnVtlWL1OfZFQFuqvnvs6MiAGk9UNsBostyBOB0Is9wb5uRESevA6rnl/rkksXaGX3GzZhPup5d6Vp1nFew=="],
"react": ["react@19.2.0", "", {}, "sha512-tmbWg6W31tQLeB5cdIBOicJDJRR2KzXsV7uSK9iNfLWQ5bIZfxuPEHp7M8wiHyHnn0DD1i7w3Zmin0FtkrwoCQ=="],
"react-dom": ["react-dom@19.2.0", "", { "dependencies": { "scheduler": "0.27.0" }, "peerDependencies": { "react": "19.2.0" } }, "sha512-UlbRu4cAiGaIewkPyiRGJk0imDN2T3JjieT6spoL2UeSf5od4n5LB/mQ4ejmxhCFT1tYe8IvaFulzynWovsEFQ=="],
"resolve-pkg-maps": ["resolve-pkg-maps@1.0.0", "", {}, "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw=="],
"scheduler": ["scheduler@0.27.0", "", {}, "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q=="],
"semver": ["semver@7.7.3", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q=="],
"server-only": ["server-only@0.0.1", "", {}, "sha512-qepMx2JxAa5jjfzxG79yPPq+8BuFToHd1hm7kI+Z4zAq1ftQiP7HcxMhDDItrbtwVeLg/cY2JnKnrcFkmiswNA=="],
"sharp": ["sharp@0.34.4", "", { "dependencies": { "@img/colour": "1.0.0", "detect-libc": "2.1.2", "semver": "7.7.3" }, "optionalDependencies": { "@img/sharp-darwin-arm64": "0.34.4", "@img/sharp-darwin-x64": "0.34.4", "@img/sharp-libvips-darwin-arm64": "1.2.3", "@img/sharp-libvips-darwin-x64": "1.2.3", "@img/sharp-libvips-linux-arm": "1.2.3", "@img/sharp-libvips-linux-arm64": "1.2.3", "@img/sharp-libvips-linux-ppc64": "1.2.3", "@img/sharp-libvips-linux-s390x": "1.2.3", "@img/sharp-libvips-linux-x64": "1.2.3", "@img/sharp-libvips-linuxmusl-arm64": "1.2.3", "@img/sharp-libvips-linuxmusl-x64": "1.2.3", "@img/sharp-linux-arm": "0.34.4", "@img/sharp-linux-arm64": "0.34.4", "@img/sharp-linux-ppc64": "0.34.4", "@img/sharp-linux-s390x": "0.34.4", "@img/sharp-linux-x64": "0.34.4", "@img/sharp-linuxmusl-arm64": "0.34.4", "@img/sharp-linuxmusl-x64": "0.34.4", "@img/sharp-wasm32": "0.34.4", "@img/sharp-win32-arm64": "0.34.4", "@img/sharp-win32-ia32": "0.34.4", "@img/sharp-win32-x64": "0.34.4" } }, "sha512-FUH39xp3SBPnxWvd5iib1X8XY7J0K0X7d93sie9CJg2PO8/7gmg89Nve6OjItK53/MlAushNNxteBYfM6DEuoA=="],
"shell-quote": ["shell-quote@1.8.3", "", {}, "sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw=="],
"source-map": ["source-map@0.6.1", "", {}, "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="],
"source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="],
"source-map-support": ["source-map-support@0.5.21", "", { "dependencies": { "buffer-from": "1.1.2", "source-map": "0.6.1" } }, "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w=="],
"styled-jsx": ["styled-jsx@5.1.6", "", { "dependencies": { "client-only": "0.0.1" }, "peerDependencies": { "react": "19.2.0" } }, "sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA=="],
"superjson": ["superjson@2.2.5", "", { "dependencies": { "copy-anything": "4.0.5" } }, "sha512-zWPTX96LVsA/eVYnqOM2+ofcdPqdS1dAF1LN4TS2/MWuUpfitd9ctTa87wt4xrYnZnkLtS69xpBdSxVBP5Rm6w=="],
"tailwindcss": ["tailwindcss@4.1.16", "", {}, "sha512-pONL5awpaQX4LN5eiv7moSiSPd/DLDzKVRJz8Q9PgzmAdd1R4307GQS2ZpfiN7ZmekdQrfhZZiSE5jkLR4WNaA=="],
"tapable": ["tapable@2.3.0", "", {}, "sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg=="],
"tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
"typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="],
"undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="],
"which": ["which@4.0.0", "", { "dependencies": { "isexe": "3.1.1" }, "bin": { "node-which": "bin/which.js" } }, "sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg=="],
"zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="],
"@esbuild-kit/core-utils/esbuild": ["esbuild@0.18.20", "", { "optionalDependencies": { "@esbuild/android-arm": "0.18.20", "@esbuild/android-arm64": "0.18.20", "@esbuild/android-x64": "0.18.20", "@esbuild/darwin-arm64": "0.18.20", "@esbuild/darwin-x64": "0.18.20", "@esbuild/freebsd-arm64": "0.18.20", "@esbuild/freebsd-x64": "0.18.20", "@esbuild/linux-arm": "0.18.20", "@esbuild/linux-arm64": "0.18.20", "@esbuild/linux-ia32": "0.18.20", "@esbuild/linux-loong64": "0.18.20", "@esbuild/linux-mips64el": "0.18.20", "@esbuild/linux-ppc64": "0.18.20", "@esbuild/linux-riscv64": "0.18.20", "@esbuild/linux-s390x": "0.18.20", "@esbuild/linux-x64": "0.18.20", "@esbuild/netbsd-x64": "0.18.20", "@esbuild/openbsd-x64": "0.18.20", "@esbuild/sunos-x64": "0.18.20", "@esbuild/win32-arm64": "0.18.20", "@esbuild/win32-ia32": "0.18.20", "@esbuild/win32-x64": "0.18.20" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA=="],
"drizzle-kit/esbuild": ["esbuild@0.19.12", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.19.12", "@esbuild/android-arm": "0.19.12", "@esbuild/android-arm64": "0.19.12", "@esbuild/android-x64": "0.19.12", "@esbuild/darwin-arm64": "0.19.12", "@esbuild/darwin-x64": "0.19.12", "@esbuild/freebsd-arm64": "0.19.12", "@esbuild/freebsd-x64": "0.19.12", "@esbuild/linux-arm": "0.19.12", "@esbuild/linux-arm64": "0.19.12", "@esbuild/linux-ia32": "0.19.12", "@esbuild/linux-loong64": "0.19.12", "@esbuild/linux-mips64el": "0.19.12", "@esbuild/linux-ppc64": "0.19.12", "@esbuild/linux-riscv64": "0.19.12", "@esbuild/linux-s390x": "0.19.12", "@esbuild/linux-x64": "0.19.12", "@esbuild/netbsd-x64": "0.19.12", "@esbuild/openbsd-x64": "0.19.12", "@esbuild/sunos-x64": "0.19.12", "@esbuild/win32-arm64": "0.19.12", "@esbuild/win32-ia32": "0.19.12", "@esbuild/win32-x64": "0.19.12" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-aARqgq8roFBj054KvQr5f1sFu0D65G+miZRCuJyJ0G13Zwx7vRar5Zhn2tkQNzIXcBrNVsv/8stehpj+GAjgbg=="],
"next/postcss": ["postcss@8.4.31", "", { "dependencies": { "nanoid": "3.3.11", "picocolors": "1.1.1", "source-map-js": "1.2.1" } }, "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ=="],
"next-auth/@auth/core": ["@auth/core@0.37.2", "", { "dependencies": { "@panva/hkdf": "1.2.1", "@types/cookie": "0.6.0", "cookie": "0.7.1", "jose": "5.10.0", "oauth4webapi": "3.8.2", "preact": "10.11.3", "preact-render-to-string": "5.2.3" } }, "sha512-kUvzyvkcd6h1vpeMAojK2y7+PAV5H+0Cc9+ZlKYDFhDY31AlvsB+GW5vNO4qE3Y07KeQgvNO9U0QUx/fN62kBw=="],
"@esbuild-kit/core-utils/esbuild/@esbuild/android-arm": ["@esbuild/android-arm@0.18.20", "", { "os": "android", "cpu": "arm" }, "sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw=="],
"@esbuild-kit/core-utils/esbuild/@esbuild/android-arm64": ["@esbuild/android-arm64@0.18.20", "", { "os": "android", "cpu": "arm64" }, "sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ=="],
"@esbuild-kit/core-utils/esbuild/@esbuild/android-x64": ["@esbuild/android-x64@0.18.20", "", { "os": "android", "cpu": "x64" }, "sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg=="],
"@esbuild-kit/core-utils/esbuild/@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.18.20", "", { "os": "darwin", "cpu": "arm64" }, "sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA=="],
"@esbuild-kit/core-utils/esbuild/@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.18.20", "", { "os": "darwin", "cpu": "x64" }, "sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ=="],
"@esbuild-kit/core-utils/esbuild/@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.18.20", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw=="],
"@esbuild-kit/core-utils/esbuild/@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.18.20", "", { "os": "freebsd", "cpu": "x64" }, "sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ=="],
"@esbuild-kit/core-utils/esbuild/@esbuild/linux-arm": ["@esbuild/linux-arm@0.18.20", "", { "os": "linux", "cpu": "arm" }, "sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg=="],
"@esbuild-kit/core-utils/esbuild/@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.18.20", "", { "os": "linux", "cpu": "arm64" }, "sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA=="],
"@esbuild-kit/core-utils/esbuild/@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.18.20", "", { "os": "linux", "cpu": "ia32" }, "sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA=="],
"@esbuild-kit/core-utils/esbuild/@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.18.20", "", { "os": "linux", "cpu": "none" }, "sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg=="],
"@esbuild-kit/core-utils/esbuild/@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.18.20", "", { "os": "linux", "cpu": "none" }, "sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ=="],
"@esbuild-kit/core-utils/esbuild/@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.18.20", "", { "os": "linux", "cpu": "ppc64" }, "sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA=="],
"@esbuild-kit/core-utils/esbuild/@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.18.20", "", { "os": "linux", "cpu": "none" }, "sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A=="],
"@esbuild-kit/core-utils/esbuild/@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.18.20", "", { "os": "linux", "cpu": "s390x" }, "sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ=="],
"@esbuild-kit/core-utils/esbuild/@esbuild/linux-x64": ["@esbuild/linux-x64@0.18.20", "", { "os": "linux", "cpu": "x64" }, "sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w=="],
"@esbuild-kit/core-utils/esbuild/@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.18.20", "", { "os": "none", "cpu": "x64" }, "sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A=="],
"@esbuild-kit/core-utils/esbuild/@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.18.20", "", { "os": "openbsd", "cpu": "x64" }, "sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg=="],
"@esbuild-kit/core-utils/esbuild/@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.18.20", "", { "os": "sunos", "cpu": "x64" }, "sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ=="],
"@esbuild-kit/core-utils/esbuild/@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.18.20", "", { "os": "win32", "cpu": "arm64" }, "sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg=="],
"@esbuild-kit/core-utils/esbuild/@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.18.20", "", { "os": "win32", "cpu": "ia32" }, "sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g=="],
"@esbuild-kit/core-utils/esbuild/@esbuild/win32-x64": ["@esbuild/win32-x64@0.18.20", "", { "os": "win32", "cpu": "x64" }, "sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ=="],
"drizzle-kit/esbuild/@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.19.12", "", { "os": "aix", "cpu": "ppc64" }, "sha512-bmoCYyWdEL3wDQIVbcyzRyeKLgk2WtWLTWz1ZIAZF/EGbNOwSA6ew3PftJ1PqMiOOGu0OyFMzG53L0zqIpPeNA=="],
"drizzle-kit/esbuild/@esbuild/android-arm": ["@esbuild/android-arm@0.19.12", "", { "os": "android", "cpu": "arm" }, "sha512-qg/Lj1mu3CdQlDEEiWrlC4eaPZ1KztwGJ9B6J+/6G+/4ewxJg7gqj8eVYWvao1bXrqGiW2rsBZFSX3q2lcW05w=="],
"drizzle-kit/esbuild/@esbuild/android-arm64": ["@esbuild/android-arm64@0.19.12", "", { "os": "android", "cpu": "arm64" }, "sha512-P0UVNGIienjZv3f5zq0DP3Nt2IE/3plFzuaS96vihvD0Hd6H/q4WXUGpCxD/E8YrSXfNyRPbpTq+T8ZQioSuPA=="],
"drizzle-kit/esbuild/@esbuild/android-x64": ["@esbuild/android-x64@0.19.12", "", { "os": "android", "cpu": "x64" }, "sha512-3k7ZoUW6Q6YqhdhIaq/WZ7HwBpnFBlW905Fa4s4qWJyiNOgT1dOqDiVAQFwBH7gBRZr17gLrlFCRzF6jFh7Kew=="],
"drizzle-kit/esbuild/@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.19.12", "", { "os": "darwin", "cpu": "arm64" }, "sha512-B6IeSgZgtEzGC42jsI+YYu9Z3HKRxp8ZT3cqhvliEHovq8HSX2YX8lNocDn79gCKJXOSaEot9MVYky7AKjCs8g=="],
"drizzle-kit/esbuild/@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.19.12", "", { "os": "darwin", "cpu": "x64" }, "sha512-hKoVkKzFiToTgn+41qGhsUJXFlIjxI/jSYeZf3ugemDYZldIXIxhvwN6erJGlX4t5h417iFuheZ7l+YVn05N3A=="],
"drizzle-kit/esbuild/@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.19.12", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-4aRvFIXmwAcDBw9AueDQ2YnGmz5L6obe5kmPT8Vd+/+x/JMVKCgdcRwH6APrbpNXsPz+K653Qg8HB/oXvXVukA=="],
"drizzle-kit/esbuild/@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.19.12", "", { "os": "freebsd", "cpu": "x64" }, "sha512-EYoXZ4d8xtBoVN7CEwWY2IN4ho76xjYXqSXMNccFSx2lgqOG/1TBPW0yPx1bJZk94qu3tX0fycJeeQsKovA8gg=="],
"drizzle-kit/esbuild/@esbuild/linux-arm": ["@esbuild/linux-arm@0.19.12", "", { "os": "linux", "cpu": "arm" }, "sha512-J5jPms//KhSNv+LO1S1TX1UWp1ucM6N6XuL6ITdKWElCu8wXP72l9MM0zDTzzeikVyqFE6U8YAV9/tFyj0ti+w=="],
"drizzle-kit/esbuild/@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.19.12", "", { "os": "linux", "cpu": "arm64" }, "sha512-EoTjyYyLuVPfdPLsGVVVC8a0p1BFFvtpQDB/YLEhaXyf/5bczaGeN15QkR+O4S5LeJ92Tqotve7i1jn35qwvdA=="],
"drizzle-kit/esbuild/@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.19.12", "", { "os": "linux", "cpu": "ia32" }, "sha512-Thsa42rrP1+UIGaWz47uydHSBOgTUnwBwNq59khgIwktK6x60Hivfbux9iNR0eHCHzOLjLMLfUMLCypBkZXMHA=="],
"drizzle-kit/esbuild/@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.19.12", "", { "os": "linux", "cpu": "none" }, "sha512-LiXdXA0s3IqRRjm6rV6XaWATScKAXjI4R4LoDlvO7+yQqFdlr1Bax62sRwkVvRIrwXxvtYEHHI4dm50jAXkuAA=="],
"drizzle-kit/esbuild/@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.19.12", "", { "os": "linux", "cpu": "none" }, "sha512-fEnAuj5VGTanfJ07ff0gOA6IPsvrVHLVb6Lyd1g2/ed67oU1eFzL0r9WL7ZzscD+/N6i3dWumGE1Un4f7Amf+w=="],
"drizzle-kit/esbuild/@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.19.12", "", { "os": "linux", "cpu": "ppc64" }, "sha512-nYJA2/QPimDQOh1rKWedNOe3Gfc8PabU7HT3iXWtNUbRzXS9+vgB0Fjaqr//XNbd82mCxHzik2qotuI89cfixg=="],
"drizzle-kit/esbuild/@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.19.12", "", { "os": "linux", "cpu": "none" }, "sha512-2MueBrlPQCw5dVJJpQdUYgeqIzDQgw3QtiAHUC4RBz9FXPrskyyU3VI1hw7C0BSKB9OduwSJ79FTCqtGMWqJHg=="],
"drizzle-kit/esbuild/@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.19.12", "", { "os": "linux", "cpu": "s390x" }, "sha512-+Pil1Nv3Umes4m3AZKqA2anfhJiVmNCYkPchwFJNEJN5QxmTs1uzyy4TvmDrCRNT2ApwSari7ZIgrPeUx4UZDg=="],
"drizzle-kit/esbuild/@esbuild/linux-x64": ["@esbuild/linux-x64@0.19.12", "", { "os": "linux", "cpu": "x64" }, "sha512-B71g1QpxfwBvNrfyJdVDexenDIt1CiDN1TIXLbhOw0KhJzE78KIFGX6OJ9MrtC0oOqMWf+0xop4qEU8JrJTwCg=="],
"drizzle-kit/esbuild/@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.19.12", "", { "os": "none", "cpu": "x64" }, "sha512-3ltjQ7n1owJgFbuC61Oj++XhtzmymoCihNFgT84UAmJnxJfm4sYCiSLTXZtE00VWYpPMYc+ZQmB6xbSdVh0JWA=="],
"drizzle-kit/esbuild/@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.19.12", "", { "os": "openbsd", "cpu": "x64" }, "sha512-RbrfTB9SWsr0kWmb9srfF+L933uMDdu9BIzdA7os2t0TXhCRjrQyCeOt6wVxr79CKD4c+p+YhCj31HBkYcXebw=="],
"drizzle-kit/esbuild/@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.19.12", "", { "os": "sunos", "cpu": "x64" }, "sha512-HKjJwRrW8uWtCQnQOz9qcU3mUZhTUQvi56Q8DPTLLB+DawoiQdjsYq+j+D3s9I8VFtDr+F9CjgXKKC4ss89IeA=="],
"drizzle-kit/esbuild/@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.19.12", "", { "os": "win32", "cpu": "arm64" }, "sha512-URgtR1dJnmGvX864pn1B2YUYNzjmXkuJOIqG2HdU62MVS4EHpU2946OZoTMnRUHklGtJdJZ33QfzdjGACXhn1A=="],
"drizzle-kit/esbuild/@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.19.12", "", { "os": "win32", "cpu": "ia32" }, "sha512-+ZOE6pUkMOJfmxmBZElNOx72NKpIa/HFOMGzu8fqzQJ5kgf6aTGrcJaFsNiVMH4JKpMipyK+7k0n2UXN7a8YKQ=="],
"drizzle-kit/esbuild/@esbuild/win32-x64": ["@esbuild/win32-x64@0.19.12", "", { "os": "win32", "cpu": "x64" }, "sha512-T1QyPSDCyMXaO3pzBkF96E8xMkiRYbUEZADd29SyPGabqxMViNoii+NcK7eWJAEoU6RZyEm5lVSIjTmcdoB9HA=="],
"next-auth/@auth/core/jose": ["jose@5.10.0", "", {}, "sha512-s+3Al/p9g32Iq+oqXxkW//7jk2Vig6FF1CFqzVXoTUXt2qz89YWbL+OwS17NFYEvxC35n0FKeGO2LGYSxeM2Gg=="],
"next-auth/@auth/core/preact": ["preact@10.11.3", "", {}, "sha512-eY93IVpod/zG3uMF22Unl8h9KkrcKIRs2EGar8hwLZZDU1lkjph303V9HZBwufh2s736U6VXuhD109LYqPoffg=="],
"next-auth/@auth/core/preact-render-to-string": ["preact-render-to-string@5.2.3", "", { "dependencies": { "pretty-format": "3.8.0" }, "peerDependencies": { "preact": "10.11.3" } }, "sha512-aPDxUn5o3GhWdtJtW0svRC2SS/l8D9MAgo2+AWml+BhDImb27ALf04Q2d+AHqUUOc6RdSXFIBVa2gxzgMKgtZA=="],
}
}

View File

@@ -1,5 +0,0 @@
/// <reference types="next" />
/// <reference types="next/image-types/global" />
// NOTE: This file should not be edited
// see https://nextjs.org/docs/basic-features/typescript for more information.

View File

@@ -1,10 +0,0 @@
/**
* Run `build` or `dev` with `SKIP_ENV_VALIDATION` to skip env validation. This is especially useful
* for Docker builds.
*/
import "./src/env.js";
/** @type {import("next").NextConfig} */
const config = {};
export default config;

View File

@@ -1,52 +1,31 @@
{
"name": "installbench",
"version": "0.1.0",
"private": true,
"type": "module",
"sideEffects": false,
"scripts": {
"build": "next build",
"check": "biome check .",
"check:unsafe": "biome check --write --unsafe .",
"check:write": "biome check --write .",
"db:generate": "drizzle-kit generate",
"db:migrate": "drizzle-kit migrate",
"db:push": "drizzle-kit push",
"db:studio": "drizzle-kit studio",
"dev": "next dev --turbo",
"preview": "next build && next start",
"start": "next start",
"typecheck": "tsc --noEmit"
"build": "remix build",
"dev": "remix dev",
"start": "remix-serve build",
"typecheck": "tsc",
"clean": "rm -rf node_modules",
"bench": "hyperfine --prepare 'rm -rf node_modules' --warmup 1 --runs 3 'bun install' 'pnpm install' 'yarn' 'npm install'"
},
"dependencies": {
"@auth/drizzle-adapter": "^1.7.2",
"@t3-oss/env-nextjs": "^0.12.0",
"@tanstack/react-query": "^5.69.0",
"@trpc/client": "^11.0.0",
"@trpc/react-query": "^11.0.0",
"@trpc/server": "^11.0.0",
"drizzle-orm": "^0.41.0",
"esbuild": "^0.25.11",
"next": "^15.2.3",
"next-auth": "5.0.0-beta.25",
"postgres": "^3.4.4",
"react": "^19.0.0",
"react-dom": "^19.0.0",
"server-only": "^0.0.1",
"superjson": "^2.2.1",
"zod": "^3.24.2"
"@remix-run/node": "^1.15.0",
"@remix-run/react": "^1.15.0",
"@remix-run/serve": "^1.15.0",
"isbot": "^3.6.5",
"react": "^18.2.0",
"react-dom": "^18.2.0"
},
"devDependencies": {
"@biomejs/biome": "1.9.4",
"@tailwindcss/postcss": "^4.0.15",
"@types/node": "^20.14.10",
"@types/react": "^19.0.0",
"@types/react-dom": "^19.0.0",
"drizzle-kit": "^0.30.5",
"postcss": "^8.5.3",
"tailwindcss": "^4.0.15",
"typescript": "^5.8.2"
"@remix-run/dev": "^1.15.0",
"@remix-run/eslint-config": "^1.15.0",
"@types/react": "^18.0.25",
"@types/react-dom": "^18.0.8",
"eslint": "^8.27.0",
"typescript": "^4.8.4"
},
"ct3aMetadata": {
"initVersion": "7.39.3"
"engines": {
"node": ">=14"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

View File

@@ -0,0 +1,14 @@
/** @type {import('@remix-run/dev').AppConfig} */
module.exports = {
ignoredRouteFiles: ["**/.*"],
// appDirectory: "app",
// assetsBuildDirectory: "public/build",
// serverBuildPath: "build/index.js",
// publicPath: "/build/",
future: {
v2_errorBoundary: true,
v2_meta: true,
v2_normalizeFormMethod: true,
v2_routeConvention: true,
},
};

2
bench/install/remix.env.d.ts vendored Normal file
View File

@@ -0,0 +1,2 @@
/// <reference types="@remix-run/dev" />
/// <reference types="@remix-run/node" />

View File

@@ -0,0 +1,22 @@
{
"include": ["remix.env.d.ts", "**/*.ts", "**/*.tsx"],
"compilerOptions": {
"lib": ["DOM", "DOM.Iterable", "ES2019"],
"isolatedModules": true,
"esModuleInterop": true,
"jsx": "react-jsx",
"moduleResolution": "node",
"resolveJsonModule": true,
"target": "ES2019",
"strict": true,
"allowJs": true,
"forceConsistentCasingInFileNames": true,
"baseUrl": ".",
"paths": {
"~/*": ["./app/*"]
},
// Remix takes care of building everything in `remix build`.
"noEmit": true
}
}

View File

@@ -4,16 +4,20 @@
"": {
"name": "react-hello-world",
"dependencies": {
"react": "^19.2.0",
"react-dom": "^19.2.0",
"react": "next",
"react-dom": "next",
},
},
},
"packages": {
"react": ["react@19.2.0", "", {}, "sha512-tmbWg6W31tQLeB5cdIBOicJDJRR2KzXsV7uSK9iNfLWQ5bIZfxuPEHp7M8wiHyHnn0DD1i7w3Zmin0FtkrwoCQ=="],
"js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="],
"react-dom": ["react-dom@19.2.0", "", { "dependencies": { "scheduler": "^0.27.0" }, "peerDependencies": { "react": "^19.2.0" } }, "sha512-UlbRu4cAiGaIewkPyiRGJk0imDN2T3JjieT6spoL2UeSf5od4n5LB/mQ4ejmxhCFT1tYe8IvaFulzynWovsEFQ=="],
"loose-envify": ["loose-envify@1.4.0", "", { "dependencies": { "js-tokens": "^3.0.0 || ^4.0.0" }, "bin": { "loose-envify": "cli.js" } }, "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q=="],
"scheduler": ["scheduler@0.27.0", "", {}, "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q=="],
"react": ["react@18.3.0-next-b72ed698f-20230303", "", { "dependencies": { "loose-envify": "^1.1.0" } }, "sha512-l6RbwXa9Peerh9pQEq62DDypxSQfavbybY0wV1vwZ63X0P5VaaEesZAz1KPpnVvXjTtQaOMQsIPvnQwmaVqzTQ=="],
"react-dom": ["react-dom@18.3.0-next-b72ed698f-20230303", "", { "dependencies": { "loose-envify": "^1.1.0", "scheduler": "0.24.0-next-b72ed698f-20230303" }, "peerDependencies": { "react": "18.3.0-next-b72ed698f-20230303" } }, "sha512-0Gh/gmTT6H8KxswIQB/8shdTTfs6QIu86nNqZf3Y0RBqIwgTVxRaQVz14/Fw4/Nt81nK/Jt6KT4bx3yvOxZDGQ=="],
"scheduler": ["scheduler@0.24.0-next-b72ed698f-20230303", "", { "dependencies": { "loose-envify": "^1.1.0" } }, "sha512-ct4DMMFbc2kFxCdvbG+i/Jn1S1oqrIFSn2VX/mam+Ya0iuNy+lb8rgT7A+YBUqrQNDaNEqABYI2sOQgqoRxp7w=="],
}
}

View File

@@ -4,14 +4,13 @@
"description": "",
"main": "react-hello-world.node.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build:workerd": "bun build react-hello-world.workerd.jsx --outfile=react-hello-world.workerd.js --format=esm --production && (echo '// MessageChannel polyfill for workerd'; echo 'if (typeof MessageChannel === \"undefined\") {'; echo ' globalThis.MessageChannel = class MessageChannel {'; echo ' constructor() {'; echo ' this.port1 = { onmessage: null, postMessage: () => {} };'; echo ' this.port2 = {'; echo ' postMessage: (msg) => {'; echo ' if (this.port1.onmessage) {'; echo ' queueMicrotask(() => this.port1.onmessage({ data: msg }));'; echo ' }'; echo ' }'; echo ' };'; echo ' }'; echo ' };'; echo '}'; cat react-hello-world.workerd.js) > temp.js && mv temp.js react-hello-world.workerd.js"
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "Colin McDonnell",
"license": "ISC",
"dependencies": {
"react": "^19.2.0",
"react-dom": "^19.2.0"
"react": "next",
"react-dom": "next"
}
}

View File

@@ -1,23 +0,0 @@
using Workerd = import "/workerd/workerd.capnp";
const config :Workerd.Config = (
services = [
(name = "main", worker = .mainWorker),
],
sockets = [
( name = "http",
address = "*:3001",
http = (),
service = "main"
),
]
);
const mainWorker :Workerd.Worker = (
modules = [
(name = "worker", esModule = embed "react-hello-world.workerd.js"),
],
compatibilityDate = "2025-01-01",
compatibilityFlags = ["nodejs_compat_v2"],
);

File diff suppressed because one or more lines are too long

View File

@@ -1,40 +0,0 @@
// Cloudflare Workers version with export default fetch
// Run with: workerd serve react-hello-world.workerd.config.capnp
// Polyfill MessageChannel for workerd
if (typeof MessageChannel === 'undefined') {
globalThis.MessageChannel = class MessageChannel {
constructor() {
this.port1 = { onmessage: null, postMessage: () => {} };
this.port2 = {
postMessage: (msg) => {
if (this.port1.onmessage) {
queueMicrotask(() => this.port1.onmessage({ data: msg }));
}
}
};
}
};
}
import React from "react";
import { renderToReadableStream } from "react-dom/server";
const headers = {
"Content-Type": "text/html",
};
const App = () => (
<html>
<body>
<h1>Hello World</h1>
<p>This is an example.</p>
</body>
</html>
);
export default {
async fetch(request) {
return new Response(await renderToReadableStream(<App />), { headers });
},
};

View File

@@ -13,4 +13,7 @@ export function run(opts = {}) {
}
export const bench = Mitata.bench;
export const group = Mitata.group;
export function group(_name, fn) {
return Mitata.group(fn);
}

View File

@@ -1,156 +0,0 @@
import { bench, group, run } from "../runner.mjs";
const runAll = !process.argv.includes("--simple");
const small = new Uint8Array(1024);
const medium = new Uint8Array(1024 * 100);
const large = new Uint8Array(1024 * 1024);
for (let i = 0; i < large.length; i++) {
const value = Math.floor(Math.sin(i / 100) * 128 + 128);
if (i < small.length) small[i] = value;
if (i < medium.length) medium[i] = value;
large[i] = value;
}
const format = new Intl.NumberFormat("en-US", { notation: "compact", unit: "byte" });
async function compress(data, format) {
const cs = new CompressionStream(format);
const writer = cs.writable.getWriter();
const reader = cs.readable.getReader();
writer.write(data);
writer.close();
const chunks = [];
while (true) {
const { done, value } = await reader.read();
if (done) break;
chunks.push(value);
}
const result = new Uint8Array(chunks.reduce((acc, chunk) => acc + chunk.length, 0));
let offset = 0;
for (const chunk of chunks) {
result.set(chunk, offset);
offset += chunk.length;
}
return result;
}
async function decompress(data, format) {
const ds = new DecompressionStream(format);
const writer = ds.writable.getWriter();
const reader = ds.readable.getReader();
writer.write(data);
writer.close();
const chunks = [];
while (true) {
const { done, value } = await reader.read();
if (done) break;
chunks.push(value);
}
const result = new Uint8Array(chunks.reduce((acc, chunk) => acc + chunk.length, 0));
let offset = 0;
for (const chunk of chunks) {
result.set(chunk, offset);
offset += chunk.length;
}
return result;
}
async function roundTrip(data, format) {
const compressed = await compress(data, format);
return await decompress(compressed, format);
}
const formats = ["deflate", "gzip", "deflate-raw"];
if (runAll) formats.push("brotli", "zstd");
// Small data benchmarks (1KB)
group(`CompressionStream ${format.format(small.length)}`, () => {
for (const fmt of formats) {
try {
new CompressionStream(fmt);
bench(fmt, async () => await compress(small, fmt));
} catch (e) {
// Skip unsupported formats
}
}
});
// Medium data benchmarks (100KB)
group(`CompressionStream ${format.format(medium.length)}`, () => {
for (const fmt of formats) {
try {
new CompressionStream(fmt);
bench(fmt, async () => await compress(medium, fmt));
} catch (e) {}
}
});
// Large data benchmarks (1MB)
group(`CompressionStream ${format.format(large.length)}`, () => {
for (const fmt of formats) {
try {
new CompressionStream(fmt);
bench(fmt, async () => await compress(large, fmt));
} catch (e) {
// Skip unsupported formats
}
}
});
const compressedData = {};
for (const fmt of formats) {
try {
compressedData[fmt] = {
small: await compress(small, fmt),
medium: await compress(medium, fmt),
large: await compress(large, fmt),
};
} catch (e) {
// Skip unsupported formats
}
}
group(`DecompressionStream ${format.format(small.length)}`, () => {
for (const fmt of formats) {
if (compressedData[fmt]) {
bench(fmt, async () => await decompress(compressedData[fmt].small, fmt));
}
}
});
group(`DecompressionStream ${format.format(medium.length)}`, () => {
for (const fmt of formats) {
if (compressedData[fmt]) {
bench(fmt, async () => await decompress(compressedData[fmt].medium, fmt));
}
}
});
group(`DecompressionStream ${format.format(large.length)}`, () => {
for (const fmt of formats) {
if (compressedData[fmt]) {
bench(fmt, async () => await decompress(compressedData[fmt].large, fmt));
}
}
});
group(`roundtrip ${format.format(large.length)}`, () => {
for (const fmt of formats) {
try {
new CompressionStream(fmt);
bench(fmt, async () => await roundTrip(large, fmt));
} catch (e) {
// Skip unsupported formats
}
}
});
await run();

View File

@@ -11,10 +11,10 @@ const builtin = ${JSON.stringify(builtin)};
const now = performance.now();
require(builtin);
const end = performance.now();
process.stdout.write(JSON.stringify({ builtin, time: end - now }) + "\\n");
process.stdout.write(JSON.stringify({builtin, time: end - now}) + "\\n");
`,
);
spawnSync(process.execPath, [path], {
const result = spawnSync(typeof Bun !== "undefined" ? "bun" : "node", [path], {
stdio: ["inherit", "inherit", "inherit"],
env: {
...process.env,

View File

@@ -1,48 +0,0 @@
import { bench, group, run } from "../runner.mjs";
const patterns = [
{ name: "string pattern", input: "https://(sub.)?example(.com/)foo" },
{ name: "hostname IDN", input: { hostname: "xn--caf-dma.com" } },
{
name: "pathname + search + hash + baseURL",
input: {
pathname: "/foo",
search: "bar",
hash: "baz",
baseURL: "https://example.com:8080",
},
},
{ name: "pathname with regex", input: { pathname: "/([[a-z]--a])" } },
{ name: "named groups", input: { pathname: "/users/:id/posts/:postId" } },
{ name: "wildcard", input: { pathname: "/files/*" } },
];
const testURL = "https://sub.example.com/foo";
group("URLPattern parse (constructor)", () => {
for (const { name, input } of patterns) {
bench(name, () => {
return new URLPattern(input);
});
}
});
group("URLPattern.test()", () => {
for (const { name, input } of patterns) {
const pattern = new URLPattern(input);
bench(name, () => {
return pattern.test(testURL);
});
}
});
group("URLPattern.exec()", () => {
for (const { name, input } of patterns) {
const pattern = new URLPattern(input);
bench(name, () => {
return pattern.exec(testURL);
});
}
});
await run();

View File

@@ -18,6 +18,22 @@ const OperatingSystem = @import("src/env.zig").OperatingSystem;
const pathRel = fs.path.relative;
/// When updating this, make sure to adjust SetupZig.cmake
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 ++ ", 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.",
// );
// }
// }
const zero_sha = "0000000000000000000000000000000000000000";
const BunBuildOptions = struct {
@@ -32,9 +48,7 @@ const BunBuildOptions = struct {
/// enable debug logs in release builds
enable_logs: bool = false,
enable_asan: bool,
enable_fuzzilli: bool,
enable_valgrind: bool,
use_mimalloc: bool,
tracy_callstack_depth: u16,
reported_nodejs_version: Version,
/// To make iterating on some '@embedFile's faster, we load them at runtime
@@ -82,10 +96,8 @@ const BunBuildOptions = struct {
opts.addOption(bool, "baseline", this.isBaseline());
opts.addOption(bool, "enable_logs", this.enable_logs);
opts.addOption(bool, "enable_asan", this.enable_asan);
opts.addOption(bool, "enable_fuzzilli", this.enable_fuzzilli);
opts.addOption(bool, "enable_valgrind", this.enable_valgrind);
opts.addOption(bool, "use_mimalloc", this.use_mimalloc);
opts.addOption([]const u8, "reported_nodejs_version", b.fmt("{f}", .{this.reported_nodejs_version}));
opts.addOption([]const u8, "reported_nodejs_version", b.fmt("{}", .{this.reported_nodejs_version}));
opts.addOption(bool, "zig_self_hosted_backend", this.no_llvm);
opts.addOption(bool, "override_no_export_cpp_apis", this.override_no_export_cpp_apis);
@@ -120,8 +132,8 @@ pub fn getOSVersionMin(os: OperatingSystem) ?Target.Query.OsVersion {
pub fn getOSGlibCVersion(os: OperatingSystem) ?Version {
return switch (os) {
// Compiling with a newer glibc than this will break certain cloud environments. See symbols.test.ts.
.linux => .{ .major = 2, .minor = 26, .patch = 0 },
// Compiling with a newer glibc than this will break certain cloud environments.
.linux => .{ .major = 2, .minor = 27, .patch = 0 },
else => null,
};
@@ -257,9 +269,7 @@ pub fn build(b: *Build) !void {
.tracy_callstack_depth = b.option(u16, "tracy_callstack_depth", "") orelse 10,
.enable_logs = b.option(bool, "enable_logs", "Enable logs in release") orelse false,
.enable_asan = b.option(bool, "enable_asan", "Enable asan") orelse false,
.enable_fuzzilli = b.option(bool, "enable_fuzzilli", "Enable fuzzilli instrumentation") orelse false,
.enable_valgrind = b.option(bool, "enable_valgrind", "Enable valgrind") orelse false,
.use_mimalloc = b.option(bool, "use_mimalloc", "Use mimalloc as default allocator") orelse false,
.llvm_codegen_threads = b.option(u32, "llvm_codegen_threads", "Number of threads to use for LLVM codegen") orelse 1,
};
@@ -277,16 +287,14 @@ pub fn build(b: *Build) !void {
var o = build_options;
var unit_tests = b.addTest(.{
.name = "bun-test",
.optimize = build_options.optimize,
.root_source_file = b.path("src/unit_test.zig"),
.test_runner = .{ .path = b.path("src/main_test.zig"), .mode = .simple },
.root_module = b.createModule(.{
.optimize = build_options.optimize,
.root_source_file = b.path("src/unit_test.zig"),
.target = build_options.target,
.omit_frame_pointer = false,
.strip = false,
}),
.target = build_options.target,
.use_llvm = !build_options.no_llvm,
.use_lld = if (build_options.os == .mac) false else !build_options.no_llvm,
.omit_frame_pointer = false,
.strip = false,
});
configureObj(b, &o, unit_tests);
// Setting `linker_allow_shlib_undefined` causes the linker to ignore
@@ -320,7 +328,6 @@ pub fn build(b: *Build) !void {
var step = b.step("check", "Check for semantic analysis errors");
var bun_check_obj = addBunObject(b, &build_options);
bun_check_obj.generated_bin = null;
// bun_check_obj.use_llvm = false;
step.dependOn(&bun_check_obj.step);
// The default install step will run zig build check. This is so ZLS
@@ -493,8 +500,6 @@ fn addMultiCheck(
.no_llvm = root_build_options.no_llvm,
.enable_asan = root_build_options.enable_asan,
.enable_valgrind = root_build_options.enable_valgrind,
.enable_fuzzilli = root_build_options.enable_fuzzilli,
.use_mimalloc = root_build_options.use_mimalloc,
.override_no_export_cpp_apis = root_build_options.override_no_export_cpp_apis,
};
@@ -607,22 +612,15 @@ fn configureObj(b: *Build, opts: *BunBuildOptions, obj: *Compile) void {
obj.llvm_codegen_threads = opts.llvm_codegen_threads orelse 0;
}
obj.no_link_obj = opts.os != .windows;
obj.no_link_obj = true;
if (opts.enable_asan and !enableFastBuild(b)) {
if (@hasField(Build.Module, "sanitize_address")) {
if (opts.enable_fuzzilli) {
obj.sanitize_coverage_trace_pc_guard = true;
}
obj.root_module.sanitize_address = true;
} else {
const fail_step = b.addFail("asan is not supported on this platform");
obj.step.dependOn(&fail_step.step);
}
} else if (opts.enable_fuzzilli) {
const fail_step = b.addFail("fuzzilli requires asan");
obj.step.dependOn(&fail_step.step);
}
obj.bundle_compiler_rt = false;
obj.bundle_ubsan_rt = false;
@@ -722,7 +720,6 @@ fn addInternalImports(b: *Build, mod: *Module, opts: *BunBuildOptions) void {
// Generated code exposed as individual modules.
inline for (.{
.{ .file = "ZigGeneratedClasses.zig", .import = "ZigGeneratedClasses" },
.{ .file = "bindgen_generated.zig", .import = "bindgen_generated" },
.{ .file = "ResolvedSourceTag.zig", .import = "ResolvedSourceTag" },
.{ .file = "ErrorCode.zig", .import = "ErrorCode" },
.{ .file = "runtime.out.js", .enable = opts.shouldEmbedCode() },
@@ -777,13 +774,6 @@ fn addInternalImports(b: *Build, mod: *Module, opts: *BunBuildOptions) void {
mod.addImport("cpp", cppImport);
cppImport.addImport("bun", mod);
}
{
const ciInfoImport = b.createModule(.{
.root_source_file = (std.Build.LazyPath{ .cwd_relative = opts.codegen_path }).path(b, "ci_info.zig"),
});
mod.addImport("ci_info", ciInfoImport);
ciInfoImport.addImport("bun", mod);
}
inline for (.{
.{ .import = "completions-bash", .file = b.path("completions/bun.bash") },
.{ .import = "completions-zsh", .file = b.path("completions/bun.zsh") },
@@ -809,7 +799,7 @@ fn addInternalImports(b: *Build, mod: *Module, opts: *BunBuildOptions) void {
fn propagateImports(source_mod: *Module) !void {
var seen = std.AutoHashMap(*Module, void).init(source_mod.owner.graph.arena);
defer seen.deinit();
var queue = std.array_list.Managed(*Module).init(source_mod.owner.graph.arena);
var queue = std.ArrayList(*Module).init(source_mod.owner.graph.arena);
defer queue.deinit();
try queue.appendSlice(source_mod.import_table.values());
while (queue.pop()) |mod| {

View File

@@ -1,6 +1,5 @@
{
"lockfileVersion": 1,
"configVersion": 1,
"workspaces": {
"": {
"name": "bun",
@@ -9,14 +8,14 @@
"@lezer/cpp": "^1.1.3",
"@types/bun": "workspace:*",
"bun-tracestrings": "github:oven-sh/bun.report#912ca63e26c51429d3e6799aa2a6ab079b188fd8",
"esbuild": "^0.21.5",
"mitata": "^0.1.14",
"esbuild": "^0.21.4",
"mitata": "^0.1.11",
"peechy": "0.4.34",
"prettier": "^3.6.2",
"prettier-plugin-organize-imports": "^4.3.0",
"prettier": "^3.5.3",
"prettier-plugin-organize-imports": "^4.0.0",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"source-map-js": "^1.2.1",
"source-map-js": "^1.2.0",
"typescript": "5.9.2",
},
},
@@ -32,6 +31,12 @@
"dependencies": {
"@types/node": "*",
},
"devDependencies": {
"@types/react": "^19",
},
"peerDependencies": {
"@types/react": "^19",
},
},
},
"overrides": {
@@ -85,13 +90,13 @@
"@esbuild/win32-x64": ["@esbuild/win32-x64@0.21.5", "", { "os": "win32", "cpu": "x64" }, "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw=="],
"@lezer/common": ["@lezer/common@1.3.0", "", {}, "sha512-L9X8uHCYU310o99L3/MpJKYxPzXPOS7S0NmBaM7UO/x2Kb2WbmMLSkfvdr1KxRIFYOpbY0Jhn7CfLSUDzL8arQ=="],
"@lezer/common": ["@lezer/common@1.2.3", "", {}, "sha512-w7ojc8ejBqr2REPsWxJjrMFsA/ysDCFICn8zEOR9mrqzOu2amhITYuLD8ag6XZf0CFXDrhKqw7+tW8cX66NaDA=="],
"@lezer/cpp": ["@lezer/cpp@1.1.3", "", { "dependencies": { "@lezer/common": "^1.2.0", "@lezer/highlight": "^1.0.0", "@lezer/lr": "^1.0.0" } }, "sha512-ykYvuFQKGsRi6IcE+/hCSGUhb/I4WPjd3ELhEblm2wS2cOznDFzO+ubK2c+ioysOnlZ3EduV+MVQFCPzAIoY3w=="],
"@lezer/highlight": ["@lezer/highlight@1.2.3", "", { "dependencies": { "@lezer/common": "^1.3.0" } }, "sha512-qXdH7UqTvGfdVBINrgKhDsVTJTxactNNxLk7+UMwZhU13lMHaOBlJe9Vqp907ya56Y3+ed2tlqzys7jDkTmW0g=="],
"@lezer/highlight": ["@lezer/highlight@1.2.1", "", { "dependencies": { "@lezer/common": "^1.0.0" } }, "sha512-Z5duk4RN/3zuVO7Jq0pGLJ3qynpxUVsh7IbUbGj88+uV2ApSAn6kWg2au3iJb+0Zi7kKtqffIESgNcRXWZWmSA=="],
"@lezer/lr": ["@lezer/lr@1.4.3", "", { "dependencies": { "@lezer/common": "^1.0.0" } }, "sha512-yenN5SqAxAPv/qMnpWW0AT7l+SxVrgG+u0tNsRQWqbrz66HIl8DnEbBObvy21J5K7+I1v7gsAnlE2VQ5yYVSeA=="],
"@lezer/lr": ["@lezer/lr@1.4.2", "", { "dependencies": { "@lezer/common": "^1.0.0" } }, "sha512-pu0K1jCIdnQ12aWNaAVU5bzi7Bd1w54J3ECgANPmYLtQKP0HBj2cE/5coBD66MT10xbtIuUr7tg0Shbsvk0mDA=="],
"@octokit/app": ["@octokit/app@14.1.0", "", { "dependencies": { "@octokit/auth-app": "^6.0.0", "@octokit/auth-unauthenticated": "^5.0.0", "@octokit/core": "^5.0.0", "@octokit/oauth-app": "^6.0.0", "@octokit/plugin-paginate-rest": "^9.0.0", "@octokit/types": "^12.0.0", "@octokit/webhooks": "^12.0.4" } }, "sha512-g3uEsGOQCBl1+W1rgfwoRFUIR6PtvB2T1E4RpygeUU5LrLvlOqcxrt5lfykIeRpUPpupreGJUYl70fqMDXdTpw=="],
@@ -145,7 +150,7 @@
"@sentry/types": ["@sentry/types@7.120.4", "", {}, "sha512-cUq2hSSe6/qrU6oZsEP4InMI5VVdD86aypE+ENrQ6eZEVLTCYm1w6XhW1NvIu3UuWh7gZec4a9J7AFpYxki88Q=="],
"@types/aws-lambda": ["@types/aws-lambda@8.10.159", "", {}, "sha512-SAP22WSGNN12OQ8PlCzGzRCZ7QDCwI85dQZbmpz7+mAk+L7j+wI7qnvmdKh+o7A5LaOp6QnOZ2NJphAZQTTHQg=="],
"@types/aws-lambda": ["@types/aws-lambda@8.10.152", "", {}, "sha512-soT/c2gYBnT5ygwiHPmd9a1bftj462NWVk2tKCc1PYHSIacB2UwbTS2zYG4jzag1mRDuzg/OjtxQjQ2NKRB6Rw=="],
"@types/btoa-lite": ["@types/btoa-lite@1.0.2", "", {}, "sha512-ZYbcE2x7yrvNFJiU7xJGrpF/ihpkM7zKgw8bha3LNJSesvTtUNxbpzaT7WXBIryf6jovisrxTBvymxMeLLj1Mg=="],
@@ -155,7 +160,9 @@
"@types/ms": ["@types/ms@2.1.0", "", {}, "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA=="],
"@types/node": ["@types/node@24.10.1", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-GNWcUTRBgIRJD5zj+Tq0fKOJ5XZajIiBroOF0yvj2bSU1WvNdYS/dn9UxwsujGW4JX06dnHyjV2y9rRaybH0iQ=="],
"@types/node": ["@types/node@24.2.1", "", { "dependencies": { "undici-types": "~7.10.0" } }, "sha512-DRh5K+ka5eJic8CjH7td8QpYEV6Zo10gfRkjHCO3weqZHWDtAaSTFtl4+VMqOJ4N5jcuhZ9/l+yy8rVgw7BQeQ=="],
"@types/react": ["@types/react@19.1.10", "", { "dependencies": { "csstype": "^3.0.2" } }, "sha512-EhBeSYX0Y6ye8pNebpKrwFJq7BoQ8J5SO6NlvNwwHjSj6adXJViPQrKlsyPw7hLBLvckEMO1yxeGdR82YBBlDg=="],
"aggregate-error": ["aggregate-error@3.1.0", "", { "dependencies": { "clean-stack": "^2.0.0", "indent-string": "^4.0.0" } }, "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA=="],
@@ -185,9 +192,11 @@
"constant-case": ["constant-case@3.0.4", "", { "dependencies": { "no-case": "^3.0.4", "tslib": "^2.0.3", "upper-case": "^2.0.2" } }, "sha512-I2hSBi7Vvs7BEuJDr5dDHfzb/Ruj3FyvFyh7KLilAjNQw3Be+xgqUBA2W6scVEcL0hL1dwPRtIqEPVUCKkSsyQ=="],
"csstype": ["csstype@3.1.3", "", {}, "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="],
"deprecation": ["deprecation@2.3.1", "", {}, "sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ=="],
"detect-libc": ["detect-libc@2.1.2", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="],
"detect-libc": ["detect-libc@2.0.4", "", {}, "sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA=="],
"dot-case": ["dot-case@3.0.4", "", { "dependencies": { "no-case": "^3.0.4", "tslib": "^2.0.3" } }, "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w=="],
@@ -211,29 +220,27 @@
"jws": ["jws@3.2.2", "", { "dependencies": { "jwa": "^1.4.1", "safe-buffer": "^5.0.1" } }, "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA=="],
"lightningcss": ["lightningcss@1.30.2", "", { "dependencies": { "detect-libc": "^2.0.3" }, "optionalDependencies": { "lightningcss-android-arm64": "1.30.2", "lightningcss-darwin-arm64": "1.30.2", "lightningcss-darwin-x64": "1.30.2", "lightningcss-freebsd-x64": "1.30.2", "lightningcss-linux-arm-gnueabihf": "1.30.2", "lightningcss-linux-arm64-gnu": "1.30.2", "lightningcss-linux-arm64-musl": "1.30.2", "lightningcss-linux-x64-gnu": "1.30.2", "lightningcss-linux-x64-musl": "1.30.2", "lightningcss-win32-arm64-msvc": "1.30.2", "lightningcss-win32-x64-msvc": "1.30.2" } }, "sha512-utfs7Pr5uJyyvDETitgsaqSyjCb2qNRAtuqUeWIAKztsOYdcACf2KtARYXg2pSvhkt+9NfoaNY7fxjl6nuMjIQ=="],
"lightningcss": ["lightningcss@1.30.1", "", { "dependencies": { "detect-libc": "^2.0.3" }, "optionalDependencies": { "lightningcss-darwin-arm64": "1.30.1", "lightningcss-darwin-x64": "1.30.1", "lightningcss-freebsd-x64": "1.30.1", "lightningcss-linux-arm-gnueabihf": "1.30.1", "lightningcss-linux-arm64-gnu": "1.30.1", "lightningcss-linux-arm64-musl": "1.30.1", "lightningcss-linux-x64-gnu": "1.30.1", "lightningcss-linux-x64-musl": "1.30.1", "lightningcss-win32-arm64-msvc": "1.30.1", "lightningcss-win32-x64-msvc": "1.30.1" } }, "sha512-xi6IyHML+c9+Q3W0S4fCQJOym42pyurFiJUHEcEyHS0CeKzia4yZDEsLlqOFykxOdHpNy0NmvVO31vcSqAxJCg=="],
"lightningcss-android-arm64": ["lightningcss-android-arm64@1.30.2", "", { "os": "android", "cpu": "arm64" }, "sha512-BH9sEdOCahSgmkVhBLeU7Hc9DWeZ1Eb6wNS6Da8igvUwAe0sqROHddIlvU06q3WyXVEOYDZ6ykBZQnjTbmo4+A=="],
"lightningcss-darwin-arm64": ["lightningcss-darwin-arm64@1.30.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-c8JK7hyE65X1MHMN+Viq9n11RRC7hgin3HhYKhrMyaXflk5GVplZ60IxyoVtzILeKr+xAJwg6zK6sjTBJ0FKYQ=="],
"lightningcss-darwin-arm64": ["lightningcss-darwin-arm64@1.30.2", "", { "os": "darwin", "cpu": "arm64" }, "sha512-ylTcDJBN3Hp21TdhRT5zBOIi73P6/W0qwvlFEk22fkdXchtNTOU4Qc37SkzV+EKYxLouZ6M4LG9NfZ1qkhhBWA=="],
"lightningcss-darwin-x64": ["lightningcss-darwin-x64@1.30.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-k1EvjakfumAQoTfcXUcHQZhSpLlkAuEkdMBsI/ivWw9hL+7FtilQc0Cy3hrx0AAQrVtQAbMI7YjCgYgvn37PzA=="],
"lightningcss-darwin-x64": ["lightningcss-darwin-x64@1.30.2", "", { "os": "darwin", "cpu": "x64" }, "sha512-oBZgKchomuDYxr7ilwLcyms6BCyLn0z8J0+ZZmfpjwg9fRVZIR5/GMXd7r9RH94iDhld3UmSjBM6nXWM2TfZTQ=="],
"lightningcss-freebsd-x64": ["lightningcss-freebsd-x64@1.30.1", "", { "os": "freebsd", "cpu": "x64" }, "sha512-kmW6UGCGg2PcyUE59K5r0kWfKPAVy4SltVeut+umLCFoJ53RdCUWxcRDzO1eTaxf/7Q2H7LTquFHPL5R+Gjyig=="],
"lightningcss-freebsd-x64": ["lightningcss-freebsd-x64@1.30.2", "", { "os": "freebsd", "cpu": "x64" }, "sha512-c2bH6xTrf4BDpK8MoGG4Bd6zAMZDAXS569UxCAGcA7IKbHNMlhGQ89eRmvpIUGfKWNVdbhSbkQaWhEoMGmGslA=="],
"lightningcss-linux-arm-gnueabihf": ["lightningcss-linux-arm-gnueabihf@1.30.1", "", { "os": "linux", "cpu": "arm" }, "sha512-MjxUShl1v8pit+6D/zSPq9S9dQ2NPFSQwGvxBCYaBYLPlCWuPh9/t1MRS8iUaR8i+a6w7aps+B4N0S1TYP/R+Q=="],
"lightningcss-linux-arm-gnueabihf": ["lightningcss-linux-arm-gnueabihf@1.30.2", "", { "os": "linux", "cpu": "arm" }, "sha512-eVdpxh4wYcm0PofJIZVuYuLiqBIakQ9uFZmipf6LF/HRj5Bgm0eb3qL/mr1smyXIS1twwOxNWndd8z0E374hiA=="],
"lightningcss-linux-arm64-gnu": ["lightningcss-linux-arm64-gnu@1.30.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-gB72maP8rmrKsnKYy8XUuXi/4OctJiuQjcuqWNlJQ6jZiWqtPvqFziskH3hnajfvKB27ynbVCucKSm2rkQp4Bw=="],
"lightningcss-linux-arm64-gnu": ["lightningcss-linux-arm64-gnu@1.30.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-UK65WJAbwIJbiBFXpxrbTNArtfuznvxAJw4Q2ZGlU8kPeDIWEX1dg3rn2veBVUylA2Ezg89ktszWbaQnxD/e3A=="],
"lightningcss-linux-arm64-musl": ["lightningcss-linux-arm64-musl@1.30.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-jmUQVx4331m6LIX+0wUhBbmMX7TCfjF5FoOH6SD1CttzuYlGNVpA7QnrmLxrsub43ClTINfGSYyHe2HWeLl5CQ=="],
"lightningcss-linux-arm64-musl": ["lightningcss-linux-arm64-musl@1.30.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-5Vh9dGeblpTxWHpOx8iauV02popZDsCYMPIgiuw97OJ5uaDsL86cnqSFs5LZkG3ghHoX5isLgWzMs+eD1YzrnA=="],
"lightningcss-linux-x64-gnu": ["lightningcss-linux-x64-gnu@1.30.1", "", { "os": "linux", "cpu": "x64" }, "sha512-piWx3z4wN8J8z3+O5kO74+yr6ze/dKmPnI7vLqfSqI8bccaTGY5xiSGVIJBDd5K5BHlvVLpUB3S2YCfelyJ1bw=="],
"lightningcss-linux-x64-gnu": ["lightningcss-linux-x64-gnu@1.30.2", "", { "os": "linux", "cpu": "x64" }, "sha512-Cfd46gdmj1vQ+lR6VRTTadNHu6ALuw2pKR9lYq4FnhvgBc4zWY1EtZcAc6EffShbb1MFrIPfLDXD6Xprbnni4w=="],
"lightningcss-linux-x64-musl": ["lightningcss-linux-x64-musl@1.30.1", "", { "os": "linux", "cpu": "x64" }, "sha512-rRomAK7eIkL+tHY0YPxbc5Dra2gXlI63HL+v1Pdi1a3sC+tJTcFrHX+E86sulgAXeI7rSzDYhPSeHHjqFhqfeQ=="],
"lightningcss-linux-x64-musl": ["lightningcss-linux-x64-musl@1.30.2", "", { "os": "linux", "cpu": "x64" }, "sha512-XJaLUUFXb6/QG2lGIW6aIk6jKdtjtcffUT0NKvIqhSBY3hh9Ch+1LCeH80dR9q9LBjG3ewbDjnumefsLsP6aiA=="],
"lightningcss-win32-arm64-msvc": ["lightningcss-win32-arm64-msvc@1.30.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-mSL4rqPi4iXq5YVqzSsJgMVFENoa4nGTT/GjO2c0Yl9OuQfPsIfncvLrEW6RbbB24WtZ3xP/2CCmI3tNkNV4oA=="],
"lightningcss-win32-arm64-msvc": ["lightningcss-win32-arm64-msvc@1.30.2", "", { "os": "win32", "cpu": "arm64" }, "sha512-FZn+vaj7zLv//D/192WFFVA0RgHawIcHqLX9xuWiQt7P0PtdFEVaxgF9rjM/IRYHQXNnk61/H/gb2Ei+kUQ4xQ=="],
"lightningcss-win32-x64-msvc": ["lightningcss-win32-x64-msvc@1.30.2", "", { "os": "win32", "cpu": "x64" }, "sha512-5g1yc73p+iAkid5phb4oVFMB45417DkRevRbt/El/gKXJk4jid+vPFF/AXbxn05Aky8PapwzZrdJShv5C0avjw=="],
"lightningcss-win32-x64-msvc": ["lightningcss-win32-x64-msvc@1.30.1", "", { "os": "win32", "cpu": "x64" }, "sha512-PVqXh48wh4T53F/1CCu8PIPCxLzWyCnn/9T5W1Jpmdy5h9Cwd+0YQS6/LwhHXSafuc61/xg9Lv5OrCby6a++jg=="],
"lodash.includes": ["lodash.includes@4.3.0", "", {}, "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w=="],
@@ -277,7 +284,7 @@
"prettier": ["prettier@3.6.2", "", { "bin": { "prettier": "bin/prettier.cjs" } }, "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ=="],
"prettier-plugin-organize-imports": ["prettier-plugin-organize-imports@4.3.0", "", { "peerDependencies": { "prettier": ">=2.0", "typescript": ">=2.9", "vue-tsc": "^2.1.0 || 3" }, "optionalPeers": ["vue-tsc"] }, "sha512-FxFz0qFhyBsGdIsb697f/EkvHzi5SZOhWAjxcx2dLt+Q532bAlhswcXGYB1yzjZ69kW8UoadFBw7TyNwlq96Iw=="],
"prettier-plugin-organize-imports": ["prettier-plugin-organize-imports@4.2.0", "", { "peerDependencies": { "prettier": ">=2.0", "typescript": ">=2.9", "vue-tsc": "^2.1.0 || 3" }, "optionalPeers": ["vue-tsc"] }, "sha512-Zdy27UhlmyvATZi67BTnLcKTo8fm6Oik59Sz6H64PgZJVs6NJpPD1mT240mmJn62c98/QaL+r3kx9Q3gRpDajg=="],
"react": ["react@18.3.1", "", { "dependencies": { "loose-envify": "^1.1.0" } }, "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ=="],
@@ -289,7 +296,7 @@
"scheduler": ["scheduler@0.23.2", "", { "dependencies": { "loose-envify": "^1.1.0" } }, "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ=="],
"semver": ["semver@7.7.3", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q=="],
"semver": ["semver@7.7.2", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA=="],
"sentence-case": ["sentence-case@3.0.4", "", { "dependencies": { "no-case": "^3.0.4", "tslib": "^2.0.3", "upper-case-first": "^2.0.2" } }, "sha512-8LS0JInaQMCRoQ7YUytAo/xUu5W2XnQxV2HI/6uM6U7CITS1RqPElr30V6uIqyMKM9lJGRVFy5/4CuzcixNYSg=="],
@@ -305,7 +312,7 @@
"uglify-js": ["uglify-js@3.19.3", "", { "bin": { "uglifyjs": "bin/uglifyjs" } }, "sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ=="],
"undici-types": ["undici-types@7.16.0", "", {}, "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw=="],
"undici-types": ["undici-types@7.10.0", "", {}, "sha512-t5Fy/nfn+14LuOc2KNYg75vZqClpAiqscVvMygNnlsHBFpSXdJaYtXMcdNLpl/Qvc3P2cB3s6lOV51nqsFq4ag=="],
"universal-github-app-jwt": ["universal-github-app-jwt@1.2.0", "", { "dependencies": { "@types/jsonwebtoken": "^9.0.0", "jsonwebtoken": "^9.0.2" } }, "sha512-dncpMpnsKBk0eetwfN8D8OUHGfiDhhJ+mtsbMl+7PfW7mYjiH8LIcqRmYMtzYLgSh47HjfdBtrBwIQ/gizKR3g=="],

View File

@@ -10,4 +10,3 @@ preload = "./test/preload.ts"
[install]
linker = "isolated"
minimumReleaseAge = 259200 # three days

View File

@@ -51,23 +51,6 @@ if(ENABLE_ASAN)
)
endif()
if(ENABLE_FUZZILLI)
register_compiler_flags(
DESCRIPTION "Enable coverage instrumentation for fuzzing"
-fsanitize-coverage=trace-pc-guard
)
register_linker_flags(
DESCRIPTION "Link coverage instrumentation"
-fsanitize-coverage=trace-pc-guard
)
register_compiler_flags(
DESCRIPTION "Enable fuzzilli-specific code"
-DFUZZILLI_ENABLED
)
endif()
# --- Optimization level ---
if(DEBUG)
register_compiler_flags(
@@ -103,20 +86,11 @@ elseif(APPLE)
endif()
if(UNIX)
# Nix LLVM doesn't support zstd compression, use zlib instead
if(DEFINED ENV{NIX_CC})
register_compiler_flags(
DESCRIPTION "Enable debug symbols (zlib-compressed for Nix)"
-g3 -gz=zlib ${DEBUG}
-g1 ${RELEASE}
)
else()
register_compiler_flags(
DESCRIPTION "Enable debug symbols (zstd-compressed)"
-g3 -gz=zstd ${DEBUG}
-g1 ${RELEASE}
)
endif()
register_compiler_flags(
DESCRIPTION "Enable debug symbols"
-g3 -gz=zstd ${DEBUG}
-g1 ${RELEASE}
)
register_compiler_flags(
DESCRIPTION "Optimize debug symbols for LLDB"
@@ -232,6 +206,43 @@ if(ENABLE_ASSERTIONS)
DESCRIPTION "Do not eliminate null-pointer checks"
-fno-delete-null-pointer-checks
)
register_compiler_definitions(
DESCRIPTION "Enable libc++ assertions"
_LIBCPP_ENABLE_ASSERTIONS=1
_LIBCPP_HARDENING_MODE=_LIBCPP_HARDENING_MODE_EXTENSIVE ${RELEASE}
_LIBCPP_HARDENING_MODE=_LIBCPP_HARDENING_MODE_DEBUG ${DEBUG}
)
register_compiler_definitions(
DESCRIPTION "Enable fortified sources"
_FORTIFY_SOURCE=3
)
if(LINUX)
register_compiler_definitions(
DESCRIPTION "Enable glibc++ assertions"
_GLIBCXX_ASSERTIONS=1
)
endif()
else()
register_compiler_definitions(
DESCRIPTION "Disable debug assertions"
NDEBUG=1
)
register_compiler_definitions(
DESCRIPTION "Disable libc++ assertions"
_LIBCPP_ENABLE_ASSERTIONS=0
_LIBCPP_HARDENING_MODE=_LIBCPP_HARDENING_MODE_NONE
)
if(LINUX)
register_compiler_definitions(
DESCRIPTION "Disable glibc++ assertions"
_GLIBCXX_ASSERTIONS=0
)
endif()
endif()
# --- Diagnostics ---
@@ -282,6 +293,14 @@ if(UNIX AND CI)
)
endif()
# --- Features ---
# Valgrind cannot handle SSE4.2 instructions
# This is needed for picohttpparser
if(ENABLE_VALGRIND AND ARCH STREQUAL "x64")
register_compiler_definitions(__SSE4_2__=0)
endif()
# --- Other ---
# Workaround for CMake and clang-cl bug.

View File

@@ -125,8 +125,7 @@ setx(CWD ${CMAKE_SOURCE_DIR})
setx(BUILD_PATH ${CMAKE_BINARY_DIR})
optionx(CACHE_PATH FILEPATH "The path to the cache directory" DEFAULT ${BUILD_PATH}/cache)
optionx(CACHE_STRATEGY "auto|distributed|local|none" "The strategy to use for caching" DEFAULT
"auto")
optionx(CACHE_STRATEGY "read-write|read-only|write-only|none" "The strategy to use for caching" DEFAULT "read-write")
optionx(CI BOOL "If CI is enabled" DEFAULT OFF)
optionx(ENABLE_ANALYSIS BOOL "If static analysis targets should be enabled" DEFAULT OFF)
@@ -137,44 +136,21 @@ else()
set(WARNING WARNING)
endif()
# TODO: This causes flaky zig builds in CI, so temporarily disable it.
# if(CI)
# set(DEFAULT_VENDOR_PATH ${CACHE_PATH}/vendor)
# else()
# set(DEFAULT_VENDOR_PATH ${CWD}/vendor)
# endif()
optionx(VENDOR_PATH FILEPATH "The path to the vendor directory" DEFAULT ${CWD}/vendor)
optionx(TMP_PATH FILEPATH "The path to the temporary directory" DEFAULT ${BUILD_PATH}/tmp)
# --- Helper functions ---
# list_filter_out_regex()
#
# Description:
# Filters out elements from a list that match a regex pattern.
#
# Arguments:
# list - The list of strings to traverse
# pattern - The regex pattern to filter out
# touched - A variable to set if any items were removed
function(list_filter_out_regex list pattern touched)
set(result_list "${${list}}")
set(keep_list)
set(was_modified OFF)
foreach(line IN LISTS result_list)
if(line MATCHES "${pattern}")
set(was_modified ON)
else()
list(APPEND keep_list ${line})
endif()
endforeach()
set(${list} "${keep_list}" PARENT_SCOPE)
set(${touched} ${was_modified} PARENT_SCOPE)
endfunction()
# setenv()
# Description:
# Sets an environment variable during the build step, and writes it to a .env file.
#
# See Also:
# unsetenv()
#
# Arguments:
# variable string - The variable to set
# value string - The value to set the variable to
@@ -187,7 +163,13 @@ function(setenv variable value)
if(EXISTS ${ENV_PATH})
file(STRINGS ${ENV_PATH} ENV_FILE ENCODING UTF-8)
list_filter_out_regex(ENV_FILE "^${variable}=" ENV_MODIFIED)
foreach(line ${ENV_FILE})
if(line MATCHES "^${variable}=")
list(REMOVE_ITEM ENV_FILE ${line})
set(ENV_MODIFIED ON)
endif()
endforeach()
if(ENV_MODIFIED)
list(APPEND ENV_FILE "${variable}=${value}")
@@ -203,28 +185,6 @@ function(setenv variable value)
message(STATUS "Set ENV ${variable}: ${value}")
endfunction()
# See setenv()
# Description:
# Exact opposite of setenv().
# Arguments:
# variable string - The variable to unset.
# See Also:
# setenv()
function(unsetenv variable)
set(ENV_PATH ${BUILD_PATH}/.env)
if(NOT EXISTS ${ENV_PATH})
return()
endif()
file(STRINGS ${ENV_PATH} ENV_FILE ENCODING UTF-8)
list_filter_out_regex(ENV_FILE "^${variable}=" ENV_MODIFIED)
if(ENV_MODIFIED)
list(JOIN ENV_FILE "\n" ENV_FILE)
file(WRITE ${ENV_PATH} ${ENV_FILE})
endif()
endfunction()
# satisfies_range()
# Description:
# Check if a version satisfies a version range or list of ranges
@@ -357,7 +317,7 @@ function(find_command)
${FIND_VALIDATOR}
)
if(FIND_REQUIRED AND ${FIND_VARIABLE} MATCHES "NOTFOUND")
if(NOT FIND_REQUIRED STREQUAL "OFF" AND ${FIND_VARIABLE} MATCHES "NOTFOUND")
set(error "Command not found: \"${FIND_NAME}\"")
if(FIND_VERSION)
@@ -957,6 +917,10 @@ function(register_compiler_flags)
endforeach()
endfunction()
function(register_compiler_definitions)
endfunction()
# register_linker_flags()
# Description:
# Registers a linker flag, similar to `add_link_options()`.

View File

@@ -127,8 +127,6 @@ if (NOT ENABLE_ASAN)
set(ENABLE_ZIG_ASAN OFF)
endif()
optionx(ENABLE_FUZZILLI BOOL "If fuzzilli support should be enabled" DEFAULT OFF)
if(RELEASE AND LINUX AND CI AND NOT ENABLE_ASSERTIONS AND NOT ENABLE_ASAN)
set(DEFAULT_LTO ON)
else()
@@ -142,6 +140,11 @@ if(ENABLE_ASAN AND ENABLE_LTO)
setx(ENABLE_LTO OFF)
endif()
if(USE_VALGRIND AND NOT USE_BASELINE)
message(WARNING "If valgrind is enabled, baseline must also be enabled")
setx(USE_BASELINE ON)
endif()
if(BUILDKITE_COMMIT)
set(DEFAULT_REVISION ${BUILDKITE_COMMIT})
else()
@@ -199,9 +202,4 @@ optionx(USE_WEBKIT_ICU BOOL "Use the ICU libraries from WebKit" DEFAULT ${DEFAUL
optionx(ERROR_LIMIT STRING "Maximum number of errors to show when compiling C++ code" DEFAULT "100")
# This is not an `option` because setting this variable to OFF is experimental
# and unsupported. This replaces the `use_mimalloc` variable previously in
# bun.zig, and enables C++ code to also be aware of the option.
set(USE_MIMALLOC_AS_DEFAULT_ALLOCATOR ON)
list(APPEND CMAKE_ARGS -DCMAKE_EXPORT_COMPILE_COMMANDS=ON)

View File

@@ -31,14 +31,6 @@
"output": "BindgenSources.txt",
"paths": ["src/**/*.bind.ts"]
},
{
"output": "BindgenV2Sources.txt",
"paths": ["src/**/*.bindv2.ts"]
},
{
"output": "BindgenV2InternalSources.txt",
"paths": ["src/codegen/bindgenv2/**/*.ts"]
},
{
"output": "ZigSources.txt",
"paths": ["src/**/*.zig"]

View File

@@ -34,6 +34,26 @@ register_command(
ALWAYS_RUN
)
if(GIT_CHANGED_SOURCES)
set(CLANG_FORMAT_CHANGED_SOURCES)
foreach(source ${CLANG_FORMAT_SOURCES})
list(FIND GIT_CHANGED_SOURCES ${source} index)
if(NOT ${index} EQUAL -1)
list(APPEND CLANG_FORMAT_CHANGED_SOURCES ${source})
endif()
endforeach()
endif()
if(CLANG_FORMAT_CHANGED_SOURCES)
set(CLANG_FORMAT_DIFF_COMMAND ${CLANG_FORMAT_PROGRAM}
-i # edits files in-place
--verbose
${CLANG_FORMAT_CHANGED_SOURCES}
)
else()
set(CLANG_FORMAT_DIFF_COMMAND ${CMAKE_COMMAND} -E echo "No changed files for clang-format")
endif()
register_command(
TARGET
clang-format-diff

View File

@@ -3,7 +3,7 @@
set(CLANG_TIDY_SOURCES ${BUN_C_SOURCES} ${BUN_CXX_SOURCES})
set(CLANG_TIDY_COMMAND ${CLANG_TIDY_PROGRAM}
-p ${BUILD_PATH}
-p ${BUILD_PATH}
--config-file=${CWD}/.clang-tidy
)
@@ -40,6 +40,27 @@ register_command(
ALWAYS_RUN
)
if(GIT_CHANGED_SOURCES)
set(CLANG_TIDY_CHANGED_SOURCES)
foreach(source ${CLANG_TIDY_SOURCES})
list(FIND GIT_CHANGED_SOURCES ${source} index)
if(NOT ${index} EQUAL -1)
list(APPEND CLANG_TIDY_CHANGED_SOURCES ${source})
endif()
endforeach()
endif()
if(CLANG_TIDY_CHANGED_SOURCES)
set(CLANG_TIDY_DIFF_COMMAND ${CLANG_TIDY_PROGRAM}
${CLANG_TIDY_CHANGED_SOURCES}
--fix
--fix-errors
--fix-notes
)
else()
set(CLANG_TIDY_DIFF_COMMAND ${CMAKE_COMMAND} -E echo "No changed files for clang-tidy")
endif()
register_command(
TARGET
clang-tidy-diff

View File

@@ -0,0 +1,33 @@
# https://cppcheck.sourceforge.io/
find_command(
VARIABLE
CPPCHECK_EXECUTABLE
COMMAND
cppcheck
REQUIRED
OFF
)
set(CPPCHECK_COMMAND ${CPPCHECK_EXECUTABLE}
--cppcheck-build-dir=${BUILD_PATH}/cppcheck
--project=${BUILD_PATH}/compile_commands.json
--clang=${CMAKE_CXX_COMPILER}
--std=c++${CMAKE_CXX_STANDARD}
--report-progress
--showtime=summary
)
register_command(
TARGET
cppcheck
COMMENT
"Running cppcheck"
COMMAND
${CMAKE_COMMAND} -E make_directory cppcheck
&& ${CPPCHECK_COMMAND}
CWD
${BUILD_PATH}
TARGETS
${bun}
)

View File

@@ -0,0 +1,22 @@
find_command(
VARIABLE
CPPLINT_PROGRAM
COMMAND
cpplint
REQUIRED
OFF
)
register_command(
TARGET
cpplint
COMMENT
"Running cpplint"
COMMAND
${CPPLINT_PROGRAM}
${BUN_CPP_SOURCES}
CWD
${BUILD_PATH}
TARGETS
${bun}
)

View File

@@ -0,0 +1,67 @@
# IWYU = "Include What You Use"
# https://include-what-you-use.org/
setx(IWYU_SOURCE_PATH ${CACHE_PATH}/iwyu-${LLVM_VERSION})
setx(IWYU_BUILD_PATH ${IWYU_SOURCE_PATH}/build)
setx(IWYU_PROGRAM ${IWYU_BUILD_PATH}/bin/include-what-you-use)
register_repository(
NAME
iwyu
REPOSITORY
include-what-you-use/include-what-you-use
BRANCH
clang_${LLVM_VERSION}
PATH
${IWYU_SOURCE_PATH}
)
register_command(
TARGET
build-iwyu
COMMENT
"Building iwyu"
COMMAND
${CMAKE_COMMAND}
-B${IWYU_BUILD_PATH}
-G${CMAKE_GENERATOR}
-DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}
-DCMAKE_CXX_COMPILER_LAUNCHER=${CMAKE_CXX_COMPILER_LAUNCHER}
-DIWYU_LLVM_ROOT_PATH=${LLVM_PREFIX}
&& ${CMAKE_COMMAND}
--build ${IWYU_BUILD_PATH}
CWD
${IWYU_SOURCE_PATH}
TARGETS
clone-iwyu
)
find_command(
VARIABLE
PYTHON_EXECUTABLE
COMMAND
python3
python
VERSION
>=3.0.0
REQUIRED
OFF
)
register_command(
TARGET
iwyu
COMMENT
"Running iwyu"
COMMAND
${CMAKE_COMMAND}
-E env IWYU_BINARY=${IWYU_PROGRAM}
${PYTHON_EXECUTABLE}
${IWYU_SOURCE_PATH}/iwyu_tool.py
-p ${BUILD_PATH}
CWD
${BUILD_PATH}
TARGETS
build-iwyu
${bun}
)

View File

@@ -92,6 +92,26 @@ register_command(
ALWAYS_RUN
)
if(GIT_CHANGED_SOURCES)
set(PRETTIER_CHANGED_SOURCES)
foreach(source ${PRETTIER_SOURCES})
list(FIND GIT_CHANGED_SOURCES ${source} index)
if(NOT ${index} EQUAL -1)
list(APPEND PRETTIER_CHANGED_SOURCES ${source})
endif()
endforeach()
endif()
if(PRETTIER_CHANGED_SOURCES)
set(PRETTIER_DIFF_COMMAND ${PRETTIER_COMMAND}
--write
--plugin=prettier-plugin-organize-imports
${PRETTIER_CHANGED_SOURCES}
)
else()
set(PRETTIER_DIFF_COMMAND ${CMAKE_COMMAND} -E echo "No changed files for prettier")
endif()
register_command(
TARGET
prettier-diff

View File

@@ -25,6 +25,25 @@ register_command(
ALWAYS_RUN
)
if(GIT_CHANGED_SOURCES)
set(ZIG_FORMAT_CHANGED_SOURCES)
foreach(source ${ZIG_FORMAT_SOURCES})
list(FIND GIT_CHANGED_SOURCES ${source} index)
if(NOT ${index} EQUAL -1)
list(APPEND ZIG_FORMAT_CHANGED_SOURCES ${source})
endif()
endforeach()
endif()
if(ZIG_FORMAT_CHANGED_SOURCES)
set(ZIG_FORMAT_DIFF_COMMAND ${ZIG_EXECUTABLE}
fmt
${ZIG_FORMAT_CHANGED_SOURCES}
)
else()
set(ZIG_FORMAT_DIFF_COMMAND ${CMAKE_COMMAND} -E echo "No changed files for zig-format")
endif()
register_command(
TARGET
zig-format-diff

View File

@@ -45,6 +45,12 @@ else()
endif()
set(LLVM_ZIG_CODEGEN_THREADS 0)
# This makes the build slower, so we turn it off for now.
# if (DEBUG)
# include(ProcessorCount)
# ProcessorCount(CPU_COUNT)
# set(LLVM_ZIG_CODEGEN_THREADS ${CPU_COUNT})
# endif()
# --- Dependencies ---
@@ -65,6 +71,9 @@ set(BUN_DEPENDENCIES
)
include(CloneZstd)
# foreach(dependency ${BUN_DEPENDENCIES})
# include(Clone${dependency})
# endforeach()
# --- Codegen ---
@@ -317,10 +326,6 @@ set(BUN_CPP_OUTPUTS
${CODEGEN_PATH}/cpp.zig
)
set(BUN_CI_INFO_OUTPUTS
${CODEGEN_PATH}/ci_info.zig
)
register_command(
TARGET
bun-cppbind
@@ -338,21 +343,6 @@ register_command(
${BUN_CPP_OUTPUTS}
)
register_command(
TARGET
bun-ci-info
COMMENT
"Generating CI info"
COMMAND
${BUN_EXECUTABLE}
${CWD}/src/codegen/ci_info.ts
${CODEGEN_PATH}/ci_info.zig
SOURCES
${BUN_JAVASCRIPT_CODEGEN_SOURCES}
OUTPUTS
${BUN_CI_INFO_OUTPUTS}
)
register_command(
TARGET
bun-js-modules
@@ -405,54 +395,6 @@ register_command(
${BUN_BAKE_RUNTIME_OUTPUTS}
)
set(BUN_BINDGENV2_SCRIPT ${CWD}/src/codegen/bindgenv2/script.ts)
absolute_sources(BUN_BINDGENV2_SOURCES ${CWD}/cmake/sources/BindgenV2Sources.txt)
# These sources include the script itself.
absolute_sources(BUN_BINDGENV2_INTERNAL_SOURCES
${CWD}/cmake/sources/BindgenV2InternalSources.txt)
string(REPLACE ";" "," BUN_BINDGENV2_SOURCES_COMMA_SEPARATED
"${BUN_BINDGENV2_SOURCES}")
execute_process(
COMMAND ${BUN_EXECUTABLE} run ${BUN_BINDGENV2_SCRIPT}
--command=list-outputs
--sources=${BUN_BINDGENV2_SOURCES_COMMA_SEPARATED}
--codegen-path=${CODEGEN_PATH}
RESULT_VARIABLE bindgen_result
OUTPUT_VARIABLE bindgen_outputs
)
if(${bindgen_result})
message(FATAL_ERROR "bindgenv2/script.ts exited with non-zero status")
endif()
foreach(output IN LISTS bindgen_outputs)
if(output MATCHES "\.cpp$")
list(APPEND BUN_BINDGENV2_CPP_OUTPUTS ${output})
elseif(output MATCHES "\.zig$")
list(APPEND BUN_BINDGENV2_ZIG_OUTPUTS ${output})
else()
message(FATAL_ERROR "unexpected bindgen output: [${output}]")
endif()
endforeach()
register_command(
TARGET
bun-bindgen-v2
COMMENT
"Generating bindings (v2)"
COMMAND
${BUN_EXECUTABLE} run ${BUN_BINDGENV2_SCRIPT}
--command=generate
--codegen-path=${CODEGEN_PATH}
--sources=${BUN_BINDGENV2_SOURCES_COMMA_SEPARATED}
SOURCES
${BUN_BINDGENV2_SOURCES}
${BUN_BINDGENV2_INTERNAL_SOURCES}
OUTPUTS
${BUN_BINDGENV2_CPP_OUTPUTS}
${BUN_BINDGENV2_ZIG_OUTPUTS}
)
set(BUN_BINDGEN_SCRIPT ${CWD}/src/codegen/bindgen.ts)
absolute_sources(BUN_BINDGEN_SOURCES ${CWD}/cmake/sources/BindgenSources.txt)
@@ -631,8 +573,6 @@ set(BUN_ZIG_GENERATED_SOURCES
${BUN_ZIG_GENERATED_CLASSES_OUTPUTS}
${BUN_JAVASCRIPT_OUTPUTS}
${BUN_CPP_OUTPUTS}
${BUN_CI_INFO_OUTPUTS}
${BUN_BINDGENV2_ZIG_OUTPUTS}
)
# In debug builds, these are not embedded, but rather referenced at runtime.
@@ -695,9 +635,7 @@ register_command(
-Dcpu=${ZIG_CPU}
-Denable_logs=$<IF:$<BOOL:${ENABLE_LOGS}>,true,false>
-Denable_asan=$<IF:$<BOOL:${ENABLE_ZIG_ASAN}>,true,false>
-Denable_fuzzilli=$<IF:$<BOOL:${ENABLE_FUZZILLI}>,true,false>
-Denable_valgrind=$<IF:$<BOOL:${ENABLE_VALGRIND}>,true,false>
-Duse_mimalloc=$<IF:$<BOOL:${USE_MIMALLOC_AS_DEFAULT_ALLOCATOR}>,true,false>
-Dllvm_codegen_threads=${LLVM_ZIG_CODEGEN_THREADS}
-Dversion=${VERSION}
-Dreported_nodejs_version=${NODEJS_VERSION}
@@ -774,7 +712,6 @@ list(APPEND BUN_CPP_SOURCES
${BUN_JAVASCRIPT_OUTPUTS}
${BUN_OBJECT_LUT_OUTPUTS}
${BUN_BINDGEN_CPP_OUTPUTS}
${BUN_BINDGENV2_CPP_OUTPUTS}
)
if(WIN32)
@@ -831,7 +768,7 @@ set_target_properties(${bun} PROPERTIES
CXX_STANDARD_REQUIRED YES
CXX_EXTENSIONS YES
CXX_VISIBILITY_PRESET hidden
C_STANDARD 17 # Cannot uprev to C23 because MSVC doesn't have support.
C_STANDARD 17
C_STANDARD_REQUIRED YES
VISIBILITY_INLINES_HIDDEN YES
)
@@ -912,10 +849,6 @@ if(WIN32)
)
endif()
if(USE_MIMALLOC_AS_DEFAULT_ALLOCATOR)
target_compile_definitions(${bun} PRIVATE USE_MIMALLOC=1)
endif()
target_compile_definitions(${bun} PRIVATE
_HAS_EXCEPTIONS=0
LIBUS_USE_OPENSSL=1
@@ -956,7 +889,7 @@ if(NOT WIN32)
if (NOT ABI STREQUAL "musl")
target_compile_options(${bun} PUBLIC
-fsanitize=null
-fno-sanitize-recover=all
-fsanitize-recover=all
-fsanitize=bounds
-fsanitize=return
-fsanitize=nullability-arg
@@ -1011,20 +944,6 @@ if(NOT WIN32)
)
if(ENABLE_ASAN)
target_compile_options(${bun} PUBLIC
-fsanitize=null
-fno-sanitize-recover=all
-fsanitize=bounds
-fsanitize=return
-fsanitize=nullability-arg
-fsanitize=nullability-assign
-fsanitize=nullability-return
-fsanitize=returns-nonnull-attribute
-fsanitize=unreachable
)
target_link_libraries(${bun} PRIVATE
-fsanitize=null
)
target_compile_options(${bun} PUBLIC -fsanitize=address)
target_link_libraries(${bun} PUBLIC -fsanitize=address)
endif()
@@ -1071,6 +990,7 @@ if(APPLE)
-Wl,-no_compact_unwind
-Wl,-stack_size,0x1200000
-fno-keep-static-consts
-Wl,-map,${bun}.linker-map
)
if(DEBUG)
@@ -1090,7 +1010,6 @@ if(APPLE)
target_link_options(${bun} PUBLIC
-dead_strip
-dead_strip_dylibs
-Wl,-map,${bun}.linker-map
)
endif()
endif()
@@ -1124,17 +1043,6 @@ if(LINUX)
)
endif()
if (ENABLE_LTO)
# We are optimizing for size at a slight debug-ability cost
target_link_options(${bun} PUBLIC
-Wl,--no-eh-frame-hdr
)
else()
target_link_options(${bun} PUBLIC
-Wl,--eh-frame-hdr
)
endif()
target_link_options(${bun} PUBLIC
--ld-path=${LLD_PROGRAM}
-fno-pic
@@ -1149,9 +1057,11 @@ if(LINUX)
# make debug info faster to load
-Wl,--gdb-index
-Wl,-z,combreloc
-Wl,--no-eh-frame-hdr
-Wl,--sort-section=name
-Wl,--hash-style=both
-Wl,--build-id=sha1 # Better for debugging than default
-Wl,-Map=${bun}.linker-map
)
# don't strip in debug, this seems to be needed so that the Zig std library
@@ -1166,7 +1076,6 @@ if(LINUX)
if (NOT DEBUG AND NOT ENABLE_ASAN AND NOT ENABLE_VALGRIND)
target_link_options(${bun} PUBLIC
-Wl,-icf=safe
-Wl,-Map=${bun}.linker-map
)
endif()
@@ -1273,9 +1182,15 @@ if(LINUX)
target_link_libraries(${bun} PUBLIC libatomic.so)
endif()
target_link_libraries(${bun} PRIVATE ${WEBKIT_LIB_PATH}/libicudata.a)
target_link_libraries(${bun} PRIVATE ${WEBKIT_LIB_PATH}/libicui18n.a)
target_link_libraries(${bun} PRIVATE ${WEBKIT_LIB_PATH}/libicuuc.a)
if(USE_SYSTEM_ICU)
target_link_libraries(${bun} PRIVATE libicudata.a)
target_link_libraries(${bun} PRIVATE libicui18n.a)
target_link_libraries(${bun} PRIVATE libicuuc.a)
else()
target_link_libraries(${bun} PRIVATE ${WEBKIT_LIB_PATH}/libicudata.a)
target_link_libraries(${bun} PRIVATE ${WEBKIT_LIB_PATH}/libicui18n.a)
target_link_libraries(${bun} PRIVATE ${WEBKIT_LIB_PATH}/libicuuc.a)
endif()
endif()
if(WIN32)
@@ -1328,32 +1243,32 @@ if(NOT BUN_CPP_ONLY)
OUTPUTS
${BUILD_PATH}/${bunStripExe}
)
# Then sign both executables on Windows
if(WIN32 AND ENABLE_WINDOWS_CODESIGNING)
set(SIGN_SCRIPT "${CMAKE_SOURCE_DIR}/.buildkite/scripts/sign-windows.ps1")
# Verify signing script exists
if(NOT EXISTS "${SIGN_SCRIPT}")
message(FATAL_ERROR "Windows signing script not found: ${SIGN_SCRIPT}")
endif()
# Use PowerShell for Windows code signing (native Windows, no path issues)
find_program(POWERSHELL_EXECUTABLE
find_program(POWERSHELL_EXECUTABLE
NAMES pwsh.exe powershell.exe
PATHS
PATHS
"C:/Program Files/PowerShell/7"
"C:/Program Files (x86)/PowerShell/7"
"C:/Windows/System32/WindowsPowerShell/v1.0"
DOC "Path to PowerShell executable"
)
if(NOT POWERSHELL_EXECUTABLE)
set(POWERSHELL_EXECUTABLE "powershell.exe")
endif()
message(STATUS "Using PowerShell executable: ${POWERSHELL_EXECUTABLE}")
# Sign both bun-profile.exe and bun.exe after stripping
register_command(
TARGET
@@ -1482,7 +1397,7 @@ if(NOT BUN_CPP_ONLY)
list(APPEND bunFiles ${bun}.dSYM)
endif()
if((APPLE OR LINUX) AND NOT ENABLE_ASAN)
if(APPLE OR LINUX)
list(APPEND bunFiles ${bun}.linker-map)
endif()

View File

@@ -4,7 +4,7 @@ register_repository(
REPOSITORY
google/highway
COMMIT
ac0d5d297b13ab1b89f48484fc7911082d76a93f
12b325bc1793dee68ab2157995a690db859fe9e0
)
set(HIGHWAY_CMAKE_ARGS

View File

@@ -4,7 +4,7 @@ register_repository(
REPOSITORY
ebiggers/libdeflate
COMMIT
c8c56a20f8f621e6a966b716b31f1dedab6a41e3
96836d7d9d10e3e0d53e6edb54eb908514e336c4
)
register_cmake_command(

View File

@@ -4,8 +4,8 @@ register_repository(
REPOSITORY
libuv/libuv
COMMIT
# Latest HEAD (includes recursion bug fix #4784)
f3ce527ea940d926c40878ba5de219640c362811
# Corresponds to v1.51.0
5152db2cbfeb5582e9c27c5ea1dba2cd9e10759b
)
if(WIN32)

View File

@@ -49,6 +49,3 @@ else()
setenv(CCACHE_MAXSIZE 100G)
setenv(CCACHE_SLOPPINESS "pch_defines,time_macros,locale,random_seed,clang_index_store,gcno_cwd")
endif()

View File

@@ -4,9 +4,41 @@ find_command(
COMMAND
git
REQUIRED
${CI}
OFF
)
if(NOT GIT_PROGRAM)
return()
endif()
set(GIT_DIFF_COMMAND ${GIT_PROGRAM} diff --no-color --name-only --diff-filter=AMCR origin/main HEAD)
execute_process(
COMMAND
${GIT_DIFF_COMMAND}
WORKING_DIRECTORY
${CWD}
OUTPUT_STRIP_TRAILING_WHITESPACE
OUTPUT_VARIABLE
GIT_DIFF
ERROR_STRIP_TRAILING_WHITESPACE
ERROR_VARIABLE
GIT_DIFF_ERROR
RESULT_VARIABLE
GIT_DIFF_RESULT
)
if(NOT GIT_DIFF_RESULT EQUAL 0)
message(WARNING "Command failed: ${GIT_DIFF_COMMAND} ${GIT_DIFF_ERROR}")
return()
endif()
string(REPLACE "\n" ";" GIT_CHANGED_SOURCES "${GIT_DIFF}")
if(CI)
set(GIT_CHANGED_SOURCES "${GIT_CHANGED_SOURCES}")
message(STATUS "Set GIT_CHANGED_SOURCES: ${GIT_CHANGED_SOURCES}")
endif()
list(TRANSFORM GIT_CHANGED_SOURCES PREPEND ${CWD}/)
list(LENGTH GIT_CHANGED_SOURCES GIT_CHANGED_SOURCES_COUNT)

View File

@@ -1,123 +0,0 @@
# Setup sccache as the C and C++ compiler launcher to speed up builds by caching
if(CACHE_STRATEGY STREQUAL "none")
return()
endif()
set(SCCACHE_SHARED_CACHE_REGION "us-west-1")
set(SCCACHE_SHARED_CACHE_BUCKET "bun-build-sccache-store")
# Function to check if the system AWS credentials have access to the sccache S3 bucket.
function(check_aws_credentials OUT_VAR)
# Install dependencies first
execute_process(
COMMAND ${BUN_EXECUTABLE} install --frozen-lockfile
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/scripts/build-cache
RESULT_VARIABLE INSTALL_EXIT_CODE
OUTPUT_VARIABLE INSTALL_OUTPUT
ERROR_VARIABLE INSTALL_ERROR
)
if(NOT INSTALL_EXIT_CODE EQUAL 0)
message(FATAL_ERROR "Failed to install dependencies in scripts/build-cache\n"
"Exit code: ${INSTALL_EXIT_CODE}\n"
"Output: ${INSTALL_OUTPUT}\n"
"Error: ${INSTALL_ERROR}")
endif()
# Check AWS credentials
execute_process(
COMMAND
${BUN_EXECUTABLE}
run
have-access.ts
--bucket ${SCCACHE_SHARED_CACHE_BUCKET}
--region ${SCCACHE_SHARED_CACHE_REGION}
WORKING_DIRECTORY
${CMAKE_SOURCE_DIR}/scripts/build-cache
RESULT_VARIABLE HAVE_ACCESS_EXIT_CODE
)
if(HAVE_ACCESS_EXIT_CODE EQUAL 0)
set(HAS_CREDENTIALS TRUE)
else()
set(HAS_CREDENTIALS FALSE)
endif()
set(${OUT_VAR} ${HAS_CREDENTIALS} PARENT_SCOPE)
endfunction()
# Configure sccache to use the local cache only.
function(sccache_configure_local_filesystem)
unsetenv(SCCACHE_BUCKET)
unsetenv(SCCACHE_REGION)
setenv(SCCACHE_DIR "${CACHE_PATH}/sccache")
endfunction()
# Configure sccache to use the distributed cache (S3 + local).
function(sccache_configure_distributed)
setenv(SCCACHE_BUCKET "${SCCACHE_SHARED_CACHE_BUCKET}")
setenv(SCCACHE_REGION "${SCCACHE_SHARED_CACHE_REGION}")
setenv(SCCACHE_DIR "${CACHE_PATH}/sccache")
endfunction()
function(sccache_configure_environment_ci)
if(CACHE_STRATEGY STREQUAL "auto" OR CACHE_STRATEGY STREQUAL "distributed")
check_aws_credentials(HAS_AWS_CREDENTIALS)
if(HAS_AWS_CREDENTIALS)
sccache_configure_distributed()
message(NOTICE "sccache: Using distributed cache strategy.")
else()
message(FATAL_ERROR "CI CACHE_STRATEGY is set to '${CACHE_STRATEGY}', but no valid AWS "
"credentials were found. Note that 'auto' requires AWS credentials to access the shared "
"cache in CI.")
endif()
elseif(CACHE_STRATEGY STREQUAL "local")
# We disallow this because we want our CI runs to always used the shared cache to accelerate
# builds.
# none, distributed and auto are all okay.
#
# If local is configured, it's as good as "none", so this is probably user error.
message(FATAL_ERROR "CI CACHE_STRATEGY is set to 'local', which is not allowed.")
endif()
endfunction()
function(sccache_configure_environment_developer)
# Local environments can use any strategy they like. S3 is set up in such a way so as to clean
# itself from old entries automatically.
if (CACHE_STRATEGY STREQUAL "auto" OR CACHE_STRATEGY STREQUAL "local")
# In the local environment, we prioritize using the local cache. This is because sccache takes
# into consideration the whole absolute path of the files being compiled, and it's very
# unlikely users will have the same absolute paths on their local machines.
sccache_configure_local_filesystem()
message(NOTICE "sccache: Using local cache strategy.")
elseif(CACHE_STRATEGY STREQUAL "distributed")
check_aws_credentials(HAS_AWS_CREDENTIALS)
if(HAS_AWS_CREDENTIALS)
sccache_configure_distributed()
message(NOTICE "sccache: Using distributed cache strategy.")
else()
message(FATAL_ERROR "CACHE_STRATEGY is set to 'distributed', but no valid AWS credentials "
"were found.")
endif()
endif()
endfunction()
find_command(VARIABLE SCCACHE_PROGRAM COMMAND sccache REQUIRED ${CI})
if(NOT SCCACHE_PROGRAM)
message(WARNING "sccache not found. Your builds will be slower.")
return()
endif()
set(SCCACHE_ARGS CMAKE_C_COMPILER_LAUNCHER CMAKE_CXX_COMPILER_LAUNCHER)
foreach(arg ${SCCACHE_ARGS})
setx(${arg} ${SCCACHE_PROGRAM})
list(APPEND CMAKE_ARGS -D${arg}=${${arg}})
endforeach()
setenv(SCCACHE_LOG "info")
if (CI)
sccache_configure_environment_ci()
else()
sccache_configure_environment_developer()
endif()

View File

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

View File

@@ -20,7 +20,7 @@ else()
unsupported(CMAKE_SYSTEM_NAME)
endif()
set(ZIG_COMMIT "c1423ff3fc7064635773a4a4616c5bf986eb00fe")
set(ZIG_COMMIT "55fdbfa0c86be86b68d43a4ba761e6909eb0d7b2")
optionx(ZIG_TARGET STRING "The zig target to use" DEFAULT ${DEFAULT_ZIG_TARGET})
if(CMAKE_BUILD_TYPE STREQUAL "Release")
@@ -55,7 +55,13 @@ optionx(ZIG_OBJECT_FORMAT "obj|bc" "Output file format for Zig object files" DEF
optionx(ZIG_LOCAL_CACHE_DIR FILEPATH "The path to local the zig cache directory" DEFAULT ${CACHE_PATH}/zig/local)
optionx(ZIG_GLOBAL_CACHE_DIR FILEPATH "The path to the global zig cache directory" DEFAULT ${CACHE_PATH}/zig/global)
optionx(ZIG_COMPILER_SAFE BOOL "Download a ReleaseSafe build of the Zig compiler." DEFAULT ${CI})
if(CI)
set(ZIG_COMPILER_SAFE_DEFAULT ON)
else()
set(ZIG_COMPILER_SAFE_DEFAULT OFF)
endif()
optionx(ZIG_COMPILER_SAFE BOOL "Download a ReleaseSafe build of the Zig compiler." DEFAULT ${ZIG_COMPILER_SAFE_DEFAULT})
setenv(ZIG_LOCAL_CACHE_DIR ${ZIG_LOCAL_CACHE_DIR})
setenv(ZIG_GLOBAL_CACHE_DIR ${ZIG_GLOBAL_CACHE_DIR})

View File

@@ -1,4 +1,4 @@
FROM alpine:3.22 AS build
FROM alpine:3.20 AS build
# https://github.com/oven-sh/bun/releases
ARG BUN_VERSION=latest
@@ -44,7 +44,7 @@ RUN apk --no-cache add ca-certificates curl dirmngr gpg gpg-agent unzip \
&& rm -f "bun-linux-$build.zip" SHASUMS256.txt.asc SHASUMS256.txt \
&& chmod +x /usr/local/bin/bun
FROM alpine:3.22
FROM alpine:3.20
# Disable the runtime transpiler cache by default inside Docker containers.
# On ephemeral containers, the cache is not useful

View File

@@ -1,4 +1,4 @@
FROM debian:trixie-slim AS build
FROM debian:bookworm-slim AS build
# https://github.com/oven-sh/bun/releases
ARG BUN_VERSION=latest
@@ -55,7 +55,7 @@ RUN apt-get update -qq \
&& which bun \
&& bun --version
FROM debian:trixie-slim
FROM debian:bookworm-slim
# Disable the runtime transpiler cache by default inside Docker containers.
# On ephemeral containers, the cache is not useful

View File

@@ -1,4 +1,4 @@
FROM debian:trixie-slim AS build
FROM debian:bookworm-slim AS build
# https://github.com/oven-sh/bun/releases
ARG BUN_VERSION=latest
@@ -56,7 +56,7 @@ RUN apt-get update -qq \
&& rm -f "bun-linux-$build.zip" SHASUMS256.txt.asc SHASUMS256.txt \
&& chmod +x /usr/local/bin/bun
FROM debian:trixie
FROM debian:bookworm
COPY docker-entrypoint.sh /usr/local/bin
COPY --from=build /usr/local/bin/bun /usr/local/bin/bun

View File

@@ -1,4 +1,4 @@
FROM debian:trixie-slim AS build
FROM debian:bookworm-slim AS build
# https://github.com/oven-sh/bun/releases
ARG BUN_VERSION=latest
@@ -55,7 +55,7 @@ RUN apt-get update -qq \
&& which bun \
&& bun --version
FROM gcr.io/distroless/base-nossl-debian13
FROM gcr.io/distroless/base-nossl-debian11
# Disable the runtime transpiler cache by default inside Docker containers.
# On ephemeral containers, the cache is not useful

View File

@@ -1,28 +0,0 @@
<p align="center">
<a href="https://bun.com">
<img src="https://github.com/user-attachments/assets/50282090-adfd-4ddb-9e27-c30753c6b161" alt="Logo" height="170" />
</a>
</p>
<h1 align="center">Bun Documentation</h1>
Official documentation for Bun: the fast, all-in-one JavaScript runtime.
## Development
Install the [Mintlify CLI](https://www.npmjs.com/package/mint) to preview the documentation locally:
```bash
bun install -g mint
```
Run the development server:
```bash
mint dev
```
The site will be available at `http://localhost:3000`.
## Contributing
Contributions are welcome! Please open an issue or submit a pull request.

View File

@@ -1,23 +1,41 @@
---
title: Binary Data
description: Working with binary data in JavaScript
---
This page is intended as an introduction to working with binary data in JavaScript. Bun implements a number of data types and utilities for working with binary data, most of which are Web-standard. Any Bun-specific APIs will be noted as such.
Below is a quick "cheat sheet" that doubles as a table of contents. Click an item in the left column to jump to that section.
| Class | Description |
| --------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| [`TypedArray`](#typedarray) | A family of classes that provide an `Array`-like interface for interacting with binary data. Includes `Uint8Array`, `Uint16Array`, `Int8Array`, and more. |
| [`Buffer`](#buffer) | A subclass of `Uint8Array` that implements a wide range of convenience methods. Unlike the other elements in this table, this is a Node.js API (which Bun implements). It can't be used in the browser. |
| [`DataView`](#dataview) | A class that provides a `get/set` API for writing some number of bytes to an `ArrayBuffer` at a particular byte offset. Often used reading or writing binary protocols. |
| [`Blob`](#blob) | A readonly blob of binary data usually representing a file. Has a MIME `type`, a `size`, and methods for converting to `ArrayBuffer`, `ReadableStream`, and string. |
| [`File`](#file) | A subclass of `Blob` that represents a file. Has a `name` and `lastModified` timestamp. There is experimental support in Node.js v20. |
| [`BunFile`](#bunfile) | _Bun only_. A subclass of `Blob` that represents a lazily-loaded file on disk. Created with `Bun.file(path)`. |
{% table %}
---
- [`TypedArray`](#typedarray)
- A family of classes that provide an `Array`-like interface for interacting with binary data. Includes `Uint8Array`, `Uint16Array`, `Int8Array`, and more.
---
- [`Buffer`](#buffer)
- A subclass of `Uint8Array` that implements a wide range of convenience methods. Unlike the other elements in this table, this is a Node.js API (which Bun implements). It can't be used in the browser.
---
- [`DataView`](#dataview)
- A class that provides a `get/set` API for writing some number of bytes to an `ArrayBuffer` at a particular byte offset. Often used reading or writing binary protocols.
---
- [`Blob`](#blob)
- A readonly blob of binary data usually representing a file. Has a MIME `type`, a `size`, and methods for converting to `ArrayBuffer`, `ReadableStream`, and string.
---
- [`File`](#file)
- A subclass of `Blob` that represents a file. Has a `name` and `lastModified` timestamp. There is experimental support in Node.js v20.
---
- [`BunFile`](#bunfile)
- _Bun only_. A subclass of `Blob` that represents a lazily-loaded file on disk. Created with `Bun.file(path)`.
{% /table %}
## `ArrayBuffer` and views
Until 2009, there was no language-native way to store and manipulate binary data in JavaScript. ECMAScript v5 introduced a range of new mechanisms for this. The most fundamental building block is `ArrayBuffer`, a simple data structure that represents a sequence of bytes in memory.
@@ -80,28 +98,70 @@ dv.setFloat64(0, 3.1415);
The following methods are available on `DataView`:
| Getters | Setters |
| -------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------- |
| [`getBigInt64()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/getBigInt64) | [`setBigInt64()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/setBigInt64) |
| [`getBigUint64()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/getBigUint64) | [`setBigUint64()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/setBigUint64) |
| [`getFloat32()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/getFloat32) | [`setFloat32()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/setFloat32) |
| [`getFloat64()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/getFloat64) | [`setFloat64()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/setFloat64) |
| [`getInt16()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/getInt16) | [`setInt16()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/setInt16) |
| [`getInt32()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/getInt32) | [`setInt32()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/setInt32) |
| [`getInt8()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/getInt8) | [`setInt8()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/setInt8) |
| [`getUint16()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/getUint16) | [`setUint16()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/setUint16) |
| [`getUint32()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/getUint32) | [`setUint32()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/setUint32) |
| [`getUint8()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/getUint8) | [`setUint8()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/setUint8) |
{% table %}
- Getters
- Setters
---
- [`getBigInt64()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/getBigInt64)
- [`setBigInt64()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/setBigInt64)
---
- [`getBigUint64()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/getBigUint64)
- [`setBigUint64()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/setBigUint64)
---
- [`getFloat32()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/getFloat32)
- [`setFloat32()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/setFloat32)
---
- [`getFloat64()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/getFloat64)
- [`setFloat64()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/setFloat64)
---
- [`getInt16()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/getInt16)
- [`setInt16()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/setInt16)
---
- [`getInt32()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/getInt32)
- [`setInt32()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/setInt32)
---
- [`getInt8()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/getInt8)
- [`setInt8()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/setInt8)
---
- [`getUint16()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/getUint16)
- [`setUint16()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/setUint16)
---
- [`getUint32()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/getUint32)
- [`setUint32()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/setUint32)
---
- [`getUint8()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/getUint8)
- [`setUint8()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/setUint8)
{% /table %}
### `TypedArray`
Typed arrays are a family of classes that provide an `Array`-like interface for interacting with data in an `ArrayBuffer`. Whereas a `DataView` lets you write numbers of varying size at a particular offset, a `TypedArray` interprets the underlying bytes as an array of numbers, each of a fixed size.
<Note>
It's common to refer to this family of classes collectively by their shared superclass `TypedArray`. This class as
_internal_ to JavaScript; you can't directly create instances of it, and `TypedArray` is not defined in the global
scope. Think of it as an `interface` or an abstract class.
</Note>
{% callout %}
**Note** — It's common to refer to this family of classes collectively by their shared superclass `TypedArray`. This class as _internal_ to JavaScript; you can't directly create instances of it, and `TypedArray` is not defined in the global scope. Think of it as an `interface` or an abstract class.
{% /callout %}
```ts
const buffer = new ArrayBuffer(3);
@@ -122,32 +182,121 @@ The top row contains the raw bytes, and the later rows contain how these bytes w
The following classes are typed arrays, along with a description of how they interpret the bytes in an `ArrayBuffer`:
Here's the first table formatted as a markdown table:
{% table %}
| Class | Description |
| ------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| [`Uint8Array`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array) | Every one (1) byte is interpreted as an unsigned 8-bit integer. Range 0 to 255. |
| [`Uint16Array`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint16Array) | Every two (2) bytes are interpreted as an unsigned 16-bit integer. Range 0 to 65535. |
| [`Uint32Array`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint32Array) | Every four (4) bytes are interpreted as an unsigned 32-bit integer. Range 0 to 4294967295. |
| [`Int8Array`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Int8Array) | Every one (1) byte is interpreted as a signed 8-bit integer. Range -128 to 127. |
| [`Int16Array`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Int16Array) | Every two (2) bytes are interpreted as a signed 16-bit integer. Range -32768 to 32767. |
| [`Int32Array`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Int32Array) | Every four (4) bytes are interpreted as a signed 32-bit integer. Range -2147483648 to 2147483647. |
| [`Float16Array`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Float16Array) | Every two (2) bytes are interpreted as a 16-bit floating point number. Range -6.104e5 to 6.55e4. |
| [`Float32Array`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Float32Array) | Every four (4) bytes are interpreted as a 32-bit floating point number. Range -3.4e38 to 3.4e38. |
| [`Float64Array`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Float64Array) | Every eight (8) bytes are interpreted as a 64-bit floating point number. Range -1.7e308 to 1.7e308. |
| [`BigInt64Array`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt64Array) | Every eight (8) bytes are interpreted as a signed `BigInt`. Range -9223372036854775808 to 9223372036854775807 (though `BigInt` is capable of representing larger numbers). |
| [`BigUint64Array`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigUint64Array) | Every eight (8) bytes are interpreted as an unsigned `BigInt`. Range 0 to 18446744073709551615 (though `BigInt` is capable of representing larger numbers). |
| [`Uint8ClampedArray`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8ClampedArray) | Same as `Uint8Array`, but automatically "clamps" to the range 0-255 when assigning a value to an element. |
- Class
- Description
---
- [`Uint8Array`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array)
- Every one (1) byte is interpreted as an unsigned 8-bit integer. Range 0 to 255.
---
- [`Uint16Array`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint16Array)
- Every two (2) bytes are interpreted as an unsigned 16-bit integer. Range 0 to 65535.
---
- [`Uint32Array`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint32Array)
- Every four (4) bytes are interpreted as an unsigned 32-bit integer. Range 0 to 4294967295.
---
- [`Int8Array`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Int8Array)
- Every one (1) byte is interpreted as a signed 8-bit integer. Range -128 to 127.
---
- [`Int16Array`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Int16Array)
- Every two (2) bytes are interpreted as a signed 16-bit integer. Range -32768 to 32767.
---
- [`Int32Array`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Int32Array)
- Every four (4) bytes are interpreted as a signed 32-bit integer. Range -2147483648 to 2147483647.
---
- [`Float16Array`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Float16Array)
- Every two (2) bytes are interpreted as a 16-bit floating point number. Range -6.104e5 to 6.55e4.
---
- [`Float32Array`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Float32Array)
- Every four (4) bytes are interpreted as a 32-bit floating point number. Range -3.4e38 to 3.4e38.
---
- [`Float64Array`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Float64Array)
- Every eight (8) bytes are interpreted as a 64-bit floating point number. Range -1.7e308 to 1.7e308.
---
- [`BigInt64Array`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt64Array)
- Every eight (8) bytes are interpreted as a signed `BigInt`. Range -9223372036854775808 to 9223372036854775807 (though `BigInt` is capable of representing larger numbers).
---
- [`BigUint64Array`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigUint64Array)
- Every eight (8) bytes are interpreted as an unsigned `BigInt`. Range 0 to 18446744073709551615 (though `BigInt` is capable of representing larger numbers).
---
- [`Uint8ClampedArray`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8ClampedArray)
- Same as `Uint8Array`, but automatically "clamps" to the range 0-255 when assigning a value to an element.
{% /table %}
The table below demonstrates how the bytes in an `ArrayBuffer` are interpreted when viewed using different typed array classes.
| | Byte 0 | Byte 1 | Byte 2 | Byte 3 | Byte 4 | Byte 5 | Byte 6 | Byte 7 |
| ---------------- | ------------------- | ---------- | ------------------- | ---------- | -------------------- | ---------- | -------------------- | ---------- |
| `ArrayBuffer` | `00000000` | `00000001` | `00000010` | `00000011` | `00000100` | `00000101` | `00000110` | `00000111` |
| `Uint8Array` | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
| `Uint16Array` | 256 (`1 * 256 + 0`) | | 770 (`3 * 256 + 2`) | | 1284 (`5 * 256 + 4`) | | 1798 (`7 * 256 + 6`) | |
| `Uint32Array` | 50462976 | | | | 117835012 | | | |
| `BigUint64Array` | 506097522914230528n | | | | | | | |
{% table %}
---
- `ArrayBuffer`
- `00000000`
- `00000001`
- `00000010`
- `00000011`
- `00000100`
- `00000101`
- `00000110`
- `00000111`
---
- `Uint8Array`
- 0
- 1
- 2
- 3
- 4
- 5
- 6
- 7
---
- `Uint16Array`
- 256 (`1 * 256 + 0`) {% colspan=2 %}
- 770 (`3 * 256 + 2`) {% colspan=2 %}
- 1284 (`5 * 256 + 4`) {% colspan=2 %}
- 1798 (`7 * 256 + 6`) {% colspan=2 %}
---
- `Uint32Array`
- 50462976 {% colspan=4 %}
- 117835012 {% colspan=4 %}
---
- `BigUint64Array`
- 506097522914230528n {% colspan=8 %}
{% /table %}
To create a typed array from a pre-defined `ArrayBuffer`:
@@ -319,7 +468,9 @@ const file = Bun.file("index.txt");
### `File`
<Warning>Browser only. Experimental support in Node.js 20.</Warning>
{% callout %}
Browser only. Experimental support in Node.js 20.
{% /callout %}
[`File`](https://developer.mozilla.org/en-US/docs/Web/API/File) is a subclass of `Blob` that adds a `name` and `lastModified` property. It's commonly used in the browser to represent files uploaded via a `<input type="file">` element. Node.js and Bun implement `File`.
@@ -339,21 +490,15 @@ const file = new File(["<html>Hello</html>"], "index.html", {
Refer to the [MDN documentation](https://developer.mozilla.org/en-US/docs/Web/API/Blob) for complete docs information.
---
## Streams
Streams are an important abstraction for working with binary data without loading it all into memory at once. They are commonly used for reading and writing files, sending and receiving network requests, and processing large amounts of data.
Bun implements the Web APIs [`ReadableStream`](https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream) and [`WritableStream`](https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream).
<Note>
Bun also implements the `node:stream` module, including
[`Readable`](https://nodejs.org/api/stream.html#stream_readable_streams),
[`Writable`](https://nodejs.org/api/stream.html#stream_writable_streams), and
[`Duplex`](https://nodejs.org/api/stream.html#stream_duplex_and_transform_streams). For complete documentation, refer
to the Node.js docs.
</Note>
{% callout %}
Bun also implements the `node:stream` module, including [`Readable`](https://nodejs.org/api/stream.html#stream_readable_streams), [`Writable`](https://nodejs.org/api/stream.html#stream_writable_streams), and [`Duplex`](https://nodejs.org/api/stream.html#stream_duplex_and_transform_streams). For complete documentation, refer to the Node.js docs.
{% /callout %}
To create a simple readable stream:
@@ -372,15 +517,12 @@ The contents of this stream can be read chunk-by-chunk with `for await` syntax.
```ts
for await (const chunk of stream) {
console.log(chunk);
// => "hello"
// => "world"
}
// => "hello"
// => "world"
```
For a more complete discussion of streams in Bun, see [API > Streams](/runtime/streams).
---
For a more complete discussion of streams in Bun, see [API > Streams](https://bun.com/docs/api/streams).
## Conversion
@@ -432,6 +574,12 @@ Array.from(new Uint8Array(buf));
new Blob([buf], { type: "text/plain" });
```
<!-- #### To `File`
```ts
new File([buf], "filename.txt", { type: "text/plain", lastModified: Date.now() });
``` -->
#### To `ReadableStream`
The following snippet creates a `ReadableStream` and enqueues the entire `ArrayBuffer` as a single chunk.
@@ -445,7 +593,7 @@ new ReadableStream({
});
```
<Accordion title="With chunking">
{% details summary="With chunking" %}
To stream the `ArrayBuffer` in chunks, use a `Uint8Array` view and enqueue each chunk.
```ts
@@ -462,7 +610,7 @@ new ReadableStream({
});
```
</Accordion>
{% /details %}
### From `TypedArray`
@@ -509,6 +657,12 @@ Array.from(arr);
new Blob([arr.buffer], { type: "text/plain" });
```
<!-- #### To `File`
```ts
new File([arr.buffer], "filename.txt", { type: "text/plain", lastModified: Date.now() });
``` -->
#### To `ReadableStream`
```ts
@@ -520,8 +674,7 @@ new ReadableStream({
});
```
<Accordion title="With chunking">
{% details summary="With chunking" %}
To stream the `ArrayBuffer` in chunks, split the `TypedArray` into chunks and enqueue each one individually.
```ts
@@ -535,7 +688,7 @@ new ReadableStream({
});
```
</Accordion>
{% /details %}
### From `DataView`
@@ -582,6 +735,12 @@ Array.from(view);
new Blob([view.buffer], { type: "text/plain" });
```
<!-- #### To `File`
```ts
new File([view.buffer], "filename.txt", { type: "text/plain", lastModified: Date.now() });
``` -->
#### To `ReadableStream`
```ts
@@ -593,7 +752,7 @@ new ReadableStream({
});
```
<Accordion title="With chunking">
{% details summary="With chunking" %}
To stream the `ArrayBuffer` in chunks, split the `DataView` into chunks and enqueue each one individually.
```ts
@@ -607,7 +766,7 @@ new ReadableStream({
});
```
</Accordion>
{% /details %}
### From `Buffer`
@@ -661,6 +820,12 @@ Array.from(buf);
new Blob([buf], { type: "text/plain" });
```
<!-- #### To `File`
```ts
new File([buf], "filename.txt", { type: "text/plain", lastModified: Date.now() });
``` -->
#### To `ReadableStream`
```ts
@@ -672,7 +837,7 @@ new ReadableStream({
});
```
<Accordion title="With chunking">
{% details summary="With chunking" %}
To stream the `ArrayBuffer` in chunks, split the `Buffer` into chunks and enqueue each one individually.
```ts
@@ -686,7 +851,7 @@ new ReadableStream({
});
```
</Accordion>
{% /details %}
### From `Blob`
@@ -736,6 +901,8 @@ Array.from(await blob.bytes());
blob.stream();
```
<!-- ### From `File` -->
### From `ReadableStream`
It's common to use [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response) as a convenient intermediate representation to make it easier to convert `ReadableStream` to other formats.
@@ -837,6 +1004,14 @@ Bun.readableStreamToArray(stream);
new Response(stream).blob();
```
<!-- #### To `File`
```ts
new Response(stream)
.blob()
.then(blob => new File([blob], "filename.txt", { type: "text/plain", lastModified: Date.now() }));
``` -->
#### To `ReadableStream`
To split a `ReadableStream` into two streams that can be consumed independently:
@@ -844,3 +1019,20 @@ To split a `ReadableStream` into two streams that can be consumed independently:
```ts
const [a, b] = stream.tee();
```
<!-- - Use Buffer
- TextEncoder
- `Bun.ArrayBufferSink`
- ReadableStream
- AsyncIterator
- TypedArray vs ArrayBuffer vs DataView
- Bun.indexOfLine
- “direct” readablestream
- readable stream has assumptions about
- its very generic
- all data is copies and queued
- direct : no queueing
- just a write function
- you can write strings
- more synchronous
- corking works better -->

View File

@@ -1,19 +1,12 @@
---
title: C Compiler
description: Compile and run C from JavaScript with low overhead
---
`bun:ffi` has experimental support for compiling and running C from JavaScript with low overhead.
---
## Usage (cc in `bun:ffi`)
See the [introduction blog post](https://bun.com/blog/compile-and-run-c-in-js) for more information.
JavaScript:
```ts hello.ts icon="file-code"
```ts#hello.js
import { cc } from "bun:ffi";
import source from "./hello.c" with { type: "file" };
@@ -34,7 +27,7 @@ console.log("What is the answer to the universe?", hello());
C source:
```c hello.c
```c#hello.c
int hello() {
return 42;
}
@@ -42,8 +35,8 @@ int hello() {
When you run `hello.js`, it will print:
```sh terminal icon="terminal"
bun hello.js
```sh
$ bun hello.js
What is the answer to the universe? 42
```
@@ -51,7 +44,7 @@ Under the hood, `cc` uses [TinyCC](https://bellard.org/tcc/) to compile the C co
### Primitive types
The same `FFIType` values in [`dlopen`](/runtime/ffi) are supported in `cc`.
The same `FFIType` values in [`dlopen`](/docs/api/ffi) are supported in `cc`.
| `FFIType` | C Type | Aliases |
| ---------- | -------------- | --------------------------- |
@@ -87,7 +80,7 @@ You can also pass a `napi_env` to receive the N-API environment used to call the
For example, if you have a string in C, you can return it to JavaScript like this:
```ts hello.ts
```ts#hello.js
import { cc } from "bun:ffi";
import source from "./hello.c" with { type: "file" };
@@ -108,7 +101,7 @@ const result = hello();
And in C:
```c hello.c
```c#hello.c
#include <node/node_api.h>
napi_value hello(napi_env env) {
@@ -120,7 +113,7 @@ napi_value hello(napi_env env) {
You can also use this to return other types like objects and arrays:
```c hello.c
```c#hello.c
#include <node/node_api.h>
napi_value hello(napi_env env) {
@@ -196,7 +189,7 @@ type Defines = Record<string, string>;
cc({
source: "hello.c",
define: {
NDEBUG: "1",
"NDEBUG": "1",
},
});
```

View File

@@ -1,8 +1,3 @@
---
title: Color
description: Format colors as CSS, ANSI, numbers, hex strings, and more
---
`Bun.color(input, outputFormat?)` leverages Bun's CSS parser to parse, normalize, and convert colors from user input to a variety of output formats, including:
| Format | Example |
@@ -247,7 +242,7 @@ Bun.color([255, 0, 0], "HEX"); // "#FF0000"
Like many of Bun's APIs, you can use macros to invoke `Bun.color` at bundle-time for use in client-side JavaScript builds:
```ts client-side.ts
```ts#client-side.ts
import { color } from "bun" with { type: "macro" };
console.log(color("#f00", "css"));

View File

@@ -1,14 +1,6 @@
---
title: Console
description: The console object in Bun
---
<Note>
Bun provides a browser- and Node.js-compatible [console](https://developer.mozilla.org/en-US/docs/Web/API/console)
global. This page only documents Bun-native APIs.
</Note>
---
{% callout %}
**Note** — Bun provides a browser- and Node.js-compatible [console](https://developer.mozilla.org/en-US/docs/Web/API/console) global. This page only documents Bun-native APIs.
{% /callout %}
## Object inspection depth
@@ -27,13 +19,11 @@ console.log(nested);
The CLI flag takes precedence over the configuration file setting.
---
## Reading from stdin
In Bun, the `console` object can be used as an `AsyncIterable` to sequentially read lines from `process.stdin`.
```ts adder.ts icon="/icons/typescript.svg"
```ts
for await (const line of console) {
console.log(line);
}
@@ -41,7 +31,7 @@ for await (const line of console) {
This is useful for implementing interactive programs, like the following addition calculator.
```ts adder.ts icon="/icons/typescript.svg"
```ts#adder.ts
console.log(`Let's add some numbers!`);
console.write(`Count: 0\n> `);
@@ -54,8 +44,8 @@ for await (const line of console) {
To run the file:
```bash terminal icon="terminal"
bun adder.ts
```bash
$ bun adder.ts
Let's add some numbers!
Count: 0
> 5

View File

@@ -1,15 +1,10 @@
---
title: Cookies
description: Use Bun's native APIs for working with HTTP cookies
---
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 title="cookies.ts" icon="/icons/typescript.svg"
```ts
// Empty cookie map
const cookies = new Bun.CookieMap();
@@ -33,7 +28,7 @@ const cookies3 = new Bun.CookieMap([
In Bun's HTTP server, the `cookies` property on the request object (in `routes`) is an instance of `CookieMap`:
```ts title="server.ts" icon="/icons/typescript.svg"
```ts
const server = Bun.serve({
routes: {
"/": req => {
@@ -68,7 +63,7 @@ console.log("Server listening at: " + server.url);
Retrieves a cookie by name. Returns `null` if the cookie doesn't exist.
```ts title="get-cookie.ts" icon="/icons/typescript.svg"
```ts
// Get by name
const cookie = cookies.get("session");
@@ -81,7 +76,7 @@ if (cookie != null) {
Checks if a cookie with the given name exists.
```ts title="has-cookie.ts" icon="/icons/typescript.svg"
```ts
// Check if cookie exists
if (cookies.has("session")) {
// Cookie exists
@@ -96,7 +91,7 @@ if (cookies.has("session")) {
Adds or updates a cookie in the map. Cookies default to `{ path: "/", sameSite: "lax" }`.
```ts title="set-cookie.ts" icon="/icons/typescript.svg"
```ts
// Set by name and value
cookies.set("session", "abc123");
@@ -119,7 +114,7 @@ cookies.set(cookie);
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 title="delete-cookie.ts" icon="/icons/typescript.svg"
```ts
// Delete by name using default domain and path.
cookies.delete("session");
@@ -135,7 +130,7 @@ cookies.delete({
Converts the cookie map to a serializable format.
```ts title="cookie-to-json.ts" icon="/icons/typescript.svg"
```ts
const json = cookies.toJSON();
```
@@ -145,7 +140,7 @@ Returns an array of values for Set-Cookie headers that can be used to apply all
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 title="node-server.js" icon="file-code"
```js
import { createServer } from "node:http";
import { CookieMap } from "bun";
@@ -172,7 +167,7 @@ server.listen(3000, () => {
`CookieMap` provides several methods for iteration:
```ts title="iterate-cookies.ts" icon="/icons/typescript.svg"
```ts
// Iterate over [name, cookie] entries
for (const [name, value] of cookies) {
console.log(`${name}: ${value}`);
@@ -205,7 +200,7 @@ cookies.forEach((value, name) => {
Returns the number of cookies in the map.
```ts title="cookie-size.ts" icon="/icons/typescript.svg"
```ts
console.log(cookies.size); // Number of cookies
```
@@ -213,7 +208,7 @@ console.log(cookies.size); // Number of cookies
`Bun.Cookie` represents an HTTP cookie with its name, value, and attributes.
```ts title="cookie-class.ts" icon="/icons/typescript.svg"
```ts
import { Cookie } from "bun";
// Create a basic cookie
@@ -243,7 +238,7 @@ const objCookie = new Bun.Cookie({
### Constructors
```ts title="constructors.ts" icon="/icons/typescript.svg"
```ts
// Basic constructor with name/value
new Bun.Cookie(name: string, value: string);
@@ -259,7 +254,7 @@ new Bun.Cookie(options: CookieInit);
### Properties
```ts title="cookie-properties.ts" icon="/icons/typescript.svg"
```ts
cookie.name; // string - Cookie name
cookie.value; // string - Cookie value
cookie.domain; // string | null - Domain scope (null if not specified)
@@ -278,7 +273,7 @@ cookie.httpOnly; // boolean - Accessible only via HTTP (not JavaScript)
Checks if the cookie has expired.
```ts title="is-expired.ts" icon="/icons/typescript.svg"
```ts
// Expired cookie (Date in the past)
const expiredCookie = new Bun.Cookie("name", "value", {
expires: new Date(Date.now() - 1000),
@@ -302,7 +297,7 @@ console.log(sessionCookie.isExpired()); // false
Returns a string representation of the cookie suitable for a `Set-Cookie` header.
```ts title="serialize-cookie.ts" icon="/icons/typescript.svg"
```ts
const cookie = new Bun.Cookie("session", "abc123", {
domain: "example.com",
path: "/admin",
@@ -322,7 +317,7 @@ console.log(cookie.toString());
Converts the cookie to a plain object suitable for JSON serialization.
```ts title="cookie-json.ts" icon="/icons/typescript.svg"
```ts
const cookie = new Bun.Cookie("session", "abc123", {
secure: true,
httpOnly: true,
@@ -349,7 +344,7 @@ const jsonString = JSON.stringify(cookie);
Parses a cookie string into a `Cookie` instance.
```ts title="parse-cookie.ts" icon="/icons/typescript.svg"
```ts
const cookie = Bun.Cookie.parse("name=value; Path=/; Secure; SameSite=Lax");
console.log(cookie.name); // "name"
@@ -363,7 +358,7 @@ console.log(cookie.sameSite); // "lax"
Factory method to create a cookie.
```ts title="cookie-from.ts" icon="/icons/typescript.svg"
```ts
const cookie = Bun.Cookie.from("session", "abc123", {
httpOnly: true,
secure: true,
@@ -373,7 +368,7 @@ const cookie = Bun.Cookie.from("session", "abc123", {
## Types
```ts title="types.ts" icon="/icons/typescript.svg"
```ts
interface CookieInit {
name?: string;
value?: string;

View File

@@ -1,9 +1,4 @@
---
title: DNS
description: Use Bun's DNS module to resolve DNS records
---
Bun implements it's own `dns` module, and the `node:dns` module.
Bun implements the `node:dns` module.
```ts
import * as dns from "node:dns";
@@ -13,17 +8,9 @@ console.log(addrs);
// => [{ address: "172.67.161.226", family: 4, ttl: 0 }, ...]
```
```ts
import { dns } from "bun";
dns.prefetch("bun.com", 443);
```
---
## DNS caching in Bun
Bun supports DNS caching. This cache makes repeated connections to the same hosts faster.
In Bun v1.1.9, we added support for DNS caching. This cache makes repeated connections to the same hosts faster.
At the time of writing, we cache up to 255 entries for a maximum of 30 seconds (each). If any connections to a host fail, we remove the entry from the cache. When multiple connections are made to the same host simultaneously, DNS lookups are deduplicated to avoid making multiple requests for the same host.
@@ -52,7 +39,9 @@ An example where you might want to use this is a database driver. When your appl
### `dns.prefetch`
<Warning>This API is experimental and may change in the future.</Warning>
{% callout %}
**🚧** — This API is experimental and may change in the future.
{% /callout %}
To prefetch a DNS entry, you can use the `dns.prefetch` API. This API is useful when you know you'll need to connect to a host soon and want to avoid the initial DNS lookup.
@@ -73,18 +62,28 @@ await fetch("https://bun.com");
### `dns.getCacheStats()`
<Warning>This API is experimental and may change in the future.</Warning>
{% callout %}
**🚧** — This API is experimental and may change in the future.
{% /callout %}
To get the current cache stats, you can use the `dns.getCacheStats` API. This API returns an object with the following properties:
To get the current cache stats, you can use the `dns.getCacheStats` API.
This API returns an object with the following properties:
```ts
{
cacheHitsCompleted: number; // Cache hits completed
cacheHitsInflight: number; // Cache hits in flight
cacheMisses: number; // Cache misses
size: number; // Number of items in the DNS cache
errors: number; // Number of times a connection failed
totalCount: number; // Number of times a connection was requested at all (including cache hits and misses)
// Cache hits
cacheHitsCompleted: number;
cacheHitsInflight: number;
cacheMisses: number;
// Number of items in the DNS cache
size: number;
// Number of times a connection failed
errors: number;
// Number of times a connection was requested at all (including cache hits and misses)
totalCount: number;
}
```

View File

@@ -1,8 +1,3 @@
---
title: Fetch
description: Send HTTP requests with Bun's fetch API
---
Bun implements the WHATWG `fetch` standard, with some extensions to meet the needs of server-side JavaScript.
Bun also implements `node:http`, but `fetch` is generally recommended instead.
@@ -51,7 +46,7 @@ const response = await fetch("http://example.com", {
### Proxying requests
To proxy a request, pass an object with the `proxy` property set to a URL string:
To proxy a request, pass an object with the `proxy` property set to a URL.
```ts
const response = await fetch("http://example.com", {
@@ -59,22 +54,6 @@ const response = await fetch("http://example.com", {
});
```
You can also use an object format to send custom headers to the proxy server:
```ts
const response = await fetch("http://example.com", {
proxy: {
url: "http://proxy.com",
headers: {
"Proxy-Authorization": "Bearer my-token",
"X-Custom-Proxy-Header": "value",
},
},
});
```
The `headers` are sent directly to the proxy in `CONNECT` requests (for HTTPS targets) or in the proxy request (for HTTP targets). If you provide a `Proxy-Authorization` header, it overrides any credentials in the proxy URL.
### Custom headers
To set custom headers, pass an object with the `headers` property set to an object.
@@ -289,7 +268,7 @@ const response = await fetch("s3://my-bucket/path/to/object", {
Note: Only PUT and POST methods support request bodies when using S3. For uploads, Bun automatically uses multipart upload for streaming bodies.
You can read more about Bun's S3 support in the [S3](/runtime/s3) documentation.
You can read more about Bun's S3 support in the [S3](https://bun.com/docs/api/s3) documentation.
#### File URLs - `file://`
@@ -358,7 +337,7 @@ This will print the request and response headers to your terminal:
```sh
[fetch] > HTTP/1.1 GET http://example.com/
[fetch] > Connection: keep-alive
[fetch] > User-Agent: Bun/1.3.3
[fetch] > User-Agent: Bun/$BUN_LATEST_VERSION
[fetch] > Accept: */*
[fetch] > Host: example.com
[fetch] > Accept-Encoding: gzip, deflate, br, zstd
@@ -404,7 +383,7 @@ dns.prefetch("bun.com");
By default, Bun caches and deduplicates DNS queries in-memory for up to 30 seconds. You can see the cache stats by calling `dns.getCacheStats()`:
To learn more about DNS caching in Bun, see the [DNS caching](/runtime/networking/dns) documentation.
To learn more about DNS caching in Bun, see the [DNS caching](https://bun.com/docs/api/dns) documentation.
### Preconnect to a host
@@ -423,7 +402,7 @@ Note: calling `fetch` immediately after `fetch.preconnect` will not make your re
To preconnect to a host at startup, you can pass `--fetch-preconnect`:
```sh
bun --fetch-preconnect https://bun.com ./my-script.ts
$ bun --fetch-preconnect https://bun.com ./my-script.ts
```
This is sort of like `<link rel="preconnect">` in HTML.
@@ -446,7 +425,7 @@ When the limit is exceeded, the requests are queued and sent as soon as the next
You can increase the maximum number of simultaneous connections via the `BUN_CONFIG_MAX_HTTP_REQUESTS` environment variable:
```sh
BUN_CONFIG_MAX_HTTP_REQUESTS=512 bun ./my-script.ts
$ BUN_CONFIG_MAX_HTTP_REQUESTS=512 bun ./my-script.ts
```
The max value for this limit is currently set to 65,336. The maximum port number is 65,535, so it's quite difficult for any one computer to exceed this limit.

View File

@@ -1,17 +1,9 @@
---
title: FFI
description: Use Bun's FFI module to efficiently call native libraries from JavaScript
---
<Warning>
`bun:ffi` is **experimental**, with known bugs and limitations, and should not be relied on in production. The most
stable way to interact with native code from Bun is to write a [Node-API module](/runtime/node-api).
</Warning>
{% callout %}
**⚠️ Warning** — `bun:ffi` is **experimental**, with known bugs and limitations, and should not be relied on in production. The most stable way to interact with native code from Bun is to write a [Node-API module](/docs/api/node-api).
{% /callout %}
Use the built-in `bun:ffi` module to efficiently call native libraries from JavaScript. It works with languages that support the C ABI (Zig, Rust, C/C++, C#, Nim, Kotlin, etc).
---
## dlopen usage (`bun:ffi`)
To print the version number of `sqlite3`:
@@ -41,23 +33,20 @@ const {
console.log(`SQLite 3 version: ${sqlite3_libversion()}`);
```
---
## Performance
According to [our benchmark](https://github.com/oven-sh/bun/tree/main/bench/ffi), `bun:ffi` is roughly 2-6x faster than Node.js FFI via `Node-API`.
<Image src="/images/ffi.png" height="400" />
{% image src="/images/ffi.png" height="400" /%}
Bun generates & just-in-time compiles C bindings that efficiently convert values between JavaScript types and native types. To compile C, Bun embeds [TinyCC](https://github.com/TinyCC/tinycc), a small and fast C compiler.
---
## Usage
### Zig
```zig add.zig icon="file-code"
```zig
// add.zig
pub export fn add(a: i32, b: i32) i32 {
return a + b;
}
@@ -65,8 +54,8 @@ pub export fn add(a: i32, b: i32) i32 {
To compile:
```bash terminal icon="terminal"
zig build-lib add.zig -dynamic -OReleaseFast
```bash
$ zig build-lib add.zig -dynamic -OReleaseFast
```
Pass a path to the shared library and a map of symbols to import into `dlopen`:
@@ -100,7 +89,7 @@ pub extern "C" fn add(a: i32, b: i32) -> i32 {
To compile:
```bash
rustc --crate-type cdylib add.rs
$ rustc --crate-type cdylib add.rs
```
### C++
@@ -116,11 +105,9 @@ extern "C" int32_t add(int32_t a, int32_t b) {
To compile:
```bash
zig build-lib add.cpp -dynamic -lc -lc++
$ zig build-lib add.cpp -dynamic -lc -lc++
```
---
## FFI types
The following `FFIType` values are supported.
@@ -150,13 +137,11 @@ The following `FFIType` values are supported.
Note: `buffer` arguments must be a `TypedArray` or `DataView`.
---
## Strings
JavaScript strings and C-like strings are different, and that complicates using strings with native libraries.
<Accordion title="How are JavaScript strings and C strings different?">
{% details summary="How are JavaScript strings and C strings different?" %}
JavaScript strings:
- UTF16 (2 bytes per letter) or potentially latin1, depending on the JavaScript engine &amp; what characters are used
@@ -169,7 +154,7 @@ C strings:
- The length is not stored. Instead, the string is null-terminated which means the length is the index of the first `\0` it finds
- Mutable
</Accordion>
{% /details %}
To solve this, `bun:ffi` exports `CString` which extends JavaScript's built-in `String` to support null-terminated strings and add a few extras:
@@ -216,11 +201,13 @@ console.log(myString);
When used in `returns`, `FFIType.cstring` coerces the pointer to a JavaScript `string`. When used in `args`, `FFIType.cstring` is identical to `ptr`.
---
## Function pointers
<Note>Async functions are not yet supported</Note>
{% callout %}
**Note** — Async functions are not yet supported.
{% /callout %}
To call a function pointer from JavaScript, use `CFunction`. This is useful if using Node-API (napi) with Bun, and you've already loaded some symbols.
@@ -268,11 +255,13 @@ const lib = linkSymbols({
},
});
const [major, minor, patch] = [lib.symbols.getMajor(), lib.symbols.getMinor(), lib.symbols.getPatch()];
const [major, minor, patch] = [
lib.symbols.getMajor(),
lib.symbols.getMinor(),
lib.symbols.getPatch(),
];
```
---
## Callbacks
Use `JSCallback` to create JavaScript callback functions that can be passed to C/FFI functions. The C/FFI function can call into the JavaScript/TypeScript code. This is useful for asynchronous code or whenever you want to call into JavaScript code from C.
@@ -290,10 +279,13 @@ const {
},
});
const searchIterator = new JSCallback((ptr, length) => /hello/.test(new CString(ptr, length)), {
returns: "bool",
args: ["ptr", "usize"],
});
const searchIterator = new JSCallback(
(ptr, length) => /hello/.test(new CString(ptr, length)),
{
returns: "bool",
args: ["ptr", "usize"],
},
);
const str = Buffer.from("wwutwutwutwutwutwutwutwutwutwutut\0", "utf8");
if (search(ptr(str), searchIterator)) {
@@ -313,17 +305,21 @@ When you're done with a JSCallback, you should call `close()` to free the memory
`JSCallback` has experimental support for thread-safe callbacks. This will be needed if you pass a callback function into a different thread from its instantiation context. You can enable it with the optional `threadsafe` parameter.
Currently, thread-safe callbacks work best when run from another thread that is running JavaScript code, i.e. a [`Worker`](/runtime/workers). A future version of Bun will enable them to be called from any thread (such as new threads spawned by your native library that Bun is not aware of).
Currently, thread-safe callbacks work best when run from another thread that is running JavaScript code, i.e. a [`Worker`](/docs/api/workers). A future version of Bun will enable them to be called from any thread (such as new threads spawned by your native library that Bun is not aware of).
```ts
const searchIterator = new JSCallback((ptr, length) => /hello/.test(new CString(ptr, length)), {
returns: "bool",
args: ["ptr", "usize"],
threadsafe: true, // Optional. Defaults to `false`
});
const searchIterator = new JSCallback(
(ptr, length) => /hello/.test(new CString(ptr, length)),
{
returns: "bool",
args: ["ptr", "usize"],
threadsafe: true, // Optional. Defaults to `false`
},
);
```
<Note>
{% callout %}
**⚡️ Performance tip** — For a slight performance boost, directly pass `JSCallback.prototype.ptr` instead of the `JSCallback` object:
```ts
@@ -344,21 +340,17 @@ setOnResolve(onResolve.ptr);
setOnResolve(onResolve);
```
</Note>
---
{% /callout %}
## Pointers
Bun represents [pointers](<https://en.wikipedia.org/wiki/Pointer_(computer_programming)>) as a `number` in JavaScript.
<Accordion title="How does a 64 bit pointer fit in a JavaScript number?">
{% details summary="How does a 64 bit pointer fit in a JavaScript number?" %}
64-bit processors support up to [52 bits of addressable space](https://en.wikipedia.org/wiki/64-bit_computing#Limits_of_processors). [JavaScript numbers](https://en.wikipedia.org/wiki/Double-precision_floating-point_format#IEEE_754_double-precision_binary_floating-point_format:_binary64) support 53 bits of usable space, so that leaves us with about 11 bits of extra space.
**Why not `BigInt`?** `BigInt` is slower. JavaScript engines allocate a separate `BigInt` which means they can't fit into a regular JavaScript value. If you pass a `BigInt` to a function, it will be converted to a `number`
</Accordion>
{% /details %}
To convert from a `TypedArray` to a pointer:
@@ -511,7 +503,7 @@ const out = encode_png(
The [auto-generated wrapper](https://github.com/oven-sh/bun/blob/6a65631cbdcae75bfa1e64323a6ad613a922cd1a/src/bun.js/ffi.exports.js#L180-L182) converts the pointer to a `TypedArray`.
<Accordion title="Hardmode">
{% details summary="Hardmode" %}
If you don't want the automatic conversion or you want a pointer to a specific byte offset within the `TypedArray`, you can also directly get the pointer to the `TypedArray`:
@@ -543,7 +535,7 @@ const out = encode_png(
);
```
</Accordion>
{% /details %}
### Reading pointers

View File

@@ -1,15 +1,12 @@
---
title: File I/O
description: Bun provides a set of optimized APIs for reading and writing files.
---
{% callout %}
<Note>
<!-- **Note** — The `Bun.file` and `Bun.write` APIs documented on this page are heavily optimized and represent the recommended way to perform file-system tasks using Bun. Existing Node.js projects may use Bun's [nearly complete](https://bun.com/docs/runtime/nodejs-apis#node-fs) implementation of the [`node:fs`](https://nodejs.org/api/fs.html) module. -->
The `Bun.file` and `Bun.write` APIs documented on this page are heavily optimized and represent the recommended way to perform file-system tasks using Bun. For operations that are not yet available with `Bun.file`, such as `mkdir` or `readdir`, you can use Bun's [nearly complete](/runtime/nodejs-compat#node-fs) implementation of the [`node:fs`](https://nodejs.org/api/fs.html) module.
**Note** — The `Bun.file` and `Bun.write` APIs documented on this page are heavily optimized and represent the recommended way to perform file-system tasks using Bun. For operations that are not yet available with `Bun.file`, such as `mkdir` or `readdir`, you can use Bun's [nearly complete](https://bun.com/docs/runtime/nodejs-apis#node-fs) implementation of the [`node:fs`](https://nodejs.org/api/fs.html) module.
</Note>
{% /callout %}
---
Bun provides a set of optimized APIs for reading and writing files.
## Reading files (`Bun.file()`)
@@ -29,7 +26,6 @@ The reference conforms to the [`Blob`](https://developer.mozilla.org/en-US/docs/
const foo = Bun.file("foo.txt");
await foo.text(); // contents as a string
await foo.json(); // contents as a JSON object
await foo.stream(); // contents as ReadableStream
await foo.arrayBuffer(); // contents as ArrayBuffer
await foo.bytes(); // contents as Uint8Array
@@ -74,8 +70,6 @@ You can delete a file by calling the `.delete()` function.
await Bun.file("logs.json").delete();
```
---
## Writing files (`Bun.write()`)
`Bun.write(destination, data): Promise<number>`
@@ -98,22 +92,88 @@ The second argument is the data to be written. It can be any of the following:
All possible permutations are handled using the fastest available system calls on the current platform.
<Accordion title="See syscalls">
{% details summary="See syscalls" %}
| Output | Input | System call | Platform |
| -------------------- | -------------- | ----------------------------- | -------- |
| file | file | copy_file_range | Linux |
| file | pipe | sendfile | Linux |
| pipe | pipe | splice | Linux |
| terminal | file | sendfile | Linux |
| terminal | terminal | sendfile | Linux |
| socket | file or pipe | sendfile (if http, not https) | Linux |
| file (doesn't exist) | file (path) | clonefile | macOS |
| file (exists) | file | fcopyfile | macOS |
| file | Blob or string | write | macOS |
| file | Blob or string | write | Linux |
{% table %}
</Accordion>
- Output
- Input
- System call
- Platform
---
- file
- file
- copy_file_range
- Linux
---
- file
- pipe
- sendfile
- Linux
---
- pipe
- pipe
- splice
- Linux
---
- terminal
- file
- sendfile
- Linux
---
- terminal
- terminal
- sendfile
- Linux
---
- socket
- file or pipe
- sendfile (if http, not https)
- Linux
---
- file (doesn't exist)
- file (path)
- clonefile
- macOS
---
- file (exists)
- file
- fcopyfile
- macOS
---
- file
- Blob or string
- write
- macOS
---
- file
- Blob or string
- write
- Linux
{% /table %}
{% /details %}
To write a string to disk:
@@ -124,7 +184,7 @@ await Bun.write("output.txt", data);
To copy a file to another location on disk:
```ts
```js
const input = Bun.file("input.txt");
const output = Bun.file("output.txt"); // doesn't exist yet!
await Bun.write(output, input);
@@ -152,8 +212,6 @@ const response = await fetch("https://bun.com");
await Bun.write("index.html", response);
```
---
## Incremental writing with `FileSink`
Bun provides a native incremental file writing API called `FileSink`. To retrieve a `FileSink` instance from a `BunFile`:
@@ -201,8 +259,6 @@ writer.unref();
writer.ref();
```
---
## Directories
Bun's implementation of `node:fs` is fast, and we haven't implemented a Bun-specific API for reading directories just yet. For now, you should use `node:fs` for working with directories in Bun.
@@ -239,15 +295,13 @@ import { mkdir } from "node:fs/promises";
await mkdir("path/to/dir", { recursive: true });
```
---
## Benchmarks
The following is a 3-line implementation of the Linux `cat` command.
```ts cat.ts icon="/icons/typescript.svg"
```ts#cat.ts
// Usage
// bun ./cat.ts ./path-to-file
// $ bun ./cat.ts ./path-to-file
import { resolve } from "path";
@@ -257,15 +311,13 @@ await Bun.write(Bun.stdout, Bun.file(path));
To run the file:
```bash terminal icon="terminal"
bun ./cat.ts ./path-to-file
```bash
$ bun ./cat.ts ./path-to-file
```
It runs 2x faster than GNU `cat` for large files on Linux.
<Frame>![Cat screenshot](/images/cat.jpg)</Frame>
---
{% image src="/images/cat.jpg" /%}
## Reference
@@ -279,7 +331,13 @@ interface Bun {
write(
destination: string | number | BunFile | URL,
input: string | Blob | ArrayBuffer | SharedArrayBuffer | TypedArray | Response,
input:
| string
| Blob
| ArrayBuffer
| SharedArrayBuffer
| TypedArray
| Response,
): Promise<number>;
}
@@ -296,7 +354,9 @@ interface BunFile {
}
export interface FileSink {
write(chunk: string | ArrayBufferView | ArrayBuffer | SharedArrayBuffer): number;
write(
chunk: string | ArrayBufferView | ArrayBuffer | SharedArrayBuffer,
): number;
flush(): number | Promise<number>;
end(error?: Error): number | Promise<number>;
start(options?: { highWaterMark?: number }): void;

View File

@@ -1,9 +1,4 @@
---
title: File System Router
description: Bun provides a fast API for resolving routes against file-system paths
---
This API is primarily intended for library authors. At the moment only Next.js-style file-system routing is supported, but other styles may be added in the future.
Bun provides a fast API for resolving routes against file-system paths. This API is primarily intended for library authors. At the moment only Next.js-style file-system routing is supported, but other styles may be added in the future.
## Next.js-style
@@ -21,14 +16,13 @@ pages
The `FileSystemRouter` can be used to resolve routes against this directory:
```ts router.ts
```ts
const router = new Bun.FileSystemRouter({
style: "nextjs",
dir: "./pages",
origin: "https://mydomain.com",
assetPrefix: "_next/static/"
});
router.match("/");
// =>

19
docs/api/file.md Normal file
View File

@@ -0,0 +1,19 @@
Bun.js has fast paths for common use cases that make Web APIs live up to the performance demands of servers and CLIs.
`Bun.file(path)` returns a [`Blob`](https://developer.mozilla.org/en-US/docs/Web/API/Blob) that represents a lazily-loaded file.
When you pass a file blob to `Bun.write`, Bun automatically uses a faster system call:
```js
const blob = Bun.file("input.txt");
await Bun.write("output.txt", blob);
```
On Linux, this uses the [`copy_file_range`](https://man7.org/linux/man-pages/man2/copy_file_range.2.html) syscall and on macOS, this becomes `clonefile` (or [`fcopyfile`](https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man3/copyfile.3.html)).
`Bun.write` also supports [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response) objects. It automatically converts to a [`Blob`](https://developer.mozilla.org/en-US/docs/Web/API/Blob).
```js
// Eventually, this will stream the response to disk but today it buffers
await Bun.write("index.html", await fetch("https://example.com"));
```

View File

@@ -1,7 +1,4 @@
---
title: Glob
description: Use Bun's fast native implementation of file globbing
---
Bun includes a fast native implementation of file globbing.
## Quickstart

387
docs/api/globals.md Normal file
View File

@@ -0,0 +1,387 @@
Bun implements the following globals.
{% table %}
- Global
- Source
- Notes
---
- [`AbortController`](https://developer.mozilla.org/en-US/docs/Web/API/AbortController)
- Web
- &nbsp;
---
- [`AbortSignal`](https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal)
- Web
- &nbsp;
---
- [`alert`](https://developer.mozilla.org/en-US/docs/Web/API/Window/alert)
- Web
- Intended for command-line tools
---
- [`Blob`](https://developer.mozilla.org/en-US/docs/Web/API/Blob)
- Web
- &nbsp;
---
- [`Buffer`](https://nodejs.org/api/buffer.html#class-buffer)
- Node.js
- See [Node.js > `Buffer`](https://bun.com/docs/runtime/nodejs-apis#node-buffer)
---
- `Bun`
- Bun
- Subject to change as additional APIs are added
---
- [`ByteLengthQueuingStrategy`](https://developer.mozilla.org/en-US/docs/Web/API/ByteLengthQueuingStrategy)
- Web
- &nbsp;
---
- [`confirm`](https://developer.mozilla.org/en-US/docs/Web/API/Window/confirm)
- Web
- Intended for command-line tools
---
- [`__dirname`](https://nodejs.org/api/globals.html#__dirname)
- Node.js
- &nbsp;
---
- [`__filename`](https://nodejs.org/api/globals.html#__filename)
- Node.js
- &nbsp;
---
- [`atob()`](https://developer.mozilla.org/en-US/docs/Web/API/atob)
- Web
- &nbsp;
---
- [`btoa()`](https://developer.mozilla.org/en-US/docs/Web/API/btoa)
- Web
- &nbsp;
---
- `BuildMessage`
- Bun
- &nbsp;
---
- [`clearImmediate()`](https://developer.mozilla.org/en-US/docs/Web/API/Window/clearImmediate)
- Web
- &nbsp;
---
- [`clearInterval()`](https://developer.mozilla.org/en-US/docs/Web/API/Window/clearInterval)
- Web
- &nbsp;
---
- [`clearTimeout()`](https://developer.mozilla.org/en-US/docs/Web/API/Window/clearTimeout)
- Web
- &nbsp;
---
- [`console`](https://developer.mozilla.org/en-US/docs/Web/API/console)
- Web
- &nbsp;
---
- [`CountQueuingStrategy`](https://developer.mozilla.org/en-US/docs/Web/API/CountQueuingStrategy)
- Web
- &nbsp;
---
- [`Crypto`](https://developer.mozilla.org/en-US/docs/Web/API/Crypto)
- Web
- &nbsp;
---
- [`crypto`](https://developer.mozilla.org/en-US/docs/Web/API/crypto)
- Web
- &nbsp;
---
- [`CryptoKey`](https://developer.mozilla.org/en-US/docs/Web/API/CryptoKey)
- Web
- &nbsp;
---
- [`CustomEvent`](https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent)
- Web
- &nbsp;
---
- [`Event`](https://developer.mozilla.org/en-US/docs/Web/API/Event)
- Web
- Also [`ErrorEvent`](https://developer.mozilla.org/en-US/docs/Web/API/ErrorEvent) [`CloseEvent`](https://developer.mozilla.org/en-US/docs/Web/API/CloseEvent) [`MessageEvent`](https://developer.mozilla.org/en-US/docs/Web/API/MessageEvent).
---
- [`EventTarget`](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget)
- Web
- &nbsp;
---
- [`exports`](https://nodejs.org/api/globals.html#exports)
- Node.js
- &nbsp;
---
- [`fetch`](https://developer.mozilla.org/en-US/docs/Web/API/fetch)
- Web
- &nbsp;
---
- [`FormData`](https://developer.mozilla.org/en-US/docs/Web/API/FormData)
- Web
- &nbsp;
---
- [`global`](https://nodejs.org/api/globals.html#global)
- Node.js
- See [Node.js > `global`](https://bun.com/docs/runtime/nodejs-apis#global).
---
- [`globalThis`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/globalThis)
- Cross-platform
- Aliases to `global`
---
- [`Headers`](https://developer.mozilla.org/en-US/docs/Web/API/Headers)
- Web
- &nbsp;
---
- [`HTMLRewriter`](https://bun.com/docs/api/html-rewriter)
- Cloudflare
- &nbsp;
---
- [`JSON`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON)
- Web
- &nbsp;
---
- [`MessageEvent`](https://developer.mozilla.org/en-US/docs/Web/API/MessageEvent)
- Web
- &nbsp;
---
- [`module`](https://nodejs.org/api/globals.html#module)
- Node.js
- &nbsp;
---
- [`performance`](https://developer.mozilla.org/en-US/docs/Web/API/performance)
- Web
- &nbsp;
---
- [`process`](https://nodejs.org/api/process.html)
- Node.js
- See [Node.js > `process`](https://bun.com/docs/runtime/nodejs-apis#node-process)
---
- [`prompt`](https://developer.mozilla.org/en-US/docs/Web/API/Window/prompt)
- Web
- Intended for command-line tools
---
- [`queueMicrotask()`](https://developer.mozilla.org/en-US/docs/Web/API/queueMicrotask)
- Web
- &nbsp;
---
- [`ReadableByteStreamController`](https://developer.mozilla.org/en-US/docs/Web/API/ReadableByteStreamController)
- Web
- &nbsp;
---
- [`ReadableStream`](https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream)
- Web
- &nbsp;
---
- [`ReadableStreamDefaultController`](https://developer.mozilla.org/en-US/docs/Web/API/ReadableStreamDefaultController)
- Web
- &nbsp;
---
- [`ReadableStreamDefaultReader`](https://developer.mozilla.org/en-US/docs/Web/API/ReadableStreamDefaultReader)
- Web
- &nbsp;
---
- [`reportError`](https://developer.mozilla.org/en-US/docs/Web/API/reportError)
- Web
- &nbsp;
---
- [`require()`](https://nodejs.org/api/globals.html#require)
- Node.js
- &nbsp;
---
- `ResolveMessage`
- Bun
- &nbsp;
---
- [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response)
- Web
- &nbsp;
---
- [`Request`](https://developer.mozilla.org/en-US/docs/Web/API/Request)
- Web
- &nbsp;
---
- [`setImmediate()`](https://developer.mozilla.org/en-US/docs/Web/API/Window/setImmediate)
- Web
- &nbsp;
---
- [`setInterval()`](https://developer.mozilla.org/en-US/docs/Web/API/Window/setInterval)
- Web
- &nbsp;
---
- [`setTimeout()`](https://developer.mozilla.org/en-US/docs/Web/API/Window/setTimeout)
- Web
- &nbsp;
---
- [`ShadowRealm`](https://github.com/tc39/proposal-shadowrealm)
- Web
- Stage 3 proposal
---
- [`SubtleCrypto`](https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto)
- Web
- &nbsp;
---
- [`DOMException`](https://developer.mozilla.org/en-US/docs/Web/API/DOMException)
- Web
- &nbsp;
---
- [`TextDecoder`](https://developer.mozilla.org/en-US/docs/Web/API/TextDecoder)
- Web
- &nbsp;
---
- [`TextEncoder`](https://developer.mozilla.org/en-US/docs/Web/API/TextEncoder)
- Web
- &nbsp;
---
- [`TransformStream`](https://developer.mozilla.org/en-US/docs/Web/API/TransformStream)
- Web
- &nbsp;
---
- [`TransformStreamDefaultController`](https://developer.mozilla.org/en-US/docs/Web/API/TransformStreamDefaultController)
- Web
- &nbsp;
---
- [`URL`](https://developer.mozilla.org/en-US/docs/Web/API/URL)
- Web
- &nbsp;
---
- [`URLSearchParams`](https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams)
- Web
- &nbsp;
---
- [`WebAssembly`](https://nodejs.org/api/globals.html#webassembly)
- Web
- &nbsp;
---
- [`WritableStream`](https://developer.mozilla.org/en-US/docs/Web/API/WritableStream)
- Web
- &nbsp;
---
- [`WritableStreamDefaultController`](https://developer.mozilla.org/en-US/docs/Web/API/WritableStreamDefaultController)
- Web
- &nbsp;
---
- [`WritableStreamDefaultWriter`](https://developer.mozilla.org/en-US/docs/Web/API/WritableStreamDefaultWriter)
- Web
- &nbsp;
{% /table %}

View File

@@ -1,14 +1,8 @@
---
title: Hashing
description: Bun provides a set of utility functions for hashing and verifying passwords with various cryptographically secure algorithms
---
{% callout %}
<Note>
Bun implements the `createHash` and `createHmac` functions from [`node:crypto`](https://nodejs.org/api/crypto.html) in
addition to the Bun-native APIs documented below.
</Note>
Bun implements the `createHash` and `createHmac` functions from [`node:crypto`](https://nodejs.org/api/crypto.html) in addition to the Bun-native APIs documented below.
---
{% /callout %}
## `Bun.password`
@@ -138,8 +132,6 @@ The format is composed of:
- `salt`: `$xXnlSvPh4ym5KYmxKAuuHVlDvy2QGHBNuI6bJJrRDOs`
- `hash`: `$2YY6M48XmHn+s5NoBaL+ficzXajq2Yj8wut3r0vnrwI`
---
## `Bun.hash`
`Bun.hash` is a collection of utilities for _non-cryptographic_ hashing. Non-cryptographic hashing algorithms are optimized for speed of computation over collision-resistance or security.
@@ -186,14 +178,13 @@ Bun.hash.murmur64v2("data", 1234);
Bun.hash.rapidhash("data", 1234);
```
---
## `Bun.CryptoHasher`
`Bun.CryptoHasher` is a general-purpose utility class that lets you incrementally compute a hash of string or binary data using a range of cryptographic hash algorithms. The following algorithms are supported:
- `"blake2b256"`
- `"blake2b512"`
- `"blake2s256"`
- `"md4"`
- `"md5"`
- `"ripemd160"`
@@ -230,11 +221,24 @@ hasher.update(new ArrayBuffer(10));
If a `string` is passed, an optional second parameter can be used to specify the encoding (default `'utf-8'`). The following encodings are supported:
| Category | Encodings |
| -------------------------- | ------------------------------------------- |
| Binary encodings | `"base64"` `"base64url"` `"hex"` `"binary"` |
| Character encodings | `"utf8"` `"utf-8"` `"utf16le"` `"latin1"` |
| Legacy character encodings | `"ascii"` `"binary"` `"ucs2"` `"ucs-2"` |
{% table %}
---
- Binary encodings
- `"base64"` `"base64url"` `"hex"` `"binary"`
---
- Character encodings
- `"utf8"` `"utf-8"` `"utf16le"` `"latin1"`
---
- Legacy character encodings
- `"ascii"` `"binary"` `"ucs2"` `"ucs-2"`
{% /table %}
```ts
hasher.update("hello world"); // defaults to utf8

View File

@@ -1,12 +1,5 @@
---
title: HTMLRewriter
description: Use Bun's HTMLRewriter to transform HTML documents with CSS selectors
---
HTMLRewriter lets you use CSS selectors to transform HTML documents. It works with `Request`, `Response`, as well as `string`. Bun's implementation is based on Cloudflare's [lol-html](https://github.com/cloudflare/lol-html).
---
## Usage
A common usecase is rewriting URLs in HTML content. Here's an example that rewrites image sources and link URLs to use a CDN domain:
@@ -16,12 +9,16 @@ A common usecase is rewriting URLs in HTML content. Here's an example that rewri
const rewriter = new HTMLRewriter().on("img", {
element(img) {
// Famous rickroll video thumbnail
img.setAttribute("src", "https://img.youtube.com/vi/dQw4w9WgXcQ/maxresdefault.jpg");
img.setAttribute(
"src",
"https://img.youtube.com/vi/dQw4w9WgXcQ/maxresdefault.jpg",
);
// Wrap the image in a link to the video
img.before('<a href="https://www.youtube.com/watch?v=dQw4w9WgXcQ" target="_blank">', {
html: true,
});
img.before(
'<a href="https://www.youtube.com/watch?v=dQw4w9WgXcQ" target="_blank">',
{ html: true },
);
img.after("</a>", { html: true });
// Add some fun alt text
@@ -46,22 +43,21 @@ console.log(result);
This replaces all images with a thumbnail of Rick Astley and wraps each `<img>` in a link, producing a diff like this:
{/* prettier-ignore */}
```html
```html-diff
<html>
<body>
<img src="/cat.jpg" /> <!-- [!code --] -->
<img src="dog.png" /> <!-- [!code --] -->
<img src="https://example.com/bird.webp" /> <!-- [!code --] -->
<a href="https://www.youtube.com/watch?v=dQw4w9WgXcQ" target="_blank"> <!-- [!code ++] -->
<img src="https://img.youtube.com/vi/dQw4w9WgXcQ/maxresdefault.jpg" alt="Definitely not a rickroll" /> <!-- [!code ++] -->
</a> <!-- [!code ++] -->
<a href="https://www.youtube.com/watch?v=dQw4w9WgXcQ" target="_blank"> <!-- [!code ++] -->
<img src="https://img.youtube.com/vi/dQw4w9WgXcQ/maxresdefault.jpg" alt="Definitely not a rickroll" /> <!-- [!code ++] -->
</a> <!-- [!code ++] -->
<a href="https://www.youtube.com/watch?v=dQw4w9WgXcQ" target="_blank"> <!-- [!code ++] -->
<img src="https://img.youtube.com/vi/dQw4w9WgXcQ/maxresdefault.jpg" alt="Definitely not a rickroll" /> <!-- [!code ++] -->
</a> <!-- [!code ++] -->
- <img src="/cat.jpg">
- <img src="dog.png">
- <img src="https://example.com/bird.webp">
+ <a href="https://www.youtube.com/watch?v=dQw4w9WgXcQ" target="_blank">
+ <img src="https://img.youtube.com/vi/dQw4w9WgXcQ/maxresdefault.jpg" alt="Definitely not a rickroll">
+ </a>
+ <a href="https://www.youtube.com/watch?v=dQw4w9WgXcQ" target="_blank">
+ <img src="https://img.youtube.com/vi/dQw4w9WgXcQ/maxresdefault.jpg" alt="Definitely not a rickroll">
+ </a>
+ <a href="https://www.youtube.com/watch?v=dQw4w9WgXcQ" target="_blank">
+ <img src="https://img.youtube.com/vi/dQw4w9WgXcQ/maxresdefault.jpg" alt="Definitely not a rickroll">
+ </a>
</body>
</html>
```
@@ -184,7 +180,10 @@ rewriter.on("div", {
el.setInnerContent(""); // Clear content
// Position manipulation
el.before("Content before").after("Content after").prepend("First child").append("Last child");
el.before("Content before")
.after("Content after")
.prepend("First child")
.append("Last child");
// HTML content insertion
el.before("<span>before</span>", { html: true })
@@ -256,7 +255,11 @@ rewriter.on("*", {
console.log(comment.removed); // Whether comment was removed
// Manipulation
comment.before("Before comment").after("After comment").replace("New comment").remove();
comment
.before("Before comment")
.after("After comment")
.replace("New comment")
.remove();
// HTML content insertion
comment
@@ -326,8 +329,6 @@ try {
}
```
---
## See also
You can also read the [Cloudflare documentation](https://developers.cloudflare.com/workers/runtime-apis/html-rewriter/), which this API is intended to be compatible with.

1408
docs/api/http.md Normal file

File diff suppressed because it is too large Load Diff

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

@@ -0,0 +1,69 @@
The `import.meta` object is a way for a module to access information about itself. It's part of the JavaScript language, but its contents are not standardized. Each "host" (browser, runtime, etc) is free to implement any properties it wishes on the `import.meta` object.
Bun implements the following properties.
```ts#/path/to/project/file.ts
import.meta.dir; // => "/path/to/project"
import.meta.file; // => "file.ts"
import.meta.path; // => "/path/to/project/file.ts"
import.meta.url; // => "file:///path/to/project/file.ts"
import.meta.main; // `true` if this file is directly executed by `bun run`
// `false` otherwise
import.meta.resolve("zod"); // => "file:///path/to/project/node_modules/zod/index.js"
```
{% table %}
---
- `import.meta.dir`
- Absolute path to the directory containing the current file, e.g. `/path/to/project`. Equivalent to `__dirname` in CommonJS modules (and Node.js)
---
- `import.meta.dirname`
- An alias to `import.meta.dir`, for Node.js compatibility
---
- `import.meta.env`
- An alias to `process.env`.
---
- `import.meta.file`
- The name of the current file, e.g. `index.tsx`
---
- `import.meta.path`
- Absolute path to the current file, e.g. `/path/to/project/index.ts`. Equivalent to `__filename` in CommonJS modules (and Node.js)
---
- `import.meta.filename`
- An alias to `import.meta.path`, for Node.js compatibility
---
- `import.meta.main`
- Indicates whether the current file is the entrypoint to the current `bun` process. Is the file being directly executed by `bun run` or is it being imported?
---
- `import.meta.resolve`
- Resolve a module specifier (e.g. `"zod"` or `"./file.tsx"`) to a url. Equivalent to [`import.meta.resolve` in browsers](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/import.meta#resolve)
```ts
import.meta.resolve("zod");
// => "file:///path/to/project/node_modules/zod/index.ts"
```
---
- `import.meta.url`
- A `string` url to the current file, e.g. `file:///path/to/project/index.ts`. Equivalent to [`import.meta.url` in browsers](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/import.meta#url)
{% /table %}

View File

@@ -1,8 +1,3 @@
---
title: Node-API
description: Use Bun's Node-API module to build native add-ons to Node.js
---
Node-API is an interface for building native add-ons to Node.js. Bun implements 95% of this interface from scratch, so most existing Node-API extensions will work with Bun out of the box. Track the completion status of it in [this issue](https://github.com/oven-sh/bun/issues/158).
As in Node.js, `.node` files (Node-API modules) can be required directly in Bun.

View File

@@ -1,13 +1,6 @@
---
title: Redis
description: Use Bun's native Redis client with a Promise-based API
---
Bun provides native bindings for working with Redis databases with a modern, Promise-based API. The interface is designed to be simple and performant, with built-in connection management, fully typed responses, and TLS support. **New in Bun v1.2.9**
<Note>Bun's Redis client supports Redis server versions 7.2 and up.</Note>
Bun provides native bindings for working with Redis databases with a modern, Promise-based API. The interface is designed to be simple and performant, with built-in connection management, fully typed responses, and TLS support.
```ts redis.ts icon="/icons/typescript.svg"
```ts
import { redis } from "bun";
// Set a key
@@ -28,13 +21,11 @@ const exists = await redis.exists("greeting");
await redis.del("greeting");
```
---
## Getting Started
To use the Redis client, you first need to create a connection:
```ts redis.ts icon="/icons/typescript.svg"
```ts
import { redis, RedisClient } from "bun";
// Using the default client (reads connection info from environment)
@@ -51,14 +42,13 @@ await client.incr("counter");
By default, the client reads connection information from the following environment variables (in order of precedence):
- `REDIS_URL`
- `VALKEY_URL`
- If not set, defaults to `"redis://localhost:6379"`
### Connection Lifecycle
The Redis client automatically handles connections in the background:
```ts redis.ts icon="/icons/typescript.svg"
```ts
// No connection is made until a command is executed
const client = new RedisClient();
@@ -74,7 +64,7 @@ client.close();
You can also manually control the connection lifecycle:
```ts redis.ts icon="/icons/typescript.svg"
```ts
const client = new RedisClient();
// Explicitly connect
@@ -87,13 +77,11 @@ await client.set("key", "value");
client.close();
```
---
## Basic Operations
### String Operations
```ts redis.ts icon="/icons/typescript.svg"
```ts
// Set a key
await redis.set("user:1:name", "Alice");
@@ -119,7 +107,7 @@ const ttl = await redis.ttl("session:123");
### Numeric Operations
```ts redis.ts icon="/icons/typescript.svg"
```ts
// Set initial value
await redis.set("counter", "0");
@@ -132,9 +120,16 @@ await redis.decr("counter");
### Hash Operations
```ts redis.ts icon="/icons/typescript.svg"
```ts
// Set multiple fields in a hash
await redis.hmset("user:123", ["name", "Alice", "email", "alice@example.com", "active", "true"]);
await redis.hmset("user:123", [
"name",
"Alice",
"email",
"alice@example.com",
"active",
"true",
]);
// Get multiple fields from a hash
const userFields = await redis.hmget("user:123", ["name", "email"]);
@@ -153,7 +148,7 @@ await redis.hincrbyfloat("user:123", "score", 1.5);
### Set Operations
```ts redis.ts icon="/icons/typescript.svg"
```ts
// Add member to set
await redis.sadd("tags", "javascript");
@@ -173,25 +168,23 @@ const randomTag = await redis.srandmember("tags");
const poppedTag = await redis.spop("tags");
```
---
## Pub/Sub
Bun provides native bindings for the [Redis
Pub/Sub](https://redis.io/docs/latest/develop/pubsub/) protocol. **New in Bun
1.2.23**
<Warning>
The Redis Pub/Sub feature is experimental. Although we expect it to be stable, we're currently actively looking for
feedback and areas for improvement.
</Warning>
{% callout %}
**🚧** — The Redis Pub/Sub feature is experimental. Although we expect it to be
stable, we're currently actively looking for feedback and areas for improvement.
{% /callout %}
### Basic Usage
To get started publishing messages, you can set up a publisher in
`publisher.ts`:
```typescript publisher.ts icon="/icons/typescript.svg"
```typescript#publisher.ts
import { RedisClient } from "bun";
const writer = new RedisClient("redis://localhost:6739");
@@ -204,7 +197,7 @@ writer.close();
In another file, create the subscriber in `subscriber.ts`:
```typescript subscriber.ts icon="/icons/typescript.svg"
```typescript#subscriber.ts
import { RedisClient } from "bun";
const listener = new RedisClient("redis://localhost:6739");
@@ -217,40 +210,40 @@ await listener.subscribe("general", (message, channel) => {
In one shell, run your subscriber:
```bash terminal icon="terminal"
```bash
bun run subscriber.ts
```
and, in another, run your publisher:
```bash terminal icon="terminal"
```bash
bun run publisher.ts
```
<Note>
The subscription mode takes over the `RedisClient` connection. A
{% callout %}
**Note:** The subscription mode takes over the `RedisClient` connection. A
client with subscriptions can only call `RedisClient.prototype.subscribe()`. In
other words, applications which need to message Redis need a separate
connection, acquirable through `.duplicate()`:
```ts redis.ts icon="/icons/typescript.svg"
```typescript
import { RedisClient } from "bun";
const redis = new RedisClient("redis://localhost:6379");
await redis.connect();
const subscriber = await redis.duplicate(); // [!code ++]
const subscriber = await redis.duplicate();
await subscriber.subscribe("foo", () => {});
await redis.set("bar", "baz");
```
</Note>
{% /callout %}
### Publishing
Publishing messages is done through the `publish()` method:
```typescript redis.ts icon="/icons/typescript.svg"
```typescript
await client.publish(channelName, message);
```
@@ -259,13 +252,13 @@ await client.publish(channelName, message);
The Bun `RedisClient` allows you to subscribe to channels through the
`.subscribe()` method:
```typescript redis.ts icon="/icons/typescript.svg"
```typescript
await client.subscribe(channel, (message, channel) => {});
```
You can unsubscribe through the `.unsubscribe()` method:
```typescript redis.ts icon="/icons/typescript.svg"
```typescript
await client.unsubscribe(); // Unsubscribe from all channels.
await client.unsubscribe(channel); // Unsubscribe a particular channel.
await client.unsubscribe(channel, listener); // Unsubscribe a particular listener.
@@ -277,16 +270,19 @@ await client.unsubscribe(channel, listener); // Unsubscribe a particular listene
The client automatically pipelines commands, improving performance by sending multiple commands in a batch and processing responses as they arrive.
```ts redis.ts icon="/icons/typescript.svg"
```ts
// Commands are automatically pipelined by default
const [infoResult, listResult] = await Promise.all([redis.get("user:1:name"), redis.get("user:2:email")]);
const [infoResult, listResult] = await Promise.all([
redis.get("user:1:name"),
redis.get("user:2:email"),
]);
```
To disable automatic pipelining, you can set the `enableAutoPipelining` option to `false`:
```ts redis.ts icon="/icons/typescript.svg"
```ts
const client = new RedisClient("redis://localhost:6379", {
enableAutoPipelining: false, // [!code ++]
enableAutoPipelining: false,
});
```
@@ -294,7 +290,7 @@ const client = new RedisClient("redis://localhost:6379", {
When you need to use commands that don't have convenience methods, you can use the `send` method:
```ts redis.ts icon="/icons/typescript.svg"
```ts
// Run any Redis command
const info = await redis.send("INFO", []);
@@ -311,7 +307,7 @@ The `send` method allows you to use any Redis command, even ones that don't have
You can register handlers for connection events:
```ts redis.ts icon="/icons/typescript.svg"
```ts
const client = new RedisClient();
// Called when successfully connected to Redis server
@@ -331,7 +327,7 @@ client.close();
### Connection Status and Monitoring
```ts redis.ts icon="/icons/typescript.svg"
```ts
// Check if connected
console.log(client.connected); // boolean indicating connection status
@@ -376,13 +372,11 @@ The following commands disable automatic pipelining:
- `UNSUBSCRIBE`
- `UNPSUBSCRIBE`
---
## Connection Options
When creating a client, you can pass various options to configure the connection:
```ts redis.ts icon="/icons/typescript.svg"
```ts
const client = new RedisClient("redis://localhost:6379", {
// Connection timeout in milliseconds (default: 10000)
connectionTimeout: 5000,
@@ -425,13 +419,11 @@ When a connection is lost, the client automatically attempts to reconnect with e
- Queued if `enableOfflineQueue` is true (default)
- Rejected immediately if `enableOfflineQueue` is false
---
## Supported URL Formats
The Redis client supports various URL formats:
```ts redis.ts icon="/icons/typescript.svg"
```ts
// Standard Redis URL
new RedisClient("redis://localhost:6379");
new RedisClient("redis://localhost:6379");
@@ -457,13 +449,11 @@ new RedisClient("redis+tls+unix:///path/to/socket");
new RedisClient("redis+tls+unix:///path/to/socket");
```
---
## Error Handling
The Redis client throws typed errors for different scenarios:
```ts redis.ts icon="/icons/typescript.svg"
```ts
try {
await redis.get("non-existent-key");
} catch (error) {
@@ -483,13 +473,11 @@ Common error codes:
- `ERR_REDIS_AUTHENTICATION_FAILED` - Failed to authenticate with the server
- `ERR_REDIS_INVALID_RESPONSE` - Received an invalid response from the server
---
## Example Use Cases
### Caching
```ts redis.ts icon="/icons/typescript.svg"
```ts
async function getUserWithCache(userId) {
const cacheKey = `user:${userId}`;
@@ -512,7 +500,7 @@ async function getUserWithCache(userId) {
### Rate Limiting
```ts redis.ts icon="/icons/typescript.svg"
```ts
async function rateLimit(ip, limit = 100, windowSecs = 3600) {
const key = `ratelimit:${ip}`;
@@ -534,13 +522,20 @@ async function rateLimit(ip, limit = 100, windowSecs = 3600) {
### Session Storage
```ts redis.ts icon="/icons/typescript.svg"
```ts
async function createSession(userId, data) {
const sessionId = crypto.randomUUID();
const key = `session:${sessionId}`;
// Store session with expiration
await redis.hmset(key, ["userId", userId.toString(), "created", Date.now().toString(), "data", JSON.stringify(data)]);
await redis.hmset(key, [
"userId",
userId.toString(),
"created",
Date.now().toString(),
"data",
JSON.stringify(data),
]);
await redis.expire(key, 86400); // 24 hours
return sessionId;
@@ -553,7 +548,11 @@ async function getSession(sessionId) {
const exists = await redis.exists(key);
if (!exists) return null;
const [userId, created, data] = await redis.hmget(key, ["userId", "created", "data"]);
const [userId, created, data] = await redis.hmget(key, [
"userId",
"created",
"data",
]);
return {
userId: Number(userId),
@@ -563,19 +562,33 @@ async function getSession(sessionId) {
}
```
---
## Implementation Notes
Bun's Redis client is implemented in Zig and uses the Redis Serialization Protocol (RESP3). It manages connections efficiently and provides automatic reconnection with exponential backoff.
The client supports pipelining commands, meaning multiple commands can be sent without waiting for the replies to previous commands. This significantly improves performance when sending multiple commands in succession.
### RESP3 Protocol Support
Bun's Redis client uses the newer RESP3 protocol by default, which provides more data types and features compared to RESP2:
- Better error handling with typed errors
- Native Boolean responses
- Map/Dictionary responses (key-value objects)
- Set responses
- Double (floating point) values
- BigNumber support for large integer values
When connecting to Redis servers using older versions that don't support RESP3, the client automatically fallbacks to compatible modes.
## Limitations and Future Plans
Current limitations of the Redis client we are planning to address in future versions:
- Transactions (MULTI/EXEC) must be done through raw commands for now
- [ ] Transactions (MULTI/EXEC) must be done through raw commands for now
- [ ] Streams are supported but without dedicated methods
- [ ] Pub/Sub does not currently support binary data, nor pattern-based
subscriptions.
Unsupported features:

View File

@@ -1,19 +1,16 @@
---
title: S3
description: Bun provides fast, native bindings for interacting with S3-compatible object storage services.
---
Production servers often read, upload, and write files to S3-compatible object storage services instead of the local filesystem. Historically, that means local filesystem APIs you use in development can't be used in production. When you use Bun, things are different.
{% callout %}
### Bun's S3 API is fast
<Frame caption="Left: Bun v1.1.44. Right: Node.js v23.6.0">
<img src="/images/bun-s3-node.gif" alt="Bun's S3 API is fast" />
</Frame>
{% image src="https://bun.com/bun-s3-node.gif" alt="Bun's S3 API is fast" caption="Left: Bun v1.1.44. Right: Node.js v23.6.0" /%}
{% /callout %}
Bun provides fast, native bindings for interacting with S3-compatible object storage services. Bun's S3 API is designed to be simple and feel similar to fetch's `Response` and `Blob` APIs (like Bun's local filesystem APIs).
```ts s3.ts icon="/icons/typescript.svg"
```ts
import { s3, write, S3Client } from "bun";
// Bun.s3 reads environment variables for credentials
@@ -55,7 +52,7 @@ There are several ways to interact with Bun's S3 API.
To explicitly set credentials, pass them to the `Bun.S3Client` constructor.
```ts s3.ts icon="/icons/typescript.svg"
```ts
import { S3Client } from "bun";
const client = new S3Client({
@@ -77,7 +74,7 @@ const client = new S3Client({
The **`file`** method in `S3Client` returns a **lazy reference to a file on S3**.
```ts s3.ts icon="/icons/typescript.svg"
```ts
// A lazy reference to a file on S3
const s3file: S3File = client.file("123.json");
```
@@ -88,7 +85,7 @@ Like `Bun.file(path)`, the `S3Client`'s `file` method is synchronous. It does ze
If you've used the `fetch` API, you're familiar with the `Response` and `Blob` APIs. `S3File` extends `Blob`. The same methods that work on `Blob` also work on `S3File`.
```ts s3.ts icon="/icons/typescript.svg"
```ts
// Read an S3File as text
const text = await s3file.text();
@@ -120,7 +117,7 @@ These helper methods not only simplify the API, they also make it faster.
Writing to S3 is just as simple.
```ts s3.ts icon="/icons/typescript.svg"
```ts
// Write a string (replacing the file)
await s3file.write("Hello World!");
@@ -149,7 +146,7 @@ await Bun.write(s3file, "Hello World!");
Bun automatically handles multipart uploads for large files and provides streaming capabilities. The same API that works for local files also works for S3 files.
```ts s3.ts icon="/icons/typescript.svg"
```ts
// Write a large file
const bigFile = Buffer.alloc(10 * 1024 * 1024); // 10MB
const writer = s3file.writer({
@@ -169,8 +166,6 @@ for (let i = 0; i < 10; i++) {
await writer.end();
```
---
## Presigning URLs
When your production service needs to let users upload files to your server, it's often more reliable for the user to upload directly to S3 instead of your server acting as an intermediary.
@@ -179,7 +174,7 @@ To facilitate this, you can presign URLs for S3 files. This generates a URL with
The default behaviour is to generate a `GET` URL that expires in 24 hours. Bun attempts to infer the content type from the file extension. If inference is not possible, it will default to `application/octet-stream`.
```ts s3.ts icon="/icons/typescript.svg"
```ts
import { s3 } from "bun";
// Generate a presigned URL that expires in 24 hours (default)
@@ -203,7 +198,7 @@ const presignedFile = myFile.presign({
To set an ACL (access control list) on a presigned URL, pass the `acl` option:
```ts s3.ts icon="/icons/typescript.svg"
```ts
const url = s3file.presign({
acl: "public-read",
expiresIn: 3600,
@@ -227,7 +222,7 @@ You can pass any of the following ACLs:
To set an expiration time for a presigned URL, pass the `expiresIn` option.
```ts s3.ts icon="/icons/typescript.svg"
```ts
const url = s3file.presign({
// Seconds
expiresIn: 3600, // 1 hour
@@ -244,7 +239,7 @@ const url = s3file.presign({
To set the HTTP method for a presigned URL, pass the `method` option.
```ts s3.ts icon="/icons/typescript.svg"
```ts
const url = s3file.presign({
method: "PUT",
// method: "DELETE",
@@ -259,14 +254,14 @@ const url = s3file.presign({
To quickly redirect users to a presigned URL for an S3 file, pass an `S3File` instance to a `Response` object as the body.
This will automatically redirect the user to the presigned URL for the S3 file, saving you the memory, time, and bandwidth cost of downloading the file to your server and sending it back to the user.
```ts s3.ts icon="/icons/typescript.svg"
```ts
const response = new Response(s3file);
console.log(response);
```
```txt
This will automatically redirect the user to the presigned URL for the S3 file, saving you the memory, time, and bandwidth cost of downloading the file to your server and sending it back to the user.
```ts
Response (0 KB) {
ok: false,
url: "",
@@ -280,8 +275,6 @@ Response (0 KB) {
}
```
---
## Support for S3-Compatible Services
Bun's S3 implementation works with any S3-compatible storage service. Just specify the appropriate endpoint:
@@ -290,7 +283,7 @@ Bun's S3 implementation works with any S3-compatible storage service. Just speci
AWS S3 is the default. You can also pass a `region` option instead of an `endpoint` option for AWS S3.
```ts s3.ts icon="/icons/typescript.svg"
```ts
import { S3Client } from "bun";
// AWS S3
@@ -307,7 +300,7 @@ const s3 = new S3Client({
To use Bun's S3 client with [Google Cloud Storage](https://cloud.google.com/storage), set `endpoint` to `"https://storage.googleapis.com"` in the `S3Client` constructor.
```ts s3.ts icon="/icons/typescript.svg" highlight={8}
```ts
import { S3Client } from "bun";
// Google Cloud Storage
@@ -323,7 +316,7 @@ const gcs = new S3Client({
To use Bun's S3 client with [Cloudflare R2](https://developers.cloudflare.com/r2/), set `endpoint` to the R2 endpoint in the `S3Client` constructor. The R2 endpoint includes your account ID.
```ts s3.ts icon="/icons/typescript.svg" highlight={8}
```ts
import { S3Client } from "bun";
// CloudFlare R2
@@ -339,7 +332,7 @@ const r2 = new S3Client({
To use Bun's S3 client with [DigitalOcean Spaces](https://www.digitalocean.com/products/spaces/), set `endpoint` to the DigitalOcean Spaces endpoint in the `S3Client` constructor.
```ts s3.ts icon="/icons/typescript.svg" highlight={8}
```ts
import { S3Client } from "bun";
const spaces = new S3Client({
@@ -355,7 +348,7 @@ const spaces = new S3Client({
To use Bun's S3 client with [MinIO](https://min.io/), set `endpoint` to the URL that MinIO is running on in the `S3Client` constructor.
```ts s3.ts icon="/icons/typescript.svg" highlight={10}
```ts
import { S3Client } from "bun";
const minio = new S3Client({
@@ -371,9 +364,9 @@ const minio = new S3Client({
### Using Bun's S3Client with supabase
To use Bun's S3 client with [supabase](https://supabase.com/), set `endpoint` to the supabase endpoint in the `S3Client` constructor. The supabase endpoint includes your account ID and /storage/v1/s3 path. Make sure to set Enable connection via S3 protocol on in the supabase dashboard in `https://supabase.com/dashboard/project/<account-id>/settings/storage` and to set the region informed in the same section.
To use Bun's S3 client with [supabase](https://supabase.com/), set `endpoint` to the supabase endpoint in the `S3Client` constructor. The supabase endpoint includes your account ID and /storage/v1/s3 path. Make sure to set Enable connection via S3 protocol on in the supabase dashboard in https://supabase.com/dashboard/project/<account-id>/settings/storage and to set the region informed in the same section.
```ts s3.ts icon="/icons/typescript.svg" highlight={3-10}
```ts
import { S3Client } from "bun";
const supabase = new S3Client({
@@ -387,15 +380,9 @@ const supabase = new S3Client({
### Using Bun's S3Client with S3 Virtual Hosted-Style endpoints
When using a S3 Virtual Hosted-Style endpoint, you need to set the `virtualHostedStyle` option to `true`.
When using a S3 Virtual Hosted-Style endpoint, you need to set the `virtualHostedStyle` option to `true` and if no endpoint is provided, Bun will use region and bucket to infer the endpoint to AWS S3, if no region is provided it will use `us-east-1`. If you provide a the endpoint, there are no need to provide the bucket name.
<Note>
- If you dont specify an endpoint, Bun will automatically determine the AWS S3 endpoint using the provided region and
bucket. - If no region is specified, Bun defaults to us-east-1. - If you explicitly provide an endpoint, you dont
need to specify a bucket name.
</Note>
```ts s3.ts icon="/icons/typescript.svg" highlight={17, 25}
```ts
import { S3Client } from "bun";
// AWS S3 endpoint inferred from region and bucket
@@ -403,7 +390,7 @@ const s3 = new S3Client({
accessKeyId: "access-key",
secretAccessKey: "secret-key",
bucket: "my-bucket",
virtualHostedStyle: true, // [!code ++]
virtualHostedStyle: true,
// endpoint: "https://my-bucket.s3.us-east-1.amazonaws.com",
// region: "us-east-1",
});
@@ -413,7 +400,7 @@ const s3WithEndpoint = new S3Client({
accessKeyId: "access-key",
secretAccessKey: "secret-key",
endpoint: "https://<bucket-name>.s3.<region>.amazonaws.com",
virtualHostedStyle: true, // [!code ++]
virtualHostedStyle: true,
});
// Cloudflare R2
@@ -421,12 +408,10 @@ const r2WithEndpoint = new S3Client({
accessKeyId: "access-key",
secretAccessKey: "secret-key",
endpoint: "https://<bucket-name>.<account-id>.r2.cloudflarestorage.com",
virtualHostedStyle: true, // [!code ++]
virtualHostedStyle: true,
});
```
---
## Credentials
Credentials are one of the hardest parts of using S3, and we've tried to make it as easy as possible. By default, Bun reads the following environment variables for credentials.
@@ -451,7 +436,7 @@ If the `S3_*` environment variable is not set, Bun will also check for the `AWS_
| `bucket` | `AWS_BUCKET` |
| `sessionToken` | `AWS_SESSION_TOKEN` |
These environment variables are read from [`.env` files](/runtime/environment-variables) or from the process environment at initialization time (`process.env` is not used for this).
These environment variables are read from [`.env` files](/docs/runtime/env) or from the process environment at initialization time (`process.env` is not used for this).
These defaults are overridden by the options you pass to `s3.file(credentials)`, `new Bun.S3Client(credentials)`, or any of the methods that accept credentials. So if, for example, you use the same credentials for different buckets, you can set the credentials once in your `.env` file and then pass `bucket: "my-bucket"` to the `s3.file()` function without having to specify all the credentials again.
@@ -459,7 +444,7 @@ These defaults are overridden by the options you pass to `s3.file(credentials)`,
When you're not using environment variables or using multiple buckets, you can create a `S3Client` object to explicitly set credentials.
```ts s3.ts icon="/icons/typescript.svg" highlight={3-11}
```ts
import { S3Client } from "bun";
const client = new S3Client({
@@ -489,14 +474,13 @@ await file.delete();
To upload or write a file to S3, call `write` on the `S3Client` instance.
```ts s3.ts icon="/icons/typescript.svg" highlight={8, 9}
```ts
const client = new Bun.S3Client({
accessKeyId: "your-access-key",
secretAccessKey: "your-secret-key",
endpoint: "https://s3.us-east-1.amazonaws.com",
bucket: "my-bucket",
});
await client.write("my-file.txt", "Hello World!");
await client.write("my-file.txt", new Response("Hello World!"));
@@ -508,7 +492,7 @@ await client.write("my-file.txt", new Response("Hello World!"));
To delete a file from S3, call `delete` on the `S3Client` instance.
```ts s3.ts icon="/icons/typescript.svg" highlight={7}
```ts
const client = new Bun.S3Client({
accessKeyId: "your-access-key",
secretAccessKey: "your-secret-key",
@@ -524,7 +508,7 @@ await client.delete("my-file.txt");
To check if a file exists in S3, call `exists` on the `S3Client` instance.
```ts s3.ts icon="/icons/typescript.svg" highlight={7}
```ts
const client = new Bun.S3Client({
accessKeyId: "your-access-key",
secretAccessKey: "your-secret-key",
@@ -540,7 +524,7 @@ const exists = await client.exists("my-file.txt");
`S3File` instances are created by calling the `S3Client` instance method or the `s3.file()` function. Like `Bun.file()`, `S3File` instances are lazy. They don't refer to something that necessarily exists at the time of creation. That's why all the methods that don't involve network requests are fully synchronous.
```ts Type Reference icon="/icons/typescript.svg" expandable
```ts
interface S3File extends Blob {
slice(start: number, end?: number): S3File;
exists(): Promise<boolean>;
@@ -552,7 +536,14 @@ interface S3File extends Blob {
arrayBuffer(): Promise<ArrayBuffer>;
stream(options: S3Options): ReadableStream;
write(
data: string | Uint8Array | ArrayBuffer | Blob | ReadableStream | Response | Request,
data:
| string
| Uint8Array
| ArrayBuffer
| Blob
| ReadableStream
| Response
| Request,
options?: BlobPropertyBag,
): Promise<number>;
@@ -589,7 +580,7 @@ That means using `S3File` instances with `fetch()`, `Response`, and other web AP
To read a partial range of a file, you can use the `slice` method.
```ts s3.ts icon="/icons/typescript.svg" highlight={1}
```ts
const partial = s3file.slice(0, 1024);
// Read the partial range as a Uint8Array
@@ -605,7 +596,7 @@ Internally, this works by using the HTTP `Range` header to request only the byte
To delete a file from S3, you can use the `delete` method.
```ts s3.ts icon="/icons/typescript.svg" highlight={1}
```ts
await s3file.delete();
// await s3File.unlink();
```
@@ -633,7 +624,7 @@ The `S3Client` class provides several static methods for interacting with S3.
To write data directly to a path in the bucket, you can use the `S3Client.write` static method.
```ts s3.ts icon="/icons/typescript.svg" highlight={12, 15-18, 22, 25-29}
```ts
import { S3Client } from "bun";
const credentials = {
@@ -671,7 +662,7 @@ This is equivalent to calling `new S3Client(credentials).write("my-file.txt", "H
To generate a presigned URL for an S3 file, you can use the `S3Client.presign` static method.
```ts s3.ts icon="/icons/typescript.svg" highlight={11-14}
```ts
import { S3Client } from "bun";
const credentials = {
@@ -694,7 +685,7 @@ This is equivalent to calling `new S3Client(credentials).presign("my-file.txt",
To list some or all (up to 1,000) objects in a bucket, you can use the `S3Client.list` static method.
```ts s3.ts icon="/icons/typescript.svg" highlight={12, 15-20, 24-29}
```ts
import { S3Client } from "bun";
const credentials = {
@@ -733,7 +724,7 @@ This is equivalent to calling `new S3Client(credentials).list()`.
To check if an S3 file exists, you can use the `S3Client.exists` static method.
```ts s3.ts icon="/icons/typescript.svg" highlight={11}
```ts
import { S3Client } from "bun";
const credentials = {
@@ -749,13 +740,12 @@ const exists = await S3Client.exists("my-file.txt", credentials);
The same method also works on `S3File` instances.
```ts s3.ts icon="/icons/typescript.svg" highlight=7}
```ts
import { s3 } from "bun";
const s3file = s3.file("my-file.txt", {
// ...credentials,
...credentials,
});
const exists = await s3file.exists();
```
@@ -763,7 +753,7 @@ const exists = await s3file.exists();
To quickly check the size of S3 file without downloading it, you can use the `S3Client.size` static method.
```ts s3.ts icon="/icons/typescript.svg" highlight={11}
```ts
import { S3Client } from "bun";
const credentials = {
@@ -783,7 +773,7 @@ This is equivalent to calling `new S3Client(credentials).size("my-file.txt")`.
To get the size, etag, and other metadata of an S3 file, you can use the `S3Client.stat` static method.
```ts s3.ts icon="/icons/typescript.svg"
```ts
import { S3Client } from "bun";
const credentials = {
@@ -795,22 +785,19 @@ const credentials = {
};
const stat = await S3Client.stat("my-file.txt", credentials);
```
```txt
{
etag: "\"7a30b741503c0b461cc14157e2df4ad8\"",
lastModified: 2025-01-07T00:19:10.000Z,
size: 1024,
type: "text/plain;charset=utf-8",
}
// {
// etag: "\"7a30b741503c0b461cc14157e2df4ad8\"",
// lastModified: 2025-01-07T00:19:10.000Z,
// size: 1024,
// type: "text/plain;charset=utf-8",
// }
```
### `S3Client.delete` (static)
To delete an S3 file, you can use the `S3Client.delete` static method.
```ts s3.ts icon="/icons/typescript.svg" highlight={10, 15}
```ts
import { S3Client } from "bun";
const credentials = {
@@ -832,14 +819,14 @@ await S3Client.unlink("my-file.txt", credentials);
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()`.
```ts s3.ts icon="/icons/typescript.svg"
```ts
const response = await fetch("s3://my-bucket/my-file.txt");
const file = Bun.file("s3://my-bucket/my-file.txt");
```
You can additionally pass `s3` options to the `fetch` and `Bun.file` functions.
```ts s3.ts icon="/icons/typescript.svg" highlight={2-6}
```ts
const response = await fetch("s3://my-bucket/my-file.txt", {
s3: {
accessKeyId: "your-access-key",
@@ -847,7 +834,7 @@ const response = await fetch("s3://my-bucket/my-file.txt", {
endpoint: "https://s3.us-east-1.amazonaws.com",
},
headers: {
range: "bytes=0-1023",
"range": "bytes=0-1023",
},
});
```

View File

@@ -1,40 +1,30 @@
---
title: Secrets
description: Use Bun's Secrets API to store and retrieve sensitive credentials securely
---
Store and retrieve sensitive credentials securely using the operating system's native credential storage APIs.
<Warning>This API is new and experimental. It may change in the future.</Warning>
**Experimental:** This API is new and experimental. It may change in the future.
```typescript index.ts icon="/icons/typescript.svg"
```typescript
import { secrets } from "bun";
let githubToken: string | null = await secrets.get({
const githubToken = await secrets.get({
service: "my-cli-tool",
name: "github-token",
});
if (!githubToken) {
githubToken = prompt("Please enter your GitHub token");
const response = await fetch("https://api.github.com/name", {
headers: { "Authorization": `token ${githubToken}` },
});
console.log("Please enter your GitHub token");
} else {
await secrets.set({
service: "my-cli-tool",
name: "github-token",
value: githubToken,
value: prompt("Please enter your GitHub token"),
});
console.log("GitHub token stored");
}
const response = await fetch("https://api.github.com/name", {
headers: { Authorization: `token ${githubToken}` },
});
console.log(`Logged in as ${(await response.json()).login}`);
```
---
## Overview
`Bun.secrets` provides a cross-platform API for managing sensitive credentials that CLI tools and development applications typically store in plaintext files like `~/.npmrc`, `~/.aws/credentials`, or `.env` files. It uses:
@@ -45,12 +35,7 @@ console.log(`Logged in as ${(await response.json()).login}`);
All operations are asynchronous and non-blocking, running on Bun's threadpool.
<Note>
In the future, we may add an additional `provider` option to make this better for production deployment secrets, but
today this API is mostly useful for local development tools.
</Note>
---
Note: in the future, we may add an additional `provider` option to make this better for production deployment secrets, but today this API is mostly useful for local development tools.
## API
@@ -127,8 +112,6 @@ const deleted = await Bun.secrets.delete({
- `Promise<boolean>` - `true` if a credential was deleted, `false` if not found
---
## Examples
### Storing CLI Tool Credentials
@@ -160,7 +143,7 @@ const token = await Bun.secrets.get({
if (token) {
const response = await fetch("https://api.github.com/name", {
headers: {
Authorization: `token ${token}`,
"Authorization": `token ${token}`,
},
});
}
@@ -235,8 +218,6 @@ await Bun.secrets.set({
// The old password is replaced
```
---
## Platform Behavior
### macOS (Keychain)
@@ -278,8 +259,6 @@ await Bun.secrets.set({
- macOS: Keychain Access must be available
- Windows: Credential Manager service must be enabled
---
## Comparison with Environment Variables
Unlike environment variables, `Bun.secrets`:
@@ -292,8 +271,6 @@ Unlike environment variables, `Bun.secrets`:
- ❌ Requires OS credential service
- ❌ Not very useful for deployment secrets (use environment variables in production)
---
## Best Practices
1. **Use descriptive service names**: Match the tool or application name
@@ -317,8 +294,6 @@ Unlike environment variables, `Bun.secrets`:
- ✅ Personal API keys for testing
- ❌ Production servers (use proper secret management)
---
## TypeScript
```typescript
@@ -337,3 +312,8 @@ namespace Bun {
const secrets: Secrets;
}
```
## See Also
- [Environment Variables](./env.md) - For deployment configuration
- [Bun.password](./password.md) - For password hashing and verification

View File

@@ -1,17 +1,12 @@
---
title: Semver
description: Use Bun's semantic versioning API
---
Bun implements a semantic versioning API which can be used to compare versions and determine if a version is compatible with another range of versions. The versions and ranges are designed to be compatible with `node-semver`, which is used by npm clients.
It's about 20x faster than `node-semver`.
<Frame>![Benchmark](https://github.com/oven-sh/bun/assets/709451/94746adc-8aba-4baf-a143-3c355f8e0f78)</Frame>
![Benchmark](https://github.com/oven-sh/bun/assets/709451/94746adc-8aba-4baf-a143-3c355f8e0f78)
Currently, this API provides two functions:
Currently, this API provides two functions :
## `Bun.semver.satisfies(version: string, range: string): boolean`
#### `Bun.semver.satisfies(version: string, range: string): boolean`
Returns `true` if `version` satisfies `range`, otherwise `false`.
@@ -36,7 +31,7 @@ semver.satisfies("1.0.0", "1.0.0 - 1.0.1"); // true
If `range` is invalid, it returns false. If `version` is invalid, it returns false.
## `Bun.semver.order(versionA: string, versionB: string): 0 | 1 | -1`
#### `Bun.semver.order(versionA: string, versionB: string): 0 | 1 | -1`
Returns `0` if `versionA` and `versionB` are equal, `1` if `versionA` is greater than `versionB`, and `-1` if `versionA` is less than `versionB`.

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