@kichu12348/bunify 1.0.3 → 1.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/core.ts +124 -110
package/package.json
CHANGED
package/src/core.ts
CHANGED
|
@@ -22,10 +22,10 @@ export type BunifyRequest<T extends RouteSchema = RouteSchema, D = {}> = {
|
|
|
22
22
|
|
|
23
23
|
type Reply = ReturnType<typeof createReply>;
|
|
24
24
|
|
|
25
|
-
export type BunifyReply<T extends RouteSchema = RouteSchema> = Reply & {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
};
|
|
25
|
+
// export type BunifyReply<T extends RouteSchema = RouteSchema> = Reply & {
|
|
26
|
+
// send: (content: T["Reply"] | Response) => Response;
|
|
27
|
+
// json: (data: T["Reply"]) => Response;
|
|
28
|
+
// };
|
|
29
29
|
|
|
30
30
|
export type Handler<T extends RouteSchema = RouteSchema, D = {}> = (
|
|
31
31
|
request: BunifyRequest<T, D>,
|
|
@@ -68,59 +68,67 @@ interface RegisterOptions {
|
|
|
68
68
|
prefix?: string;
|
|
69
69
|
}
|
|
70
70
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
return {
|
|
78
|
-
get sent() {
|
|
79
|
-
return sent;
|
|
80
|
-
},
|
|
81
|
-
|
|
82
|
-
status(code: number) {
|
|
83
|
-
status = code;
|
|
84
|
-
return this;
|
|
85
|
-
},
|
|
86
|
-
code(code: number) {
|
|
87
|
-
return this.status(code);
|
|
88
|
-
},
|
|
89
|
-
headers(key: string, value: string) {
|
|
90
|
-
headers.set(key, value);
|
|
91
|
-
return this;
|
|
92
|
-
},
|
|
93
|
-
json(data: any) {
|
|
94
|
-
return this.headers("Content-Type", "application/json").send(data);
|
|
95
|
-
},
|
|
96
|
-
redirect(url: string) {
|
|
97
|
-
return this.status(302).headers("Location", url).send(null);
|
|
98
|
-
},
|
|
99
|
-
html(content: string) {
|
|
100
|
-
return this.headers("Content-Type", "text/html").send(content);
|
|
101
|
-
},
|
|
102
|
-
send(content: any) {
|
|
103
|
-
if (sent) {
|
|
104
|
-
throw new Error("Reply has already been sent.");
|
|
105
|
-
}
|
|
106
|
-
sent = true;
|
|
107
|
-
if (content instanceof Response) return content;
|
|
71
|
+
// Look at how lean this is! No closures, no heavy standard objects.
|
|
72
|
+
export class BunifyReply<T extends RouteSchema = RouteSchema> {
|
|
73
|
+
private _status: number = 200;
|
|
74
|
+
private _headers: Record<string, string> = {}; // Fast, plain object
|
|
75
|
+
private _body: any = null;
|
|
76
|
+
public sent: boolean = false;
|
|
108
77
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
78
|
+
status(code: number) {
|
|
79
|
+
this._status = code;
|
|
80
|
+
return this;
|
|
81
|
+
}
|
|
113
82
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
83
|
+
code(code: number) {
|
|
84
|
+
return this.status(code);
|
|
85
|
+
}
|
|
117
86
|
|
|
118
|
-
|
|
119
|
-
|
|
87
|
+
headers(key: string, value: string) {
|
|
88
|
+
this._headers[key] = value;
|
|
89
|
+
return this;
|
|
90
|
+
}
|
|
120
91
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
}
|
|
92
|
+
json(data: T["Reply"]) {
|
|
93
|
+
return this.headers("Content-Type", "application/json").send(data);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
redirect(url: string) {
|
|
97
|
+
return this.status(302).headers("Location", url).send(null);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
html(content: string) {
|
|
101
|
+
return this.headers("Content-Type", "text/html").send(content);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
send(content: T["Reply"] | Response | null) {
|
|
105
|
+
if (this.sent) {
|
|
106
|
+
throw new Error("Reply has already been sent.");
|
|
107
|
+
}
|
|
108
|
+
this.sent = true;
|
|
109
|
+
|
|
110
|
+
if (content instanceof Response) return content;
|
|
111
|
+
|
|
112
|
+
if (typeof content === "object" && content !== null) {
|
|
113
|
+
this._headers["Content-Type"] = "application/json";
|
|
114
|
+
content = JSON.stringify(content) as any;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
this._body = content;
|
|
118
|
+
return this.build();
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
build() {
|
|
122
|
+
if (this._body instanceof Response) return this._body;
|
|
123
|
+
return new Response(this._body, {
|
|
124
|
+
status: this._status,
|
|
125
|
+
headers: this._headers,
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function createReply() {
|
|
131
|
+
return new BunifyReply();
|
|
124
132
|
}
|
|
125
133
|
|
|
126
134
|
export class Bunify<Decorators extends Record<string, any> = {}> {
|
|
@@ -156,14 +164,26 @@ export class Bunify<Decorators extends Record<string, any> = {}> {
|
|
|
156
164
|
handler,
|
|
157
165
|
);
|
|
158
166
|
const wrappedHandler = async (req: Bun.BunRequest) => {
|
|
159
|
-
|
|
160
|
-
|
|
167
|
+
let parsedQuery: any = null;
|
|
161
168
|
const request: BunifyRequest<T, Decorators> = {
|
|
162
169
|
raw: req,
|
|
163
|
-
query
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
170
|
+
// Lazily parse query parameters when accessed for the first time to optimize performance
|
|
171
|
+
get query() {
|
|
172
|
+
if (parsedQuery !== null) return parsedQuery;
|
|
173
|
+
|
|
174
|
+
const urlStr = req.url;
|
|
175
|
+
const queryStart = urlStr.indexOf("?", 8);
|
|
176
|
+
|
|
177
|
+
if (queryStart === -1) {
|
|
178
|
+
parsedQuery = {};
|
|
179
|
+
return parsedQuery;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
const queryString = urlStr.substring(queryStart + 1);
|
|
183
|
+
parsedQuery = Object.fromEntries(new URLSearchParams(queryString));
|
|
184
|
+
|
|
185
|
+
return parsedQuery;
|
|
186
|
+
},
|
|
167
187
|
params: (req.params || {}) as BunifyRequest<T, Decorators>["params"],
|
|
168
188
|
json: () => req.json(),
|
|
169
189
|
text: () => req.text(),
|
|
@@ -207,23 +227,15 @@ export class Bunify<Decorators extends Record<string, any> = {}> {
|
|
|
207
227
|
request: BunifyRequest<T, Decorators>,
|
|
208
228
|
reply: BunifyReply<T>,
|
|
209
229
|
) => {
|
|
210
|
-
let
|
|
211
|
-
|
|
212
|
-
if (
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
index = i;
|
|
216
|
-
|
|
217
|
-
if (i === middlewares.length) {
|
|
218
|
-
return handler(request, reply);
|
|
230
|
+
for (let i = 0; i < middlewares.length; i++) {
|
|
231
|
+
const fn = middlewares[i];
|
|
232
|
+
if (fn) {
|
|
233
|
+
await fn(request, reply, async () => {});
|
|
234
|
+
if (reply.sent) return;
|
|
219
235
|
}
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
return mw(request, reply, () => next(i + 1));
|
|
224
|
-
}
|
|
225
|
-
};
|
|
226
|
-
return next(0);
|
|
236
|
+
}
|
|
237
|
+
const res = await handler(request, reply);
|
|
238
|
+
if (res !== undefined && !reply.sent) return reply.send(res);
|
|
227
239
|
};
|
|
228
240
|
}
|
|
229
241
|
|
|
@@ -236,25 +248,18 @@ export class Bunify<Decorators extends Record<string, any> = {}> {
|
|
|
236
248
|
return { handler, middlewares };
|
|
237
249
|
}
|
|
238
250
|
|
|
239
|
-
private runHooks<T extends RouteSchema>(
|
|
251
|
+
private async runHooks<T extends RouteSchema>(
|
|
240
252
|
hooks: Middleware<T, Decorators>[],
|
|
241
253
|
request: BunifyRequest<T, Decorators>,
|
|
242
254
|
reply: BunifyReply<T>,
|
|
243
255
|
) {
|
|
244
|
-
let
|
|
245
|
-
|
|
246
|
-
const next = async (i: number): Promise<any> => {
|
|
247
|
-
if (i <= index) {
|
|
248
|
-
throw new Error("next() called multiple times in hooks");
|
|
249
|
-
}
|
|
250
|
-
index = i;
|
|
251
|
-
|
|
256
|
+
for (let i = 0; i < hooks.length; i++) {
|
|
252
257
|
const fn = hooks[i];
|
|
253
|
-
if (
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
+
if (fn) {
|
|
259
|
+
await fn(request, reply, async () => {});
|
|
260
|
+
if (reply.sent) return;
|
|
261
|
+
}
|
|
262
|
+
}
|
|
258
263
|
}
|
|
259
264
|
|
|
260
265
|
private async runPipeline<T extends RouteSchema>(
|
|
@@ -262,29 +267,34 @@ export class Bunify<Decorators extends Record<string, any> = {}> {
|
|
|
262
267
|
reply: BunifyReply<T>,
|
|
263
268
|
handler: Handler<T, Decorators>,
|
|
264
269
|
) {
|
|
265
|
-
|
|
266
|
-
this.
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
this.hooks.preHandler as Middleware<T, Decorators>[],
|
|
274
|
-
request,
|
|
275
|
-
reply,
|
|
276
|
-
);
|
|
277
|
-
if (reply.sent) return;
|
|
270
|
+
if (this.hooks.onRequest.length > 0) {
|
|
271
|
+
await this.runHooks(
|
|
272
|
+
this.hooks.onRequest as Middleware<T, Decorators>[],
|
|
273
|
+
request,
|
|
274
|
+
reply,
|
|
275
|
+
);
|
|
276
|
+
if (reply.sent) return;
|
|
277
|
+
}
|
|
278
278
|
|
|
279
|
-
|
|
280
|
-
|
|
279
|
+
if (this.hooks.preHandler.length > 0) {
|
|
280
|
+
await this.runHooks(
|
|
281
|
+
this.hooks.preHandler as Middleware<T, Decorators>[],
|
|
282
|
+
request,
|
|
283
|
+
reply,
|
|
284
|
+
);
|
|
285
|
+
if (reply.sent) return;
|
|
286
|
+
}
|
|
281
287
|
|
|
282
|
-
await
|
|
283
|
-
|
|
284
|
-
request,
|
|
285
|
-
reply,
|
|
286
|
-
);
|
|
288
|
+
const res = await handler(request, reply);
|
|
289
|
+
if (res !== undefined && !reply.sent) return reply.send(res);
|
|
287
290
|
|
|
291
|
+
if (this.hooks.onResponse.length > 0) {
|
|
292
|
+
await this.runHooks(
|
|
293
|
+
this.hooks.onResponse as Middleware<T, Decorators>[],
|
|
294
|
+
request,
|
|
295
|
+
reply,
|
|
296
|
+
);
|
|
297
|
+
}
|
|
288
298
|
return res;
|
|
289
299
|
}
|
|
290
300
|
|
|
@@ -360,9 +370,13 @@ export class Bunify<Decorators extends Record<string, any> = {}> {
|
|
|
360
370
|
this.addRoute("PATCH", path, handlers);
|
|
361
371
|
}
|
|
362
372
|
|
|
363
|
-
listen(
|
|
373
|
+
listen(
|
|
374
|
+
{ port, host = "" }: { port: number; host?: string },
|
|
375
|
+
callback?: (address: string) => void,
|
|
376
|
+
) {
|
|
364
377
|
const server = Bun.serve({
|
|
365
378
|
port: port,
|
|
379
|
+
hostname: host,
|
|
366
380
|
routes: this.routes,
|
|
367
381
|
fetch: async () => {
|
|
368
382
|
return new Response("Not Found", { status: 404 });
|