mirror of
https://github.com/oven-sh/bun
synced 2026-02-02 15:08:46 +00:00
types: correct ReadableStream methods, allow Response instance for serve routes under a method (#24872)
This commit is contained in:
25
packages/bun-types/overrides.d.ts
vendored
25
packages/bun-types/overrides.d.ts
vendored
@@ -23,16 +23,6 @@ interface BunConsumerConvenienceMethods {
|
||||
* Consume as JSON
|
||||
*/
|
||||
json(): Promise<any>;
|
||||
|
||||
/**
|
||||
* Consume as a FormData instance
|
||||
*/
|
||||
formData(): Promise<FormData>;
|
||||
|
||||
/**
|
||||
* Consume as an ArrayBuffer
|
||||
*/
|
||||
arrayBuffer(): Promise<ArrayBuffer>;
|
||||
}
|
||||
|
||||
declare module "stream/web" {
|
||||
@@ -51,6 +41,21 @@ declare module "buffer" {
|
||||
// slightly different from just "copying in the methods" (the difference is
|
||||
// related to how type parameters are resolved)
|
||||
bytes(): Promise<Uint8Array<ArrayBuffer>>;
|
||||
|
||||
/**
|
||||
* Consume the blob as a FormData instance
|
||||
*/
|
||||
formData(): Promise<FormData>;
|
||||
|
||||
/**
|
||||
* Consume the blob as an ArrayBuffer
|
||||
*/
|
||||
arrayBuffer(): Promise<ArrayBuffer>;
|
||||
|
||||
/**
|
||||
* Returns a readable stream of the blob's contents
|
||||
*/
|
||||
stream(): ReadableStream<Uint8Array<ArrayBuffer>>;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
29
packages/bun-types/serve.d.ts
vendored
29
packages/bun-types/serve.d.ts
vendored
@@ -283,7 +283,8 @@ declare module "bun" {
|
||||
* return new Response();
|
||||
* },
|
||||
* websocket: {
|
||||
* open(ws) {
|
||||
* data: {} as {accessToken: string | null},
|
||||
* message(ws) {
|
||||
* console.log(ws.data.accessToken);
|
||||
* }
|
||||
* }
|
||||
@@ -486,13 +487,15 @@ declare module "bun" {
|
||||
}
|
||||
|
||||
namespace Serve {
|
||||
type ExtractRouteParams<T> = T extends `${string}:${infer Param}/${infer Rest}`
|
||||
? { [K in Param]: string } & ExtractRouteParams<Rest>
|
||||
: T extends `${string}:${infer Param}`
|
||||
? { [K in Param]: string }
|
||||
: T extends `${string}*`
|
||||
? {}
|
||||
: {};
|
||||
type ExtractRouteParams<T> = string extends T
|
||||
? Record<string, string>
|
||||
: T extends `${string}:${infer Param}/${infer Rest}`
|
||||
? { [K in Param]: string } & ExtractRouteParams<Rest>
|
||||
: T extends `${string}:${infer Param}`
|
||||
? { [K in Param]: string }
|
||||
: T extends `${string}*`
|
||||
? {}
|
||||
: {};
|
||||
|
||||
/**
|
||||
* Development configuration for {@link Bun.serve}
|
||||
@@ -549,14 +552,16 @@ declare module "bun" {
|
||||
[Path in R]:
|
||||
| BaseRouteValue
|
||||
| Handler<BunRequest<Path>, Server<WebSocketData>, Response>
|
||||
| Partial<Record<HTTPMethod, Handler<BunRequest<Path>, Server<WebSocketData>, Response>>>;
|
||||
| Partial<Record<HTTPMethod, Handler<BunRequest<Path>, Server<WebSocketData>, Response> | Response>>;
|
||||
};
|
||||
|
||||
type RoutesWithUpgrade<WebSocketData, R extends string> = {
|
||||
[Path in R]:
|
||||
| BaseRouteValue
|
||||
| Handler<BunRequest<Path>, Server<WebSocketData>, Response | undefined | void>
|
||||
| Partial<Record<HTTPMethod, Handler<BunRequest<Path>, Server<WebSocketData>, Response | undefined | void>>>;
|
||||
| Partial<
|
||||
Record<HTTPMethod, Handler<BunRequest<Path>, Server<WebSocketData>, Response | undefined | void> | Response>
|
||||
>;
|
||||
};
|
||||
|
||||
type FetchOrRoutes<WebSocketData, R extends string> =
|
||||
@@ -786,7 +791,7 @@ declare module "bun" {
|
||||
* } satisfies Bun.Serve.Options<{ name: string }>;
|
||||
* ```
|
||||
*/
|
||||
type Options<WebSocketData, R extends string = never> = Bun.__internal.XOR<
|
||||
type Options<WebSocketData, R extends string = string> = Bun.__internal.XOR<
|
||||
HostnamePortServeOptions<WebSocketData>,
|
||||
UnixServeOptions<WebSocketData>
|
||||
> &
|
||||
@@ -1276,7 +1281,7 @@ declare module "bun" {
|
||||
* });
|
||||
* ```
|
||||
*/
|
||||
function serve<WebSocketData = undefined, R extends string = string>(
|
||||
function serve<WebSocketData = undefined, R extends string = never>(
|
||||
options: Serve.Options<WebSocketData, R>,
|
||||
): Server<WebSocketData>;
|
||||
}
|
||||
|
||||
@@ -435,7 +435,7 @@ describe("@types/bun integration test", () => {
|
||||
code: 2322,
|
||||
line: "24154.ts:11:3",
|
||||
message:
|
||||
"Type 'Blob' is not assignable to type 'import(\"buffer\").Blob'.\nThe types returned by 'stream()' are incompatible between these types.\nType 'ReadableStream<Uint8Array<ArrayBuffer>>' is missing the following properties from type 'ReadableStream<any>': blob, text, bytes, json, and 2 more.",
|
||||
"Type 'Blob' is not assignable to type 'import(\"buffer\").Blob'.\nThe types returned by 'stream()' are incompatible between these types.\nType 'ReadableStream<Uint8Array<ArrayBuffer>>' is missing the following properties from type 'ReadableStream<any>': blob, text, bytes, json",
|
||||
},
|
||||
{
|
||||
code: 2769,
|
||||
|
||||
@@ -335,3 +335,8 @@ new Error("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>();
|
||||
|
||||
@@ -20,13 +20,18 @@ export default {
|
||||
expectType(ws.data).is<{ name: string }>();
|
||||
},
|
||||
},
|
||||
} satisfies Bun.ServeOptions<{ 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 = never>(
|
||||
function test<T = undefined, R extends string = string>(
|
||||
name: string,
|
||||
options: Bun.Serve.Options<T, R>,
|
||||
{
|
||||
@@ -71,6 +76,11 @@ function test<T = undefined, R extends string = never>(
|
||||
}
|
||||
|
||||
test("basic", {
|
||||
routes: {
|
||||
"/123": {
|
||||
"GET": new Response("Cool/great"),
|
||||
},
|
||||
},
|
||||
fetch(req) {
|
||||
console.log(req.url); // => http://localhost:3000/
|
||||
return new Response("Hello World");
|
||||
@@ -459,15 +469,15 @@ 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: () => {
|
||||
//
|
||||
message: ws => {
|
||||
expectType(ws).is<Bun.ServerWebSocket<undefined>>();
|
||||
},
|
||||
},
|
||||
fetch: (req, server) => {
|
||||
@@ -481,8 +491,8 @@ test("yet another basic fetch and websocket message handler", {
|
||||
|
||||
test("websocket + upgrade on a route path", {
|
||||
websocket: {
|
||||
message: () => {
|
||||
//
|
||||
message: ws => {
|
||||
expectType(ws).is<Bun.ServerWebSocket<undefined>>();
|
||||
},
|
||||
},
|
||||
routes: {
|
||||
@@ -563,7 +573,7 @@ test(
|
||||
if (Math.random() > 0.5) return undefined;
|
||||
return new Response();
|
||||
},
|
||||
websocket: { message() {} },
|
||||
websocket: { message: ws => expectType(ws).is<Bun.ServerWebSocket<undefined>>() },
|
||||
},
|
||||
{
|
||||
overrideExpectBehavior: server => {
|
||||
@@ -816,3 +826,44 @@ test("multiple properties combined", {
|
||||
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">>();
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
@@ -69,3 +69,15 @@ Bun.file("./foo.csv")
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
// @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>>>();
|
||||
|
||||
Reference in New Issue
Block a user