initial commit

This commit is contained in:
i2p
2026-08-27 21:09:14 +00:00
commit a5b6d59437
12681 changed files with 3253832 additions and 0 deletions
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,31 @@
import { expect, test } from "bun:test";
const eachWithDifferingTypes = test.each([
["hello", "world"],
["hello", 2],
]);
eachWithDifferingTypes("each with differing types", (a, b) => {
expect(typeof a).toBe("string");
expect(["world", 2]).toContain(b);
});
const eachWithConst = test.each([
["hello", "world"],
["hello", "alistair"],
] as const);
eachWithConst("each with `as const`", (a, b) => {
expect(a).toBe("hello");
expect(["world", "alistair"]).toContain(b);
});
const eachWithConstAndDifferingTypes = test.each([
["hello", "world"],
["hello", 2],
] as const);
eachWithConstAndDifferingTypes("each with `as const` and differing types", (a, b) => {
expect(a).toBe("hello");
expect(["world", 2]).toContain(b);
});
@@ -0,0 +1,26 @@
import { expectType } from "./utilities";
async function example(): Promise<Blob> {
const response = await fetch("foo");
return await response.blob();
}
import { Blob as NodeBlob } from "node:buffer";
async function example2(): Promise<NodeBlob> {
const response = await fetch("foo");
return await response.blob();
}
expectType(Blob.prototype).extends<{
json(): Promise<unknown>;
bytes(): Promise<Uint8Array>;
text(): Promise<string>;
formData(): Promise<FormData>;
}>();
expectType(new Blob(["hello"])).extends<{
json(): Promise<unknown>;
bytes(): Promise<Uint8Array>;
text(): Promise<string>;
formData(): Promise<FormData>;
}>();
@@ -0,0 +1,28 @@
import { describe, expect, it, jest, mock, spyOn } from "bun:test";
class AnyDTO {
anyField: string = "any_value";
}
class AnyClass {
async anyMethod(): Promise<AnyDTO> {
return new AnyDTO();
}
}
const anyObject: AnyClass = {
anyMethod: jest.fn(),
};
describe("Any describe", () => {
it("should return any value", async () => {
spyOn(anyObject, "anyMethod").mockResolvedValue({ anyField: "any_value" });
const anyValue = await anyObject.anyMethod();
expect(anyValue).toEqual({ anyField: "any_value" });
});
});
const mockSomething = mock((): string => "hi");
mockSomething.mockImplementation(() => "hello");
mockSomething.mockReturnValue("hello");
@@ -0,0 +1,42 @@
// This page test can return once we implement ssg/ssr/rsc again
// import { join } from "path";
// import { expectType } from "./utilities";
// // we're just checking types here really
// declare function markdownToJSX(markdown: string): React.ReactNode;
// type Params = {
// slug: string;
// };
// const Index: Bun.__experimental.SSGPage<Params> = async ({ params }) => {
// expectType(params.slug).is<string>();
// const content = await Bun.file(join(process.cwd(), "posts", params.slug + ".md")).text();
// const node = markdownToJSX(content);
// return <div>{node}</div>;
// };
// expectType(Index.displayName).is<string | undefined>();
// export default Index;
// export const getStaticPaths: Bun.__experimental.GetStaticPaths<Params> = async () => {
// const glob = new Bun.Glob("**/*.md");
// const postsDir = join(process.cwd(), "posts");
// const paths: Bun.__experimental.SSGPaths<Params> = [];
// for (const file of glob.scanSync({ cwd: postsDir })) {
// const slug = file.replace(/\.md$/, "");
// paths.push({
// params: { slug },
// });
// }
// return { paths };
// };
export {};
@@ -0,0 +1,15 @@
import { expectType } from "./utilities";
const buffer = new ArrayBuffer(1024, {
maxByteLength: 2048,
});
console.log(buffer.byteLength); // 1024
buffer.resize(2048);
console.log(buffer.byteLength); // 2048
TextDecoder;
const buf = new SharedArrayBuffer(1024);
buf.grow(2048);
expectType(buffer[Symbol.toStringTag]).extends<string>();
@@ -0,0 +1,32 @@
import { expectType } from "./utilities";
async function* listReleases() {
for (let page = 1; ; page++) {
const response = await fetch(`https://api.github.com/repos/oven-sh/bun/releases?page=${page}`);
const releases = (await response.json()) as Array<{ data: string }>;
if (!releases.length) {
break;
}
for (const release of releases) {
yield release;
}
}
}
await Array.fromAsync(listReleases());
// Tests from issue #8484
// https://github.com/oven-sh/bun/issues/8484
async function* naturals() {
for (let i = 0; i < 10; i++) {
yield i;
}
}
const test1 = await Array.fromAsync(naturals(), n => Promise.resolve(`${n}`));
expectType<string[]>(test1);
const test2 = await Array.fromAsync([Promise.resolve(1), Promise.resolve(2)]);
expectType<number[]>(test2);
export {};
@@ -0,0 +1,40 @@
// Test Atomics global type definitions with TypeScript
declare const buffer: SharedArrayBuffer;
declare const view: Int32Array;
declare const view16: Int16Array;
declare const view8: Int8Array;
declare const viewU32: Uint32Array;
declare const bigView: BigInt64Array;
// Test basic Atomics operations - type signatures only
const stored: number = Atomics.store(view, 0, 42);
const loaded: number = Atomics.load(view, 0);
const added: number = Atomics.add(view, 0, 8);
const subtracted: number = Atomics.sub(view, 0, 5);
// Test compare and exchange operations
const exchanged: number = Atomics.compareExchange(view, 0, 50, 100);
const swapped: number = Atomics.exchange(view, 0, 200);
// Test bitwise operations
const anded: number = Atomics.and(view, 0, 0xFF);
const ored: number = Atomics.or(view, 0, 0x10);
const xored: number = Atomics.xor(view, 0, 0x0F);
// Test utility functions
const lockFree4: boolean = Atomics.isLockFree(4);
const lockFree8: boolean = Atomics.isLockFree(8);
// Test synchronization primitives
const waitResult: "ok" | "not-equal" | "timed-out" = Atomics.wait(view, 0, 0, 1000);
const notified: number = Atomics.notify(view, 0, 1);
// Test with different integer TypedArray types
const stored16: number = Atomics.store(view16, 0, 42);
const loaded8: number = Atomics.load(view8, 0);
const addedU32: number = Atomics.add(viewU32, 0, 1);
// Test BigInt64Array support
const storedBig: bigint = Atomics.store(bigView, 0, 42n);
const loadedBig: bigint = Atomics.load(bigView, 0);
const addedBig: bigint = Atomics.add(bigView, 0, 8n);
@@ -0,0 +1,11 @@
const channel = new BroadcastChannel("my-channel");
const message = { hello: "world" };
channel.onmessage = event => {
console.log(event);
};
channel.postMessage(message);
const error = new Error("hello world");
const clone = structuredClone(error);
console.log(clone.message); // "hello world"
@@ -0,0 +1,68 @@
import { expectAssignable, expectType } from "./utilities";
Bun.build({
entrypoints: ["hey"],
splitting: false,
});
// Build.CompileTarget should accept SIMD variants (issue #26247)
expectAssignable<Bun.Build.CompileTarget>("bun-linux-x64-modern");
expectAssignable<Bun.Build.CompileTarget>("bun-linux-x64-baseline");
expectAssignable<Bun.Build.CompileTarget>("bun-linux-arm64-modern");
expectAssignable<Bun.Build.CompileTarget>("bun-linux-arm64-baseline");
expectAssignable<Bun.Build.CompileTarget>("bun-linux-x64-modern-glibc");
expectAssignable<Bun.Build.CompileTarget>("bun-linux-x64-modern-musl");
expectAssignable<Bun.Build.CompileTarget>("bun-darwin-x64-modern");
expectAssignable<Bun.Build.CompileTarget>("bun-darwin-arm64-baseline");
expectAssignable<Bun.Build.CompileTarget>("bun-windows-x64-modern");
Bun.build({
entrypoints: ["hey"],
splitting: false,
compile: {},
});
Bun.build({
entrypoints: ["hey"],
plugins: [
{
name: "my-terrible-plugin",
setup(build) {
expectType(build).is<Bun.PluginBuilder>();
build.onResolve({ filter: /^hey$/ }, args => {
expectType(args).is<Bun.OnResolveArgs>();
return { path: args.path };
});
build.onLoad({ filter: /^hey$/ }, args => {
expectType(args).is<Bun.OnLoadArgs>();
return { contents: "hey", loader: "js" };
});
build.onStart(() => {});
build.onEnd(result => {
expectType(result).is<Bun.BuildOutput>();
expectType(result.success).is<boolean>();
expectType(result.outputs).is<Bun.BuildArtifact[]>();
expectType(result.logs).is<Array<BuildMessage | ResolveMessage>>();
});
build.onBeforeParse(
{
namespace: "file",
filter: /\.tsx$/,
},
{
napiModule: {},
symbol: "replace_foo_with_bar",
// external: myNativeAddon.getSharedState()
},
);
},
},
],
});
+95
View File
@@ -0,0 +1,95 @@
import type { BunFile, BunPlugin, FileBlob } from "bun";
import * as tsd from "./utilities";
{
const _plugin: BunPlugin = {
name: "asdf",
setup() {},
};
_plugin;
}
{
// tslint:disable-next-line:no-void-expression
const arg = Bun.plugin({
name: "arg",
setup() {},
});
// eslint-disable-next-line @typescript-eslint/no-invalid-void-type
tsd.expectType<void>(arg);
}
{
// tslint:disable-next-line:no-void-expression
const arg = Bun.plugin({
name: "arg",
async setup() {},
});
tsd.expectType<Promise<void>>(arg);
}
{
const f = Bun.file("asdf");
tsd.expectType<BunFile>(f);
tsd.expectType<FileBlob>(f);
}
{
Bun.spawn(["anything"], {
env: process.env,
});
Bun.spawn(["anything"], {
env: { ...process.env },
});
Bun.spawn(["anything"], {
env: { ...process.env, dummy: "" },
});
}
{
Bun.TOML.parse("asdf = asdf");
}
DOMException;
tsd
.expectType(
Bun.secrets.get({
service: "hey",
name: "hey",
}),
)
.is<Promise<string | null>>();
tsd
.expectType(
Bun.secrets.set({
service: "hey",
name: "hey",
value: "hey",
allowUnrestrictedAccess: true,
}),
)
.is<Promise<void>>();
tsd
.expectType(
Bun.secrets.delete({
service: "hey",
name: "hey",
}),
)
.is<Promise<boolean>>();
tsd
.expectType(
Bun.mmap("./data.bin", {
shared: true,
sync: false,
offset: 4096,
size: 1024,
}),
)
.is<Uint8Array<ArrayBuffer>>();
tsd.expectType(Bun.mmap("./data.bin", { offset: 4096 })).is<Uint8Array<ArrayBuffer>>();
tsd.expectType(Bun.mmap("./data.bin", { size: 1024 })).is<Uint8Array<ArrayBuffer>>();
@@ -0,0 +1,46 @@
/**
* Type tests for the "bun:bundle" module.
*/
import { feature } from "bun:bundle";
import { expectType } from "./utilities";
// feature() returns boolean
expectType(feature("DEBUG")).is<boolean>();
// Import alias works
import { feature as checkFeature } from "bun:bundle";
expectType(checkFeature("FLAG")).is<boolean>();
// Bun.build features option accepts string array
Bun.build({
entrypoints: ["./index.ts"],
outdir: "./dist",
features: ["FEATURE_A", "FEATURE_B"],
});
// Error cases:
// @ts-expect-error - feature() requires exactly one argument
feature();
// @ts-expect-error - feature() requires a string argument
feature(123);
// @ts-expect-error - feature() requires a string argument
feature(true);
// @ts-expect-error - feature() requires a string argument
feature(null);
// @ts-expect-error - feature() requires a string argument
feature(undefined);
// @ts-expect-error - feature() doesn't accept multiple arguments
feature("A", "B");
// @ts-expect-error - feature() doesn't accept objects
feature({ flag: "DEBUG" });
// @ts-expect-error - feature() doesn't accept arrays
feature(["DEBUG"]);
@@ -0,0 +1,24 @@
import c2 from "console";
import c1 from "node:console";
c1.log();
c2.log();
async () => {
// tslint:disable-next-line:await-promise
for await (const line of c1) {
console.log("Received:", line);
}
// tslint:disable-next-line:await-promise
for await (const line of c2) {
console.log("Received:", line);
}
// tslint:disable-next-line:await-promise
for await (const line of console) {
console.log("Received:", line);
}
return null;
};
@@ -0,0 +1,84 @@
import { expectType } from "./utilities";
// -- Bun.cron() --
// 5-field expressions
Bun.cron("./worker.ts", "* * * * *", "all-stars");
Bun.cron("./worker.ts", "30 2 * * 1", "weekly-report");
Bun.cron("./worker.ts", "0 0 1 1 *", "new-year");
Bun.cron("./worker.ts", "*/15 * * * *", "every-15-min");
Bun.cron("./worker.ts", "0 9 * * 1-5", "weekday-morning");
Bun.cron("./worker.ts", "0 0 1-15/2 1-3 *", "biweekly-q1");
Bun.cron("./worker.ts", "0,15,30,45 * * * *", "quarter-hours");
Bun.cron("./worker.ts", "30 2 * * MON", "weekly-named");
Bun.cron("./worker.ts", "0 0 * * MON-FRI", "weekday-range");
Bun.cron("./worker.ts", "0 0 * JAN-MAR *", "month-range");
// All nicknames
Bun.cron("./worker.ts", "@yearly", "yearly");
Bun.cron("./worker.ts", "@annually", "annually");
Bun.cron("./worker.ts", "@monthly", "monthly");
Bun.cron("./worker.ts", "@weekly", "weekly");
Bun.cron("./worker.ts", "@daily", "daily");
Bun.cron("./worker.ts", "@midnight", "midnight");
Bun.cron("./worker.ts", "@hourly", "hourly");
// -- Bun.cron.parse() --
expectType(Bun.cron.parse("* * * * *")).is<Date | null>();
expectType(Bun.cron.parse("@daily")).is<Date | null>();
expectType(Bun.cron.parse("30 9 * * MON-FRI")).is<Date | null>();
expectType(Bun.cron.parse("@hourly", new Date())).is<Date | null>();
expectType(Bun.cron.parse("@hourly", Date.now())).is<Date | null>();
expectType(Bun.cron.parse("0 0 1 1 *", Date.UTC(2025, 0, 1))).is<Date | null>();
// -- Bun.cron.remove() --
expectType(Bun.cron.remove("weekly-report")).is<Promise<void>>();
// -- Return type --
expectType(Bun.cron("./worker.ts", "@daily", "daily")).is<Promise<void>>();
// -- In-process callback overload --
expectType(Bun.cron("* * * * *", () => {})).is<Bun.CronJob>();
expectType(Bun.cron("@hourly", async () => {})).is<Bun.CronJob>();
expectType(Bun.cron("*/30 * * * *", () => fetch("http://x"))).is<Bun.CronJob>();
Bun.cron("* * * * *", function () {
this.stop();
});
using job = Bun.cron("0 * * * *", () => {});
expectType(job.cron).is<string>();
expectType(job.stop()).is<Bun.CronJob>();
expectType(job.ref()).is<Bun.CronJob>();
expectType(job.unref()).is<Bun.CronJob>();
expectType(job[Symbol.dispose]()).is<void>();
// -- @ts-expect-error cases --
// @ts-expect-error - missing schedule and title
Bun.cron("./worker.ts");
// -- Cron type is accessible --
declare const schedule: Bun.CronWithAutocomplete;
// -- CronController type is accessible --
declare const controller: Bun.CronController;
expectType(controller.type).is<"scheduled">();
expectType(controller.cron).is<string>();
expectType(controller.scheduledTime).is<number>();
// -- { tz } option --
expectType(Bun.cron.parse("@hourly", Date.now(), { tz: "UTC" })).is<Date | null>();
expectType(Bun.cron.parse("0 9 * * *", new Date(), { tz: "America/New_York" })).is<Date | null>();
expectType(Bun.cron("* * * * *", () => {}, { tz: "UTC" })).is<Bun.CronJob>();
expectType(Bun.cron("0 9 * * *", async () => {}, { tz: "America/New_York" })).is<Bun.CronJob>();
// -- CronOptions type is accessible --
declare const opts: Bun.CronOptions;
expectType(opts.tz).is<string | undefined>();
@@ -0,0 +1,41 @@
import { expectType } from "./utilities";
crypto.getRandomValues(new Uint8Array(1));
// TODO(@alii): Failing with @types/[email protected]
// crypto.subtle.deriveKey(
// "HMAC",
// await crypto.subtle.importKey("raw", new TextEncoder().encode("secret"), "HMAC", false, ["deriveKey"]),
// { name: "HMAC", hash: "SHA-256" },
// false,
// ["sign", "verify"],
// );
await crypto.subtle.generateKey("HMAC", false, ["sign", "verify"]);
expectType<CryptoKeyPair>(
await crypto.subtle.generateKey({ namedCurve: "Ed25519" } as import("node:crypto").webcrypto.EcKeyGenParams, false, [
"sign",
"verify",
]),
);
declare const key: CryptoKey;
crypto.subtle.digest("SHA-256", new TextEncoder().encode("secret"));
crypto.subtle.exportKey("jwk", key);
crypto.subtle.importKey("raw", new TextEncoder().encode("secret"), "HMAC", false, ["sign", "verify"]);
crypto.subtle.encrypt("AES-CBC", key, new TextEncoder().encode("secret"));
crypto.subtle.decrypt("AES-CBC", key, new TextEncoder().encode("secret"));
expectType(crypto.getRandomValues(new Uint8Array(1))).is<Uint8Array<ArrayBuffer>>();
expectType(crypto.subtle.digest("SHA-256", new TextEncoder().encode("secret"))).is<Promise<ArrayBuffer>>();
expectType(crypto.subtle.importKey("raw", new TextEncoder().encode("secret"), "HMAC", false, ["sign", "verify"])).is<
Promise<CryptoKey>
>();
expectType(crypto.subtle.encrypt("AES-CBC", key, new TextEncoder().encode("secret"))).is<Promise<ArrayBuffer>>();
expectType(crypto.subtle.decrypt("AES-CBC", key, new TextEncoder().encode("secret"))).is<Promise<ArrayBuffer>>();
expectType(crypto.randomUUID()).is<`${string}-${string}-${string}-${string}-${string}`>();
expectType(
crypto.timingSafeEqual(new TextEncoder().encode("secret"), new TextEncoder().encode("secret")),
).is<boolean>();
@@ -0,0 +1,12 @@
import diagnostics_channel from "diagnostics_channel";
// Create a channel object
const channel = diagnostics_channel.channel("my-channel");
// Subscribe to the channel
channel.subscribe((message, name) => {
console.log("Received message:", message);
});
// Publish a message to the channel
channel.publish({ some: "data" });
+19
View File
@@ -0,0 +1,19 @@
import { dns as bun_dns } from "bun";
import * as dns from "node:dns";
import { expectType } from "./utilities";
dns.resolve("asdf", "A", () => {});
dns.reverse("asdf", () => {});
dns.getServers();
expectType(Bun.dns.getCacheStats()).is<{
cacheHitsCompleted: number;
cacheHitsInflight: number;
cacheMisses: number;
size: number;
errors: number;
totalCount: number;
}>();
expectType(Bun.dns.V4MAPPED).is<number>();
expectType(bun_dns.prefetch("bun.sh")).is<void>();
+25
View File
@@ -0,0 +1,25 @@
import { INSPECT_MAX_BYTES } from "buffer";
INSPECT_MAX_BYTES;
{
new Blob([]);
}
{
new MessagePort();
}
{
new MessageChannel();
}
{
new BroadcastChannel("zxgdfg");
}
{
new Response("asdf");
}
{
Response.json({ asdf: "asdf" }).ok;
const r = Response.json({ hello: "world" });
r.body;
}
+59
View File
@@ -0,0 +1,59 @@
import { expectType } from "./utilities";
import { env as bun_env } from "bun";
import { env as node_env } from "node:process";
declare module "bun" {
interface Env {
FOO: "FOO";
}
}
expectType(Bun.env.FOO).is<"FOO">();
expectType(process.env.FOO).is<"FOO">();
expectType(import.meta.env.FOO).is<"FOO">();
expectType(bun_env.FOO).is<"FOO">();
expectType(node_env.FOO).is<"FOO">();
declare global {
namespace NodeJS {
interface ProcessEnv {
BAR: "BAR";
}
}
}
expectType(Bun.env.BAR).is<"BAR">();
expectType(process.env.BAR).is<"BAR">();
expectType(import.meta.env.BAR).is<"BAR">();
expectType(node_env.BAR).is<"BAR">();
expectType(bun_env.BAR).is<"BAR">();
declare global {
interface ImportMetaEnv {
BAZ: "BAZ";
}
}
expectType(Bun.env.BAZ).is<"BAZ">();
// expectType(process.env.BAZ).is<"BAZ">(); // ProcessEnv does NOT extend ImportMetaEnv
expectType(import.meta.env.BAZ).is<"BAZ">();
// expectType(node_env.BAZ).is<"BAZ">(); // ProcessEnv does NOT extend ImportMetaEnv
expectType(bun_env.BAZ).is<"BAZ">();
expectType(Bun.env.OTHER).is<string | undefined>();
expectType(process.env.OTHER).is<string | undefined>();
expectType(import.meta.env.OTHER).is<string | undefined>();
expectType(node_env.OTHER).is<string | undefined>();
expectType(bun_env.OTHER).is<string | undefined>();
function isAllSame<T>(a: T, b: T, c: T, d: T, e: T) {
return a === b && b === c && c === d && d === e;
}
//prettier-ignore
{
isAllSame <"FOO"> (process.env.FOO, Bun.env.FOO, import.meta.env.FOO, node_env.FOO, bun_env.FOO);
isAllSame <"BAR"> (process.env.BAR, Bun.env.BAR, import.meta.env.BAR, node_env.BAR, bun_env.BAR);
isAllSame <"BAZ"> ( "BAZ", Bun.env.BAZ, import.meta.env.BAZ, "BAZ", bun_env.BAZ); // ProcessEnv does NOT extend ImportMetaEnv
isAllSame <string | undefined> (process.env.OTHER, Bun.env.OTHER, import.meta.env.OTHER, node_env.OTHER, bun_env.OTHER);
}
@@ -0,0 +1,20 @@
import { EventEmitter } from "events";
import { expectType } from "./utilities";
// eslint-disable-next-line @definitelytyped/no-single-element-tuple-type
// EventEmitter<
// const e1 = new EventEmitter<{ a: [string] }>();
// e1.on("a", (arg) => {
// expectType<string>(arg);
// });
// // @ts-expect-error
// e1.on("qwer", (_) => {});
const e2 = new EventEmitter();
e2.on("qwer", (_: any) => {
_;
});
e2.on("asdf", arg => {
expectType<any>(arg);
});
+334
View File
@@ -0,0 +1,334 @@
// Valid body types
fetch("https://example.com", { body: "string body" });
fetch("https://example.com", { body: JSON.stringify({ key: "value" }) });
fetch("https://example.com", { body: new Blob(["blob content"]) });
fetch("https://example.com", { body: new File(["file content"], "file.txt") });
fetch("https://example.com", { body: new ArrayBuffer(8) });
fetch("https://example.com", { body: new Uint8Array([1, 2, 3, 4]) });
fetch("https://example.com", { body: new Int32Array([1, 2, 3, 4]) });
fetch("https://example.com", { body: new DataView(new ArrayBuffer(8)) });
fetch("https://example.com", { body: new URLSearchParams({ key: "value" }) });
fetch("https://example.com", { body: new FormData() });
fetch("https://example.com", { body: new ReadableStream() });
fetch("https://example.com", { body: Buffer.from("buffer content") });
fetch("https://example.com", { body: Bun.file("path") });
fetch("https://example.com", { body: Bun.file("hey").stream() });
fetch("https://example.com", { body: new Response("bun").body });
fetch("https://example.com", { body: Bun.s3.file("hey") });
fetch("https://example.com", { body: Bun.s3.file("hey").stream() });
fetch("https://example.com", { body: Bun.s3.file("hey").readable });
async function* asyncGenerator() {
yield "chunk1";
yield "chunk2";
}
fetch("https://example.com", { body: asyncGenerator() });
const asyncIterable = {
async *[Symbol.asyncIterator]() {
yield "data1";
yield "data2";
},
};
fetch("https://example.com", { body: asyncIterable });
fetch("https://example.com").then(res => {
fetch("https://example.com", { body: res.body });
});
const req = new Request("https://example.com", { body: "request body" });
fetch("https://example.com", { body: req.body });
fetch("https://example.com", { body: null });
fetch("https://example.com", { body: undefined });
fetch("https://example.com", {}); // No body
{
function* syncGenerator() {
yield new Uint8Array([1, 2, 3]);
yield new Uint8Array([4, 5, 6]);
}
// @ts-expect-error Unsupported
fetch("https://example.com", { body: syncGenerator() });
}
{
const iterable = {
*[Symbol.iterator]() {
yield new Uint8Array([7, 8, 9]);
},
};
// @ts-expect-error normal iterators are not supported
fetch("https://example.com", { body: iterable });
}
{
// @ts-expect-error
fetch("https://example.com", { body: 123 });
}
{
// @ts-expect-error
fetch("https://example.com", { body: true });
}
{
// @ts-expect-error
fetch("https://example.com", { body: false });
}
{
// @ts-expect-error
fetch("https://example.com", { body: { plain: "object" } });
}
{
// @ts-expect-error
fetch("https://example.com", { body: ["array", "of", "strings"] });
}
{
// @ts-expect-error
fetch("https://example.com", { body: new Date() });
}
{
// @ts-expect-error
fetch("https://example.com", { body: /regex/ });
}
{
// @ts-expect-error
fetch("https://example.com", { body: Symbol("symbol") });
}
{
// @ts-expect-error
fetch("https://example.com", { body: BigInt(123) });
}
{
// @ts-expect-error
fetch("https://example.com", { body: new Map() });
}
{
// @ts-expect-error
fetch("https://example.com", { body: new Set() });
}
{
// @ts-expect-error
fetch("https://example.com", { body: new WeakMap() });
}
{
// @ts-expect-error
fetch("https://example.com", { body: new WeakSet() });
}
{
// @ts-expect-error
fetch("https://example.com", { body: Promise.resolve("promise") });
}
{
// @ts-expect-error
fetch("https://example.com", { body: () => "function" });
}
{
// @ts-expect-error
fetch("https://example.com", { body: class MyClass {} });
}
{
// @ts-expect-error
fetch("https://example.com", { body: new Error("error") });
}
{
fetch("https://example.com", { method: "GET", body: "should not have body but types should still allow it" });
fetch("https://example.com", { method: "HEAD", body: "should not have body but types should still allow it" });
}
{
const multipartForm = new FormData();
multipartForm.append("field1", "value1");
multipartForm.append("file", new File(["content"], "test.txt"));
fetch("https://example.com", { body: multipartForm });
}
{
const searchParams = new URLSearchParams();
searchParams.append("key1", "value1");
searchParams.append("key2", "value2");
fetch("https://example.com", { body: searchParams });
}
{
fetch("https://example.com", { body: new SharedArrayBuffer(16) });
}
{
fetch("https://example.com", { body: new Float32Array([1.1, 2.2, 3.3]) });
fetch("https://example.com", { body: new Float64Array([1.1, 2.2, 3.3]) });
fetch("https://example.com", { body: new Int8Array([-128, 0, 127]) });
fetch("https://example.com", { body: new Uint16Array([0, 32768, 65535]) });
fetch("https://example.com", { body: new BigInt64Array([BigInt(1), BigInt(2)]) });
fetch("https://example.com", { body: new BigUint64Array([BigInt(1), BigInt(2)]) });
}
{
const textStream = new ReadableStream<string>({
start(controller) {
controller.enqueue("chunk1");
controller.enqueue("chunk2");
controller.close();
},
});
fetch("https://example.com", { body: textStream });
}
{
const byteStream = new ReadableStream<Uint8Array<ArrayBuffer>>({
start(controller) {
controller.enqueue(new Uint8Array([1, 2, 3]));
controller.enqueue(new Uint8Array([4, 5, 6]));
controller.close();
},
});
fetch("https://example.com", { body: byteStream });
}
{
async function notGenerator() {
return "not a generator";
}
// @ts-expect-error - Invalid async without generator
fetch("https://example.com", { body: notGenerator() });
}
{
const invalidIterable = {
notAnIterator() {
return "invalid";
},
};
// @ts-expect-error - Invalid object without proper iterator
fetch("https://example.com", { body: invalidIterable });
}
if (typeof process !== "undefined") {
// @ts-expect-error - Node.js specific invalid types
fetch("https://example.com", { body: process });
}
{
// @ts-expect-error - Invalid number array (not typed)
fetch("https://example.com", { body: [1, 2, 3, 4] });
}
{
// @ts-expect-error - Invalid nested structure
fetch("https://example.com", { body: { nested: { object: { structure: "invalid" } } } });
}
{
// @ts-expect-error - NaN
fetch("https://example.com", { body: NaN });
}
{
// @ts-expect-error - Infinity
fetch("https://example.com", { body: Infinity });
}
{
// @ts-expect-error - -Infinity
fetch("https://example.com", { body: -Infinity });
}
// Proxy option types
{
// String proxy URL is valid
fetch("https://example.com", { proxy: "http://proxy.example.com:8080" });
fetch("https://example.com", { proxy: "https://user:[email protected]:8080" });
}
{
// Object proxy with url is valid
fetch("https://example.com", {
proxy: {
url: "http://proxy.example.com:8080",
},
});
}
{
// Object proxy with url as a URL object is valid
fetch("https://example.com", {
proxy: {
url: new URL("http://proxy.example.com:8080"),
},
});
}
{
// Object proxy with url and headers (plain object) is valid
fetch("https://example.com", {
proxy: {
url: "http://proxy.example.com:8080",
headers: {
"Proxy-Authorization": "Bearer token",
"X-Custom-Header": "value",
},
},
});
}
{
// Object proxy with url and headers (Headers instance) is valid
fetch("https://example.com", {
proxy: {
url: "http://proxy.example.com:8080",
headers: new Headers({ "Proxy-Authorization": "Bearer token" }),
},
});
}
{
// Object proxy with url and headers (array of tuples) is valid
fetch("https://example.com", {
proxy: {
url: "http://proxy.example.com:8080",
headers: [
["Proxy-Authorization", "Bearer token"],
["X-Custom", "value"],
],
},
});
}
{
// @ts-expect-error - Proxy object without url is invalid
fetch("https://example.com", { proxy: { headers: { "X-Custom": "value" } } });
}
{
// @ts-expect-error - Proxy url must be string, not number
fetch("https://example.com", { proxy: { url: 8080 } });
}
{
// @ts-expect-error - Proxy must be string or object, not number
fetch("https://example.com", { proxy: 8080 });
}
{
// @ts-expect-error - Proxy must be string or object, not boolean
fetch("https://example.com", { proxy: true });
}
{
// @ts-expect-error - Proxy must be string or object, not array
fetch("https://example.com", { proxy: ["http://proxy.example.com"] });
}
+183
View File
@@ -0,0 +1,183 @@
import { dlopen, FFIType, JSCallback, read, suffix, type CString, type Pointer } from "bun:ffi";
import * as tsd from "./utilities";
// `suffix` is either "dylib", "so", or "dll" depending on the platform
// you don't have to use "suffix", it's just there for convenience
const path = `libsqlite3.${suffix}`;
const lib = dlopen(
path, // a library name or file path
{
sqlite3_libversion: {
// no arguments, returns a string
args: [],
returns: FFIType.cstring,
},
add: {
args: [FFIType.i32, FFIType.i32],
returns: FFIType.i32,
},
ptr_type: {
args: [FFIType.pointer],
returns: FFIType.pointer,
},
fn_type: {
args: [FFIType.function],
returns: FFIType.function,
},
allArgs: {
args: [
FFIType.char, // string
FFIType.int8_t,
FFIType.i8,
FFIType.uint8_t,
FFIType.u8,
FFIType.int16_t,
FFIType.i16,
FFIType.uint16_t,
FFIType.u16,
FFIType.int32_t,
FFIType.i32,
FFIType.int,
FFIType.uint32_t,
FFIType.u32,
FFIType.int64_t,
FFIType.i64,
FFIType.uint64_t,
FFIType.u64,
FFIType.double,
FFIType.f64,
FFIType.float,
FFIType.f32,
FFIType.bool,
FFIType.ptr,
FFIType.pointer,
FFIType.void,
FFIType.cstring,
FFIType.i64_fast,
FFIType.u64_fast,
],
returns: FFIType.void,
},
},
);
declare const ptr: Pointer;
tsd.expectType<string | null>(lib.symbols.sqlite3_libversion());
tsd.expectType<number>(lib.symbols.add(1, 2));
tsd.expectType<Pointer | bigint | null>(lib.symbols.ptr_type(ptr));
tsd.expectType<Pointer | bigint | null>(lib.symbols.fn_type(new JSCallback(() => {}, {})));
function _arg(
...params: [
number,
number,
number,
number,
number,
number,
number,
number,
number,
number,
number,
number,
number,
number,
number,
number,
number,
number,
number,
number,
number,
number,
boolean,
Pointer,
Pointer,
// tslint:disable-next-line: void-return
// eslint-disable-next-line @typescript-eslint/no-invalid-void-type
void,
CString,
number | bigint,
number | bigint,
]
) {
console.log("asdf");
}
_arg;
type libParams = Parameters<(typeof lib)["symbols"]["allArgs"]>;
tsd.expectTypeEquals<libParams[0], number>(true);
tsd.expectTypeEquals<libParams[1], number>(true);
tsd.expectTypeEquals<libParams[2], number>(true);
tsd.expectTypeEquals<libParams[3], number>(true);
tsd.expectTypeEquals<libParams[4], number>(true);
tsd.expectTypeEquals<libParams[5], number>(true);
tsd.expectTypeEquals<libParams[6], number>(true);
tsd.expectTypeEquals<libParams[7], number>(true);
tsd.expectTypeEquals<libParams[8], number>(true);
tsd.expectTypeEquals<libParams[9], number>(true);
tsd.expectTypeEquals<libParams[10], number>(true);
tsd.expectTypeEquals<libParams[11], number>(true);
tsd.expectTypeEquals<libParams[12], number>(true);
tsd.expectTypeEquals<libParams[13], number>(true);
tsd.expectTypeEquals<libParams[14], number>(true);
tsd.expectTypeEquals<libParams[15], number>(true);
tsd.expectTypeEquals<libParams[16], number>(true);
tsd.expectTypeEquals<libParams[17], number>(true);
tsd.expectTypeEquals<libParams[18], number>(true);
tsd.expectTypeEquals<libParams[19], number>(true);
tsd.expectTypeEquals<libParams[20], number>(true);
tsd.expectTypeEquals<libParams[21], number>(true);
tsd.expectTypeEquals<libParams[22], boolean>(true);
tsd.expectTypeEquals<libParams[23], Pointer>(true);
tsd.expectTypeEquals<libParams[24], Pointer>(true);
tsd.expectTypeEquals<libParams[25], undefined>(true);
tsd.expectTypeEquals<libParams[26], CString>(true);
tsd.expectTypeEquals<libParams[27], number | bigint>(true);
tsd.expectTypeEquals<libParams[28], number | bigint>(true);
// tslint:disable-next-line:no-object-literal-type-assertion
const as_const_test = {
sqlite3_libversion: {
args: [],
returns: FFIType.cstring,
},
multi_args: {
args: [FFIType.i32, FFIType.f32],
returns: FFIType.void,
},
no_returns: {
args: [FFIType.i32],
},
no_args: {
returns: FFIType.i32,
},
} as const;
const lib2 = dlopen(path, as_const_test);
tsd.expectType<string | null>(lib2.symbols.sqlite3_libversion());
// tslint:disable-next-line:no-void-expression
// eslint-disable-next-line @typescript-eslint/no-invalid-void-type
tsd.expectType<void>(lib2.symbols.multi_args(1, 2));
tsd.expectTypeEquals<ReturnType<(typeof lib2)["symbols"]["no_returns"]>, undefined>(true);
tsd.expectTypeEquals<Parameters<(typeof lib2)["symbols"]["no_args"]>, []>(true);
tsd.expectType<number>(read.u8(ptr));
tsd.expectType<number>(read.u8(ptr, 0));
tsd.expectType<number>(read.i8(ptr, 0));
tsd.expectType<number>(read.u16(ptr, 0));
tsd.expectType<number>(read.i16(ptr, 0));
tsd.expectType<number>(read.u32(ptr, 0));
tsd.expectType<number>(read.i32(ptr, 0));
tsd.expectType<bigint>(read.u64(ptr, 0));
tsd.expectType<bigint>(read.i64(ptr, 0));
tsd.expectType<number>(read.f32(ptr, 0));
tsd.expectType<number>(read.f64(ptr, 0));
tsd.expectType<number>(read.ptr(ptr, 0));
tsd.expectType<number>(read.intptr(ptr, 0));
@@ -0,0 +1 @@
{ "bun": "is cool", "fact": true }
+22
View File
@@ -0,0 +1,22 @@
import { constants, readdir, watch } from "node:fs";
constants.O_APPEND;
import * as fs from "fs";
import { exists } from "fs/promises";
import * as tsd from "./utilities";
tsd.expectType<Promise<boolean>>(exists("/etc/passwd"));
tsd.expectType<Promise<boolean>>(fs.promises.exists("/etc/passwd"));
// file path
watch(".", (eventType, filename) => {
console.log(`event type = ${eventType}`);
if (filename) {
console.log(`filename = ${filename}`);
}
});
await Bun.file("sdf").exists();
readdir(".", { recursive: true }, (err, files) => {});
@@ -0,0 +1,13 @@
import { FileSystemRouter } from "bun";
import { expectType } from "./utilities";
const router = new FileSystemRouter({
dir: "/pages",
style: "nextjs",
});
const match = router.match("/");
expectType<string>(match?.name!);
expectType<string>(match?.pathname!);
expectType<Record<string, string>>(match?.query!);
expectType<Record<string, string>>(match?.params!);
@@ -0,0 +1,342 @@
import * as fs from "fs";
import * as fsPromises from "fs/promises";
import { expectAssignable, expectType } from "./utilities";
// FileBlob
expectType<ReadableStream<Uint8Array>>(Bun.file("index.test-d.ts").stream());
expectType<Promise<ArrayBuffer>>(Bun.file("index.test-d.ts").arrayBuffer());
expectType<Promise<Uint8Array>>(Bun.file("index.test-d.ts").bytes());
expectType<Promise<string>>(Bun.file("index.test-d.ts").text());
expectType<number>(Bun.file("index.test-d.ts").size);
expectType<string>(Bun.file("index.test-d.ts").type);
// Hash
expectType<string>(new Bun.MD4().update("test").digest("hex"));
expectType<string>(new Bun.MD5().update("test").digest("hex"));
expectType<string>(new Bun.SHA1().update("test").digest("hex"));
expectType<string>(new Bun.SHA224().update("test").digest("hex"));
expectType<string>(new Bun.SHA256().update("test").digest("hex"));
expectType<string>(new Bun.SHA384().update("test").digest("hex"));
expectType<string>(new Bun.SHA512().update("test").digest("hex"));
expectType<string>(new Bun.SHA512_256().update("test").digest("hex"));
// Zlib Functions
expectType<Uint8Array>(Bun.deflateSync(new Uint8Array(128)));
expectType<Uint8Array>(Bun.gzipSync(new Uint8Array(128)));
expectType<Uint8Array>(
Bun.deflateSync(new Uint8Array(128), {
level: -1,
memLevel: 8,
strategy: 0,
windowBits: 15,
}),
);
expectType<Uint8Array>(Bun.gzipSync(new Uint8Array(128), { level: 9, memLevel: 6, windowBits: 27 }));
expectType<Uint8Array>(Bun.inflateSync(new Uint8Array(64))); // Pretend this is DEFLATE compressed data
expectType<Uint8Array>(Bun.gunzipSync(new Uint8Array(64))); // Pretend this is GZIP compressed data
expectAssignable<Bun.ZlibCompressionOptions>({ windowBits: -11 });
// Other
expectType<Promise<number>>(Bun.write("test.json", "lol"));
expectType<Promise<number>>(Bun.write("test.json", new ArrayBuffer(32)));
expectType<URL>(Bun.pathToFileURL("/foo/bar.txt"));
expectType<string>(Bun.fileURLToPath(new URL("file:///foo/bar.txt")));
// Testing ../fs.d.ts
expectType<string>(fs.readFileSync("./index.d.ts", { encoding: "utf-8" }).toString());
expectType<boolean>(fs.existsSync("./index.d.ts"));
expectType<void>(fs.accessSync("./index.d.ts"));
expectType<void>(fs.appendFileSync("./index.d.ts", "test"));
expectType<void>(fs.mkdirSync("./index.d.ts"));
// Testing ^promises.d.ts
expectType<string>((await fsPromises.readFile("./index.d.ts", { encoding: "utf-8" })).toString());
expectType<Promise<void>>(fsPromises.access("./index.d.ts"));
expectType<Promise<void>>(fsPromises.appendFile("./index.d.ts", "test"));
expectType<Promise<void>>(fsPromises.mkdir("./index.d.ts"));
Bun.env;
Bun.version;
setImmediate;
clearImmediate;
setInterval;
clearInterval;
setTimeout;
clearTimeout;
const arg = new AbortSignal();
arg;
const e = new CustomEvent("asdf");
console.log(e);
exports;
module.exports;
global.AbortController;
global.Bun;
const er = new DOMException();
er.name;
er.HIERARCHY_REQUEST_ERR;
new Request(new Request("https://example.com"), {});
new Request("", { method: "POST" });
Bun.sleepSync(1); // sleep for 1 ms (not recommended)
await Bun.sleep(1); // sleep for 1 ms (recommended)
Blob;
WebSocket;
Request;
Response;
Headers;
FormData;
URL;
URLSearchParams;
ReadableStream;
WritableStream;
TransformStream;
ByteLengthQueuingStrategy;
CountQueuingStrategy;
TextEncoder;
TextDecoder;
ReadableStreamDefaultReader;
ReadableStreamBYOBReader;
ReadableStreamDefaultController;
ReadableByteStreamController;
WritableStreamDefaultWriter;
declare function stuff(arg: Blob): any;
declare function stuff(arg: WebSocket): any;
declare function stuff(arg: Request): any;
declare function stuff(arg: Response): any;
declare function stuff(arg: Headers): any;
declare function stuff(arg: FormData): any;
declare function stuff(arg: URL): any;
declare function stuff(arg: URLSearchParams): any;
declare function stuff(arg: ReadableStream): any;
declare function stuff(arg: WritableStream): any;
declare function stuff(arg: TransformStream): any;
declare function stuff(arg: ByteLengthQueuingStrategy): any;
declare function stuff(arg: CountQueuingStrategy): any;
declare function stuff(arg: TextEncoder): any;
declare function stuff(arg: TextDecoder): any;
declare function stuff(arg: ReadableStreamDefaultReader): any;
declare function stuff(arg: ReadableStreamDefaultController): any;
declare function stuff(arg: WritableStreamDefaultWriter): any;
stuff("asdf" as any as Blob);
new ReadableStream();
new WritableStream();
new Worker("asdfasdf");
new Worker("asdfasdf").onmessage;
new File([{} as Blob], "asdf");
new Crypto();
new ShadowRealm();
new ErrorEvent("asdf");
new CloseEvent("asdf");
new MessageEvent("asdf");
new CustomEvent("asdf");
// new Loader();
const readableStream = new ReadableStream();
const writableStream = new WritableStream();
{
const a = new ByteLengthQueuingStrategy({ highWaterMark: 0 });
a.highWaterMark;
}
{
const a = new ReadableStreamDefaultController();
a.close();
}
{
const a = new ReadableStreamDefaultReader(readableStream);
await a.cancel();
}
{
const a = new WritableStreamDefaultController();
a.error();
}
{
const a = new WritableStreamDefaultWriter(writableStream);
await a.close();
}
{
const a = new TransformStream();
a.readable;
}
{
const a = new TransformStreamDefaultController();
a.enqueue("asdf");
}
{
const a = new CountQueuingStrategy({ highWaterMark: 0 });
a.highWaterMark;
}
{
const a = new DOMException();
a.DATA_CLONE_ERR;
}
{
const a = new SubtleCrypto();
await a.decrypt("asdf", new CryptoKey(), new Uint8Array());
}
{
const a = new CryptoKey();
a.algorithm;
}
{
const a = new BuildError();
a.level;
}
{
const a = new ResolveError();
a.level;
}
{
const a = new AbortController();
a;
}
{
const a = new AbortSignal();
a.aborted;
}
{
const a = new Request("asdf");
await a.json();
a.cache;
}
{
const a = new Response();
await a.text();
a.ok;
}
{
const a = new FormData();
a.delete("asdf");
a.get("asdf");
a.append("asdf", "asdf");
a.set("asdf", "asdf");
a.forEach((value, key) => {
console.log(value, key);
});
a.entries();
a.get("asdf");
a.getAll("asdf");
a.has("asdf");
a.keys();
a.values();
a.toString();
}
{
const a = new Headers();
a.append("asdf", "asdf");
}
{
const a = new EventTarget();
a.dispatchEvent(new Event("asdf"));
}
{
const a = new Event("asdf");
a.bubbles;
a.composedPath()[0];
}
{
const a = new Blob();
a.size;
}
{
const a = new File(["asdf"], "stuff.txt ");
a.name;
}
{
performance.now();
}
{
const a = new URL("asdf");
a.host;
a.href;
}
{
new URLSearchParams();
new URLSearchParams("");
new URLSearchParams([]);
}
{
const a = new TextDecoder();
a.decode(new Uint8Array());
}
{
const a = new TextEncoder();
a.encode("asdf");
}
{
const a = new BroadcastChannel("stuff");
a.close();
}
{
const a = new MessageChannel();
a.port1;
new MessageChannel().port1.addEventListener("message", console.log);
}
{
const a = new MessagePort();
a.close();
}
{
var a!: RequestInit;
a.mode;
a.credentials;
}
{
var b!: ResponseInit;
b.status;
}
{
const ws = new WebSocket("ws://www.host.com/path");
ws.send("asdf");
new WebSocket("url", {
headers: { "Authorization": "my key" },
});
}
atob("asf");
btoa("asdf");
setInterval(() => {}, 1000);
setTimeout(() => {}, 1000);
clearInterval(1);
clearTimeout(1);
setImmediate(() => {});
clearImmediate(1);
const err = new Error("test");
err.cause = "asdf";
err.cause = new Error("asdf");
err.cause = new RangeError("asdf");
err.cause = new TypeError("asdf");
err.cause = new URIError("asdf");
expectType<unknown>(err.cause);
expectType<typeof Error>().is<ErrorConstructor>();
new Error("asdf", {
cause: "asdf",
});
new Error("asdf", {
cause: new Error("asdf"),
});
// @ts-expect-error this interface is defined top level in globals.d.ts so we
// are making sure that .d.ts is a module and that anything top level doesn't
// leak to userland
expectType<BunConsumerConvenienceMethods>();
@@ -0,0 +1,7 @@
Bun.hash.wyhash("asdf", 1234n);
// https://github.com/oven-sh/bun/issues/26043
// Bun.hash.crc32 accepts optional seed parameter for incremental CRC32 computation
let crc = 0;
crc = Bun.hash.crc32(new Uint8Array([1, 2, 3]), crc);
crc = Bun.hash.crc32(new Uint8Array([4, 5, 6]), crc);
@@ -0,0 +1,9 @@
const headers = new Headers();
headers.append("Set-Cookie", "a=1");
headers.append("Set-Cookie", "b=1; Secure");
// both of these are no longer in the types
// because Headers is declared with `class` in @types/node
// and I can't find a way to add them to the prototype
// console.log(headers.getAll("Set-Cookie")); // ["a=1", "b=1; Secure"]
// console.log(headers.toJSON()); // { "set-cookie": "a=1, b=1; Secure" }
@@ -0,0 +1,57 @@
import * as http from "http";
const server = new http.Server({});
server.address;
server.close();
server.eventNames;
server.getMaxListeners();
server.listeners;
server.on;
server.once;
server.prependListener;
server.prependOnceListener;
server.rawListeners;
server.removeAllListeners;
server.removeListener;
server.setMaxListeners;
server;
const agent = new http.Agent({});
http.globalAgent;
http.maxHeaderSize;
console.log(Object.getOwnPropertyNames(agent));
const req = http.request({ host: "localhost", port: 3000, method: "GET" });
req.abort;
req.end();
export {};
// URLSearchParams should be iterable
const sp = new URLSearchParams("q=foo&bar=baz");
for (const q of sp) {
console.log(q);
}
fetch("https://example.com", {
s3: {
accessKeyId: "123",
secretAccessKey: "456",
},
proxy: "cool",
});
const a = new Response(async function* () {
yield new Uint8Array([50, 60, 70]);
yield "hey";
await Bun.sleep(500);
});
const b_generator = async function* () {
await Bun.sleep(500);
yield new Uint8Array([1, 2, 3]);
yield "it works!";
};
const b = new Response(b_generator());
for (const r of await Promise.all([a.text(), b.text()])) console.log(r);
+474
View File
@@ -0,0 +1,474 @@
import fact from "./file.json";
console.log(fact);
import * as test from "bun:test";
test.describe;
test.it;
const options: Bun.TLSOptions = {
keyFile: "",
};
process.assert;
const error = new Error("hello world");
const clone = structuredClone(error);
console.log(clone.message); // "hello world"
new SubtleCrypto();
declare const mySubtleCrypto: SubtleCrypto;
new CryptoKey();
declare const myCryptoKey: CryptoKey;
import * as sqlite from "bun:sqlite";
sqlite.Database;
Bun satisfies typeof import("bun");
expectType(Bun).is<typeof import("bun")>();
expectType<typeof import("bun")>().is<typeof Bun>();
type ConstructorOf<T> = new (...args: any[]) => T;
import * as NodeTLS from "node:tls";
import * as TLS from "tls";
process.revision;
NodeTLS satisfies typeof TLS;
TLS satisfies typeof NodeTLS;
type NodeTLSOverrideTest = NodeTLS.BunConnectionOptions;
type TLSOverrideTest = TLS.BunConnectionOptions;
WebAssembly.Global;
WebAssembly.Memory;
WebAssembly.compile;
WebAssembly.compileStreaming;
WebAssembly.instantiate;
WebAssembly.instantiateStreaming;
WebAssembly.validate;
WebAssembly.Global satisfies ConstructorOf<Bun.WebAssembly.Global>;
WebAssembly.Memory satisfies ConstructorOf<Bun.WebAssembly.Memory>;
type wasmglobalthing = Bun.WebAssembly.Global;
type S3OptionsFromNamespace = Bun.S3Options;
type S3OptionsFromImport = import("bun").S3Options;
type c = import("bun").S3Client;
Bun.s3.file("").name;
const client = new Bun.S3Client({
secretAccessKey: "",
});
new TextEncoder();
client.file("");
Bun.fetch;
// just some APIs
new Request("url");
new Response();
new Headers();
new URL("");
new URLSearchParams([["cool", "stuff"]]);
new File([], "filename", { type: "text/plain" });
new Blob([], { type: "text/plain" });
new ReadableStream();
new WritableStream();
new TransformStream();
new AbortSignal();
new AbortController();
AbortSignal.timeout(200);
AbortSignal.any([new AbortSignal()]);
AbortSignal.abort(200);
new TextDecoder();
new TextEncoder();
fetch("url", {
proxy: "",
});
fetch(new URL("url"), {
proxy: "",
});
Bun.fetch(new URL("url"), {
proxy: "",
});
Bun.S3Client;
Bun.$`hey`;
type b = Bun.$.ShellPromise;
const myShellPromise: Bun.$.ShellPromise = Bun.$`hey`;
const myShellError: Bun.$.ShellError = new Bun.$.ShellError();
expectType(myShellPromise).is<Bun.$.ShellPromise>();
expectType(myShellError).is<Bun.$.ShellError>();
const myShellConstructor: typeof Bun.$.Shell = Bun.$.Shell;
const myShellPromiseConstructor: typeof Bun.$.ShellPromise = Bun.$.ShellPromise;
const myShellErrorConstructor: typeof Bun.$.ShellError = Bun.$.ShellError;
expectType(myShellConstructor).is<typeof Bun.$.Shell>();
expectType(myShellPromiseConstructor).is<typeof Bun.$.ShellPromise>();
expectType(myShellErrorConstructor).is<typeof Bun.$.ShellError>();
const myShellInstance: Bun.$ = new Bun.$.Shell();
await myShellInstance`hey`;
expectType(Bun.$).is<Bun.$>();
const myOtherShell = Bun.$.nothrow();
expectType(myOtherShell).is<Bun.$>();
expectType(myShellInstance).is<typeof Bun.$>();
await Bun.$.nothrow().throws(false).env({ TEST: "cool" }).cwd("/")`exit 0`;
await myShellInstance.nothrow().throws(false).env({ TEST: "cool" }).cwd("/")`exit 0`;
Bun.$;
declare const e: unknown;
if (e instanceof Bun.$.ShellError) {
expectType(e.exitCode).is<number>();
expectType(e.stderr).is<Buffer>();
expectType(e.stdout).is<Buffer>();
}
new Promise(resolve => {
resolve(1);
});
import.meta.hot.on("bun:bun:beforeFullReloadBut also allows anything", () => {
//
});
new Map();
new Set();
new WeakMap();
new WeakSet();
new Map();
new Set();
new WeakMap();
Promise.try(() => {
return 1;
});
Promise.try(() => {
throw new Error("test");
});
Promise.try((message: string) => {
throw new Error(message);
}, "Bun");
declare const myReadableStream: ReadableStream<string>;
for await (const chunk of myReadableStream) {
console.log(chunk);
expectType(chunk).is<string>();
}
for await (const chunk of Bun.stdin.stream()) {
// chunk is Uint8Array
// this converts it to text (assumes ASCII encoding)
const chunkText = Buffer.from(chunk).toString();
console.log(`Chunk: ${chunkText}`);
expectType(chunk).is<Uint8Array<ArrayBuffer>>();
expectType(chunkText).is<string>();
}
const myAsyncGenerator = async function* () {
yield new Uint8Array([1, 2, 3]);
yield new Uint8Array([4, 5, 6]);
};
new Response(myAsyncGenerator());
const statuses = [200, 400, 401, 403, 404, 500, 501, 502, 503, 504];
const r = new Request("", {
body: "",
});
await fetch(r);
await fetch("", {
tls: {
key: Bun.file("key.pem"),
cert: Bun.file("cert.pem"),
ca: [Bun.file("ca.pem")],
rejectUnauthorized: false,
},
});
r.method;
r.body;
r.headers.get("content-type");
new Request("", {});
new Bun.$.ShellError() instanceof Bun.$.ShellError;
await r.json();
await r.text();
declare const headers: Headers;
headers.toJSON();
const req1 = new Request("", {
body: "",
});
for (const header of new Headers()) {
console.log(header);
}
fetch("", {
tls: {
rejectUnauthorized: false,
checkServerIdentity: () => {
return undefined;
},
},
});
req1.body;
req1.json();
req1.formData();
req1.arrayBuffer();
req1.blob();
req1.text();
req1.arrayBuffer();
req1.blob();
req1.headers;
req1.headers.toJSON();
new ReadableStream({});
const body = await fetch(req1);
Bun.fetch satisfies typeof fetch;
Bun.fetch.preconnect satisfies typeof fetch.preconnect;
await body.text();
fetch;
fetch.preconnect(new URL(""));
Bun.serve({
port: 3000,
fetch: () => new Response("ok"),
tls: {
key: Bun.file(""), // do this!
cert: Bun.file(""), // do this!
},
});
import type { BinaryLike } from "node:crypto";
declare function asIs(value: BinaryLike): BinaryLike;
asIs(Buffer.from("Hey", "utf-8"));
new URL("", "");
const myUrl: URL = new URL("");
URL.canParse;
URL.createObjectURL;
URL.revokeObjectURL;
declare const myBodyInit: Bun.BodyInit;
declare const myHeadersInit: Bun.HeadersInit;
await new Blob().text();
await new Blob().json();
await new Blob().arrayBuffer();
await new Blob().bytes();
await new Blob().formData();
await new File(["code"], "name.ts").text();
await new File(["code"], "name.ts").json();
await new File(["code"], "name.ts").arrayBuffer();
await new File(["code"], "name.ts").bytes();
await new File(["code"], "name.ts").formData();
await Bun.file("test").text();
await Bun.file("test").json();
await Bun.file("test").arrayBuffer();
await Bun.file("test").bytes();
await Bun.file("test").formData();
new MessagePort();
new File(["code"], "name.ts");
URL.parse("bun.sh");
URL.parse("bun.sh", "bun.sh");
Error.isError(new Error());
Response.json("");
Response.redirect("bun.sh", 300);
Response.error();
Response.redirect("bun.sh", 302);
Response.redirect("bun.sh", {
headers: {
"x-bun": "is cool",
},
});
Bun.inspect.custom;
Bun.inspect;
fetch.preconnect("bun.sh");
Bun.fetch.preconnect("bun.sh");
new Uint8Array().toBase64();
Bun.fetch("", {
proxy: "",
s3: {
acl: "public-read",
},
});
new HTMLRewriter()
.on("script", {
element(element) {
console.log(element.getAttribute("src"));
},
})
.transform(new Response('<script src="/main.js"></script>'));
Buffer.from("foo").equals(Buffer.from("bar"));
const myHeaders: Headers = new Headers();
myHeaders.append("x-bun", "is cool");
myHeaders.get("x-bun");
myHeaders.has("x-bun");
myHeaders.set("x-bun", "is cool");
myHeaders.delete("x-bun");
myHeaders.getSetCookie();
myHeaders.toJSON();
myHeaders.count;
myHeaders.getAll("set-cookie");
myHeaders.getAll("Set-Cookie");
// @ts-expect-error
myHeaders.getAll("Should fail");
const myRequest: Request = new Request("", {
headers: new Headers(myHeaders),
body: "",
method: "GET",
redirect: "follow",
credentials: "include",
mode: "cors",
referrer: "about:client",
referrerPolicy: "no-referrer",
window: null,
});
const myResponse: Response = new Response("", {
headers: new Headers([]),
status: 200,
statusText: "OK",
});
const myRequestInit: RequestInit = {
body: "",
method: "GET",
};
declare const requestInitKeys: `evaluate-${keyof RequestInit}`;
requestInitKeys satisfies string;
Bun.serve({
fetch(req) {
req.headers;
const headers = req.headers.toJSON();
const body = req.method === "GET" || req.method === "HEAD" ? undefined : req.body;
return new Response(body, {
headers,
status: statuses[Math.floor(Math.random() * statuses.length)] ?? 200,
});
},
});
import.meta.hot.accept();
import.meta.hot.data;
fetch("", {
tls: {
rejectUnauthorized: false,
},
});
new AbortController();
const myAbortController: AbortController = new AbortController();
new AbortSignal();
const myAbortSignal: AbortSignal = new AbortSignal();
import { serve } from "bun";
new Worker("", {
type: "module",
preload: ["preload.ts"],
});
serve({
fetch(req) {
const headers = req.headers.toJSON();
const body = req.method === "GET" || req.method === "HEAD" ? undefined : req.body;
return new Response(body, {
headers,
status: statuses[Math.floor(Math.random() * statuses.length)] ?? 200,
});
},
});
import { s3 } from "bun";
import { expectType } from "./utilities";
s3.file("");
declare const key: string;
declare const cert: string;
Bun.serve({
fetch: () => new Response("ok"),
tls: {
key,
cert,
},
});
const signal = AbortSignal.timeout(1000);
expectType(signal).is<AbortSignal>();
expectType(signal.aborted).is<boolean>();
expectType(RegExp.escape("foo.bar")).is<string>();
const controller = new AbortController();
expectType(controller.signal).is<AbortSignal>();
expectType(controller.abort()).is<void>();
expectType(controller.abort("reason")).is<void>();
expectType(controller.signal.aborted).is<boolean>();
controller.signal.addEventListener("abort", event => {
expectType(event).is<Event>();
});
controller.signal.removeEventListener("abort", event => {
expectType(event).is<Event>();
});
@@ -0,0 +1,44 @@
// This is (for now) very loose implementation reference, mostly type testing
import { expectType } from "./utilities";
const mySecurityScanner: Bun.Security.Scanner = {
version: "1",
scan: async ({ packages }) => {
const response = await fetch("https://threat-feed.example.com");
if (!response.ok) {
throw new Error("Unable to fetch threat feed");
}
// Would recommend using a schema library or something to validate here. You
// should throw if the parsing fails rather than returning no advisories,
// this code needs to be defensive...
const myThreatFeed = (await response.json()) as Array<{
package: string;
version: string;
url: string;
description: string;
category: "unhealthy" | "spam" | "malware"; // Imagine some other categories...
}>;
return myThreatFeed.flatMap((threat): Bun.Security.Advisory[] => {
const match = packages.some(p => p.name === threat.package && p.version === threat.version);
if (!match) {
return [];
}
return [
{
level: threat.category === "malware" ? "fatal" : "warn",
package: threat.package,
url: threat.url,
description: threat.description,
},
];
});
},
};
expectType(mySecurityScanner).toBeDefined();
+78
View File
@@ -0,0 +1,78 @@
import { deepEquals } from "bun";
import {
callerSourceOrigin,
deserialize,
drainMicrotasks,
edenGC,
fullGC,
gcAndSweep,
heapSize,
heapStats,
memoryUsage,
noFTL,
noOSRExitFuzzing,
numberOfDFGCompiles,
optimizeNextInvocation,
profile,
reoptimizationRetryCount,
serialize,
startSamplingProfiler,
totalCompileTime,
type SamplingProfile,
type SamplingProfileStackFrame,
type SamplingProfileStackTraces,
} from "bun:jsc";
import { expectType } from "./utilities";
const obj = { a: 1, b: 2 };
const buffer = serialize(obj);
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
const clone = deserialize(buffer);
if (deepEquals(obj, clone)) {
console.log("They are equal!");
}
expectType(gcAndSweep()).is<number>();
expectType(fullGC()).is<number>();
expectType(edenGC()).is<number>();
expectType(heapSize()).is<number>();
const stats = heapStats();
expectType(stats.heapSize).is<number>();
expectType(stats.objectTypeCounts).is<Record<string, number>>();
expectType(stats.protectedObjectTypeCounts).is<Record<string, number>>();
expectType(memoryUsage().current).is<number>();
expectType(memoryUsage().pageFaults).is<number>();
expectType(callerSourceOrigin()).is<string | null>();
function add(a: number, b: number) {
return a + b;
}
expectType(noFTL(add)).is<void>();
expectType(noOSRExitFuzzing(add)).is<void>();
expectType(optimizeNextInvocation(add)).is<void>();
expectType(numberOfDFGCompiles(add)).is<number>();
expectType(reoptimizationRetryCount(add)).is<number>();
expectType(totalCompileTime()).is<number>();
expectType(drainMicrotasks()).is<void>();
startSamplingProfiler();
startSamplingProfiler("/tmp/profile");
startSamplingProfiler(undefined, 100);
const syncProfile = profile(add, 100, 1, 2);
expectType(syncProfile).is<SamplingProfile>();
expectType(syncProfile.functions).is<string>();
expectType(syncProfile.bytecodes).is<string>();
expectType(syncProfile.stackTraces).is<SamplingProfileStackTraces>();
expectType(syncProfile.stackTraces.interval).is<number>();
expectType(syncProfile.stackTraces.traces[0]!.frames).is<SamplingProfileStackFrame[]>();
expectType(syncProfile.stackTraces.traces[0]!.frames[0]!.sourceURL).is<string | undefined>();
expectType(syncProfile.stackTraces.sources[0]!.url).is<string | undefined>();
const asyncProfile = profile(async () => {});
expectType(asyncProfile).is<Promise<SamplingProfile>>();
@@ -0,0 +1,31 @@
import { jest, mock } from "bun:test";
import { expectType } from "./utilities";
const mock1 = mock((arg: string) => {
return arg.length;
});
const arg1 = mock1("1");
expectType<number>(arg1);
mock;
type arg2 = jest.Spied<() => string>;
declare var arg2: arg2;
arg2.mock.calls[0];
mock;
// @ts-expect-error
jest.fn<() => Promise<string>>().mockReturnValue("asdf");
// @ts-expect-error
jest.fn<() => string>().mockReturnValue(24);
jest.fn<() => string>().mockReturnValue("24");
jest.fn<() => Promise<string>>().mockResolvedValue("asdf");
// @ts-expect-error
jest.fn<() => string>().mockResolvedValue(24);
// @ts-expect-error
jest.fn<() => string>().mockResolvedValue("24");
jest.fn().mockClear();
jest.fn().mockReset();
jest.fn().mockRejectedValueOnce(new Error());
+11
View File
@@ -0,0 +1,11 @@
import * as net from "node:net";
const socket = net.connect({
port: 80,
host: "localhost",
});
socket.connect({
port: 80,
host: "localhost",
});
@@ -0,0 +1,14 @@
{
"name": "fixture",
"module": "index.ts",
"scripts": {
"check": "tsc --noEmit -p ./tsconfig.json"
},
"type": "module",
"dependencies": {
"typescript": "latest"
},
"resolutions": {
"@types/node": "latest"
}
}
@@ -0,0 +1,7 @@
import { performance as _performance } from "node:perf_hooks";
performance.now();
performance.timeOrigin;
_performance.now();
_performance.timeOrigin;
@@ -0,0 +1,50 @@
process.memoryUsage();
process.cpuUsage().system;
process.cpuUsage().user;
process.on("SIGINT", () => {
console.log("Interrupt from keyboard");
});
process.on("beforeExit", code => {
console.log("Event loop is empty and no work is left to schedule.", code);
});
process.on("exit", code => {
console.log("Exiting with code:", code);
});
process.kill(123, "SIGTERM");
process.getegid!();
process.geteuid!();
process.getgid!();
process.getgroups!();
process.getuid!();
process.once("SIGINT", () => {
console.log("Interrupt from keyboard");
});
// commented methods are not yet implemented
console.log(process.allowedNodeEnvironmentFlags);
// console.log(process.channel);
// console.log(process.connected);
// console.log(process.constrainedMemory);
console.log(process.debugPort);
// console.log(process.disconnect);
// console.log(process.getActiveResourcesInfo);
// console.log(process.setActiveResourcesInfo);
// console.log(process.setuid);
// console.log(process.setgid);
// console.log(process.setegid);
// console.log(process.seteuid);
// console.log(process.setgroups);
// console.log(process.hasUncaughtExceptionCaptureCallback);
// console.log(process.initGroups);
console.log(process.listenerCount("exit"));
console.log(process.memoryUsage());
// console.log(process.report);
// console.log(process.resourceUsage);
// console.log(process.setSourceMapsEnabled());
// console.log(process.send);
process.reallyExit();
process.assert(false, "PleAsE don't Use THIs It IS dEpReCATED");
@@ -0,0 +1,11 @@
import * as readline from "node:readline/promises";
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
terminal: true,
});
await rl.question("What is your age?\n").then(answer => {
console.log("Your age is: " + answer);
});
@@ -0,0 +1,31 @@
import { expectType } from "./utilities";
expectType(Bun.redis.publish("hello", "world")).is<Promise<number>>();
const copy = await Bun.redis.duplicate();
expectType(copy.connected).is<boolean>();
expectType(copy).is<Bun.RedisClient>();
const listener: Bun.RedisClient.StringPubSubListener = (message, channel) => {
expectType(message).is<string>();
expectType(channel).is<string>();
};
Bun.redis.subscribe("hello", listener);
// Buffer subscriptions are not yet implemented
// const bufferListener: Bun.RedisClient.BufferPubSubListener = (message, channel) => {
// expectType(message).is<Uint8Array<ArrayBuffer>>();
// expectType(channel).is<string>();
// };
// Bun.redis.subscribe("hello", bufferListener);
expectType(
copy.subscribe("hello", message => {
expectType(message).is<string>();
}),
).is<Promise<number>>();
await copy.unsubscribe();
await copy.unsubscribe("hello");
expectType(copy.unsubscribe("hello", () => {})).is<Promise<void>>();
+31
View File
@@ -0,0 +1,31 @@
import { s3 } from "bun";
async function doFileOps(file: Bun.S3File) {
console.log(file.bucket);
console.log(file.presign());
console.log(file.presign({ expiresIn: 1, method: "PUT" }));
console.log(file.type);
await file.json();
await file.arrayBuffer();
await file.delete();
await file.formData();
for await (const chunk of file.readable) {
console.log(chunk);
}
}
doFileOps(s3.file("stream.bin"));
doFileOps(
new Bun.S3Client({
accessKeyId: "123",
}).file("stream.bin"),
);
doFileOps(
s3.file("stream.bin", {
type: "application/octet-stream",
}),
);
@@ -0,0 +1,869 @@
// This file is checked in the `bun-types.test.ts` integration test for successful typechecking, but also checked
// on its own to make sure that the types line up with actual implementation of Bun.serve()
import { expect, it } from "bun:test";
import fs from "node:fs";
import os from "node:os";
import { join } from "node:path";
import html from "./html.html";
import { expectType } from "./utilities";
// XXX: importing this from "harness" caused a failure in bun-types.test.ts
function tmpdirSync(pattern: string = "bun.test."): string {
return fs.mkdtempSync(join(fs.realpathSync.native(os.tmpdir()), pattern));
}
export default {
fetch: req => Response.json(req.url),
websocket: {
message(ws) {
expectType(ws.data).is<{ name: string }>();
},
},
routes: {
"/": req => {
expectType(req.params).is<Record<string, string>>();
},
},
} satisfies Bun.Serve.Options<{ name: string }>;
function expectInstanceOf<T>(value: unknown, constructor: new (...args: any[]) => T): asserts value is T {
expect(value).toBeInstanceOf(constructor);
}
function test<T = undefined, R extends string = string>(
name: string,
options: Bun.Serve.Options<T, R>,
{
onConstructorFailure,
overrideExpectBehavior,
skip: skipOptions,
}: {
onConstructorFailure?: (error: Error) => void | Promise<void>;
overrideExpectBehavior?: (server: NoInfer<Bun.Server<T>>) => void | Promise<void>;
skip?: boolean;
} = {},
) {
const skip = skipOptions || ("unix" in options && typeof options.unix === "string" && process.platform === "win32");
async function testServer(server: Bun.Server<T>) {
if (overrideExpectBehavior) {
await overrideExpectBehavior(server);
} else {
expectInstanceOf(server.url, URL);
expect(server.hostname).toBeDefined();
expect(server.port).toBeGreaterThan(0);
expect(server.url.toString()).toStartWith("http");
expect(await fetch(server.url)).toBeInstanceOf(Response);
}
}
it.skipIf(skip)(name, async () => {
try {
using server = Bun.serve(options);
try {
await testServer(server);
} finally {
await server.stop(true);
}
} catch (error) {
if (onConstructorFailure) {
expectInstanceOf(error, Error);
await onConstructorFailure(error);
} else throw error;
}
});
}
test("basic", {
routes: {
"/123": {
"GET": new Response("Cool/great"),
},
},
fetch(req) {
console.log(req.url); // => http://localhost:3000/
return new Response("Hello World");
},
});
test(
"basic + tls",
{
fetch(req) {
console.log(req.url); // => http://localhost:3000/
return new Response("Hello World");
},
tls: {
key: "ca.pem",
cert: "cert.pem",
},
},
{
onConstructorFailure: error => {
expect(error.message).toContain("error:0900006e:PEM routines:OPENSSL_internal:NO_START_LINE");
},
},
);
test(
"basic + invalid route value",
{
routes: {
"/": new Response("Hello World"),
// @ts-expect-error Invalid value
"/2": null,
},
},
{
onConstructorFailure: error => {
expect(error.message).toContain("'routes' expects a Record<string,");
},
},
);
test("basic + websocket + upgrade", {
websocket: {
message(ws, message) {
expectType<typeof ws>().is<Bun.ServerWebSocket<undefined>>();
ws.send(message);
expectType(message).is<string | Buffer<ArrayBuffer>>();
},
},
fetch(req, server) {
expectType(req).is<Request>();
// Upgrade to a ServerWebSocket if we can
// This automatically checks for the `Sec-WebSocket-Key` header
// meaning you don't have to check headers, you can just call `upgrade()`
if (server.upgrade(req)) {
// When upgrading, we return undefined since we don't want to send a Response
// return;
}
return new Response("Regular HTTP response");
},
});
test("basic + websocket + upgrade + all handlers", {
fetch(req, server) {
expectType(server.upgrade).is<
(
req: Request,
options: {
data?: { name: string };
headers?: Bun.HeadersInit;
},
) => boolean
>;
const url = new URL(req.url);
if (url.pathname === "/chat") {
if (
server.upgrade(req, {
data: {
name: new URL(req.url).searchParams.get("name") || "Friend",
},
headers: {
"Set-Cookie": "name=" + new URL(req.url).searchParams.get("name"),
},
})
) {
return;
}
}
return new Response("Expected a websocket connection", { status: 400 });
},
websocket: {
data: {} as { name: string },
open(ws) {
console.log("WebSocket opened");
ws.subscribe("the-group-chat");
},
message(ws, message) {
expectType(message).is<string | Buffer<ArrayBuffer>>();
ws.publish("the-group-chat", `${ws.data.name}: ${message.toString()}`);
},
close(ws, code, reason) {
expectType(code).is<number>();
expectType(reason).is<string>();
ws.publish("the-group-chat", `${ws.data.name} left the chat`);
},
drain(ws) {
expectType(ws.data.name).is<string>();
console.log("Please send me data. I am ready to receive it.");
},
perMessageDeflate: true,
},
});
test(
"basic error handling",
{
fetch(req) {
throw new Error("woops!");
},
error(error) {
return new Response(`<pre>${error.message}\n${error.stack}</pre>`, {
status: 500,
headers: {
"Content-Type": "text/html",
},
});
},
},
{
overrideExpectBehavior: async server => {
const res = await fetch(server.url);
expect(res.status).toBe(500);
expect(await res.text()).toContain("woops!");
},
},
);
test("port 0 + websocket + upgrade", {
port: 0,
fetch(req, server) {
server.upgrade(req);
if (Math.random() > 0.5) return undefined;
return new Response();
},
websocket: {
message(ws) {
expectType(ws).is<Bun.ServerWebSocket<undefined>>();
},
},
});
test(
"basic unix socket",
{
unix: `${tmpdirSync()}/bun.sock`,
fetch() {
return new Response();
},
},
{
overrideExpectBehavior: server => {
expect(server.hostname).toBeUndefined();
expect(server.port).toBeUndefined();
expect(server.url.toString()).toStartWith("unix://");
},
},
);
test(
"basic unix socket + websocket + upgrade",
{
unix: `${tmpdirSync()}/bun.sock`,
fetch(req, server) {
server.upgrade(req);
if (Math.random() > 0.5) return undefined;
return new Response();
},
websocket: { message() {} },
},
{
overrideExpectBehavior: server => {
expect(server.hostname).toBeUndefined();
expect(server.port).toBeUndefined();
expect(server.url.toString()).toStartWith("unix://");
},
},
);
test(
"basic unix socket + websocket + upgrade + tls",
{
unix: `${tmpdirSync()}/bun.sock`,
fetch(req, server) {
server.upgrade(req);
if (Math.random() > 0.5) return undefined;
return new Response();
},
websocket: { message() {} },
tls: {},
},
{
overrideExpectBehavior: server => {
expect(server.hostname).toBeUndefined();
expect(server.port).toBeUndefined();
expect(server.url.toString()).toStartWith("unix://");
},
},
);
test(
"basic unix socket 2",
{
unix: `${tmpdirSync()}/bun.sock`,
fetch(req, server) {
if (server.upgrade(req)) {
return;
}
return new Response();
},
websocket: { message() {} },
},
{
overrideExpectBehavior: server => {
expect(server.hostname).toBeUndefined();
expect(server.port).toBeUndefined();
expect(server.url.toString()).toStartWith("unix://");
},
},
);
test(
"basic unix socket + upgrade + cheap request to check upgrade",
{
unix: `${tmpdirSync()}/bun.sock`,
fetch(req, server) {
if (server.upgrade(req)) {
return;
}
return new Response("failed to upgrade", { status: 500 });
},
websocket: {
message: () => {},
},
},
{
overrideExpectBehavior: async server => {
expect(server.hostname).toBeUndefined();
expect(server.port).toBeUndefined();
expect(server.url.toString()).toStartWith("unix://");
async function cheapRequest(request: string) {
const p = Promise.withResolvers<void>();
let chunks: string[] = [];
const sock = await Bun.connect({
unix: server.url.toString(),
socket: {
data: (socket, chunk) => {
chunks.push(chunk.toString());
if (chunks.length === 1) {
p.resolve();
}
},
},
});
sock.write(request);
await p.promise;
return chunks.join("\n");
}
const result = await cheapRequest(
"GET / HTTP/1.1\r\n" +
"Host: example.com\r\n" +
"Upgrade: websocket\r\n" +
"Connection: Upgrade\r\n" +
"Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n" +
"Sec-WebSocket-Version: 13\r\n" +
"\r\n",
);
expect(result).toContain("HTTP/1.1 101 Switching Protocols\r\n");
expect(result).toContain("Upgrade: websocket\r\n");
expect(result).toContain("Connection: Upgrade\r\n");
expect(result).toContain("Sec-WebSocket-Accept: ");
},
},
);
test(
"basic unix socket + routes",
{
unix: `${tmpdirSync()}/bun.sock`,
routes: {
"/": new Response("Hello World"),
},
},
{
overrideExpectBehavior: server => {
expect(server.hostname).toBeUndefined();
expect(server.port).toBeUndefined();
expect(server.url.toString()).toStartWith("unix://");
},
},
);
test(
"unix socket with no routes or fetch handler (should fail)",
// @ts-expect-error - Missing fetch or routes
{
unix: `${tmpdirSync()}/bun.sock`,
},
{
onConstructorFailure: error => {
expect(error.message).toContain("Bun.serve() needs either:");
expect(error.message).toContain("A routes object:");
expect(error.message).toContain("Or a fetch handler");
},
},
);
test("basic routes + fetch + websocket + upgrade", {
routes: {
"/:test": req => {
return new Response(req.params.test);
},
},
fetch: (req, server) => {
if (!server.upgrade(req)) {
return new Response("not upgraded");
}
},
websocket: {
message: ws => {
ws.data;
ws.send(" ");
},
},
});
test("basic routes + fetch", {
routes: {
"/:test": req => {
return new Response(req.params.test);
},
},
fetch: (req, server) => {
return new Response("cool");
},
});
test("very basic fetch", {
fetch: (req, server) => {
return new Response("cool");
},
});
test("very basic single route with url params", {
routes: {
"/:test": req => {
return new Response(req.params.test);
},
},
});
test("very basic fetch with websocket message handler", {
fetch: () => new Response("ok"),
websocket: {
message: ws => {
expectType(ws).is<Bun.ServerWebSocket<undefined>>();
},
},
});
test("yet another basic fetch and websocket message handler", {
websocket: {
message: ws => {
expectType(ws).is<Bun.ServerWebSocket<undefined>>();
},
},
fetch: (req, server) => {
if (server.upgrade(req)) {
return;
}
return new Response("not upgraded");
},
});
test("websocket + upgrade on a route path", {
websocket: {
message: ws => {
expectType(ws).is<Bun.ServerWebSocket<undefined>>();
},
},
routes: {
"/ws": (req, server) => {
if (server.upgrade(req)) {
return;
}
return new Response("not upgraded");
},
},
});
const files = {} as Record<string, Bun.BunFile>;
test("permutations of valid route values", {
routes: {
"/this/:test": Bun.file(import.meta.file),
"/index.test-d.ts": Bun.file("index.test-d.ts"),
// @ts-expect-error this is invalid
"/index.test-d.ts.2": () => Bun.file("index.test-d.ts"),
"/ping": new Response("pong"),
"/": html,
// @ts-expect-error this is invalid, but hopefully not for too long
"/index.html": new Response(html),
...files,
},
fetch: (req, server) => {
return new Response("cool");
},
});
test("basic websocket upgrade and ws publish/subscribe to topics", {
fetch(req, server) {
server.upgrade(req);
},
websocket: {
open(ws) {
console.log("WebSocket opened");
ws.subscribe("test-channel");
},
message(ws, message) {
ws.publish("test-channel", `${message.toString()}`);
},
perMessageDeflate: true,
},
});
test(
"port with unix socket (is a type error)",
// @ts-expect-error Cannot pass unix and port
{
unix: `${tmpdirSync()}/bun.sock`,
port: 0,
fetch() {
return new Response();
},
},
{
overrideExpectBehavior: server => {
expect(server.hostname).toBeUndefined();
expect(server.port).toBeUndefined();
expect(server.url.toString()).toStartWith("unix://");
},
},
);
test(
"port with unix socket with websocket + upgrade (is a type error)",
// @ts-expect-error cannot pass unix and port at same time
{
unix: `${tmpdirSync()}/bun.sock`,
port: 0,
fetch(req, server) {
server.upgrade(req);
if (Math.random() > 0.5) return undefined;
return new Response();
},
websocket: { message: ws => expectType(ws).is<Bun.ServerWebSocket<undefined>>() },
},
{
overrideExpectBehavior: server => {
expect(server.hostname).toBeUndefined();
expect(server.port).toBeUndefined();
expect(server.url.toString()).toStartWith("unix://");
},
},
);
test("hostname: 0.0.0.0 (default - listen on all interfaces)", {
hostname: "0.0.0.0",
fetch() {
return new Response("listening on all interfaces");
},
});
test("hostname: 127.0.0.1 (localhost only)", {
hostname: "127.0.0.1",
fetch() {
return new Response("listening on localhost only");
},
});
test("hostname: localhost", {
hostname: "localhost",
fetch() {
return new Response("listening on localhost");
},
});
test(
"hostname: custom IPv4 address",
{
hostname: "192.168.1.100",
fetch() {
return new Response("custom hostname");
},
},
{
onConstructorFailure: error => {
expect(error.message).toContain("Failed to start server");
},
},
);
test("port: number type", {
port: 3000,
fetch() {
return new Response("port as number");
},
});
test("port: string type", {
port: "3001",
fetch() {
return new Response("port as string");
},
});
test("port: 0 (random port assignment)", {
port: 0,
fetch() {
return new Response("random port");
},
});
test(
"port: from environment variable",
{
port: process.env.PORT || "3002",
fetch() {
return new Response("port from env");
},
},
{
overrideExpectBehavior: server => {
expect(server.port).toBeGreaterThan(0);
expect(server.url).toBeDefined();
},
},
);
test("reusePort: false (default)", {
reusePort: false,
port: 0,
fetch() {
return new Response("reusePort false");
},
});
test("reusePort: true", {
reusePort: true,
port: 0,
fetch() {
return new Response("reusePort true");
},
});
test("ipv6Only: false (default)", {
ipv6Only: false,
port: 0,
fetch() {
return new Response("ipv6Only false");
},
});
test("idleTimeout: default (10 seconds)", {
port: 0,
fetch() {
return new Response("default idleTimeout");
},
});
test("idleTimeout: custom value (30 seconds)", {
idleTimeout: 30,
port: 0,
fetch() {
return new Response("custom idleTimeout");
},
});
test("idleTimeout: 0 (no timeout)", {
idleTimeout: 0,
port: 0,
fetch() {
return new Response("no idleTimeout");
},
});
test("maxRequestBodySize: default (128MB)", {
port: 0,
fetch() {
return new Response("default maxRequestBodySize");
},
});
test("maxRequestBodySize: custom small value", {
maxRequestBodySize: 1024 * 1024, // 1MB
port: 0,
fetch() {
return new Response("small maxRequestBodySize");
},
});
test("maxRequestBodySize: custom large value", {
maxRequestBodySize: 1024 * 1024 * 1024, // 1GB
port: 0,
fetch() {
return new Response("large maxRequestBodySize");
},
});
test("development: true", {
development: true,
port: 0,
fetch() {
return new Response("development mode on");
},
});
test("development: false", {
development: false,
port: 0,
fetch() {
return new Response("development mode off");
},
});
test("development: defaults to process.env.NODE_ENV !== 'production'", {
development: process.env.NODE_ENV !== "production",
port: 0,
fetch() {
return new Response("development from env");
},
});
test(
"error callback handles errors",
{
port: 0,
fetch() {
throw new Error("Test error");
},
error(error) {
return new Response(`Error handled: ${error.message}`, { status: 500 });
},
},
{
overrideExpectBehavior: async server => {
const res = await fetch(server.url);
expect(res.status).toBe(500);
expect(await res.text()).toBe("Error handled: Test error");
},
},
);
test(
"error callback with async handler",
{
port: 0,
fetch() {
throw new Error("Async test error");
},
async error(error) {
await new Promise(resolve => setTimeout(resolve, 10));
return new Response(`Async error handled: ${error.message}`, { status: 503 });
},
},
{
overrideExpectBehavior: async server => {
const res = await fetch(server.url);
expect(res.status).toBe(503);
expect(await res.text()).toBe("Async error handled: Async test error");
},
},
);
test("id: custom server identifier", {
id: "my-custom-server-id",
port: 0,
fetch() {
return new Response("server with custom id");
},
});
test("id: null (no identifier)", {
id: null,
port: 0,
fetch() {
return new Response("server with null id");
},
});
test("multiple properties combined", {
hostname: "127.0.0.1",
port: 0,
reusePort: true,
idleTimeout: 20,
maxRequestBodySize: 1024 * 1024 * 10, // 10MB
development: true,
id: "combined-test-server",
fetch(req) {
return Response.json({
url: req.url,
method: req.method,
});
},
error(error) {
return new Response(`Combined server error: ${error.message}`, { status: 500 });
},
});
test("#24819 regression", {
development: !process.env.production,
routes: {
"/health": {
GET: new Response("OK"),
POST: req => {
expectType(req).is<Bun.BunRequest<"/health">>();
return Response.json("Sup");
},
},
},
});
// @ts-expect-error
test("#24819 regression with no response requires websocket", {
development: !process.env.production,
routes: {
"/health": {
GET: new Response("OK"),
POST: req => {
expectType(req).is<Bun.BunRequest<"/health">>();
},
},
},
});
test("#24819 regression with websocket is happy", {
websocket: {
message: console.log,
},
development: !process.env.production,
routes: {
"/health": {
GET: new Response("OK"),
POST: req => {
expectType(req).is<Bun.BunRequest<"/health">>();
},
},
},
});
@@ -0,0 +1,86 @@
// This file is merely types only, you (probably) want to put the tests in ./serve-types.test.ts instead
import { expectType } from "./utilities";
Bun.serve({
routes: {
"/:id/:test": req => {
expectType(req.params).is<{ id: string; test: string }>();
},
},
fetch: () => new Response("hello"),
websocket: {
message(ws, message) {
expectType(ws.data).is<undefined>();
expectType(message).is<string | Buffer<ArrayBuffer>>();
},
},
});
const s1 = Bun.serve({
routes: {
"/ws/:name": req => {
expectType(req.params.name).is<string>();
s1.upgrade(req, {
data: { name: req.params.name },
});
},
},
websocket: {
data: {} as { name: string },
message(ws) {
ws.send(JSON.stringify(ws.data));
},
},
});
const s2 = Bun.serve({
routes: {
"/ws/:name": req => {
expectType(req.params.name).is<string>();
// @ts-expect-error - Should error because data was not passed
s2.upgrade(req, {});
},
},
websocket: {
data: {} as { name: string },
message(ws) {
expectType(ws.data).is<{ name: string }>();
},
},
});
const s3 = Bun.serve({
routes: {
"/ws/:name": req => {
expectType(req.params.name).is<string>();
// @ts-expect-error - Should error because data and object was not passed
s3.upgrade(req);
},
},
websocket: {
data: {} as { name: string },
message(ws) {
expectType(ws.data).is<{ name: string }>();
},
},
});
const s4 = Bun.serve({
routes: {
"/ws/:name": req => {
expectType(req.params.name).is<string>();
s4.upgrade(req);
},
},
websocket: {
message(ws) {
expectType(ws.data).is<undefined>();
},
},
});
+228
View File
@@ -0,0 +1,228 @@
import type {
FileSink,
NullSubprocess,
PipedSubprocess,
ReadableSubprocess,
SyncSubprocess,
WritableSubprocess,
} from "bun";
import * as tsd from "./utilities";
Bun.spawn(["echo", "hello"]);
function depromise<T>(_promise: Promise<T>): T {
return "asdf" as any as T;
}
{
// Test cases for https://github.com/oven-sh/bun/issues/17274
{
const proc = Bun.spawn(["cat"], {
stdin: "pipe",
});
proc.stdin.write("hello");
}
{
const proc = Bun.spawn(["cat"], {
stdin: "pipe",
onExit(proc, exitCode, signalCode, error) {
tsd.expectType(proc).is<Bun.Subprocess<"pipe", "pipe", "inherit">>();
console.log(`Process exited: ${exitCode}`);
},
});
proc.stdin.write("hello");
}
}
{
const proc = Bun.spawn(["echo", "hello"], {
cwd: "./path/to/subdir", // specify a working direcory
env: { ...process.env, FOO: "bar" }, // specify environment variables
onExit(proc, exitCode, signalCode, error) {
// exit handler
},
});
tsd.expectType(proc.pid).is<number>();
tsd.expectType(proc.stdout).is<ReadableStream<Uint8Array<ArrayBuffer>>>();
tsd.expectType(proc.stderr).is<undefined>();
tsd.expectType(proc.stdin).is<undefined>();
}
{
const proc = Bun.spawn(["cat"], {
stdin: depromise(fetch("https://raw.githubusercontent.com/oven-sh/bun/main/examples/hashing.js")),
});
const text = depromise(proc.stdout.text());
console.log(text); // "const input = "hello world".repeat(400); ..."
}
{
const proc = Bun.spawn(["cat"], {
stdio: ["pipe", "pipe", "pipe", Bun.file("build.zip")],
});
tsd.expectType(proc.stdio[0]).is<null>();
tsd.expectType(proc.stdio[1]).is<null>();
tsd.expectType(proc.stdio[2]).is<null>();
tsd.expectType(proc.stdio[3]).is<number | null | undefined>();
tsd.expectType(proc.stdin).is<FileSink>();
tsd.expectType(proc.stdout).is<ReadableStream<Uint8Array<ArrayBuffer>>>();
tsd.expectType(proc.stderr).is<ReadableStream<Uint8Array<ArrayBuffer>>>();
}
{
const proc = Bun.spawn(["cat"], {
stdin: "pipe", // return a FileSink for writing
});
// enqueue string data
proc.stdin.write("hello");
// enqueue binary data
const enc = new TextEncoder();
proc.stdin.write(enc.encode(" world!"));
enc.encodeInto(" world!", {} as any as Uint8Array);
// Bun-specific overloads
// these fail when lib.dom.d.ts is present
enc.encodeInto(" world!", new Uint32Array(124));
enc.encodeInto(" world!", {} as any as DataView);
// send buffered data
await proc.stdin.flush();
// close the input stream
await proc.stdin.end();
}
{
const proc = Bun.spawn(["echo", "hello"]);
const text = depromise(proc.stdout.text());
console.log(text); // => "hello"
}
{
const proc = Bun.spawn(["echo", "hello"], {
onExit(proc, exitCode, signalCode, error) {
// exit handler
},
});
await proc.exited; // resolves when process exit
proc.killed; // boolean — was the process killed?
proc.exitCode; // null | number
proc.signalCode; // null | "SIGABRT" | "SIGALRM" | ...
proc.kill();
proc.killed; // true
proc.kill(); // specify an exit code
proc.unref();
}
{
const proc = Bun.spawn(["echo", "hello"], {
stdio: ["pipe", "pipe", "pipe"],
});
tsd.expectType<FileSink>(proc.stdin);
tsd.expectType<ReadableStream<Uint8Array>>(proc.stdout);
tsd.expectType<ReadableStream<Uint8Array>>(proc.stderr);
}
{
const proc = Bun.spawn(["echo", "hello"], {
stdio: ["inherit", "inherit", "inherit"],
});
tsd.expectType<undefined>(proc.stdin);
tsd.expectType<undefined>(proc.stdout);
tsd.expectType<undefined>(proc.stderr);
}
{
const proc = Bun.spawn(["echo", "hello"], {
stdio: ["ignore", "ignore", "ignore"],
});
tsd.expectType<undefined>(proc.stdin);
tsd.expectType<undefined>(proc.stdout);
tsd.expectType<undefined>(proc.stderr);
}
{
const proc = Bun.spawn(["echo", "hello"], {
stdio: [null, null, null],
});
tsd.expectType(proc.stdin).is<undefined>();
tsd.expectType(proc.stdout).is<undefined>();
tsd.expectType(proc.stderr).is<undefined>();
}
{
const proc = Bun.spawn(["echo", "hello"], {
stdio: [new Request("1"), null, null],
});
tsd.expectType<number>(proc.stdin);
}
{
const proc = Bun.spawn(["echo", "hello"], {
stdio: [new Response("1"), null, null],
});
tsd.expectType<number>(proc.stdin);
}
{
const proc = Bun.spawn(["echo", "hello"], {
stdio: [new Uint8Array([]), null, null],
});
tsd.expectType<number>(proc.stdin);
}
tsd.expectAssignable<PipedSubprocess>(Bun.spawn([], { stdio: ["pipe", "pipe", "pipe"] }));
tsd.expectAssignable<ReadableSubprocess>(Bun.spawn([], { stdio: ["ignore", "pipe", "pipe"] }));
tsd.expectAssignable<ReadableSubprocess>(Bun.spawn([], { stdio: ["pipe", "pipe", "pipe"] }));
tsd.expectAssignable<WritableSubprocess>(Bun.spawn([], { stdio: ["pipe", "pipe", "pipe"] }));
tsd.expectAssignable<WritableSubprocess>(Bun.spawn([], { stdio: ["pipe", "ignore", "inherit"] }));
tsd.expectAssignable<NullSubprocess>(Bun.spawn([], { stdio: ["ignore", "inherit", "ignore"] }));
tsd.expectAssignable<NullSubprocess>(Bun.spawn([], { stdio: [null, null, null] }));
tsd.expectAssignable<SyncSubprocess<Bun.SpawnOptions.Readable, Bun.SpawnOptions.Readable>>(Bun.spawnSync([], {}));
// Lazy option types (async only)
{
// valid: lazy usable with async spawn
const p1 = Bun.spawn(["echo", "hello"], {
stdout: "pipe",
stderr: "pipe",
lazy: true,
});
tsd.expectType(p1.stdout).is<ReadableStream<Uint8Array<ArrayBuffer>>>();
}
{
// valid: lazy false is also allowed
const p2 = Bun.spawn(["echo", "hello"], {
stdout: "pipe",
stderr: "pipe",
lazy: false,
});
tsd.expectType(p2.stderr).is<ReadableStream<Uint8Array<ArrayBuffer>>>();
}
{
// invalid: lazy is not supported in spawnSync
Bun.spawnSync(["echo", "hello"], {
stdout: "pipe",
stderr: "pipe",
// @ts-expect-error lazy applies only to async spawn
lazy: true,
});
}
{
// invalid: lazy is not supported in spawnSync (object overload)
// prettier-ignore
// @ts-expect-error lazy applies to async spawn
Bun.spawnSync({ cmd: ["echo", "hello"], stdout: "pipe", stderr: "pipe", lazy: true,
});
}
+301
View File
@@ -0,0 +1,301 @@
import { sql } from "bun";
import { expectAssignable, expectType } from "./utilities";
{
const postgres = new Bun.SQL();
const id = 1;
await postgres`select * from users where id = ${id}`;
}
{
const postgres = new Bun.SQL("postgres://localhost:5432/mydb");
const id = 1;
await postgres`select * from users where id = ${id}`;
}
{
const postgres = new Bun.SQL({ url: "postgres://localhost:5432/mydb" });
const id = 1;
await postgres`select * from users where id = ${id}`;
}
{
const postgres = new Bun.SQL();
postgres("ok");
}
const sql1 = new Bun.SQL();
const sql2 = new Bun.SQL("postgres://localhost:5432/mydb");
const sql3 = new Bun.SQL(new URL("postgres://localhost:5432/mydb"));
const sql4 = new Bun.SQL({ url: "postgres://localhost:5432/mydb", idleTimeout: 1000 });
const query1 = sql1<string>`SELECT * FROM users WHERE id = ${1}`;
const query2 = sql2({ foo: "bar" });
query1.cancel().simple().execute().raw().values();
expectType(query1).extends<Promise<any>>();
expectType(query1).extends<Promise<string>>();
sql1.connect();
sql1.close();
sql1.end();
sql1.flush();
const reservedPromise: Promise<Bun.ReservedSQL> = sql1.reserve();
sql1.begin(async txn => {
txn`SELECT 1`;
await txn.savepoint("sp", async sp => {
sp`SELECT 2`;
});
});
expectType(
sql1.transaction(async txn => {
txn`SELECT 3`;
}),
).is<Promise<void>>();
expectType(
sql1.begin("read write", async txn => {
txn`SELECT 4`;
}),
).is<Promise<void>>();
expectType(
sql1.transaction("read write", async txn => {
txn`SELECT 5`;
}),
).is<Promise<void>>();
expectType(
sql1.beginDistributed("foo", async txn => {
txn`SELECT 6`;
}),
).is<Promise<void>>();
expectType(
sql1.distributed("bar", async txn => {
txn`SELECT 7`;
}),
).is<Promise<void>>();
expectType(
sql1.beginDistributed("foo", async txn => {
txn`SELECT 8`;
}),
).is<Promise<void>>();
{
const tx = await sql1.transaction(async txn => {
return [await txn<[9]>`SELECT 9`, await txn<[10]>`SELECT 10`];
});
expectType(tx).is<readonly [[9], [10]]>();
}
{
const tx = await sql1.begin(async txn => {
return [await txn<[9]>`SELECT 9`, await txn<[10]>`SELECT 10`];
});
expectType(tx).is<readonly [[9], [10]]>();
}
{
const tx = await sql1.distributed("name", async txn => {
return [await txn<[9]>`SELECT 9`, await txn<[10]>`SELECT 10`];
});
expectType(tx).is<readonly [[9], [10]]>();
}
expectType(sql1.unsafe("SELECT * FROM users")).is<Bun.SQL.Query<any>>();
expectType(sql1.unsafe<{ id: string }[]>("SELECT * FROM users")).is<Bun.SQL.Query<{ id: string }[]>>();
expectType(sql1.file("query.sql", [1, 2, 3])).is<Bun.SQL.Query<any>>();
sql1.reserve().then(reserved => {
reserved.release();
expectType(reserved<[8]>`SELECT 8`).is<Bun.SQL.Query<[8]>>();
});
sql1.begin(async txn => {
txn.savepoint("sp", async sp => {
sp`SELECT 9`;
});
});
sql1.begin(async txn => {
txn.savepoint(async sp => {
sp`SELECT 10`;
});
});
// @ts-expect-error
sql1.commitDistributed();
// @ts-expect-error
sql1.rollbackDistributed();
// @ts-expect-error
sql1.file(123);
// @ts-expect-error
sql1.unsafe(123);
// @ts-expect-error
sql1.begin("read write", 123);
// @ts-expect-error
sql1.transaction("read write", 123);
const sqlQueryAny: Bun.SQL.Query<any> = {} as any;
const sqlQueryNumber: Bun.SQL.Query<number> = {} as any;
const sqlQueryString: Bun.SQL.Query<string> = {} as any;
expectAssignable<Promise<any>>(sqlQueryAny);
expectAssignable<Promise<number>>(sqlQueryNumber);
expectAssignable<Promise<string>>(sqlQueryString);
expectType(sqlQueryNumber).is<Bun.SQL.Query<number>>();
expectType(sqlQueryString).is<Bun.SQL.Query<string>>();
expectType(sqlQueryNumber).is<Bun.SQL.Query<number>>();
const queryA = sql`SELECT 1`;
expectType(queryA).is<Bun.SQL.Query<any>>();
expectType(await queryA).is<any>();
const queryB = sql({ foo: "bar" });
expectType(queryB).is<Bun.SQL.Helper<{ foo: string }>>();
expectType(sql).is<Bun.SQL>();
const opts2 = { url: "postgres://localhost" } satisfies Bun.SQL.Options;
expectType(opts2).extends<Bun.SQL.Options>();
const txCb = (async sql => [sql<[1]>`SELECT 1`]) satisfies Bun.SQL.TransactionContextCallback<unknown>;
const spCb = (async sql => [sql<[2]>`SELECT 2`]) satisfies Bun.SQL.SavepointContextCallback<unknown>;
expectType(await sql.begin(txCb)).is<[1][]>();
expectType(await sql.begin(spCb)).is<[2][]>();
expectType(queryA.cancel()).is<Bun.SQL.Query<any>>();
expectType(queryA.simple()).is<Bun.SQL.Query<any>>();
expectType(queryA.execute()).is<Bun.SQL.Query<any>>();
expectType(queryA.raw()).is<Bun.SQL.Query<any>>();
expectType(queryA.values()).is<Bun.SQL.Query<any>>();
declare const queryNum: Bun.SQL.Query<number>;
expectType(queryNum.cancel()).is<Bun.SQL.Query<number>>();
expectType(queryNum.simple()).is<Bun.SQL.Query<number>>();
expectType(queryNum.execute()).is<Bun.SQL.Query<number>>();
expectType(queryNum.raw()).is<Bun.SQL.Query<number>>();
expectType(queryNum.values()).is<Bun.SQL.Query<number>>();
expectType(await queryNum.cancel()).is<number>();
expectType(await queryNum.simple()).is<number>();
expectType(await queryNum.execute()).is<number>();
expectType(await queryNum.raw()).is<number>();
expectType(await queryNum.values()).is<number>();
expectType<Bun.SQL.Options>({
password: () => "hey",
pass: async () => "hey",
});
expectType<Bun.SQL.Options>({
password: "hey",
});
expectType(sql({ name: "Alice", email: "[email protected]" })).is<
Bun.SQL.Helper<{
name: string;
email: string;
}>
>();
expectType(
sql([
{ name: "Alice", email: "[email protected]" },
{ name: "Bob", email: "[email protected]" },
]),
).is<
Bun.SQL.Helper<{
name: string;
email: string;
}>
>();
const userWithAge = { name: "Alice", email: "[email protected]", age: 25 };
expectType(sql(userWithAge, "name", "email")).is<
Bun.SQL.Helper<{
name: string;
email: string;
}>
>();
const users = [
{ id: 1, name: "Alice" },
{ id: 2, name: "Bob" },
];
expectType(sql(users, "id")).is<Bun.SQL.Helper<{ id: number }>>();
expectType(sql([1, 2, 3])).is<Bun.SQL.Helper<number[]>>();
expectType(sql([1, 2, 3] as const)).is<Bun.SQL.Helper<readonly [1, 2, 3]>>();
expectType(sql("users")).is<Bun.SQL.Query<any>>();
expectType(sql<1>("users")).is<Bun.SQL.Query<1>>();
declare const user: { name: "Alice"; email: "[email protected]" };
// @ts-expect-error - missing key in object
sql(user, "notAKey");
// @ts-expect-error - wrong type for key argument
sql(user, 123);
// @ts-expect-error - array of objects, missing key
sql(users, "notAKey");
// @ts-expect-error - array of numbers, extra key argument
sql([1, 2, 3], "notAKey");
// Degenerate helper inputs that throw at runtime are rejected at the type
// level too. See https://github.com/oven-sh/bun/issues/32155.
// @ts-expect-error - bare null is not a valid helper value
sql(null);
// @ts-expect-error - bare undefined is not a valid helper value
sql(undefined);
// @ts-expect-error - null item in a keyed WHERE IN helper
sql([null], "id");
// @ts-expect-error - null single object in an UPDATE helper with a column
sql(null, "name");
// @ts-expect-error - undefined item in an UPDATE/keyed helper
sql([undefined], "name");
// Still allowed: a null/primitive array binds NULL for WHERE IN.
sql([null]);
sql([1, null, 2]);
// check the deprecated stuff still exists
expectType<Bun.SQLQuery<"hey">>();
expectType<Bun.SQLTransactionContextCallback<"hey">>();
expectType<Bun.SQLSavepointContextCallback<"hey">>();
// check some types exist
expectType<Bun.SQL.AwaitPromisesArray<[]>>;
expectType<Bun.SQL.SQLiteOptions>;
expectType<Bun.SQL.PostgresOrMySQLOptions>;
expectType<Bun.SQL.ContextCallbackResult<unknown>>;
declare const aSqlInstance: Bun.SQL;
expectType(aSqlInstance.options.host).is<string | undefined>(); // property exists in postgres/mysql/mariadb options
expectType(aSqlInstance.options.safeIntegers).is<boolean | undefined>(); // property exits in sqlite options
@@ -0,0 +1,52 @@
import { type Changes, Database, constants } from "bun:sqlite";
import { expectType } from "./utilities";
expectType(constants.SQLITE_FCNTL_BEGIN_ATOMIC_WRITE).is<number>();
expectType<Record<string, number>>(constants);
const db = new Database(":memory:");
const query1 = db.query<
{ name: string; dob: number }, // return type first
{ $id: string }
>("select name, dob from users where id = $id");
query1.all({ $id: "asdf" }); // => {name: string; dob:string}[]
const query2 = db.query<
{ name: string; dob: number },
[string, number] // pass tuple for positional params
>("select ?1 as name, ?2 as dob");
const allResults = query2.all("Shaq", 50); // => {name: string; dob:string}[]
const getResults = query2.get("Shaq", 50); // => {name: string; dob:string}[]
// tslint:disable-next-line:no-void-expression
const runResults = query2.run("Shaq", 50); // => {name: string; dob:string}[]
expectType<Array<{ name: string; dob: number }>>(allResults);
expectType<{ name: string; dob: number } | null>(getResults);
// tslint:disable-next-line:invalid-void
// eslint-disable-next-line @typescript-eslint/no-invalid-void-type
expectType<Changes>(runResults);
const query3 = db.prepare<
{ name: string; dob: number }, // return type first
// eslint-disable-next-line @definitelytyped/no-single-element-tuple-type
[{ $id: string }]
>("select name, dob from users where id = $id");
const allResults3 = query3.all({ $id: "asdf" });
expectType<Array<{ name: string; dob: number }>>(allResults3);
db.exec("CREATE TABLE cats (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT UNIQUE, age INTEGER)");
const insert = db.prepare("INSERT INTO cats (name, age) VALUES ($name, $age)");
const insertManyCats = db.transaction((cats: Array<{ $name: string; $age: number }>) => {
for (const cat of cats) insert.run(cat);
});
insertManyCats([
{
$name: "Joey",
$age: 2,
},
{ $name: "Sally", $age: 4 },
{ $name: "Junior", $age: 1 },
// @ts-expect-error - Should fail
{ fail: true },
]);
@@ -0,0 +1,3 @@
process.stdin;
process.stdout;
process.stderr;
@@ -0,0 +1,87 @@
import { expectType } from "./utilities";
new ReadableStream<string>({
start(controller) {
controller.enqueue("hello");
controller.enqueue("world");
// @ts-expect-error
controller.enqueue(2);
controller.close();
},
});
// This will have type errors when lib.dom.d.ts is present
// Not fixable because ReadableStream has no ReadableStreamConstructor interface
// we can merge into. See https://github.com/microsoft/TypeScript-DOM-lib-generator/pull/1941
// for details about when/why/how TypeScript might support this.
new ReadableStream({
type: "direct",
async pull(controller) {
controller.write(new TextEncoder().encode("Hello, world!"));
},
});
declare const uint8stream: ReadableStream<Uint8Array<ArrayBuffer>>;
for await (const chunk of uint8stream) {
expectType(chunk).is<Uint8Array<ArrayBuffer>>();
}
declare const uint8Array: Uint8Array<ArrayBuffer>;
expectType(uint8Array).is<Uint8Array<ArrayBuffer>>();
declare const uint8Writable: WritableStream<Uint8Array<ArrayBuffer>>;
declare const uint8Transform: TransformStream<Uint8Array<ArrayBuffer>, Uint8Array<ArrayBuffer>>;
const writer = uint8Writable.getWriter();
await writer.write(uint8Array);
await writer.close();
for await (const chunk of uint8Transform.readable) {
expectType(chunk).is<Uint8Array<ArrayBuffer>>();
}
declare const stream: ReadableStream<Uint8Array>;
expectType(stream.json()).is<Promise<any>>();
expectType(stream.bytes()).is<Promise<Uint8Array<ArrayBuffer>>>();
expectType(stream.text()).is<Promise<string>>();
expectType(stream.blob()).is<Promise<Blob>>();
import { ReadableStream as NodeStreamReadableStream } from "node:stream/web";
declare const node_stream: NodeStreamReadableStream<Uint8Array>;
expectType(node_stream.json()).is<Promise<any>>();
expectType(node_stream.bytes()).is<Promise<Uint8Array<ArrayBuffer>>>();
expectType(node_stream.text()).is<Promise<string>>();
expectType(node_stream.blob()).is<Promise<Blob>>();
Bun.file("./foo.csv").stream().pipeThrough(new TextDecoderStream()).pipeThrough(new TextEncoderStream());
Bun.file("./foo.csv").stream().pipeThrough(new CompressionStream("gzip")).pipeThrough(new DecompressionStream("gzip"));
Bun.file("./foo.csv").stream().pipeThrough(new CompressionStream("brotli")).pipeThrough(new DecompressionStream("brotli"));
Bun.file("./foo.csv").stream().pipeThrough(new CompressionStream("zstd")).pipeThrough(new DecompressionStream("zstd"));
Bun.file("./foo.csv")
.stream()
.pipeThrough(new TextDecoderStream())
.pipeTo(
new WritableStream({
write(chunk) {
expectType(chunk).is<string>();
},
}),
);
// @ts-expect-error These properties do not exist right now
expectType(new ReadableStream().arrayBuffer());
// @ts-expect-error These properties do not exist right now
expectType(new ReadableStream().formData());
expectType(new Blob([]).text()).is<Promise<string>>();
expectType(new Blob([]).arrayBuffer()).is<Promise<ArrayBuffer>>();
expectType(new Blob([]).bytes()).is<Promise<Uint8Array<ArrayBuffer>>>();
expectType(new Blob([]).json()).is<Promise<any>>();
expectType(new Blob([]).formData()).is<Promise<FormData>>();
expectType(new Blob([]).stream()).is<ReadableStream<Uint8Array<ArrayBuffer>>>();
+167
View File
@@ -0,0 +1,167 @@
import * as Bun from "bun";
await Bun.connect({
data: { arg: "asdf" },
socket: {
data(socket) {
socket.data.arg.toLocaleLowerCase();
},
open() {
console.log("asdf");
},
},
hostname: "adsf",
port: 324,
});
await Bun.connect({
data: { arg: "asdf" },
socket: {
data(socket) {
socket.data.arg.toLowerCase();
},
open() {
console.log("asdf");
},
},
hostname: "adsf",
port: 324,
});
await Bun.connect({
data: { arg: "asdf" },
socket: {
data(socket) {
socket.data.arg.toLowerCase();
},
open() {
console.log("asdf");
},
},
unix: "asdf",
});
await Bun.connect({
data: { arg: "asdf" },
socket: {
data(socket) {
socket.data.arg.toLowerCase();
},
open() {
console.log("asdf");
},
},
unix: "asdf",
});
Bun.listen({
data: { arg: "asdf" },
socket: {
data(socket) {
socket.data.arg.toLowerCase();
},
open() {
console.log("asdf");
},
},
hostname: "adsf",
port: 324,
});
Bun.listen({
data: { arg: "asdf" },
socket: {
data(socket) {
socket.data.arg.toLowerCase();
},
open() {
console.log("asdf");
},
},
hostname: "adsf",
port: 324,
tls: {
certFile: "asdf",
keyFile: "adsf",
},
});
Bun.listen({
data: { arg: "asdf" },
socket: {
data(socket) {
socket.data.arg.toLowerCase();
},
open() {
console.log("asdf");
},
},
hostname: "adsf",
port: 324,
tls: {
cert: "asdf",
key: Bun.file("adsf"),
ca: Buffer.from("asdf"),
},
});
Bun.listen({
data: { arg: "asdf" },
socket: {
data(socket) {
socket.data.arg.toLowerCase();
},
open() {
console.log("asdf");
},
},
unix: "asdf",
});
const listener = Bun.listen({
data: { arg: "asdf" },
socket: {
data(socket) {
socket.data.arg.toLowerCase();
},
open() {
console.log("asdf");
},
},
unix: "asdf",
});
listener.data.arg = "asdf";
// @ts-expect-error arg is string
listener.data.arg = 234;
// listener.reload({
// data: {arg: 'asdf'},
// });
listener.reload({
socket: {
open() {},
// ...listener.
},
});
// Test Socket.reload() type signature (issue #26290)
// The socket instance's reload() method should also accept { socket: handler }
await Bun.connect({
data: { arg: "asdf" },
socket: {
open(socket) {
// Socket.reload() should accept { socket: handler }, not handler directly
socket.reload({
socket: {
open() {},
data() {},
},
});
},
data() {},
},
hostname: "localhost",
port: 1,
});
+423
View File
@@ -0,0 +1,423 @@
import {
afterAll,
afterEach,
beforeAll,
beforeEach,
describe,
expect,
expectTypeOf,
jest,
type Matchers,
mock,
type Mock,
spyOn,
test,
xdescribe,
xit,
xtest,
} from "bun:test";
import { expectType } from "./utilities";
const hooks = [beforeAll, beforeEach, afterAll, afterEach];
for (const hook of hooks) {
hook(() => {
// ...
});
// eslint-disable-next-line
hook(async () => {
// ...
return;
});
hook((done: (err?: unknown) => void) => {
done();
done(new Error());
done("Error");
});
}
describe("bun:test", () => {
describe("expect()", () => {
test("toThrow()", () => {
function fail() {
throw new Error("Bad");
}
expect(fail).toThrow();
expect(fail).toThrow("Bad");
expect(fail).toThrow(/bad/i);
expect(fail).toThrow(Error);
expect(fail).toThrow(new Error("Bad"));
});
});
test("expect()", () => {
expect(1).toBe(1);
expect(1).not.toBe(2);
// @ts-expect-error
expect({ a: 1 }).toEqual<{ a: number }>({ a: 1, b: undefined });
// @ts-expect-error
expect({ a: 1 }).toEqual<{ a: number; b: number }>({ a: 1, b: undefined });
// Support passing a type parameter to force exact type matching
expect({ a: 1 }).toEqual<{ a: number; b: number }>({ a: 1, b: 1 });
expect({ a: 1 }).toStrictEqual({ a: 1 });
expect(new Set()).toHaveProperty("size");
expect(new Uint8Array()).toHaveProperty("byteLength", 0);
expect([]).toHaveLength(0);
expect(["bun"]).toContain("bun");
expect("hello").toContain("bun");
expect(true).toBeTruthy();
expect(false).toBeFalsy();
expect(Math.PI).toBeGreaterThan(3.14);
expect(Math.PI).toBeGreaterThan(3n);
expect(Math.PI).toBeGreaterThanOrEqual(3.14);
expect(Math.PI).toBeGreaterThanOrEqual(3n);
expect(NaN).toBeNaN();
expect(null).toBeNull();
expect(undefined).toBeUndefined();
expect(undefined).not.toBeDefined();
});
});
test.each([1, 2, 3])("test.each", a => {
expectType<1 | 2 | 3>(a);
});
// inference should work when data is passed directly in
test.each([
["a", true, 5],
["b", false, 1234],
])("test.each", (a, b, c) => {
expectType<string>(a);
expectType<boolean>(b);
expectType<number | string>(c);
});
describe.each([
["a", true, 5],
["b", false, 5],
])("test.each", (a, b, c) => {
expectType<string>(a);
expectType<boolean>(b);
expectType<number | string>(c);
});
describe.each([
["a", true, 5],
["b", false, "asdf"],
])("test.each", (a, b, c) => {
expectType<string>(a);
expectType<boolean>(b);
expectType<number | string>(c);
});
// @ts-expect-error
describe.each([{ asdf: "asdf" }, { asdf: "asdf" }])("test.each", (a, b, c) => {
// this test was wrong because this describe.each call will only have one argument, not three.
// it is now marked with ts-expect-error and the fixed test is below.
});
describe.each([{ asdf: "asdf" }, { asdf: "asdf" }])("test.each", a => {
expectType<{ asdf: string }>(a);
});
test.each([{ asdf: "asdf" }, { asdf: "asdf" }])("test.each", (a, done) => {
expectType<{ asdf: string }>(a);
expectType<(err?: unknown) => void>(done);
});
// no inference on data
const data = [
["a", true, 5],
["b", false, "asdf"],
];
test.each(data)("test.each", (a, b, c) => {
expectType<string | number | boolean | ((err?: unknown) => void)>(a);
expectType<string | number | boolean | ((err?: unknown) => void)>(b);
expectType<string | number | boolean | ((err?: unknown) => void)>(c);
});
describe.each(data)("test.each", (a, b, c) => {
expectType<string | number | boolean>(a);
expectType<string | number | boolean>(b);
expectType<string | number | boolean>(c);
});
// as const
const dataAsConst = [
["a", true, 5],
["b", false, "asdf"],
] as const;
test.each(dataAsConst)("test.each", (...args) => {
expectType<string>(args[0]);
expectType<boolean>(args[1]);
expectType<string | number>(args[2]);
});
describe.each(dataAsConst)("test.each", (...args) => {
expectType<string>(args[0]);
expectType<boolean>(args[1]);
expectType<string | number>(args[2]);
});
describe.each(dataAsConst)("test.each", (a, b, c) => {
expectType<"a" | "b">(a);
expectType<boolean>(b);
expectType<5 | "asdf">(c);
});
expect().pass();
expect().fail();
expectType(expect()).is<import("bun:test").Matchers<undefined>>();
expectType(expect<string>()).is<import("bun:test").Matchers<string | undefined>>();
expectType(expect("")).is<import("bun:test").Matchers<string>>();
expectType(expect<string>("")).is<import("bun:test").Matchers<string>>();
expectType(expect(undefined, "Fail message")).is<import("bun:test").Matchers<undefined>>();
expectType(expect<string>(undefined, "Fail message")).is<import("bun:test").Matchers<string | undefined>>();
expectType(expect("", "Fail message")).is<import("bun:test").Matchers<string>>();
expectType(expect<string>("", "Fail message")).is<import("bun:test").Matchers<string>>();
describe("Matcher Overload Type Tests", () => {
const num = 1;
const str = "hello";
const numArr = [1, 2, 3];
const strArr = ["a", "b", "c"];
const mixedArr = [1, "a", true];
const obj = { a: 1, b: "world", 10: true };
const numSet = new Set([10, 20]);
test("toBe", () => {
expect(num).toBe(1);
expect(str).toBe("hello");
// @ts-expect-error - Type 'string' is not assignable to type 'number'.
expect(num).toBe<number>("1");
// @ts-expect-error - Type 'number' is not assignable to type 'string'.
expect(str).toBe<string>(123);
// @ts-expect-error - Type 'boolean' is not assignable to type 'number'.
expect(num).toBe<number>(true);
// @ts-expect-error - Too many arguments for specific overload
expect(num).toBe<number>(1, 2);
// @ts-expect-error - Expecting number, passed function
expect(num).toBe<number>(() => {});
});
test("toEqual", () => {
expect(numArr).toEqual([1, 2, 3]);
expect(obj).toEqual({ a: 1, b: "world", 10: true });
// @ts-expect-error - Type 'string' is not assignable to type 'number' at index 0.
expect(numArr).toEqual<number[]>(["1", 2, 3]);
// @ts-expect-error - Property 'c' is missing in type '{ a: number; b: string; 10: boolean; }'.
expect(obj).toEqual<typeof obj>({ a: 1, b: "world", c: false });
// @ts-expect-error - Type 'boolean' is not assignable to type 'number[]'.
expect(numArr).toEqual<number[]>(true);
// @ts-expect-error - Too many arguments for specific overload
expect(numArr).toEqual<number[]>([1, 2], [3]);
// @ts-expect-error - Expecting object, passed number
expect(obj).toEqual<object>(123);
});
test("toStrictEqual", () => {
expect(numArr).toStrictEqual([1, 2, 3]);
expect(obj).toStrictEqual({ a: 1, b: "world", 10: true });
// @ts-expect-error - Type 'string' is not assignable to type 'number' at index 0.
expect(numArr).toStrictEqual<number[]>(["1", 2, 3]);
// @ts-expect-error - Properties are missing
expect(obj).toStrictEqual<typeof obj>({ a: 1 });
// @ts-expect-error - Type 'boolean' is not assignable to type 'number[]'.
expect(numArr).toStrictEqual<number[]>(true);
// @ts-expect-error - Too many arguments for specific overload
expect(numArr).toStrictEqual<number[]>([1, 2], [3]);
// @ts-expect-error - Expecting object, passed number
expect(obj).toStrictEqual<object>(123);
});
test("toBeOneOf", () => {
expect(num).toBeOneOf([1, 2, 3]);
expect(str).toBeOneOf(strArr);
expect(num).toBeOneOf(numSet);
// @ts-expect-error - Argument of type 'number[]' is not assignable to parameter of type 'Iterable<string>'.
expect(str).toBeOneOf<Iterable<string>>(numArr);
// @ts-expect-error - Argument of type 'string[]' is not assignable to parameter of type 'Iterable<number>'.
expect(num).toBeOneOf<Iterable<number>>(strArr);
// @ts-expect-error - Argument of type 'Set<number>' is not assignable to parameter of type 'Iterable<string>'.
expect(str).toBeOneOf<Iterable<string>>(numSet);
// @ts-expect-error - Argument must be iterable
expect(num).toBeOneOf<number>(1);
// @ts-expect-error - Expecting string iterable, passed number iterable
expect(str).toBeOneOf<Iterable<string>>([1, 2, 3]);
});
test("toContainKey", () => {
expect(obj).toContainKey("a");
expect(obj).toContainKey("b");
// @ts-expect-error simple check for key does not exist
expect(obj).toContainKey("c");
expect(obj).toContainKey(10); // object key is number
// @ts-expect-error - Argument of type '"c"' is not assignable to parameter of type 'number | "a" | "b"'.
expect(obj).toContainKey<typeof obj>("c");
// @ts-expect-error - Argument of type 'boolean' is not assignable to parameter of type 'string | number'.
expect(obj).toContainKey<typeof obj>(true);
// @ts-expect-error - Too many arguments for specific overload
expect(obj).toContainKey<typeof obj>("a", "b");
// @ts-expect-error - Argument of type 'symbol' is not assignable to parameter of type 'string | number'.
expect(obj).toContainKey<typeof obj>(Symbol("a"));
});
test("toContainAllKeys", () => {
expect(obj).toContainAllKeys(["a", "b"]);
expect(obj).toContainAllKeys([10, "a"]);
// @ts-expect-error simple check for key does not exist
expect(obj).toContainAllKeys(["c"]);
// @ts-expect-error - Type '"c"' is not assignable to type 'number | "a" | "b"'.
expect(obj).toContainAllKeys<(typeof obj)[]>(["a", "c"]);
// @ts-expect-error - Type 'boolean' is not assignable to type 'string | number'.
expect(obj).toContainAllKeys<(typeof obj)[]>(["a", true]);
// @ts-expect-error - Argument must be an array
expect(obj).toContainAllKeys<Array<typeof obj>>("a");
// @ts-expect-error - Array element type 'symbol' is not assignable to 'string | number'.
expect(obj).toContainAllKeys<(typeof obj)[]>(["a", Symbol("b")]);
});
test("toContainAnyKeys", () => {
expect(obj).toContainAnyKeys(["a", "b", 10]);
// @ts-expect-error simple check for key does not exist
expect(obj).toContainAnyKeys(["c"]);
// @ts-expect-error - 11 is not a key
expect(obj).toContainAnyKeys(["a", "b", 11]);
// @ts-expect-error - c is not a key
expect(obj).toContainAnyKeys(["a", "c"]); // c doesn't exist, but 'a' does
// @ts-expect-error d is not a key
expect(obj).toContainAnyKeys([10, "d"]);
// @ts-expect-error - Type '"c"' is not assignable to type 'number | "a" | "b"'. Type '"d"' is not assignable to type 'number | "a" | "b"'.
expect(obj).toContainAnyKeys<(typeof obj)[]>(["c", "d"]);
// @ts-expect-error - Type 'boolean' is not assignable to type 'string | number'.
expect(obj).toContainAnyKeys<(typeof obj)[]>([true, false]);
// @ts-expect-error - Argument must be an array
expect(obj).toContainAnyKeys<Array<typeof obj>>("a");
// @ts-expect-error - Array element type 'symbol' is not assignable to 'string | number'.
expect(obj).toContainAnyKeys<(typeof obj)[]>([Symbol("a")]);
});
test("toContainKeys", () => {
// Alias for toContainAllKeys
expect(obj).toContainKeys(["a", "b"]);
expect(obj).toContainKeys([10, "a"]);
// @ts-expect-error simple check for key does not exist
expect(obj).toContainKeys(["c"]);
// @ts-expect-error - Type '"c"' is not assignable to type 'number | "a" | "b"'.
expect(obj).toContainKeys<(typeof obj)[]>(["a", "c"]);
// @ts-expect-error - Type 'boolean' is not assignable to type 'string | number'.
expect(obj).toContainKeys<(typeof obj)[]>(["a", true]);
// @ts-expect-error - Argument must be an array
expect(obj).toContainKeys<Array<typeof obj>>("a");
// @ts-expect-error - Array element type 'symbol' is not assignable to 'string | number'.
expect(obj).toContainKeys<(typeof obj)[]>(["a", Symbol("b")]);
});
test("toContainEqual", () => {
expect(mixedArr).toContainEqual(1);
expect(mixedArr).toContainEqual("a");
expect(mixedArr).toContainEqual(true);
// @ts-expect-error - Argument of type 'null' is not assignable to parameter of type 'string | number | boolean'.
expect(mixedArr).toContainEqual<string | number | boolean>(null);
// @ts-expect-error - Argument of type 'number[]' is not assignable to parameter of type 'string | number | boolean'.
expect(mixedArr).toContainEqual<string | number | boolean>(numArr);
// @ts-expect-error - Too many arguments for specific overload
expect(mixedArr).toContainEqual<string | number | boolean>(1, 2);
// @ts-expect-error - Expecting string | number | boolean, got object
expect(mixedArr).toContainEqual<string | number | boolean>({ a: 1 });
});
});
const mySpyOnObjectWithOptionalMethod: {
optionalMethod?: (input: { question: string }) => { answer: string };
} = {
optionalMethod: input => ({ answer: `Aswer to ${input.question}` }),
};
const mySpiedMethodOfOptional = spyOn(mySpyOnObjectWithOptionalMethod, "optionalMethod");
mySpiedMethodOfOptional({ question: "asdf" });
expectType<Mock<(input: { question: string }) => { answer: string }>>(mySpiedMethodOfOptional);
const myNormalSpyOnObject = {
normalMethod: (name: string) => `Hello ${name}`,
};
const myNormalSpiedMethod = spyOn(myNormalSpyOnObject, "normalMethod");
myNormalSpiedMethod("asdf");
expectType<Mock<(name: string) => string>>(myNormalSpiedMethod);
const spy = spyOn(console, "log");
expectType(spy.mock.calls).is<any[][]>();
jest.spyOn(console, "log");
jest.fn(() => 123 as const);
xtest("", () => {});
xdescribe("", () => {});
xit("", () => {});
test("expectTypeOf basic type checks", () => {
expectTypeOf({ name: "test" }).toMatchObjectType<{ name: string }>();
// @ts-expect-error
expectTypeOf({ name: 123 }).toMatchObjectType<{ name: string }>();
});
mock.clearAllMocks();
test
.each([
[1, 2, 3],
[4, 5, 6],
])
.todo("test.each", (a, b, c, done) => {
expectType<number>(a);
expectType<number>(b);
expectType<number>(c);
expectType<(err?: unknown) => void>(done);
});
describe.each([
[1, 2, 3],
[4, 5, 6],
])("describe.each", (a, b, c) => {
expectType<number>(a);
expectType<number>(b);
expectType<number>(c);
});
declare let mylist: number[];
describe.each(mylist)("describe.each", a => {
expectTypeOf(a).toBeNumber();
});
test.each(mylist)("test.each", (a, done) => {
expectTypeOf(a).toBeNumber();
expectType<(err?: unknown) => void>(done);
});
// Advanced use case tests for #18511:
// 1. => When assignable to, we should pass (e.g. new Set() is assignable to Set<string>).
// But when unassigbale, we should type error (e.g `string` is not assignable to `"bun"`)
// 2. => Expect that exact matches pass
// 3. => Expect that when we opt out of type safety, any value can be passed
declare const input: "bun" | "baz" | null;
declare const expected: string;
// @ts-expect-error
/** 1. **/ expect(input).toBe(expected); // Type error - string is not assignable to `'bun' | ...`
/** 2. **/ expect(input).toBe("bun"); // happy!
/** 3. **/ expect(input).toBe<string>(expected); // happy! We opted out of type safety for this expectation
declare const setOfStrings: Set<string>;
/** 1. **/ expect(setOfStrings).toBe(new Set()); // this is inferrable to Set<string> so this should pass
/** 2. **/ expect(setOfStrings).toBe(new Set<string>()); // exact, so we are happy!
/** 3. **/ expect(setOfStrings).toBe<Set<string>>(new Set()); // happy! We opted out of type safety for this expectation
// Cases for #24591
declare const unknownMatchers: Matchers<unknown>;
unknownMatchers.toContainKeys(["a", "b"]);
unknownMatchers.toContainAnyKeys(["a", "b"]);
unknownMatchers.toContainAllKeys(["a", "b"]);
unknownMatchers.toContainKey("a");
unknownMatchers.toContainEqual([""]);
unknownMatchers.toEqual(["a", "b"]);
unknownMatchers.toBeCloseTo(2);
unknownMatchers.toBe("a");
@@ -0,0 +1,220 @@
// This test is currently failing, but it's not so important that it blocks a release
// type utf_8 = "unicode-1-1-utf-8" | "utf-8" | "utf8";
// type ibm866 = "866" | "cp866" | "csibm866" | "ibm866";
// type iso_8859_2 =
// | "csisolatin2"
// | "iso-8859-2"
// | "iso-ir-101"
// | "iso8859-2"
// | "iso88592"
// | "iso_8859-2"
// | "iso_8859-2:1987"
// | "l2"
// | "latin2";
// type iso_8859_3 =
// | "csisolatin3"
// | "iso-8859-3"
// | "iso-ir-109"
// | "iso8859-3"
// | "iso88593"
// | "iso_8859-3"
// | "iso_8859-3:1988"
// | "l3"
// | "latin3";
// type iso_8859_4 =
// | "csisolatin4"
// | "iso-8859-4"
// | "iso-ir-110"
// | "iso8859-4"
// | "iso88594"
// | "iso_8859-4"
// | "iso_8859-4:1988"
// | "l4"
// | "latin4";
// type iso_8859_5 =
// | "csisolatincyrillic"
// | "cyrillic"
// | "iso-8859-5"
// | "iso-ir-144"
// | "iso88595"
// | "iso_8859-5"
// | "iso_8859-5:1988";
// type iso_8859_6 =
// | "arabic"
// | "asmo-708"
// | "csiso88596e"
// | "csiso88596i"
// | "csisolatinarabic"
// | "ecma-114"
// | "iso-8859-6"
// | "iso-8859-6-e"
// | "iso-8859-6-i"
// | "iso-ir-127"
// | "iso8859-6"
// | "iso88596"
// | "iso_8859-6"
// | "iso_8859-6:1987";
// type iso_8859_7 =
// | "csisolatingreek"
// | "ecma-118"
// | "elot_928"
// | "greek"
// | "greek8"
// | "iso-8859-7"
// | "iso-ir-126"
// | "iso8859-7"
// | "iso88597"
// | "iso_8859-7"
// | "iso_8859-7:1987"
// | "sun_eu_greek";
// type iso_8859_8 =
// | "csiso88598e"
// | "csisolatinhebrew"
// | "hebrew"
// | "iso-8859-8"
// | "iso-8859-8-e"
// | "iso-ir-138"
// | "iso8859-8"
// | "iso88598"
// | "iso_8859-8"
// | "iso_8859-8:1988"
// | "visual";
// type iso_8859_8i = "csiso88598i" | "iso-8859-8-i" | "logical";
// type iso_8859_10 = "csisolatin6" | "iso-8859-10" | "iso-ir-157" | "iso8859-10" | "iso885910" | "l6" | "latin6";
// type iso_8859_13 = "iso-8859-13" | "iso8859-13" | "iso885913";
// type iso_8859_14 = "iso-8859-14" | "iso8859-14" | "iso885914";
// type iso_8859_15 = "csisolatin9" | "iso-8859-15" | "iso8859-15" | "iso885915" | "l9" | "latin9";
// type iso_8859_16 = "iso-8859-16";
// type koi8_r = "cskoi8r" | "koi" | "koi8" | "koi8-r" | "koi8_r";
// type koi8_u = "koi8-u";
// type macintosh = "csmacintosh" | "mac" | "macintosh" | "x-mac-roman";
// type windows_874 = "dos-874" | "iso-8859-11" | "iso8859-11" | "iso885911" | "tis-620" | "windows-874";
// type windows_1250 = "cp1250" | "windows-1250" | "x-cp1250";
// type windows_1251 = "cp1251" | "windows-1251" | "x-cp1251";
// type windows_1252 =
// | "ansi_x3.4-1968"
// | "ascii"
// | "cp1252"
// | "cp819"
// | "csisolatin1"
// | "ibm819"
// | "iso-8859-1"
// | "iso-ir-100"
// | "iso8859-1"
// | "iso88591"
// | "iso_8859-1"
// | "iso_8859-1:1987"
// | "l1"
// | "latin1"
// | "us-ascii"
// | "windows-1252"
// | "x-cp1252";
// type windows_1253 = "cp1253" | "windows-1253" | "x-cp1253";
// type windows_1254 =
// | "cp1254"
// | "csisolatin5"
// | "iso-8859-9"
// | "iso-ir-148"
// | "iso8859-9"
// | "iso88599"
// | "iso_8859-9"
// | "iso_8859-9:1989"
// | "l5"
// | "latin5"
// | "windows-1254"
// | "x-cp1254";
// type windows_1255 = "cp1255" | "windows-1255" | "x-cp1255";
// type windows_1256 = "cp1256" | "windows-1256" | "x-cp1256";
// type windows_1257 = "cp1257" | "windows-1257" | "x-cp1257";
// type windows_1258 = "cp1258" | "windows-1258" | "x-cp1258";
// type x_mac_cyrillic = "x-mac-cyrillic" | "x-mac-ukrainian";
// type gbk =
// | "chinese"
// | "csgb2312"
// | "csiso58gb231280"
// | "gb2312"
// | "gb_2312"
// | "gb_2312-80"
// | "gbk"
// | "iso-ir-58"
// | "x-gbk";
// type gb18030 = "gb18030";
// type hz_gb_2312 = "hz-gb-2312";
// type big5 = "big5" | "big5-hkscs" | "cn-big5" | "csbig5" | "x-x-big5";
// type euc_jp = "cseucpkdfmtjapanese" | "euc-jp" | "x-euc-jp";
// type iso_2022_jp = "csiso2022jp" | "iso-2022-jp";
// type shift_jis = "csshiftjis" | "ms_kanji" | "shift-jis" | "shift_jis" | "sjis" | "windows-31j" | "x-sjis";
// type euc_kr =
// | "cseuckr"
// | "csksc56011987"
// | "euc-kr"
// | "iso-ir-149"
// | "korean"
// | "ks_c_5601-1987"
// | "ks_c_5601-1989"
// | "ksc5601"
// | "ksc_5601"
// | "windows-949";
// type iso_2022_kr = "csiso2022kr" | "iso-2022-kr";
// type utf_16be = "utf-16be";
// type utf_16le = "utf-16" | "utf-16le";
// type x_user_defined = "x-user-defined";
// type replacement = "iso-2022-cn" | "iso-2022-cn-ext";
// type TextEncoding =
// | utf_8
// | ibm866
// | iso_8859_2
// | iso_8859_3
// | iso_8859_4
// | iso_8859_5
// | iso_8859_6
// | iso_8859_7
// | iso_8859_8
// | iso_8859_8i
// | iso_8859_10
// | iso_8859_13
// | iso_8859_14
// | iso_8859_15
// | iso_8859_16
// | koi8_r
// | koi8_u
// | macintosh
// | windows_874
// | windows_1250
// | windows_1251
// | windows_1252
// | windows_1253
// | windows_1254
// | windows_1255
// | windows_1256
// | windows_1257
// | windows_1258
// | x_mac_cyrillic
// | gbk
// | gb18030
// | hz_gb_2312
// | big5
// | euc_jp
// | iso_2022_jp
// | shift_jis
// | euc_kr
// | iso_2022_kr
// | utf_16be
// | utf_16le
// | x_user_defined
// | replacement;
// export type TextDecoderOptions = ConstructorParameters<typeof TextDecoder>[1];
// function decode(encoding: TextEncoding, array: ArrayBufferView | ArrayBuffer, options?: TextDecoderOptions): string {
// const decoder = new TextDecoder(encoding, options);
// return decoder.decode(array);
// }
// decode("utf-8", new Uint8Array([0x41, 0x42, 0x43]), {
// fatal: true,
// });
export {};
+32
View File
@@ -0,0 +1,32 @@
import tls from "node:tls";
tls.getCiphers()[0];
tls.connect({
host: "localhost",
port: 80,
ca: "asdf",
cert: "path to cert",
});
tls.connect({
host: "localhost",
port: 80,
ca: Bun.file("asdf"),
cert: Bun.file("path to cert"),
ciphers: "adsf",
});
tls.connect({
host: "localhost",
port: 80,
ca: Buffer.from("asdf"),
cert: Buffer.from("asdf"),
});
tls.connect({
host: "localhost",
port: 80,
ca: new Uint8Array([1, 2, 3]),
cert: new Uint8Array([1, 2, 3]),
});
@@ -0,0 +1,10 @@
import { TOML } from "bun";
import data from "./bunfig.toml";
import { expectType } from "./utilities";
expectType<any>(data);
expectType(Bun.TOML.parse(data)).is<object>();
expectType(TOML.parse(data)).is<object>();
// `undefined` when the input is `undefined`, a function, or a symbol.
expectType(Bun.TOML.stringify({ abc: "def" })).is<string | undefined>();
expectType(TOML.stringify({ abc: "def" })).is<string | undefined>();
+49
View File
@@ -0,0 +1,49 @@
import * as tty from "tty";
const rs = new tty.ReadStream(234, {
allowHalfOpen: true,
readable: true,
signal: new AbortSignal(),
writable: true,
});
const ws = new tty.WriteStream(234);
process.stdin.setRawMode(true);
process.stdin.setRawMode(false);
process.stdin.isRaw;
process.stdin.setRawMode(true).isRaw;
rs.isRaw;
rs.setRawMode(true);
rs.setRawMode(false);
rs.setRawMode(true).isRaw;
rs.isTTY;
ws.isPaused;
ws.isTTY;
ws.bytesWritten;
ws.bytesRead;
ws.columns;
ws.rows;
ws.isTTY;
ws.clearLine(1);
ws.clearLine(0);
ws.clearScreenDown();
ws.cursorTo(1);
ws.cursorTo(1, 2);
ws.cursorTo(1, () => {});
ws.cursorTo(1, 2, () => {});
ws.moveCursor(1, 2);
ws.moveCursor(1, 2, () => {});
ws.clearLine(1, () => {});
ws.clearLine(0, () => {});
ws.clearScreenDown(() => {});
ws.cursorTo(1, () => {});
process.stdout.clearLine;
process.stdout.clearScreenDown;
process.stdout.cursorTo;
process.stdout.moveCursor;
process.stdout.getColorDepth;
process.stdout.getWindowSize;
+58
View File
@@ -0,0 +1,58 @@
import * as Bun from "bun";
import { expectType } from "./utilities";
const socket = await Bun.udpSocket({
port: 0,
});
expectType(socket.hostname).is<string>();
expectType(socket.port).is<number>();
expectType(socket.address).is<Bun.SocketAddress>();
expectType(socket.binaryType).is<Bun.BinaryType>();
expectType(socket.closed).is<boolean>();
expectType(socket.send("Hello", 41234, "127.0.0.1")).is<boolean>();
expectType(socket.send(new Uint8Array([1, 2, 3]), 41234, "127.0.0.1")).is<boolean>();
expectType(socket.sendMany(["Hello", 41234, "127.0.0.1", "World", 41235, "127.0.0.2"])).is<number>();
expectType(socket.setBroadcast(true)).is<boolean>();
expectType(socket.setTTL(64)).is<number>();
expectType(socket.setMulticastTTL(2)).is<number>();
expectType(socket.setMulticastLoopback(true)).is<boolean>();
expectType(socket.setMulticastInterface("192.168.1.100")).is<boolean>();
expectType(socket.addMembership("224.0.0.1")).is<boolean>();
expectType(socket.addMembership("224.0.0.1", "192.168.1.100")).is<boolean>();
expectType(socket.dropMembership("224.0.0.1")).is<boolean>();
expectType(socket.dropMembership("224.0.0.1", "192.168.1.100")).is<boolean>();
expectType(socket.addSourceSpecificMembership("10.0.0.1", "232.0.0.1")).is<boolean>();
expectType(socket.addSourceSpecificMembership("10.0.0.1", "232.0.0.1", "192.168.1.100")).is<boolean>();
expectType(socket.dropSourceSpecificMembership("10.0.0.1", "232.0.0.1")).is<boolean>();
expectType(socket.dropSourceSpecificMembership("10.0.0.1", "232.0.0.1", "192.168.1.100")).is<boolean>();
expectType(socket.ref()).is<void>();
expectType(socket.unref()).is<void>();
expectType(socket.close()).is<void>();
const connectedSocket = await Bun.udpSocket({
port: 0,
connect: {
hostname: "127.0.0.1",
port: 41234,
},
});
expectType(connectedSocket.remoteAddress).is<Bun.SocketAddress>();
expectType(connectedSocket.send("Hello")).is<boolean>();
expectType(connectedSocket.send(new Uint8Array([1, 2, 3]))).is<boolean>();
expectType(connectedSocket.sendMany(["Hello", "World"])).is<number>();
expectType(connectedSocket.setBroadcast(false)).is<boolean>();
expectType(connectedSocket.setTTL(128)).is<number>();
expectType(connectedSocket.setMulticastTTL(1)).is<number>();
expectType(connectedSocket.setMulticastLoopback(false)).is<boolean>();
connectedSocket.close();
+21
View File
@@ -0,0 +1,21 @@
const myUrl = new URL("hello");
myUrl.searchParams.toJSON();
const mySearchParams = new URLSearchParams("hello");
mySearchParams.toJSON();
import { URL as NodeURL, URLSearchParams as NodeURLSearchParams } from "node:url";
const nodeUrl = new NodeURL("hello");
nodeUrl.searchParams.toJSON();
const nodeSearchParams = new NodeURLSearchParams("hello");
nodeSearchParams.toJSON();
import { URL as UrlURL, URLSearchParams as UrlURLSearchParams } from "url";
const urlUrl = new UrlURL("hello");
urlUrl.searchParams.toJSON();
const urlSearchParams = new UrlURLSearchParams("hello");
urlSearchParams.toJSON();
@@ -0,0 +1,7 @@
import util from "node:util";
import types from "node:util/types";
util.types;
types.isAnyArrayBuffer;
types.isCryptoKey;
util.inspect;
@@ -0,0 +1,42 @@
type IfEquals<T, U, Y = true, N = false> = (<G>() => G extends T ? 1 : 2) extends <G>() => G extends U ? 1 : 2 ? Y : N;
export function expectType<T>(): {
/**
* @example
* ```ts
* expectType<number>().is<1>(); // fail
* expectType<number>().is<any>(); // fail
* expectType<any>().is<number>(); // fail
* expectType<number>().is<unknown>(); // fail
* expectType<number>().is<number>(); // pass
* expectType<Uint8Array>().is<Uint8Array>(); // pass
* ```
*/
is<X extends T>(...args: IfEquals<X, T> extends true ? [] : [expected: X, but_got: T]): void;
extends<X>(...args: T extends X ? [] : [expected: T, but_got: X]): void;
};
export function expectType<T>(arg: T): {
/**
* @example
* ```ts
* expectType(my_number).is<1>(); // fail
* expectType(my_number).is<any>(); // fail
* expectType(my_any).is<number>(); // fail
* expectType(my_number).is<unknown>(); // fail
* expectType(my_number).is<number>(); // pass
* expectType(my_Uint8Array).is<Uint8Array>(); // pass
* ```
*/
is<X extends T>(...args: IfEquals<X, T> extends true ? [] : [expected: X, but_got: T]): void;
extends<X>(...args: T extends X ? [] : [expected: T, but_got: X]): void;
toBeDefined(...args: undefined extends T ? [expected_something_but_got: undefined] : []): void;
};
export function expectType<T>(arg?: T) {
return { is() {}, extends() {} };
}
export declare function expectNotEmpty<T>(...args: [keyof T] extends [never] ? [value: never] : [value?: T]): void;
export declare const expectAssignable: <T>(expression: T) => void;
export declare const expectTypeEquals: <T, S>(expression: T extends S ? (S extends T ? true : false) : false) => void;
@@ -0,0 +1,42 @@
async () => {
// Fetch and compile a WebAssembly module
const response = await fetch("module.wasm");
const buffer = await response.arrayBuffer();
const module = await WebAssembly.compile(buffer);
// Create a WebAssembly Memory object
const memory = new WebAssembly.Memory({ initial: 1 });
// Create a WebAssembly Table object
const table = new WebAssembly.Table({ initial: 1, element: "anyfunc" });
// Instantiate the WebAssembly module
const instance = await WebAssembly.instantiate(module, {
js: {
log: (arg: any) => console.log("Logging from WASM:", arg),
tableFunc: () => console.log("Table function called"),
},
env: {
memory: memory,
table: table,
},
});
// Exported WebAssembly functions
const { exportedFunction } = instance.exports;
exportedFunction;
// Call an exported WebAssembly function
// exportedFunction();
// Interact with WebAssembly memory
const uint8Array = new Uint8Array(memory.buffer);
uint8Array[0] = 1; // Modify memory
// Use the WebAssembly Table
table.set(0, instance.exports.exportedTableFunction);
// eslint-disable-next-line
table.get(0)(); // Call a function stored in the table
// Additional operations with instance, memory, and table can be performed here
};
@@ -0,0 +1,271 @@
import { expectType } from "./utilities";
// WebSocket constructor tests
{
// Constructor with string URL only
new WebSocket("wss://dev.local");
// Constructor with string URL and protocols array
new WebSocket("wss://dev.local", ["proto1", "proto2"]);
// Constructor with string URL and single protocol string
new WebSocket("wss://dev.local", "proto1");
// Constructor with URL object only
new WebSocket(new URL("wss://dev.local"));
// Constructor with URL object and protocols array
new WebSocket(new URL("wss://dev.local"), ["proto1", "proto2"]);
// Constructor with URL object and single protocol string
new WebSocket(new URL("wss://dev.local"), "proto1");
// Constructor with string URL and options object with protocols
new WebSocket("wss://dev.local", {
protocols: ["proto1", "proto2"],
});
// Constructor with string URL and options object with protocol
new WebSocket("wss://dev.local", {
protocol: "proto1",
});
// Constructor with URL object and options with TLS settings
new WebSocket(new URL("wss://dev.local"), {
protocol: "proto1",
tls: {
rejectUnauthorized: false,
},
});
// Constructor with headers
new WebSocket("wss://dev.local", {
headers: {
"Cookie": "session=123456",
"User-Agent": "BunWebSocketTest",
},
});
// Constructor with full options object
new WebSocket("wss://dev.local", {
protocols: ["proto1", "proto2"],
headers: {
"Cookie": "session=123456",
},
tls: {
rejectUnauthorized: true,
},
});
}
// Assignability test
{
function toAny<T>(value: T): any {
return value;
}
const AnySocket = toAny(WebSocket);
const ws: WebSocket = new AnySocket("wss://dev.local");
ws.close();
ws.addEventListener("open", e => expectType(e).is<Event>());
ws.addEventListener("message", e => expectType(e).is<MessageEvent>());
ws.addEventListener("message", (e: MessageEvent<string>) => expectType(e).is<MessageEvent<string>>());
ws.addEventListener("message", (e: MessageEvent<string>) => expectType(e.data).is<string>());
}
// WebSocket static properties test
{
expectType(WebSocket.CONNECTING).is<0>();
expectType(WebSocket.OPEN).is<1>();
expectType(WebSocket.CLOSING).is<2>();
expectType(WebSocket.CLOSED).is<3>();
const instance: WebSocket = null as never;
expectType(instance.CONNECTING).is<0>();
expectType(instance.OPEN).is<1>();
expectType(instance.CLOSING).is<2>();
expectType(instance.CLOSED).is<3>();
}
// WebSocket event handlers test
{
const ws = new WebSocket("wss://dev.local");
// Using event handler properties
ws.onopen = (event: Event) => {
expectType(event).is<Event>();
};
ws.onmessage = (event: MessageEvent<string>) => {
expectType(event.data).is<string>();
};
ws.onerror = (event: Event) => {
expectType(event).is<Event>();
};
ws.onclose = (event: CloseEvent) => {
expectType(event).is<CloseEvent>();
expectType(event.code).is<number>();
expectType(event.reason).is<string>();
expectType(event.wasClean).is<boolean>();
};
// Using event handler properties without typing the agument
ws.onopen = event => {
expectType(event).is<Event>();
};
ws.onmessage = event => {
expectType(event.data).is<any>();
if (typeof event.data === "string") {
expectType(event.data).is<string>();
} else if (event.data instanceof ArrayBuffer) {
expectType(event.data).is<ArrayBuffer>();
}
};
ws.onerror = event => {
expectType(event).is<Event>();
};
ws.onclose = event => {
expectType(event).is<CloseEvent>();
expectType(event.code).is<number>();
expectType(event.reason).is<string>();
expectType(event.wasClean).is<boolean>();
};
}
// WebSocket addEventListener test
{
const ws = new WebSocket("wss://dev.local");
// Event handler functions
const handleOpen = (event: Event) => {
expectType(event).is<Event>();
};
const handleMessage = (event: MessageEvent<string>) => {
expectType(event.data).is<string>();
};
const handleError = (event: Event) => {
expectType(event).is<Event>();
};
const handleClose = (event: CloseEvent) => {
expectType(event).is<CloseEvent>();
expectType(event.code).is<number>();
expectType(event.reason).is<string>();
expectType(event.wasClean).is<boolean>();
};
// Add event listeners
ws.addEventListener("open", handleOpen);
ws.addEventListener("message", handleMessage);
ws.addEventListener("error", handleError);
ws.addEventListener("close", handleClose);
// Remove event listeners
ws.removeEventListener("open", handleOpen);
ws.removeEventListener("message", handleMessage);
ws.removeEventListener("error", handleError);
ws.removeEventListener("close", handleClose);
}
// WebSocket property access test
{
const ws = new WebSocket("wss://dev.local");
// Read various properties
expectType(ws.readyState).is<0 | 2 | 1 | 3>();
expectType(ws.bufferedAmount).is<number>();
expectType(ws.url).is<string>();
expectType(ws.protocol).is<string>();
expectType(ws.extensions).is<string>();
// Legacy URL property (deprecated but exists)
expectType(ws.URL).is<string>();
// Set binary type
ws.binaryType = "arraybuffer";
ws.binaryType = "nodebuffer";
}
// WebSocket send method test
{
const ws = new WebSocket("wss://dev.local");
// Send string data
ws.send("Hello, server!");
// Send ArrayBuffer
const buffer = new ArrayBuffer(10);
ws.send(buffer);
// Send ArrayBufferView (Uint8Array)
const uint8Array = new Uint8Array(buffer);
ws.send(uint8Array);
// --------------------------------------- //
// `.send(blob)` is not supported yet
// --------------------------------------- //
// // Send Blob
// const blob = new Blob(["Hello, server!"]);
// ws.send(blob);
// --------------------------------------- //
}
// WebSocket close method test
{
const ws = new WebSocket("wss://dev.local");
// Close without parameters
ws.close();
// Close with code
ws.close(1000);
// Close with code and reason
ws.close(1001, "Going away");
}
// Bun-specific WebSocket extensions test
{
const ws = new WebSocket("wss://dev.local");
// Send ping frame with no data
ws.ping();
// Send ping frame with string data
ws.ping("ping data");
// Send ping frame with ArrayBuffer
const pingBuffer = new ArrayBuffer(4);
ws.ping(pingBuffer);
// Send ping frame with ArrayBufferView
const pingView = new Uint8Array(pingBuffer);
ws.ping(pingView);
// Send pong frame with no data
ws.pong();
// Send pong frame with string data
ws.pong("pong data");
// Send pong frame with ArrayBuffer
const pongBuffer = new ArrayBuffer(4);
ws.pong(pongBuffer);
// Send pong frame with ArrayBufferView
const pongView = new Uint8Array(pongBuffer);
ws.pong(pongView);
// Terminate the connection immediately
ws.terminate();
}
@@ -0,0 +1,62 @@
import { Worker as NodeWorker } from "node:worker_threads";
import * as tsd from "./utilities";
const webWorker = new Worker("./worker.js");
webWorker.addEventListener("message", event => {
tsd.expectType<MessageEvent>(event);
});
webWorker.addEventListener("error", event => {
tsd.expectType<ErrorEvent>(event);
});
webWorker.addEventListener("messageerror", event => {
tsd.expectType<MessageEvent>(event);
});
webWorker.onmessage = ev => "asdf";
webWorker.onmessageerror = ev => "asdf";
webWorker.postMessage("asdf", []);
webWorker.terminate();
webWorker.addEventListener("close", () => {});
webWorker.removeEventListener("sadf", () => {});
// these methods don't exist if lib.dom.d.ts is present
webWorker.ref();
webWorker.unref();
webWorker.threadId;
const nodeWorker = new NodeWorker("./worker.ts");
nodeWorker.on("message", event => {
console.log("Message from worker:", event);
});
nodeWorker.postMessage("Hello from main thread!");
const workerURL = new URL("worker.ts", "/path/to/").href;
const _worker2 = new Worker(workerURL);
nodeWorker.postMessage("hello");
webWorker.onmessage = event => {
console.log(event.data);
};
// On the worker thread, `postMessage` is automatically "routed" to the parent thread.
postMessage({ hello: "world" });
// On the main thread
nodeWorker.postMessage({ hello: "world" });
// ...some time later
await nodeWorker.terminate();
// Bun.pathToFileURL
const _worker3 = new Worker(new URL("worker.ts", "/path/to/").href, {
ref: true,
smol: true,
credentials: "same-origin",
name: "a name",
env: {
envValue: "hello",
},
});
export { _worker2, _worker3, nodeWorker as worker };
+54
View File
@@ -0,0 +1,54 @@
import { XML } from "bun";
import doc from "./data.xml";
import { expectType } from "./utilities";
expectType(doc).is<XML.Document>();
expectType(Bun.XML.parse("<a/>")).is<XML.Document>();
expectType(XML.parse(new Uint8Array())).is<XML.Document>();
expectType(XML.parse("<a/>", { compact: true })).is<XML.Document>();
expectType(XML.parse("<a/>", { compact: false })).is<XML.Node>();
expectType(XML.parse("<a/>", { compact: false }).children).is<
Array<string | XML.Node | XML.Comment | XML.ProcessingInstruction>
>();
expectType(XML.parse("<a/>", {} as XML.ParseOptions)).is<XML.Document | XML.Node>();
// The compact value space is closed: narrowing needs no casts.
{
const root: XML.Value | undefined = XML.parse("<a/>").a;
if (typeof root === "object") {
const child = root.item;
expectType(child).is<XML.Value | XML.Value[] | undefined>();
for (const item of Array.isArray(child) ? child : [child]) {
if (typeof item === "object") expectType(item["@id"]).is<XML.Value | XML.Value[] | undefined>();
else expectType(item).is<string | undefined>();
}
}
}
// Tree children discriminate by key.
for (const c of XML.parse("<a/>", { compact: false }).children) {
if (typeof c === "string") expectType(c).is<string>();
else if ("name" in c) expectType(c).is<XML.Node>();
else if ("comment" in c) expectType(c).is<XML.Comment>();
else expectType(c).is<XML.ProcessingInstruction>();
}
// @ts-expect-error
XML.parse({});
// @ts-expect-error
XML.parse("<a/>", { compact: "no" });
// @ts-expect-error - reserved for a reviver, not accepted yet
XML.parse("<a/>", (key: string, value: unknown) => value);
expectType(XML.stringify({ a: { "@id": "1", b: ["x"] } })).is<string>();
expectType(XML.stringify({ name: "a", attributes: {}, children: ["x"] } satisfies XML.Node, null, 2)).is<string>();
expectType(
XML.stringify({ name: "a", children: ["x", 1, null, { comment: "c" }, { target: "p", data: "" }, { name: "b" }] }),
).is<string>();
expectType(XML.stringify(XML.parse("<a/>", { compact: false }))).is<string>();
// `undefined` when the input is `undefined`, a function, or a symbol.
expectType(XML.stringify(undefined)).is<string | undefined>();
expectType(XML.stringify(Symbol() as unknown)).is<string | undefined>();
// @ts-expect-error
XML.stringify({ a: "1" }, (key: string, value: unknown) => value);
// @ts-expect-error
XML.stringify({ a: "1" }, null, 123n);
@@ -0,0 +1,10 @@
import { expectType } from "./utilities";
expectType(Bun.YAML.parse("")).is<unknown>();
// @ts-expect-error
expectType(Bun.YAML.parse({})).is<unknown>();
expectType(Bun.YAML.stringify({ abc: "def"})).is<string>();
// @ts-expect-error
expectType(Bun.YAML.stringify("hi", {})).is<string>();
// @ts-expect-error
expectType(Bun.YAML.stringify("hi", null, 123n)).is<string>();