@kichu12348/bunify 1.0.0

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/README.md ADDED
@@ -0,0 +1,136 @@
1
+ # Bunify
2
+
3
+ A fast, lightweight, and strictly-typed web framework built specifically for [Bun](https://bun.sh/).
4
+
5
+ **Bunify** offers a developer-friendly API heavily inspired by proven web frameworks like Fastify, but optimized for the modern Bun ecosystem. It features robust typing, lifecycle hooks, powerful routing, and middleware support right out of the box.
6
+
7
+ ## Installation
8
+
9
+ Since Bunify relies on native Bun features, ensure you have [Bun installed](https://bun.sh/).
10
+
11
+ ```bash
12
+ bun add bunify
13
+ ```
14
+
15
+ ## Quick Start
16
+
17
+ ```typescript
18
+ import { Bunify } from "bunify";
19
+
20
+ const app = new Bunify({ logger: true });
21
+
22
+ app.get("/hello", (request, reply) => {
23
+ return { message: "Hello, World!" };
24
+ });
25
+
26
+ app.listen(3000, (address) => {
27
+ console.log(`Server is running at http://${address}`);
28
+ });
29
+ ```
30
+
31
+ ## Features
32
+
33
+ ### Type-Safe Routing
34
+ Bunify enforces strict typing on your `return` statements and incoming requests using `RouteSchema` definition.
35
+
36
+ ```typescript
37
+ type GreetRoute = {
38
+ Body: { name: string };
39
+ Query: { formal?: string };
40
+ Params: { id: string };
41
+ Reply: { greeting: string };
42
+ };
43
+
44
+ app.post<GreetRoute>("/greet/:id", async (request, reply) => {
45
+ const body = await request.json(); // Strongly typed to { name: string }
46
+ const formal = request.query.formal;
47
+ const id = request.params.id; // Strongly typed to string
48
+
49
+ return { greeting: `Hello ${body.name}, your id is ${id}` };
50
+ });
51
+ ```
52
+
53
+ ### Middleware (`use`)
54
+ You can use middleware to intercept incoming requests and process them before they hit your routes. Note that you MUST call `next()` to proceed.
55
+
56
+ ```typescript
57
+ app.use(async (request, reply, next) => {
58
+ console.log(`Incoming request: ${request.method} ${request.url}`);
59
+ await next();
60
+ });
61
+ ```
62
+
63
+ ### Lifecycle Hooks
64
+ Tap into requests at specific lifecycles. Available hooks are `onRequest`, `preHandler`, and `onResponse`.
65
+
66
+ ```typescript
67
+ app.addHook("onRequest", async (request, reply, next) => {
68
+ // Logic here
69
+ await next();
70
+ });
71
+ ```
72
+
73
+ ### Routing & Chaining Middlewares
74
+ You can apply middleware locally per-route by chaining them before the final handler.
75
+
76
+ ```typescript
77
+ app.get(
78
+ "/protected",
79
+ async (request, reply, next) => {
80
+ if (!request.headers.get("Authorization")) {
81
+ return reply.status(401).json({ error: "Unauthorized" });
82
+ }
83
+ await next();
84
+ },
85
+ (request, reply) => {
86
+ return { data: "Top Secret" };
87
+ }
88
+ );
89
+ ```
90
+
91
+ ### Decorators
92
+ Easily attach custom utilities, instances, or metadata to your `Bunify` application context and requests.
93
+
94
+ ```typescript
95
+ // Define expected decorators
96
+ type AppDecorators = { db: any };
97
+
98
+ const app = new Bunify<AppDecorators>();
99
+
100
+ app.decorate("db", { getDocs: () => [{ id: 1 }] });
101
+
102
+ app.get("/docs", (request, reply) => {
103
+ // request.db is available and correctly typed
104
+ return request.db.getDocs();
105
+ });
106
+ ```
107
+
108
+ ### Plugins / Sub-Routers
109
+ Divide your application into logical modules combining prefixes, scoped dependencies, and decorators securely using `.register()`.
110
+
111
+ ```typescript
112
+ app.register(
113
+ (adminApp) => {
114
+ adminApp.get("/dashboard", (request, reply) => {
115
+ reply.html("<h1>Admin Dashboard</h1>");
116
+ });
117
+ },
118
+ { prefix: "/admin" } // accessible at /admin/dashboard
119
+ );
120
+ ```
121
+
122
+ ## Reply Methods
123
+
124
+ The `reply` object gives you fine-grained control to shape responses gracefully.
125
+
126
+ - `reply.status(code)` / `reply.code(code)`: Set the HTTP status code
127
+ - `reply.headers(key, value)`: Insert a new Header
128
+ - `reply.json(data)`: Automatically sends a JSON response
129
+ - `reply.html(content)`: Easily serve HTML tags
130
+ - `reply.redirect(url)`: Responds with a 302 redirect
131
+ - `reply.send(content)`: Generic send handler
132
+ - Return directly from `handler` instead of returning `reply.send()` (See Quick Start).
133
+
134
+ ## License
135
+
136
+ [MIT](./LICENSE)
@@ -0,0 +1,54 @@
1
+ const server = Bun.serve({
2
+ port: 3001,
3
+ async fetch(req) {
4
+ const url = new URL(req.url);
5
+
6
+ // 1. Simulate Middleware (headers added to all routes)
7
+ const headers = new Headers();
8
+ headers.set("X-Benchmark-Time", Date.now().toString());
9
+
10
+ if (req.method === "GET") {
11
+ // 2. Simple GET
12
+ if (url.pathname === "/") {
13
+ headers.set("Content-Type", "application/json");
14
+ return new Response(JSON.stringify({ message: "Hello, World!" }), {
15
+ headers,
16
+ });
17
+ }
18
+
19
+ // 3. Query params map handler
20
+ if (url.pathname === "/search") {
21
+ headers.set("Content-Type", "application/json");
22
+ const q = url.searchParams.get("q") || "none";
23
+ return new Response(JSON.stringify({ results: q }), { headers });
24
+ }
25
+
26
+ // 4. Larger JSON Payload
27
+ if (url.pathname === "/data") {
28
+ headers.set("Content-Type", "application/json");
29
+ return new Response(
30
+ JSON.stringify({
31
+ items: [
32
+ { id: 1, name: "Item A" },
33
+ { id: 2, name: "Item B" },
34
+ { id: 3, name: "Item C" },
35
+ ],
36
+ status: "ok",
37
+ }),
38
+ { headers },
39
+ );
40
+ }
41
+ }
42
+
43
+ // 5. POST body JSON
44
+ if (req.method === "POST" && url.pathname === "/echo") {
45
+ const body = await req.json();
46
+ headers.set("Content-Type", "application/json");
47
+ return new Response(JSON.stringify(body), { status: 201, headers });
48
+ }
49
+
50
+ return new Response("Not Found", { status: 404 });
51
+ },
52
+ });
53
+
54
+ console.log(`Raw Bun server listening on ${server.hostname}:${server.port}`);
@@ -0,0 +1,43 @@
1
+ import { Bunify } from "../src/core";
2
+
3
+ const app = new Bunify();
4
+
5
+ // 1. App-level Middleware
6
+ app.use(async (req, reply, next) => {
7
+ reply.headers("X-Benchmark-Time", Date.now().toString());
8
+ return next();
9
+ });
10
+
11
+ // 2. Simple GET
12
+ app.get("/", () => {
13
+ return { message: "Hello, World!" };
14
+ });
15
+
16
+ // 3. Query params map handler
17
+ app.get("/search", (req) => {
18
+ return {
19
+ results: req.query.q || "none",
20
+ };
21
+ });
22
+
23
+ // 4. Larger JSON Payload
24
+ app.get("/data", () => {
25
+ return {
26
+ items: [
27
+ { id: 1, name: "Item A" },
28
+ { id: 2, name: "Item B" },
29
+ { id: 3, name: "Item C" },
30
+ ],
31
+ status: "ok",
32
+ };
33
+ });
34
+
35
+ // 5. POST body JSON
36
+ app.post("/echo", async (req, reply) => {
37
+ const body = await req.json();
38
+ return reply.status(201).json(body);
39
+ });
40
+
41
+ app.listen(3000, (url) => {
42
+ console.log(`Bunify listening on ${url}`);
43
+ });
package/bun.lock ADDED
@@ -0,0 +1,26 @@
1
+ {
2
+ "lockfileVersion": 1,
3
+ "configVersion": 1,
4
+ "workspaces": {
5
+ "": {
6
+ "name": "bunify",
7
+ "devDependencies": {
8
+ "@types/bun": "latest",
9
+ },
10
+ "peerDependencies": {
11
+ "typescript": "^5.9.3",
12
+ },
13
+ },
14
+ },
15
+ "packages": {
16
+ "@types/bun": ["@types/bun@1.3.11", "", { "dependencies": { "bun-types": "1.3.11" } }, "sha512-5vPne5QvtpjGpsGYXiFyycfpDF2ECyPcTSsFBMa0fraoxiQyMJ3SmuQIGhzPg2WJuWxVBoxWJ2kClYTcw/4fAg=="],
17
+
18
+ "@types/node": ["@types/node@25.5.2", "", { "dependencies": { "undici-types": "~7.18.0" } }, "sha512-tO4ZIRKNC+MDWV4qKVZe3Ql/woTnmHDr5JD8UI5hn2pwBrHEwOEMZK7WlNb5RKB6EoJ02gwmQS9OrjuFnZYdpg=="],
19
+
20
+ "bun-types": ["bun-types@1.3.11", "", { "dependencies": { "@types/node": "*" } }, "sha512-1KGPpoxQWl9f6wcZh57LvrPIInQMn2TQ7jsgxqpRzg+l0QPOFvJVH7HmvHo/AiPgwXy+/Thf6Ov3EdVn1vOabg=="],
21
+
22
+ "typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="],
23
+
24
+ "undici-types": ["undici-types@7.18.2", "", {}, "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w=="],
25
+ }
26
+ }
package/example.ts ADDED
@@ -0,0 +1,11 @@
1
+ import { Bunify } from "./src/core";
2
+
3
+ const app = new Bunify();
4
+
5
+ app.get("/hello", (req, res) => {
6
+ return { message: "Hello, World!" };
7
+ });
8
+
9
+ app.listen(3000, (address) => {
10
+ console.log(`Server is running at http://${address}`);
11
+ });
package/index.ts ADDED
@@ -0,0 +1,4 @@
1
+ export { Bunify } from "./src/core";
2
+
3
+ // Re-export any interfaces or types that users might need
4
+ export type { BunifyInstance, BunifyRequest, BunifyReply } from "./src/core";
package/package.json ADDED
@@ -0,0 +1,23 @@
1
+ {
2
+ "name": "@kichu12348/bunify",
3
+ "version": "1.0.0",
4
+ "author": "Kichu",
5
+ "publishConfig": {
6
+ "access": "public"
7
+ },
8
+ "module": "index.ts",
9
+ "type": "module",
10
+ "exports": {
11
+ ".": {
12
+ "import": "./index.ts",
13
+ "types": "./index.ts"
14
+ }
15
+ },
16
+ "devDependencies": {
17
+ "typescript": "^5.0.0",
18
+ "@types/bun": "latest"
19
+ },
20
+ "peerDependencies": {
21
+ "typescript": "^5.9.3"
22
+ }
23
+ }
package/src/core.ts ADDED
@@ -0,0 +1,377 @@
1
+ type Method = "GET" | "POST" | "PUT" | "DELETE" | "PATCH";
2
+
3
+ type RouteSchema = {
4
+ Query?: Record<string, any>;
5
+ Params?: Record<string, any>;
6
+ Body?: any;
7
+ Reply?: any;
8
+ };
9
+
10
+ export type BunifyRequest<T extends RouteSchema = RouteSchema, D = {}> = {
11
+ raw: Bun.BunRequest;
12
+ query: Record<string, string> & (T["Query"] extends object ? T["Query"] : {});
13
+ params: T["Params"] extends infer U
14
+ ? Record<string, string> & U
15
+ : Record<string, string>;
16
+ json: () => Promise<T["Body"]>;
17
+ text: () => Promise<string>;
18
+ headers: Headers;
19
+ method: string;
20
+ url: string;
21
+ } & D;
22
+
23
+ type Reply = ReturnType<typeof createReply>;
24
+
25
+ export type BunifyReply<T extends RouteSchema = RouteSchema> = Reply & {
26
+ send: (content: T["Reply"] | Response) => Response;
27
+ json: (data: T["Reply"]) => Response;
28
+ };
29
+
30
+ export type Handler<T extends RouteSchema = RouteSchema, D = {}> = (
31
+ request: BunifyRequest<T, D>,
32
+ reply: BunifyReply<T>,
33
+ ) => T["Reply"] | Response | Promise<T["Reply"] | Response>;
34
+
35
+ export type Middleware<T extends RouteSchema = RouteSchema, D = {}> = (
36
+ request: BunifyRequest<T, D>,
37
+ reply: BunifyReply<T>,
38
+ next: () => Promise<any>,
39
+ ) => any;
40
+
41
+ type HookType = "onRequest" | "preHandler" | "onResponse";
42
+
43
+ export interface BunifyInstance {
44
+ get<T extends RouteSchema = RouteSchema>(
45
+ path: string,
46
+ handler: Handler<T>,
47
+ ): void;
48
+ post<T extends RouteSchema = RouteSchema>(
49
+ path: string,
50
+ handler: Handler<T>,
51
+ ): void;
52
+ put<T extends RouteSchema = RouteSchema>(
53
+ path: string,
54
+ handler: Handler<T>,
55
+ ): void;
56
+ delete<T extends RouteSchema = RouteSchema>(
57
+ path: string,
58
+ handler: Handler<T>,
59
+ ): void;
60
+ patch<T extends RouteSchema = RouteSchema>(
61
+ path: string,
62
+ handler: Handler<T>,
63
+ ): void;
64
+ listen(port: number, callback?: (address: string) => void): void;
65
+ }
66
+
67
+ interface RegisterOptions {
68
+ prefix?: string;
69
+ }
70
+
71
+ function createReply() {
72
+ let status = 200;
73
+ const headers = new Headers();
74
+ let body: any = null;
75
+ let sent = false;
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;
108
+
109
+ if (typeof content === "object") {
110
+ headers.set("Content-Type", "application/json");
111
+ content = JSON.stringify(content);
112
+ }
113
+
114
+ body = content;
115
+ return this.build();
116
+ },
117
+
118
+ build() {
119
+ if (body instanceof Response) return body;
120
+
121
+ return new Response(body, { status, headers });
122
+ },
123
+ };
124
+ }
125
+
126
+ export class Bunify<Decorators extends Record<string, any> = {}> {
127
+ private logger: boolean;
128
+ private prefix: string = "";
129
+ private routes: { [key: string]: any };
130
+ private middlewares: Middleware<any, Decorators>[] = [];
131
+ private hooks: Record<HookType, Middleware<any, Decorators>[]> = {
132
+ onRequest: [],
133
+ preHandler: [],
134
+ onResponse: [],
135
+ };
136
+
137
+ private decorators: Decorators = {} as Decorators;
138
+
139
+ constructor(options?: { logger?: boolean }) {
140
+ this.logger = options?.logger ?? false;
141
+ this.routes = {};
142
+ }
143
+
144
+ private joinPaths(base: string, path: string) {
145
+ return (base + path).replace(/\/+/g, "/");
146
+ }
147
+
148
+ private addRoute<T extends RouteSchema = RouteSchema>(
149
+ method: Method,
150
+ path: string,
151
+ handlers: (Handler<T, Decorators> | Middleware<T, Decorators>)[],
152
+ ) {
153
+ const { handler, middlewares } = this.parseHandlers<T>(handlers);
154
+ const composedHandler = this.compose<T>(
155
+ [...this.middlewares, ...middlewares] as Middleware<T, Decorators>[],
156
+ handler,
157
+ );
158
+ const wrappedHandler = async (req: Bun.BunRequest) => {
159
+ const url = new URL(req.url);
160
+
161
+ const request: BunifyRequest<T, Decorators> = {
162
+ raw: req,
163
+ query: Object.fromEntries(url.searchParams.entries()) as BunifyRequest<
164
+ T,
165
+ Decorators
166
+ >["query"],
167
+ params: (req.params || {}) as BunifyRequest<T, Decorators>["params"],
168
+ json: () => req.json(),
169
+ text: () => req.text(),
170
+ headers: req.headers,
171
+ method: req.method,
172
+ url: req.url,
173
+ ...(this.decorators as Decorators),
174
+ };
175
+
176
+ const reply = createReply() as BunifyReply<T>;
177
+
178
+ try {
179
+ const res = await this.runPipeline(request, reply, composedHandler);
180
+ if (!reply.sent && res !== undefined) {
181
+ return reply.send(res);
182
+ }
183
+ return reply.build();
184
+ } catch (err) {
185
+ if (this.logger) {
186
+ console.error(`Error in [${method} ${path}]:`, err);
187
+ }
188
+ return new Response("Internal Server Error", { status: 500 });
189
+ }
190
+ };
191
+
192
+ const fullPath = this.joinPaths(this.prefix, path);
193
+
194
+ const route = this.routes[fullPath];
195
+ if (!route) {
196
+ this.routes[fullPath] = { [method]: wrappedHandler };
197
+ } else {
198
+ this.routes[fullPath][method] = wrappedHandler;
199
+ }
200
+ }
201
+
202
+ private compose<T extends RouteSchema>(
203
+ middlewares: Middleware<T, Decorators>[],
204
+ handler: Handler<T, Decorators>,
205
+ ) {
206
+ return async (
207
+ request: BunifyRequest<T, Decorators>,
208
+ reply: BunifyReply<T>,
209
+ ) => {
210
+ let index = -1;
211
+ const next = async (i: number): Promise<any> => {
212
+ if (i <= index) {
213
+ throw new Error("next() called multiple times");
214
+ }
215
+ index = i;
216
+
217
+ if (i === middlewares.length) {
218
+ return handler(request, reply);
219
+ }
220
+
221
+ const mw = middlewares[i];
222
+ if (mw) {
223
+ return mw(request, reply, () => next(i + 1));
224
+ }
225
+ };
226
+ return next(0);
227
+ };
228
+ }
229
+
230
+ private parseHandlers<T extends RouteSchema = RouteSchema>(
231
+ handlers: (Handler<T, Decorators> | Middleware<T, Decorators>)[],
232
+ ) {
233
+ const flat = handlers.flat();
234
+ const handler = flat[flat.length - 1] as Handler<T, Decorators>;
235
+ const middlewares = flat.slice(0, -1) as Middleware<T, Decorators>[];
236
+ return { handler, middlewares };
237
+ }
238
+
239
+ private runHooks<T extends RouteSchema>(
240
+ hooks: Middleware<T, Decorators>[],
241
+ request: BunifyRequest<T, Decorators>,
242
+ reply: BunifyReply<T>,
243
+ ) {
244
+ let index = -1;
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
+
252
+ const fn = hooks[i];
253
+ if (!fn) return;
254
+
255
+ return fn(request, reply, () => next(i + 1));
256
+ };
257
+ return next(0);
258
+ }
259
+
260
+ private async runPipeline<T extends RouteSchema>(
261
+ request: BunifyRequest<T, Decorators>,
262
+ reply: BunifyReply<T>,
263
+ handler: Handler<T, Decorators>,
264
+ ) {
265
+ let res = await this.runHooks(
266
+ this.hooks.onRequest as Middleware<T, Decorators>[],
267
+ request,
268
+ reply,
269
+ );
270
+ if (reply.sent) return;
271
+
272
+ res = await this.runHooks(
273
+ this.hooks.preHandler as Middleware<T, Decorators>[],
274
+ request,
275
+ reply,
276
+ );
277
+ if (reply.sent) return;
278
+
279
+ res = await handler(request, reply);
280
+ if (reply.sent) return res;
281
+
282
+ await this.runHooks(
283
+ this.hooks.onResponse as Middleware<T, Decorators>[],
284
+ request,
285
+ reply,
286
+ );
287
+
288
+ return res;
289
+ }
290
+
291
+ decorate<K extends string, V>(name: K, value: V) {
292
+ if ((this.decorators as any)[name]) {
293
+ throw new Error(`Decorator '${name}' already exists.`);
294
+ }
295
+ (this.decorators as any)[name] = value;
296
+
297
+ return this as any;
298
+ }
299
+
300
+ register(
301
+ fn: (app: Bunify<Decorators>) => void,
302
+ { prefix = "" }: RegisterOptions,
303
+ ) {
304
+ const child = new Bunify<Decorators>({ logger: this.logger });
305
+ child.routes = this.routes; // Share routes
306
+ child.prefix = this.joinPaths(this.prefix, prefix);
307
+
308
+ child.hooks = {
309
+ onRequest: [...this.hooks.onRequest],
310
+ preHandler: [...this.hooks.preHandler],
311
+ onResponse: [...this.hooks.onResponse],
312
+ };
313
+
314
+ child.decorators = { ...this.decorators };
315
+
316
+ child.middlewares = [...(this.middlewares || [])];
317
+ fn(child);
318
+ }
319
+
320
+ use(fn: Middleware<any, Decorators>) {
321
+ this.middlewares.push(fn);
322
+ }
323
+
324
+ addHook(type: HookType, fn: Middleware<any, Decorators>) {
325
+ this.hooks[type].push(fn);
326
+ }
327
+
328
+ get<T extends RouteSchema = RouteSchema>(
329
+ path: string,
330
+ ...handlers: [...Middleware<T, Decorators>[], Handler<T, Decorators>]
331
+ ) {
332
+ this.addRoute("GET", path, handlers);
333
+ }
334
+
335
+ post<T extends RouteSchema = RouteSchema>(
336
+ path: string,
337
+ ...handlers: [...Middleware<T, Decorators>[], Handler<T, Decorators>]
338
+ ) {
339
+ this.addRoute("POST", path, handlers);
340
+ }
341
+
342
+ put<T extends RouteSchema = RouteSchema>(
343
+ path: string,
344
+ ...handlers: [...Middleware<T, Decorators>[], Handler<T, Decorators>]
345
+ ) {
346
+ this.addRoute("PUT", path, handlers);
347
+ }
348
+
349
+ delete<T extends RouteSchema = RouteSchema>(
350
+ path: string,
351
+ ...handlers: [...Middleware<T, Decorators>[], Handler<T, Decorators>]
352
+ ) {
353
+ this.addRoute("DELETE", path, handlers);
354
+ }
355
+
356
+ patch<T extends RouteSchema = RouteSchema>(
357
+ path: string,
358
+ ...handlers: [...Middleware<T, Decorators>[], Handler<T, Decorators>]
359
+ ) {
360
+ this.addRoute("PATCH", path, handlers);
361
+ }
362
+
363
+ listen(port: number, callback?: (address: string) => void) {
364
+ const server = Bun.serve({
365
+ port: port,
366
+ routes: this.routes,
367
+ fetch: async () => {
368
+ return new Response("Not Found", { status: 404 });
369
+ },
370
+ });
371
+ if (callback) {
372
+ callback(server.hostname + ":" + server.port);
373
+ }
374
+
375
+ return server;
376
+ }
377
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,30 @@
1
+ {
2
+ "compilerOptions": {
3
+ // Environment setup & latest features
4
+ "lib": ["ESNext"],
5
+ "target": "ESNext",
6
+ "module": "Preserve",
7
+ "moduleDetection": "force",
8
+ "types": ["bun"],
9
+ "jsx": "react-jsx",
10
+ "allowJs": true,
11
+
12
+ // Bundler mode
13
+ "moduleResolution": "bundler",
14
+ "allowImportingTsExtensions": true,
15
+ "verbatimModuleSyntax": true,
16
+ "noEmit": true,
17
+
18
+ // Best practices
19
+ "strict": true,
20
+ "skipLibCheck": true,
21
+ "noFallthroughCasesInSwitch": true,
22
+ "noUncheckedIndexedAccess": true,
23
+ "noImplicitOverride": true,
24
+
25
+ // Some stricter flags (disabled by default)
26
+ "noUnusedLocals": false,
27
+ "noUnusedParameters": false,
28
+ "noPropertyAccessFromIndexSignature": false
29
+ }
30
+ }