initial commit
This commit is contained in:
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()
|
||||
},
|
||||
);
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
@@ -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" });
|
||||
@@ -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>();
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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);
|
||||
});
|
||||
@@ -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"] });
|
||||
}
|
||||
@@ -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 }
|
||||
@@ -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);
|
||||
@@ -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();
|
||||
@@ -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());
|
||||
@@ -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>>();
|
||||
@@ -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>();
|
||||
},
|
||||
},
|
||||
});
|
||||
@@ -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,
|
||||
});
|
||||
}
|
||||
@@ -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>>>();
|
||||
@@ -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,
|
||||
});
|
||||
@@ -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 {};
|
||||
@@ -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>();
|
||||
@@ -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;
|
||||
@@ -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();
|
||||
@@ -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 };
|
||||
@@ -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>();
|
||||
Reference in New Issue
Block a user