@flight-framework/http 0.0.1

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024-2026 Flight Contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,152 @@
1
+ # @flight/http
2
+
3
+ Ultra-fast HTTP server for Flight Framework, built on Web Standards.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @flight/http
9
+ ```
10
+
11
+ ## Quick Start
12
+
13
+ ```typescript
14
+ import { createServer } from '@flight/http';
15
+ import { serve } from '@flight/http/node';
16
+
17
+ const app = createServer();
18
+
19
+ // Simple JSON response
20
+ app.get('/', (c) => c.json({ message: 'Hello Flight!' }));
21
+
22
+ // Route params
23
+ app.get('/users/:id', (c) => {
24
+ return c.json({ userId: c.params.id });
25
+ });
26
+
27
+ // POST with body
28
+ app.post('/users', async (c) => {
29
+ const body = await c.req.json();
30
+ return c.json({ created: true, data: body }, 201);
31
+ });
32
+
33
+ // Start server
34
+ serve(app, { port: 3000 });
35
+ ```
36
+
37
+ ## Features
38
+
39
+ - 🚀 **Ultra-fast** - Radix-tree based routing
40
+ - 🌐 **Web Standards** - Uses native Request/Response
41
+ - 🔌 **Multi-runtime** - Node.js, Bun, Deno
42
+ - ðŸŠķ **Lightweight** - Minimal dependencies
43
+ - ðŸŽŊ **TypeScript** - Full type safety
44
+
45
+ ## Runtime Adapters
46
+
47
+ ### Node.js
48
+
49
+ ```typescript
50
+ import { createServer } from '@flight/http';
51
+ import { serve } from '@flight/http/node';
52
+
53
+ serve(app, { port: 3000 });
54
+ ```
55
+
56
+ ### Bun
57
+
58
+ ```typescript
59
+ import { createServer } from '@flight/http';
60
+ import { serve } from '@flight/http/bun';
61
+
62
+ serve(app, { port: 3000 });
63
+ ```
64
+
65
+ ### Deno
66
+
67
+ ```typescript
68
+ import { createServer } from '@flight/http';
69
+ import { serve } from '@flight/http/deno';
70
+
71
+ serve(app, { port: 3000 });
72
+ ```
73
+
74
+ ## Middleware
75
+
76
+ ```typescript
77
+ // Logging middleware
78
+ app.use(async (c, next) => {
79
+ const start = performance.now();
80
+ const response = await next();
81
+ const duration = performance.now() - start;
82
+ console.log(`${c.req.method} ${c.req.url} - ${duration}ms`);
83
+ return response;
84
+ });
85
+
86
+ // Auth middleware
87
+ app.use(async (c, next) => {
88
+ const token = c.header('Authorization');
89
+ if (!token) {
90
+ return c.json({ error: 'Unauthorized' }, 401);
91
+ }
92
+ c.set('user', { id: '123' });
93
+ return next();
94
+ });
95
+ ```
96
+
97
+ ## Context Helpers
98
+
99
+ ```typescript
100
+ app.get('/demo', (c) => {
101
+ // JSON response
102
+ return c.json({ data: 'value' });
103
+
104
+ // Text response
105
+ return c.text('Hello');
106
+
107
+ // HTML response
108
+ return c.html('<h1>Hello</h1>');
109
+
110
+ // Redirect
111
+ return c.redirect('/other');
112
+
113
+ // Access headers
114
+ const auth = c.header('Authorization');
115
+
116
+ // Access cookies
117
+ const session = c.cookie('session');
118
+
119
+ // Access params
120
+ const id = c.params.id;
121
+ });
122
+ ```
123
+
124
+ ## API Reference
125
+
126
+ ### `createServer(options?)`
127
+
128
+ Create a new Flight HTTP server.
129
+
130
+ ### `app.get/post/put/delete/patch(path, ...handlers)`
131
+
132
+ Register a route handler.
133
+
134
+ ### `app.use(...middleware)`
135
+
136
+ Add global middleware.
137
+
138
+ ### `app.route(path, subRouter)`
139
+
140
+ Mount a sub-router.
141
+
142
+ ### `app.notFound(handler)`
143
+
144
+ Set custom 404 handler.
145
+
146
+ ### `app.onError(handler)`
147
+
148
+ Set custom error handler.
149
+
150
+ ### `app.fetch(request)`
151
+
152
+ Handle a Request and return a Response (Web Standard).
@@ -0,0 +1,39 @@
1
+ import { E as Env, F as FlightHttpServer } from '../types-4GWmDi9X.js';
2
+
3
+ /**
4
+ * @flight/http - Bun Adapter
5
+ *
6
+ * Allows running Flight HTTP server on Bun runtime.
7
+ * Bun natively supports Web Standard Request/Response.
8
+ */
9
+
10
+ interface BunServer {
11
+ stop: () => void;
12
+ port: number;
13
+ hostname: string;
14
+ }
15
+ interface ServeOptions {
16
+ port?: number;
17
+ hostname?: string;
18
+ onListen?: (info: {
19
+ port: number;
20
+ hostname: string;
21
+ }) => void;
22
+ }
23
+ /**
24
+ * Start a Bun server with the Flight app
25
+ *
26
+ * @example
27
+ * ```typescript
28
+ * import { createServer } from '@flight/http';
29
+ * import { serve } from '@flight/http/bun';
30
+ *
31
+ * const app = createServer();
32
+ * app.get('/', (c) => c.json({ hello: 'world' }));
33
+ *
34
+ * serve(app, { port: 3000 });
35
+ * ```
36
+ */
37
+ declare function serve<E extends Env = Env>(app: FlightHttpServer<E>, options?: ServeOptions): BunServer;
38
+
39
+ export { FlightHttpServer, type ServeOptions, serve };
@@ -0,0 +1,29 @@
1
+ // src/adapters/bun.ts
2
+ function serve(app, options = {}) {
3
+ const {
4
+ port = 3e3,
5
+ hostname = "0.0.0.0",
6
+ onListen
7
+ } = options;
8
+ const server = Bun.serve({
9
+ port,
10
+ hostname,
11
+ fetch: (request) => app.fetch(request)
12
+ });
13
+ const info = { port: server.port, hostname: server.hostname };
14
+ if (onListen) {
15
+ onListen(info);
16
+ } else {
17
+ console.log(`
18
+ \u2708\uFE0F Flight HTTP Server (Bun) running!
19
+
20
+ \u279C Local: http://localhost:${info.port}/
21
+ \u279C Network: http://${info.hostname}:${info.port}/
22
+ `);
23
+ }
24
+ return server;
25
+ }
26
+
27
+ export { serve };
28
+ //# sourceMappingURL=bun.js.map
29
+ //# sourceMappingURL=bun.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/adapters/bun.ts"],"names":[],"mappings":";AAsDO,SAAS,KAAA,CACZ,GAAA,EACA,OAAA,GAAwB,EAAC,EAChB;AACT,EAAA,MAAM;AAAA,IACF,IAAA,GAAO,GAAA;AAAA,IACP,QAAA,GAAW,SAAA;AAAA,IACX;AAAA,GACJ,GAAI,OAAA;AAEJ,EAAA,MAAM,MAAA,GAAS,IAAI,KAAA,CAAM;AAAA,IACrB,IAAA;AAAA,IACA,QAAA;AAAA,IACA,KAAA,EAAO,CAAC,OAAA,KAAY,GAAA,CAAI,MAAM,OAAO;AAAA,GACxC,CAAA;AAED,EAAA,MAAM,OAAO,EAAE,IAAA,EAAM,OAAO,IAAA,EAAM,QAAA,EAAU,OAAO,QAAA,EAAS;AAE5D,EAAA,IAAI,QAAA,EAAU;AACV,IAAA,QAAA,CAAS,IAAI,CAAA;AAAA,EACjB,CAAA,MAAO;AACH,IAAA,OAAA,CAAQ,GAAA,CAAI;AAAA;AAAA;AAAA,oCAAA,EAGa,KAAK,IAAI,CAAA;AAAA,0BAAA,EACnB,IAAA,CAAK,QAAQ,CAAA,CAAA,EAAI,IAAA,CAAK,IAAI,CAAA;AAAA,CAChD,CAAA;AAAA,EACG;AAEA,EAAA,OAAO,MAAA;AACX","file":"bun.js","sourcesContent":["/**\r\n * @flight/http - Bun Adapter\r\n * \r\n * Allows running Flight HTTP server on Bun runtime.\r\n * Bun natively supports Web Standard Request/Response.\r\n */\r\n\r\nimport type { FlightHttpServer, Env } from '../types.js';\r\n\r\n// ============================================================================\r\n// Bun Server Types\r\n// ============================================================================\r\n\r\ninterface BunServeOptions {\r\n port?: number;\r\n hostname?: string;\r\n fetch: (request: Request) => Response | Promise<Response>;\r\n}\r\n\r\ninterface BunServer {\r\n stop: () => void;\r\n port: number;\r\n hostname: string;\r\n}\r\n\r\n// We don't import Bun types directly to avoid build issues\r\ndeclare const Bun: {\r\n serve: (options: BunServeOptions) => BunServer;\r\n};\r\n\r\n// ============================================================================\r\n// serve() Function\r\n// ============================================================================\r\n\r\nexport interface ServeOptions {\r\n port?: number;\r\n hostname?: string;\r\n onListen?: (info: { port: number; hostname: string }) => void;\r\n}\r\n\r\n/**\r\n * Start a Bun server with the Flight app\r\n * \r\n * @example\r\n * ```typescript\r\n * import { createServer } from '@flight/http';\r\n * import { serve } from '@flight/http/bun';\r\n * \r\n * const app = createServer();\r\n * app.get('/', (c) => c.json({ hello: 'world' }));\r\n * \r\n * serve(app, { port: 3000 });\r\n * ```\r\n */\r\nexport function serve<E extends Env = Env>(\r\n app: FlightHttpServer<E>,\r\n options: ServeOptions = {}\r\n): BunServer {\r\n const {\r\n port = 3000,\r\n hostname = '0.0.0.0',\r\n onListen,\r\n } = options;\r\n\r\n const server = Bun.serve({\r\n port,\r\n hostname,\r\n fetch: (request) => app.fetch(request),\r\n });\r\n\r\n const info = { port: server.port, hostname: server.hostname };\r\n\r\n if (onListen) {\r\n onListen(info);\r\n } else {\r\n console.log(`\r\n ✈ïļ Flight HTTP Server (Bun) running!\r\n \r\n ➜ Local: http://localhost:${info.port}/\r\n ➜ Network: http://${info.hostname}:${info.port}/\r\n`);\r\n }\r\n\r\n return server;\r\n}\r\n\r\n// Re-export useful types\r\nexport type { FlightHttpServer } from '../types.js';\r\n"]}
@@ -0,0 +1,36 @@
1
+ import { E as Env, F as FlightHttpServer } from '../types-4GWmDi9X.js';
2
+
3
+ /**
4
+ * @flight/http - Deno Adapter
5
+ *
6
+ * Allows running Flight HTTP server on Deno runtime.
7
+ * Deno natively supports Web Standard Request/Response.
8
+ */
9
+
10
+ interface ServeOptions {
11
+ port?: number;
12
+ hostname?: string;
13
+ onListen?: (info: {
14
+ port: number;
15
+ hostname: string;
16
+ }) => void;
17
+ }
18
+ /**
19
+ * Start a Deno server with the Flight app
20
+ *
21
+ * @example
22
+ * ```typescript
23
+ * import { createServer } from '@flight/http';
24
+ * import { serve } from '@flight/http/deno';
25
+ *
26
+ * const app = createServer();
27
+ * app.get('/', (c) => c.json({ hello: 'world' }));
28
+ *
29
+ * serve(app, { port: 3000 });
30
+ * ```
31
+ */
32
+ declare function serve<E extends Env = Env>(app: FlightHttpServer<E>, options?: ServeOptions): {
33
+ shutdown: () => Promise<void>;
34
+ };
35
+
36
+ export { FlightHttpServer, type ServeOptions, serve };
@@ -0,0 +1,28 @@
1
+ // src/adapters/deno.ts
2
+ function serve(app, options = {}) {
3
+ const {
4
+ port = 3e3,
5
+ hostname = "0.0.0.0",
6
+ onListen
7
+ } = options;
8
+ const server = Deno.serve(
9
+ (request) => app.fetch(request),
10
+ {
11
+ port,
12
+ hostname,
13
+ onListen: onListen ? (params) => onListen({ port: params.port, hostname: params.hostname }) : (params) => {
14
+ console.log(`
15
+ \u2708\uFE0F Flight HTTP Server (Deno) running!
16
+
17
+ \u279C Local: http://localhost:${params.port}/
18
+ \u279C Network: http://${params.hostname}:${params.port}/
19
+ `);
20
+ }
21
+ }
22
+ );
23
+ return server;
24
+ }
25
+
26
+ export { serve };
27
+ //# sourceMappingURL=deno.js.map
28
+ //# sourceMappingURL=deno.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/adapters/deno.ts"],"names":[],"mappings":";AAmDO,SAAS,KAAA,CACZ,GAAA,EACA,OAAA,GAAwB,EAAC,EACQ;AACjC,EAAA,MAAM;AAAA,IACF,IAAA,GAAO,GAAA;AAAA,IACP,QAAA,GAAW,SAAA;AAAA,IACX;AAAA,GACJ,GAAI,OAAA;AAEJ,EAAA,MAAM,SAAS,IAAA,CAAK,KAAA;AAAA,IAChB,CAAC,OAAA,KAAY,GAAA,CAAI,KAAA,CAAM,OAAO,CAAA;AAAA,IAC9B;AAAA,MACI,IAAA;AAAA,MACA,QAAA;AAAA,MACA,QAAA,EAAU,QAAA,GACJ,CAAC,MAAA,KAAW,SAAS,EAAE,IAAA,EAAM,MAAA,CAAO,IAAA,EAAM,UAAU,MAAA,CAAO,QAAA,EAAU,CAAA,GACrE,CAAC,MAAA,KAAW;AACV,QAAA,OAAA,CAAQ,GAAA,CAAI;AAAA;AAAA;AAAA,oCAAA,EAGC,OAAO,IAAI,CAAA;AAAA,0BAAA,EACrB,MAAA,CAAO,QAAQ,CAAA,CAAA,EAAI,MAAA,CAAO,IAAI,CAAA;AAAA,CACpD,CAAA;AAAA,MACe;AAAA;AACR,GACJ;AAEA,EAAA,OAAO,MAAA;AACX","file":"deno.js","sourcesContent":["/**\r\n * @flight/http - Deno Adapter\r\n * \r\n * Allows running Flight HTTP server on Deno runtime.\r\n * Deno natively supports Web Standard Request/Response.\r\n */\r\n\r\nimport type { FlightHttpServer, Env } from '../types.js';\r\n\r\n// ============================================================================\r\n// Deno Server Types\r\n// ============================================================================\r\n\r\ninterface DenoServeOptions {\r\n port?: number;\r\n hostname?: string;\r\n onListen?: (params: { hostname: string; port: number }) => void;\r\n}\r\n\r\n// We don't import Deno types directly to avoid build issues\r\ndeclare const Deno: {\r\n serve: (\r\n handler: (request: Request) => Response | Promise<Response>,\r\n options?: DenoServeOptions\r\n ) => { shutdown: () => Promise<void> };\r\n};\r\n\r\n// ============================================================================\r\n// serve() Function\r\n// ============================================================================\r\n\r\nexport interface ServeOptions {\r\n port?: number;\r\n hostname?: string;\r\n onListen?: (info: { port: number; hostname: string }) => void;\r\n}\r\n\r\n/**\r\n * Start a Deno server with the Flight app\r\n * \r\n * @example\r\n * ```typescript\r\n * import { createServer } from '@flight/http';\r\n * import { serve } from '@flight/http/deno';\r\n * \r\n * const app = createServer();\r\n * app.get('/', (c) => c.json({ hello: 'world' }));\r\n * \r\n * serve(app, { port: 3000 });\r\n * ```\r\n */\r\nexport function serve<E extends Env = Env>(\r\n app: FlightHttpServer<E>,\r\n options: ServeOptions = {}\r\n): { shutdown: () => Promise<void> } {\r\n const {\r\n port = 3000,\r\n hostname = '0.0.0.0',\r\n onListen,\r\n } = options;\r\n\r\n const server = Deno.serve(\r\n (request) => app.fetch(request),\r\n {\r\n port,\r\n hostname,\r\n onListen: onListen\r\n ? (params) => onListen({ port: params.port, hostname: params.hostname })\r\n : (params) => {\r\n console.log(`\r\n ✈ïļ Flight HTTP Server (Deno) running!\r\n \r\n ➜ Local: http://localhost:${params.port}/\r\n ➜ Network: http://${params.hostname}:${params.port}/\r\n`);\r\n },\r\n }\r\n );\r\n\r\n return server;\r\n}\r\n\r\n// Re-export useful types\r\nexport type { FlightHttpServer } from '../types.js';\r\n"]}
@@ -0,0 +1,34 @@
1
+ import { createServer } from 'node:http';
2
+ import { E as Env, F as FlightHttpServer } from '../types-4GWmDi9X.js';
3
+
4
+ /**
5
+ * @flight/http - Node.js Adapter
6
+ *
7
+ * Allows running Flight HTTP server on Node.js.
8
+ */
9
+
10
+ interface ServeOptions {
11
+ port?: number;
12
+ hostname?: string;
13
+ onListen?: (info: {
14
+ port: number;
15
+ hostname: string;
16
+ }) => void;
17
+ }
18
+ /**
19
+ * Start a Node.js HTTP server with the Flight app
20
+ *
21
+ * @example
22
+ * ```typescript
23
+ * import { createServer } from '@flight/http';
24
+ * import { serve } from '@flight/http/node';
25
+ *
26
+ * const app = createServer();
27
+ * app.get('/', (c) => c.json({ hello: 'world' }));
28
+ *
29
+ * serve(app, { port: 3000 });
30
+ * ```
31
+ */
32
+ declare function serve<E extends Env = Env>(app: FlightHttpServer<E>, options?: ServeOptions): ReturnType<typeof createServer>;
33
+
34
+ export { FlightHttpServer, type ServeOptions, serve };
@@ -0,0 +1,89 @@
1
+ import { createServer } from 'http';
2
+
3
+ // src/adapters/node.ts
4
+ async function toWebRequest(req) {
5
+ const host = req.headers.host || "localhost";
6
+ const protocol = "http";
7
+ const url = new URL(req.url || "/", `${protocol}://${host}`);
8
+ let body = null;
9
+ if (req.method !== "GET" && req.method !== "HEAD") {
10
+ const chunks = [];
11
+ for await (const chunk of req) {
12
+ chunks.push(chunk);
13
+ }
14
+ body = Buffer.concat(chunks);
15
+ }
16
+ const headers = new Headers();
17
+ for (const [key, value] of Object.entries(req.headers)) {
18
+ if (value) {
19
+ if (Array.isArray(value)) {
20
+ for (const v of value) {
21
+ headers.append(key, v);
22
+ }
23
+ } else {
24
+ headers.set(key, value);
25
+ }
26
+ }
27
+ }
28
+ return new Request(url.toString(), {
29
+ method: req.method || "GET",
30
+ headers,
31
+ body
32
+ });
33
+ }
34
+ async function writeWebResponse(webResponse, res) {
35
+ res.statusCode = webResponse.status;
36
+ res.statusMessage = webResponse.statusText;
37
+ webResponse.headers.forEach((value, key) => {
38
+ res.setHeader(key, value);
39
+ });
40
+ if (webResponse.body) {
41
+ const reader = webResponse.body.getReader();
42
+ try {
43
+ while (true) {
44
+ const { done, value } = await reader.read();
45
+ if (done) break;
46
+ res.write(value);
47
+ }
48
+ } finally {
49
+ reader.releaseLock();
50
+ }
51
+ }
52
+ res.end();
53
+ }
54
+ function serve(app, options = {}) {
55
+ const {
56
+ port = 3e3,
57
+ hostname = "0.0.0.0",
58
+ onListen
59
+ } = options;
60
+ const server = createServer(async (req, res) => {
61
+ try {
62
+ const webRequest = await toWebRequest(req);
63
+ const webResponse = await app.fetch(webRequest);
64
+ await writeWebResponse(webResponse, res);
65
+ } catch (error) {
66
+ console.error("[Flight HTTP Node] Error:", error);
67
+ res.statusCode = 500;
68
+ res.end("Internal Server Error");
69
+ }
70
+ });
71
+ server.listen(port, hostname, () => {
72
+ const info = { port, hostname };
73
+ if (onListen) {
74
+ onListen(info);
75
+ } else {
76
+ console.log(`
77
+ \u2708\uFE0F Flight HTTP Server running!
78
+
79
+ \u279C Local: http://${hostname === "0.0.0.0" ? "localhost" : hostname}:${port}/
80
+ \u279C Network: http://${hostname}:${port}/
81
+ `);
82
+ }
83
+ });
84
+ return server;
85
+ }
86
+
87
+ export { serve };
88
+ //# sourceMappingURL=node.js.map
89
+ //# sourceMappingURL=node.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/adapters/node.ts"],"names":["createNodeServer"],"mappings":"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","file":"node.js","sourcesContent":["/**\r\n * @flight/http - Node.js Adapter\r\n * \r\n * Allows running Flight HTTP server on Node.js.\r\n */\r\n\r\nimport { createServer as createNodeServer, type IncomingMessage, type ServerResponse } from 'node:http';\r\nimport type { FlightHttpServer, Env } from '../types.js';\r\n\r\n// ============================================================================\r\n// Node.js Request Converter\r\n// ============================================================================\r\n\r\nasync function toWebRequest(req: IncomingMessage): Promise<Request> {\r\n const host = req.headers.host || 'localhost';\r\n const protocol = 'http'; // Simplified, use https detection in production\r\n const url = new URL(req.url || '/', `${protocol}://${host}`);\r\n\r\n // Collect body for non-GET/HEAD requests\r\n let body: BodyInit | null = null;\r\n\r\n if (req.method !== 'GET' && req.method !== 'HEAD') {\r\n const chunks: Buffer[] = [];\r\n for await (const chunk of req) {\r\n chunks.push(chunk as Buffer);\r\n }\r\n body = Buffer.concat(chunks);\r\n }\r\n\r\n // Convert headers\r\n const headers = new Headers();\r\n for (const [key, value] of Object.entries(req.headers)) {\r\n if (value) {\r\n if (Array.isArray(value)) {\r\n for (const v of value) {\r\n headers.append(key, v);\r\n }\r\n } else {\r\n headers.set(key, value);\r\n }\r\n }\r\n }\r\n\r\n return new Request(url.toString(), {\r\n method: req.method || 'GET',\r\n headers,\r\n body,\r\n });\r\n}\r\n\r\n// ============================================================================\r\n// Node.js Response Writer\r\n// ============================================================================\r\n\r\nasync function writeWebResponse(webResponse: Response, res: ServerResponse): Promise<void> {\r\n // Set status\r\n res.statusCode = webResponse.status;\r\n res.statusMessage = webResponse.statusText;\r\n\r\n // Set headers\r\n webResponse.headers.forEach((value, key) => {\r\n res.setHeader(key, value);\r\n });\r\n\r\n // Send body\r\n if (webResponse.body) {\r\n const reader = webResponse.body.getReader();\r\n\r\n try {\r\n while (true) {\r\n const { done, value } = await reader.read();\r\n if (done) break;\r\n res.write(value);\r\n }\r\n } finally {\r\n reader.releaseLock();\r\n }\r\n }\r\n\r\n res.end();\r\n}\r\n\r\n// ============================================================================\r\n// serve() Function\r\n// ============================================================================\r\n\r\nexport interface ServeOptions {\r\n port?: number;\r\n hostname?: string;\r\n onListen?: (info: { port: number; hostname: string }) => void;\r\n}\r\n\r\n/**\r\n * Start a Node.js HTTP server with the Flight app\r\n * \r\n * @example\r\n * ```typescript\r\n * import { createServer } from '@flight/http';\r\n * import { serve } from '@flight/http/node';\r\n * \r\n * const app = createServer();\r\n * app.get('/', (c) => c.json({ hello: 'world' }));\r\n * \r\n * serve(app, { port: 3000 });\r\n * ```\r\n */\r\nexport function serve<E extends Env = Env>(\r\n app: FlightHttpServer<E>,\r\n options: ServeOptions = {}\r\n): ReturnType<typeof createNodeServer> {\r\n const {\r\n port = 3000,\r\n hostname = '0.0.0.0',\r\n onListen,\r\n } = options;\r\n\r\n const server = createNodeServer(async (req, res) => {\r\n try {\r\n const webRequest = await toWebRequest(req);\r\n const webResponse = await app.fetch(webRequest);\r\n await writeWebResponse(webResponse, res);\r\n } catch (error) {\r\n console.error('[Flight HTTP Node] Error:', error);\r\n res.statusCode = 500;\r\n res.end('Internal Server Error');\r\n }\r\n });\r\n\r\n server.listen(port, hostname, () => {\r\n const info = { port, hostname };\r\n\r\n if (onListen) {\r\n onListen(info);\r\n } else {\r\n console.log(`\r\n ✈ïļ Flight HTTP Server running!\r\n \r\n ➜ Local: http://${hostname === '0.0.0.0' ? 'localhost' : hostname}:${port}/\r\n ➜ Network: http://${hostname}:${port}/\r\n`);\r\n }\r\n });\r\n\r\n return server;\r\n}\r\n\r\n// Re-export useful types\r\nexport type { FlightHttpServer } from '../types.js';\r\n"]}
@@ -0,0 +1,100 @@
1
+ import { E as Env, S as ServerOptions, F as FlightHttpServer, H as HandlerOrMiddleware, M as Middleware, a as Handler, C as Context, b as FetchOptions, L as ListenOptions, c as HttpMethod, R as Route } from './types-4GWmDi9X.js';
2
+
3
+ /**
4
+ * @flight/http - Server
5
+ *
6
+ * Main server class that implements the FlightHttpServer interface.
7
+ * Inspired by Hono's design patterns.
8
+ */
9
+
10
+ declare class FlightHttp<E extends Env = Env> implements FlightHttpServer<E> {
11
+ private router;
12
+ private globalMiddleware;
13
+ private notFoundHandler;
14
+ private errorHandler;
15
+ private basePath;
16
+ constructor(options?: ServerOptions);
17
+ get(path: string, ...handlers: HandlerOrMiddleware<E>[]): FlightHttpServer<E>;
18
+ post(path: string, ...handlers: HandlerOrMiddleware<E>[]): FlightHttpServer<E>;
19
+ put(path: string, ...handlers: HandlerOrMiddleware<E>[]): FlightHttpServer<E>;
20
+ delete(path: string, ...handlers: HandlerOrMiddleware<E>[]): FlightHttpServer<E>;
21
+ patch(path: string, ...handlers: HandlerOrMiddleware<E>[]): FlightHttpServer<E>;
22
+ options(path: string, ...handlers: HandlerOrMiddleware<E>[]): FlightHttpServer<E>;
23
+ head(path: string, ...handlers: HandlerOrMiddleware<E>[]): FlightHttpServer<E>;
24
+ all(path: string, ...handlers: HandlerOrMiddleware<E>[]): FlightHttpServer<E>;
25
+ use(...handlers: Middleware<E>[]): FlightHttpServer<E>;
26
+ route(path: string, router: FlightHttpServer<E>): FlightHttpServer<E>;
27
+ notFound(handler: Handler<E>): FlightHttpServer<E>;
28
+ onError(handler: (error: Error, c: Context<E>) => Response | Promise<Response>): FlightHttpServer<E>;
29
+ fetch(request: Request, options?: FetchOptions): Promise<Response>;
30
+ listen(options?: ListenOptions | number): void;
31
+ private addRoute;
32
+ /**
33
+ * Compose middleware chain with final handler
34
+ */
35
+ private composeMiddleware;
36
+ }
37
+ /**
38
+ * Create a new Flight HTTP server
39
+ *
40
+ * @example
41
+ * ```typescript
42
+ * import { createServer } from '@flight/http';
43
+ *
44
+ * const app = createServer();
45
+ *
46
+ * app.get('/', (c) => c.json({ message: 'Hello Flight!' }));
47
+ *
48
+ * export default app;
49
+ * ```
50
+ */
51
+ declare function createServer<E extends Env = Env>(options?: ServerOptions): FlightHttpServer<E>;
52
+
53
+ /**
54
+ * @flight/http - Context
55
+ *
56
+ * Request/Response context object passed to handlers.
57
+ * Similar to Hono's Context for familiarity.
58
+ */
59
+
60
+ declare function createContext<E extends Env = Env>(request: Request, params?: Record<string, string>, env?: E['Bindings']): Context<E>;
61
+
62
+ /**
63
+ * @flight/http - Router
64
+ *
65
+ * Radix-tree based router for ultra-fast path matching.
66
+ * Uses radix3 under the hood.
67
+ */
68
+
69
+ interface MatchResult<E extends Env = Env> {
70
+ handler: Handler<E>;
71
+ params: Record<string, string>;
72
+ }
73
+ declare class Router<E extends Env = Env> {
74
+ private radix;
75
+ private routes;
76
+ constructor();
77
+ /**
78
+ * Add a route
79
+ */
80
+ add(method: HttpMethod | '*', path: string, handler: Handler<E>): void;
81
+ /**
82
+ * Match a request to a route
83
+ */
84
+ match(method: HttpMethod, path: string): MatchResult<E> | null;
85
+ /**
86
+ * Get all registered routes
87
+ */
88
+ getRoutes(): Route<E>[];
89
+ /**
90
+ * Normalize path
91
+ */
92
+ private normalizePath;
93
+ /**
94
+ * Extract params from matched path
95
+ * @deprecated radix3 handles param extraction internally
96
+ */
97
+ private _extractParams;
98
+ }
99
+
100
+ export { Context, Env, FetchOptions, FlightHttp, FlightHttpServer, Handler, HandlerOrMiddleware, HttpMethod, ListenOptions, Middleware, Route, Router, ServerOptions, createContext, createServer };
package/dist/index.js ADDED
@@ -0,0 +1,318 @@
1
+ import { createRouter } from 'radix3';
2
+
3
+ // src/router.ts
4
+ var Router = class {
5
+ radix;
6
+ routes = [];
7
+ constructor() {
8
+ this.radix = createRouter();
9
+ }
10
+ /**
11
+ * Add a route
12
+ */
13
+ add(method, path, handler) {
14
+ const normalizedPath = this.normalizePath(path);
15
+ let node = this.radix.lookup(normalizedPath);
16
+ if (!node) {
17
+ node = { handlers: /* @__PURE__ */ new Map() };
18
+ this.radix.insert(normalizedPath, node);
19
+ }
20
+ node.handlers.set(method, handler);
21
+ this.routes.push({
22
+ method,
23
+ path: normalizedPath,
24
+ handler,
25
+ middleware: []
26
+ });
27
+ }
28
+ /**
29
+ * Match a request to a route
30
+ */
31
+ match(method, path) {
32
+ const normalizedPath = this.normalizePath(path);
33
+ const result = this.radix.lookup(normalizedPath);
34
+ if (!result) {
35
+ return null;
36
+ }
37
+ let handler = result.handlers.get(method);
38
+ if (!handler) {
39
+ handler = result.handlers.get("*");
40
+ }
41
+ if (!handler) {
42
+ return null;
43
+ }
44
+ const params = result.params || {};
45
+ return { handler, params };
46
+ }
47
+ /**
48
+ * Get all registered routes
49
+ */
50
+ getRoutes() {
51
+ return [...this.routes];
52
+ }
53
+ /**
54
+ * Normalize path
55
+ */
56
+ normalizePath(path) {
57
+ if (!path.startsWith("/")) {
58
+ path = "/" + path;
59
+ }
60
+ if (path !== "/" && path.endsWith("/")) {
61
+ path = path.slice(0, -1);
62
+ }
63
+ path = path.replace(/\[([^\]]+)\]/g, ":$1");
64
+ path = path.replace(/\[\.\.\.([\w]+)\]/g, "**");
65
+ return path;
66
+ }
67
+ /**
68
+ * Extract params from matched path
69
+ * @deprecated radix3 handles param extraction internally
70
+ */
71
+ _extractParams(pattern, actualPath) {
72
+ const params = {};
73
+ const patternParts = pattern.split("/");
74
+ const actualParts = actualPath.split("/");
75
+ for (let i = 0; i < patternParts.length; i++) {
76
+ const patternPart = patternParts[i];
77
+ if (!patternPart) continue;
78
+ if (patternPart.startsWith(":")) {
79
+ const paramName = patternPart.slice(1);
80
+ params[paramName] = actualParts[i] || "";
81
+ } else if (patternPart === "**") {
82
+ const paramName = "slug";
83
+ params[paramName] = actualParts.slice(i).join("/");
84
+ break;
85
+ }
86
+ }
87
+ return params;
88
+ }
89
+ };
90
+
91
+ // src/context.ts
92
+ function createContext(request, params = {}, env = {}) {
93
+ const variables = {};
94
+ return {
95
+ req: request,
96
+ env,
97
+ params,
98
+ variables,
99
+ // Response helpers
100
+ json(data, status = 200) {
101
+ return new Response(JSON.stringify(data), {
102
+ status,
103
+ headers: { "Content-Type": "application/json; charset=utf-8" }
104
+ });
105
+ },
106
+ text(text, status = 200) {
107
+ return new Response(text, {
108
+ status,
109
+ headers: { "Content-Type": "text/plain; charset=utf-8" }
110
+ });
111
+ },
112
+ html(html, status = 200) {
113
+ return new Response(html, {
114
+ status,
115
+ headers: { "Content-Type": "text/html; charset=utf-8" }
116
+ });
117
+ },
118
+ redirect(url, status = 302) {
119
+ return new Response(null, {
120
+ status,
121
+ headers: { Location: url }
122
+ });
123
+ },
124
+ // Variable getter/setter
125
+ get(key) {
126
+ return variables[key];
127
+ },
128
+ set(key, value) {
129
+ variables[key] = value;
130
+ },
131
+ // Request helpers
132
+ header(name) {
133
+ return request.headers.get(name);
134
+ },
135
+ cookie(name) {
136
+ const cookieHeader = request.headers.get("cookie");
137
+ if (!cookieHeader) return void 0;
138
+ const cookies = parseCookies(cookieHeader);
139
+ return cookies[name];
140
+ }
141
+ };
142
+ }
143
+ function parseCookies(cookieHeader) {
144
+ const cookies = {};
145
+ for (const cookie of cookieHeader.split(";")) {
146
+ const [name, ...rest] = cookie.trim().split("=");
147
+ if (name) {
148
+ cookies[name] = rest.join("=");
149
+ }
150
+ }
151
+ return cookies;
152
+ }
153
+
154
+ // src/server.ts
155
+ var FlightHttp = class _FlightHttp {
156
+ router;
157
+ globalMiddleware = [];
158
+ notFoundHandler;
159
+ errorHandler;
160
+ basePath;
161
+ constructor(options = {}) {
162
+ this.router = new Router();
163
+ this.basePath = options.basePath || "";
164
+ this.notFoundHandler = (c) => c.json(
165
+ { error: "Not Found", path: new URL(c.req.url).pathname },
166
+ 404
167
+ );
168
+ this.errorHandler = (error, c) => c.json(
169
+ { error: error.message || "Internal Server Error" },
170
+ 500
171
+ );
172
+ }
173
+ // ========================================================================
174
+ // Route Registration
175
+ // ========================================================================
176
+ get(path, ...handlers) {
177
+ return this.addRoute("GET", path, handlers);
178
+ }
179
+ post(path, ...handlers) {
180
+ return this.addRoute("POST", path, handlers);
181
+ }
182
+ put(path, ...handlers) {
183
+ return this.addRoute("PUT", path, handlers);
184
+ }
185
+ delete(path, ...handlers) {
186
+ return this.addRoute("DELETE", path, handlers);
187
+ }
188
+ patch(path, ...handlers) {
189
+ return this.addRoute("PATCH", path, handlers);
190
+ }
191
+ options(path, ...handlers) {
192
+ return this.addRoute("OPTIONS", path, handlers);
193
+ }
194
+ head(path, ...handlers) {
195
+ return this.addRoute("HEAD", path, handlers);
196
+ }
197
+ all(path, ...handlers) {
198
+ return this.addRoute("*", path, handlers);
199
+ }
200
+ // ========================================================================
201
+ // Middleware
202
+ // ========================================================================
203
+ use(...handlers) {
204
+ this.globalMiddleware.push(...handlers);
205
+ return this;
206
+ }
207
+ // ========================================================================
208
+ // Sub-routing
209
+ // ========================================================================
210
+ route(path, router) {
211
+ if (router instanceof _FlightHttp) {
212
+ const routes = router.router.getRoutes();
213
+ for (const route of routes) {
214
+ const fullPath = this.basePath + path + route.path;
215
+ this.router.add(route.method, fullPath, route.handler);
216
+ }
217
+ }
218
+ return this;
219
+ }
220
+ // ========================================================================
221
+ // Error Handling
222
+ // ========================================================================
223
+ notFound(handler) {
224
+ this.notFoundHandler = handler;
225
+ return this;
226
+ }
227
+ onError(handler) {
228
+ this.errorHandler = handler;
229
+ return this;
230
+ }
231
+ // ========================================================================
232
+ // Core Fetch Handler
233
+ // ========================================================================
234
+ async fetch(request, options = {}) {
235
+ const url = new URL(request.url);
236
+ const path = url.pathname;
237
+ const method = request.method.toUpperCase();
238
+ const match = this.router.match(method, path);
239
+ const context = createContext(
240
+ request,
241
+ match?.params || {},
242
+ options.env || {}
243
+ );
244
+ try {
245
+ const middlewares = [...this.globalMiddleware];
246
+ if (match) {
247
+ const composed = this.composeMiddleware(
248
+ middlewares,
249
+ match.handler
250
+ );
251
+ return await composed(context);
252
+ } else {
253
+ if (middlewares.length > 0) {
254
+ const composed = this.composeMiddleware(
255
+ middlewares,
256
+ this.notFoundHandler
257
+ );
258
+ return await composed(context);
259
+ }
260
+ return await this.notFoundHandler(context);
261
+ }
262
+ } catch (error) {
263
+ console.error("[Flight HTTP] Error:", error);
264
+ return await this.errorHandler(
265
+ error instanceof Error ? error : new Error(String(error)),
266
+ context
267
+ );
268
+ }
269
+ }
270
+ // ========================================================================
271
+ // Listen (Node.js adapter will override this)
272
+ // ========================================================================
273
+ listen(options) {
274
+ const port = typeof options === "number" ? options : options?.port || 3e3;
275
+ console.log(`[Flight HTTP] Call adapter-specific listen() to start server on port ${port}`);
276
+ console.log('[Flight HTTP] Import from "@flight/http/node" or "@flight/http/bun"');
277
+ }
278
+ // ========================================================================
279
+ // Private Methods
280
+ // ========================================================================
281
+ addRoute(method, path, handlers) {
282
+ const mainHandler = handlers[handlers.length - 1];
283
+ const routeMiddleware = handlers.slice(0, -1);
284
+ const fullPath = this.basePath + path;
285
+ if (routeMiddleware.length > 0) {
286
+ const composedHandler = this.composeMiddleware(routeMiddleware, mainHandler);
287
+ this.router.add(method, fullPath, composedHandler);
288
+ } else {
289
+ this.router.add(method, fullPath, mainHandler);
290
+ }
291
+ return this;
292
+ }
293
+ /**
294
+ * Compose middleware chain with final handler
295
+ */
296
+ composeMiddleware(middlewares, handler) {
297
+ return async (c) => {
298
+ let index = 0;
299
+ const dispatch = async () => {
300
+ if (index < middlewares.length) {
301
+ const middleware = middlewares[index++];
302
+ if (middleware) {
303
+ return await middleware(c, dispatch);
304
+ }
305
+ }
306
+ return await handler(c);
307
+ };
308
+ return await dispatch();
309
+ };
310
+ }
311
+ };
312
+ function createServer(options = {}) {
313
+ return new FlightHttp(options);
314
+ }
315
+
316
+ export { FlightHttp, Router, createContext, createServer };
317
+ //# sourceMappingURL=index.js.map
318
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/router.ts","../src/context.ts","../src/server.ts"],"names":["createRadixRouter"],"mappings":"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","file":"index.js","sourcesContent":["/**\r\n * @flight/http - Router\r\n * \r\n * Radix-tree based router for ultra-fast path matching.\r\n * Uses radix3 under the hood.\r\n */\r\n\r\nimport { createRouter as createRadixRouter, type RadixRouter } from 'radix3';\r\nimport type { Handler, HttpMethod, Route, Env } from './types.js';\r\n\r\n// ============================================================================\r\n// Router Types\r\n// ============================================================================\r\n\r\ninterface RouterNode<E extends Env = Env> {\r\n handlers: Map<HttpMethod | '*', Handler<E>>;\r\n}\r\n\r\ninterface MatchResult<E extends Env = Env> {\r\n handler: Handler<E>;\r\n params: Record<string, string>;\r\n}\r\n\r\n// ============================================================================\r\n// Router Class\r\n// ============================================================================\r\n\r\nexport class Router<E extends Env = Env> {\r\n private radix: RadixRouter<RouterNode<E>>;\r\n private routes: Route<E>[] = [];\r\n\r\n constructor() {\r\n this.radix = createRadixRouter<RouterNode<E>>();\r\n }\r\n\r\n /**\r\n * Add a route\r\n */\r\n add(method: HttpMethod | '*', path: string, handler: Handler<E>): void {\r\n // Normalize path\r\n const normalizedPath = this.normalizePath(path);\r\n\r\n // Get or create node\r\n let node = this.radix.lookup(normalizedPath);\r\n\r\n if (!node) {\r\n node = { handlers: new Map() };\r\n this.radix.insert(normalizedPath, node);\r\n }\r\n\r\n // Add handler for method\r\n node.handlers.set(method, handler);\r\n\r\n // Track route for debugging\r\n this.routes.push({\r\n method,\r\n path: normalizedPath,\r\n handler,\r\n middleware: [],\r\n });\r\n }\r\n\r\n /**\r\n * Match a request to a route\r\n */\r\n match(method: HttpMethod, path: string): MatchResult<E> | null {\r\n const normalizedPath = this.normalizePath(path);\r\n const result = this.radix.lookup(normalizedPath);\r\n\r\n if (!result) {\r\n return null;\r\n }\r\n\r\n // Try exact method first\r\n let handler = result.handlers.get(method);\r\n\r\n // Fall back to wildcard\r\n if (!handler) {\r\n handler = result.handlers.get('*');\r\n }\r\n\r\n if (!handler) {\r\n return null;\r\n }\r\n\r\n // radix3 returns params in the result via _params property\r\n // Also extract from the lookup result directly\r\n const params: Record<string, string> = (result as any).params || {};\r\n\r\n return { handler, params };\r\n }\r\n\r\n /**\r\n * Get all registered routes\r\n */\r\n getRoutes(): Route<E>[] {\r\n return [...this.routes];\r\n }\r\n\r\n /**\r\n * Normalize path\r\n */\r\n private normalizePath(path: string): string {\r\n // Ensure leading slash\r\n if (!path.startsWith('/')) {\r\n path = '/' + path;\r\n }\r\n\r\n // Remove trailing slash (except for root)\r\n if (path !== '/' && path.endsWith('/')) {\r\n path = path.slice(0, -1);\r\n }\r\n\r\n // Convert [param] to :param for radix3\r\n path = path.replace(/\\[([^\\]]+)\\]/g, ':$1');\r\n\r\n // Convert [...slug] to ** for catch-all\r\n path = path.replace(/\\[\\.\\.\\.([\\w]+)\\]/g, '**');\r\n\r\n return path;\r\n }\r\n\r\n /**\r\n * Extract params from matched path\r\n * @deprecated radix3 handles param extraction internally\r\n */\r\n private _extractParams(pattern: string, actualPath: string): Record<string, string> {\r\n const params: Record<string, string> = {};\r\n\r\n const patternParts = pattern.split('/');\r\n const actualParts = actualPath.split('/');\r\n\r\n for (let i = 0; i < patternParts.length; i++) {\r\n const patternPart = patternParts[i];\r\n if (!patternPart) continue;\r\n\r\n if (patternPart.startsWith(':')) {\r\n const paramName = patternPart.slice(1);\r\n params[paramName] = actualParts[i] || '';\r\n } else if (patternPart === '**') {\r\n // Catch-all: collect remaining parts\r\n const paramName = 'slug';\r\n params[paramName] = actualParts.slice(i).join('/');\r\n break;\r\n }\r\n }\r\n\r\n return params;\r\n }\r\n}\r\n","/**\r\n * @flight/http - Context\r\n * \r\n * Request/Response context object passed to handlers.\r\n * Similar to Hono's Context for familiarity.\r\n */\r\n\r\nimport type { Context, Env } from './types.js';\r\n\r\n// ============================================================================\r\n// Context Factory\r\n// ============================================================================\r\n\r\nexport function createContext<E extends Env = Env>(\r\n request: Request,\r\n params: Record<string, string> = {},\r\n env: E['Bindings'] = {} as E['Bindings']\r\n): Context<E> {\r\n const variables: Record<string, unknown> = {};\r\n\r\n return {\r\n req: request,\r\n env,\r\n params,\r\n variables: variables as E['Variables'],\r\n\r\n // Response helpers\r\n json<T>(data: T, status = 200): Response {\r\n return new Response(JSON.stringify(data), {\r\n status,\r\n headers: { 'Content-Type': 'application/json; charset=utf-8' },\r\n });\r\n },\r\n\r\n text(text: string, status = 200): Response {\r\n return new Response(text, {\r\n status,\r\n headers: { 'Content-Type': 'text/plain; charset=utf-8' },\r\n });\r\n },\r\n\r\n html(html: string, status = 200): Response {\r\n return new Response(html, {\r\n status,\r\n headers: { 'Content-Type': 'text/html; charset=utf-8' },\r\n });\r\n },\r\n\r\n redirect(url: string, status: 301 | 302 | 303 | 307 | 308 = 302): Response {\r\n return new Response(null, {\r\n status,\r\n headers: { Location: url },\r\n });\r\n },\r\n\r\n // Variable getter/setter\r\n get<K extends keyof E['Variables']>(key: K): E['Variables'][K] {\r\n return variables[key as string] as E['Variables'][K];\r\n },\r\n\r\n set<K extends keyof E['Variables']>(key: K, value: E['Variables'][K]): void {\r\n variables[key as string] = value;\r\n },\r\n\r\n // Request helpers\r\n header(name: string): string | null {\r\n return request.headers.get(name);\r\n },\r\n\r\n cookie(name: string): string | undefined {\r\n const cookieHeader = request.headers.get('cookie');\r\n if (!cookieHeader) return undefined;\r\n\r\n const cookies = parseCookies(cookieHeader);\r\n return cookies[name];\r\n },\r\n };\r\n}\r\n\r\n// ============================================================================\r\n// Cookie Parser\r\n// ============================================================================\r\n\r\nfunction parseCookies(cookieHeader: string): Record<string, string> {\r\n const cookies: Record<string, string> = {};\r\n\r\n for (const cookie of cookieHeader.split(';')) {\r\n const [name, ...rest] = cookie.trim().split('=');\r\n if (name) {\r\n cookies[name] = rest.join('=');\r\n }\r\n }\r\n\r\n return cookies;\r\n}\r\n","/**\r\n * @flight/http - Server\r\n * \r\n * Main server class that implements the FlightHttpServer interface.\r\n * Inspired by Hono's design patterns.\r\n */\r\n\r\nimport { Router } from './router.js';\r\nimport { createContext } from './context.js';\r\nimport type {\r\n FlightHttpServer,\r\n Handler,\r\n Middleware,\r\n HandlerOrMiddleware,\r\n HttpMethod,\r\n Context,\r\n Env,\r\n ServerOptions,\r\n ListenOptions,\r\n FetchOptions,\r\n} from './types.js';\r\n\r\n// ============================================================================\r\n// Server Class\r\n// ============================================================================\r\n\r\nexport class FlightHttp<E extends Env = Env> implements FlightHttpServer<E> {\r\n private router: Router<E>;\r\n private globalMiddleware: Middleware<E>[] = [];\r\n private notFoundHandler: Handler<E>;\r\n private errorHandler: (error: Error, c: Context<E>) => Response | Promise<Response>;\r\n private basePath: string;\r\n\r\n constructor(options: ServerOptions = {}) {\r\n this.router = new Router<E>();\r\n this.basePath = options.basePath || '';\r\n\r\n // Default not found handler\r\n this.notFoundHandler = (c) => c.json(\r\n { error: 'Not Found', path: new URL(c.req.url).pathname },\r\n 404\r\n );\r\n\r\n // Default error handler\r\n this.errorHandler = (error, c) => c.json(\r\n { error: error.message || 'Internal Server Error' },\r\n 500\r\n );\r\n }\r\n\r\n // ========================================================================\r\n // Route Registration\r\n // ========================================================================\r\n\r\n get(path: string, ...handlers: HandlerOrMiddleware<E>[]): FlightHttpServer<E> {\r\n return this.addRoute('GET', path, handlers);\r\n }\r\n\r\n post(path: string, ...handlers: HandlerOrMiddleware<E>[]): FlightHttpServer<E> {\r\n return this.addRoute('POST', path, handlers);\r\n }\r\n\r\n put(path: string, ...handlers: HandlerOrMiddleware<E>[]): FlightHttpServer<E> {\r\n return this.addRoute('PUT', path, handlers);\r\n }\r\n\r\n delete(path: string, ...handlers: HandlerOrMiddleware<E>[]): FlightHttpServer<E> {\r\n return this.addRoute('DELETE', path, handlers);\r\n }\r\n\r\n patch(path: string, ...handlers: HandlerOrMiddleware<E>[]): FlightHttpServer<E> {\r\n return this.addRoute('PATCH', path, handlers);\r\n }\r\n\r\n options(path: string, ...handlers: HandlerOrMiddleware<E>[]): FlightHttpServer<E> {\r\n return this.addRoute('OPTIONS', path, handlers);\r\n }\r\n\r\n head(path: string, ...handlers: HandlerOrMiddleware<E>[]): FlightHttpServer<E> {\r\n return this.addRoute('HEAD', path, handlers);\r\n }\r\n\r\n all(path: string, ...handlers: HandlerOrMiddleware<E>[]): FlightHttpServer<E> {\r\n return this.addRoute('*', path, handlers);\r\n }\r\n\r\n // ========================================================================\r\n // Middleware\r\n // ========================================================================\r\n\r\n use(...handlers: Middleware<E>[]): FlightHttpServer<E> {\r\n this.globalMiddleware.push(...handlers);\r\n return this;\r\n }\r\n\r\n // ========================================================================\r\n // Sub-routing\r\n // ========================================================================\r\n\r\n route(path: string, router: FlightHttpServer<E>): FlightHttpServer<E> {\r\n // Get routes from sub-router\r\n if (router instanceof FlightHttp) {\r\n const routes = router.router.getRoutes();\r\n for (const route of routes) {\r\n const fullPath = this.basePath + path + route.path;\r\n this.router.add(route.method, fullPath, route.handler);\r\n }\r\n }\r\n return this;\r\n }\r\n\r\n // ========================================================================\r\n // Error Handling\r\n // ========================================================================\r\n\r\n notFound(handler: Handler<E>): FlightHttpServer<E> {\r\n this.notFoundHandler = handler;\r\n return this;\r\n }\r\n\r\n onError(handler: (error: Error, c: Context<E>) => Response | Promise<Response>): FlightHttpServer<E> {\r\n this.errorHandler = handler;\r\n return this;\r\n }\r\n\r\n // ========================================================================\r\n // Core Fetch Handler\r\n // ========================================================================\r\n\r\n async fetch(request: Request, options: FetchOptions = {}): Promise<Response> {\r\n const url = new URL(request.url);\r\n const path = url.pathname;\r\n const method = request.method.toUpperCase() as HttpMethod;\r\n\r\n // Match route\r\n const match = this.router.match(method, path);\r\n\r\n // Create context\r\n const context = createContext<E>(\r\n request,\r\n match?.params || {},\r\n (options.env || {}) as E['Bindings']\r\n );\r\n\r\n try {\r\n // Build middleware chain\r\n const middlewares = [...this.globalMiddleware];\r\n\r\n if (match) {\r\n // Compose middleware + handler\r\n const composed = this.composeMiddleware(\r\n middlewares,\r\n match.handler\r\n );\r\n return await composed(context);\r\n } else {\r\n // No route found\r\n if (middlewares.length > 0) {\r\n const composed = this.composeMiddleware(\r\n middlewares,\r\n this.notFoundHandler\r\n );\r\n return await composed(context);\r\n }\r\n return await this.notFoundHandler(context);\r\n }\r\n } catch (error) {\r\n console.error('[Flight HTTP] Error:', error);\r\n return await this.errorHandler(\r\n error instanceof Error ? error : new Error(String(error)),\r\n context\r\n );\r\n }\r\n }\r\n\r\n // ========================================================================\r\n // Listen (Node.js adapter will override this)\r\n // ========================================================================\r\n\r\n listen(options?: ListenOptions | number): void {\r\n const port = typeof options === 'number' ? options : options?.port || 3000;\r\n console.log(`[Flight HTTP] Call adapter-specific listen() to start server on port ${port}`);\r\n console.log('[Flight HTTP] Import from \"@flight/http/node\" or \"@flight/http/bun\"');\r\n }\r\n\r\n // ========================================================================\r\n // Private Methods\r\n // ========================================================================\r\n\r\n private addRoute(\r\n method: HttpMethod | '*',\r\n path: string,\r\n handlers: HandlerOrMiddleware<E>[]\r\n ): FlightHttpServer<E> {\r\n // Last handler is the main handler\r\n const mainHandler = handlers[handlers.length - 1] as Handler<E>;\r\n const routeMiddleware = handlers.slice(0, -1) as Middleware<E>[];\r\n\r\n const fullPath = this.basePath + path;\r\n\r\n // If there's route-specific middleware, compose it\r\n if (routeMiddleware.length > 0) {\r\n const composedHandler = this.composeMiddleware(routeMiddleware, mainHandler);\r\n this.router.add(method, fullPath, composedHandler);\r\n } else {\r\n this.router.add(method, fullPath, mainHandler);\r\n }\r\n\r\n return this;\r\n }\r\n\r\n /**\r\n * Compose middleware chain with final handler\r\n */\r\n private composeMiddleware(\r\n middlewares: Middleware<E>[],\r\n handler: Handler<E>\r\n ): Handler<E> {\r\n return async (c: Context<E>) => {\r\n let index = 0;\r\n\r\n const dispatch = async (): Promise<Response> => {\r\n if (index < middlewares.length) {\r\n const middleware = middlewares[index++];\r\n if (middleware) {\r\n return await middleware(c, dispatch);\r\n }\r\n }\r\n return await handler(c);\r\n };\r\n\r\n return await dispatch();\r\n };\r\n }\r\n}\r\n\r\n// ============================================================================\r\n// Factory Function\r\n// ============================================================================\r\n\r\n/**\r\n * Create a new Flight HTTP server\r\n * \r\n * @example\r\n * ```typescript\r\n * import { createServer } from '@flight/http';\r\n * \r\n * const app = createServer();\r\n * \r\n * app.get('/', (c) => c.json({ message: 'Hello Flight!' }));\r\n * \r\n * export default app;\r\n * ```\r\n */\r\nexport function createServer<E extends Env = Env>(\r\n options: ServerOptions = {}\r\n): FlightHttpServer<E> {\r\n return new FlightHttp<E>(options);\r\n}\r\n"]}
@@ -0,0 +1,82 @@
1
+ /**
2
+ * @flight/http - Types
3
+ *
4
+ * Core type definitions for the HTTP server.
5
+ * Based on Web Standards API (Request, Response).
6
+ */
7
+ type HttpMethod = 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH' | 'HEAD' | 'OPTIONS';
8
+ /**
9
+ * Context object passed to route handlers
10
+ * Similar to Hono's Context for familiarity
11
+ */
12
+ interface Context<E extends Env = Env> {
13
+ /** The original Web Standard Request */
14
+ req: Request;
15
+ /** Environment bindings (for edge runtimes) */
16
+ env: E['Bindings'];
17
+ /** Route parameters extracted from URL */
18
+ params: Record<string, string>;
19
+ /** Variables set by middleware */
20
+ variables: E['Variables'];
21
+ json: <T>(data: T, status?: number) => Response;
22
+ text: (text: string, status?: number) => Response;
23
+ html: (html: string, status?: number) => Response;
24
+ redirect: (url: string, status?: 301 | 302 | 303 | 307 | 308) => Response;
25
+ get: <K extends keyof E['Variables']>(key: K) => E['Variables'][K];
26
+ set: <K extends keyof E['Variables']>(key: K, value: E['Variables'][K]) => void;
27
+ header: (name: string) => string | null;
28
+ cookie: (name: string) => string | undefined;
29
+ }
30
+ interface Env {
31
+ Bindings: Record<string, unknown>;
32
+ Variables: Record<string, unknown>;
33
+ }
34
+ /** Route handler function */
35
+ type Handler<E extends Env = Env> = (c: Context<E>) => Response | Promise<Response>;
36
+ /** Middleware function */
37
+ type Middleware<E extends Env = Env> = (c: Context<E>, next: () => Promise<Response>) => Response | Promise<Response>;
38
+ /** Combined handler or middleware */
39
+ type HandlerOrMiddleware<E extends Env = Env> = Handler<E> | Middleware<E>;
40
+ interface Route<E extends Env = Env> {
41
+ method: HttpMethod | '*';
42
+ path: string;
43
+ handler: Handler<E>;
44
+ middleware: Middleware<E>[];
45
+ }
46
+ interface ServerOptions {
47
+ /** Base path prefix for all routes */
48
+ basePath?: string;
49
+ }
50
+ interface ListenOptions {
51
+ /** Port to listen on */
52
+ port?: number;
53
+ /** Hostname to bind to */
54
+ hostname?: string;
55
+ /** Callback when server starts */
56
+ onListen?: (info: {
57
+ port: number;
58
+ hostname: string;
59
+ }) => void;
60
+ }
61
+ interface FetchOptions {
62
+ /** Environment bindings */
63
+ env?: Record<string, unknown>;
64
+ }
65
+ interface FlightHttpServer<E extends Env = Env> {
66
+ get: (path: string, ...handlers: HandlerOrMiddleware<E>[]) => FlightHttpServer<E>;
67
+ post: (path: string, ...handlers: HandlerOrMiddleware<E>[]) => FlightHttpServer<E>;
68
+ put: (path: string, ...handlers: HandlerOrMiddleware<E>[]) => FlightHttpServer<E>;
69
+ delete: (path: string, ...handlers: HandlerOrMiddleware<E>[]) => FlightHttpServer<E>;
70
+ patch: (path: string, ...handlers: HandlerOrMiddleware<E>[]) => FlightHttpServer<E>;
71
+ options: (path: string, ...handlers: HandlerOrMiddleware<E>[]) => FlightHttpServer<E>;
72
+ head: (path: string, ...handlers: HandlerOrMiddleware<E>[]) => FlightHttpServer<E>;
73
+ all: (path: string, ...handlers: HandlerOrMiddleware<E>[]) => FlightHttpServer<E>;
74
+ use: (...handlers: Middleware<E>[]) => FlightHttpServer<E>;
75
+ route: (path: string, router: FlightHttpServer<E>) => FlightHttpServer<E>;
76
+ fetch: (request: Request, options?: FetchOptions) => Promise<Response>;
77
+ listen: (options?: ListenOptions | number) => void;
78
+ notFound: (handler: Handler<E>) => FlightHttpServer<E>;
79
+ onError: (handler: (error: Error, c: Context<E>) => Response | Promise<Response>) => FlightHttpServer<E>;
80
+ }
81
+
82
+ export type { Context as C, Env as E, FlightHttpServer as F, HandlerOrMiddleware as H, ListenOptions as L, Middleware as M, Route as R, ServerOptions as S, Handler as a, FetchOptions as b, HttpMethod as c };
package/package.json ADDED
@@ -0,0 +1,56 @@
1
+ {
2
+ "name": "@flight-framework/http",
3
+ "version": "0.0.1",
4
+ "description": "Ultra-fast HTTP server for Flight Framework - Built on Web Standards",
5
+ "keywords": [
6
+ "flight",
7
+ "http",
8
+ "server",
9
+ "web-standards",
10
+ "edge"
11
+ ],
12
+ "license": "MIT",
13
+ "author": "Flight Contributors",
14
+ "type": "module",
15
+ "exports": {
16
+ ".": {
17
+ "types": "./dist/index.d.ts",
18
+ "import": "./dist/index.js"
19
+ },
20
+ "./node": {
21
+ "types": "./dist/adapters/node.d.ts",
22
+ "import": "./dist/adapters/node.js"
23
+ },
24
+ "./bun": {
25
+ "types": "./dist/adapters/bun.d.ts",
26
+ "import": "./dist/adapters/bun.js"
27
+ },
28
+ "./deno": {
29
+ "types": "./dist/adapters/deno.d.ts",
30
+ "import": "./dist/adapters/deno.js"
31
+ }
32
+ },
33
+ "main": "./dist/index.js",
34
+ "types": "./dist/index.d.ts",
35
+ "files": [
36
+ "dist"
37
+ ],
38
+ "dependencies": {
39
+ "radix3": "^1.1.0"
40
+ },
41
+ "devDependencies": {
42
+ "@types/node": "^22.0.0",
43
+ "rimraf": "^6.0.0",
44
+ "tsup": "^8.0.0",
45
+ "typescript": "^5.7.0",
46
+ "vitest": "^2.0.0"
47
+ },
48
+ "scripts": {
49
+ "build": "tsup",
50
+ "dev": "tsup --watch",
51
+ "test": "vitest run",
52
+ "test:watch": "vitest",
53
+ "lint": "eslint src/",
54
+ "clean": "rimraf dist"
55
+ }
56
+ }