@flexireact/core 1.0.2 → 2.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.
Files changed (37) hide show
  1. package/README.md +112 -112
  2. package/bin/flexireact.js +23 -0
  3. package/cli/index.ts +9 -21
  4. package/core/cli/{logger.js → logger.ts} +8 -2
  5. package/core/client/{hydration.js → hydration.ts} +10 -0
  6. package/core/client/{islands.js → islands.ts} +6 -1
  7. package/core/client/{navigation.js → navigation.ts} +10 -2
  8. package/core/client/{runtime.js → runtime.ts} +16 -0
  9. package/core/{index.js → index.ts} +2 -1
  10. package/core/islands/{index.js → index.ts} +16 -4
  11. package/core/{logger.js → logger.ts} +1 -1
  12. package/core/middleware/{index.js → index.ts} +32 -9
  13. package/core/plugins/{index.js → index.ts} +9 -6
  14. package/core/render/index.ts +1069 -0
  15. package/core/{render.js → render.ts} +7 -5
  16. package/core/router/index.ts +543 -0
  17. package/core/rsc/{index.js → index.ts} +6 -5
  18. package/core/server/{index.js → index.ts} +25 -6
  19. package/core/{server.js → server.ts} +8 -2
  20. package/core/ssg/{index.js → index.ts} +30 -5
  21. package/core/start-dev.ts +6 -0
  22. package/core/start-prod.ts +6 -0
  23. package/core/tsconfig.json +28 -0
  24. package/core/types.ts +239 -0
  25. package/package.json +19 -14
  26. package/cli/index.js +0 -992
  27. package/core/render/index.js +0 -773
  28. package/core/router/index.js +0 -296
  29. /package/core/{api.js → api.ts} +0 -0
  30. /package/core/build/{index.js → index.ts} +0 -0
  31. /package/core/client/{index.js → index.ts} +0 -0
  32. /package/core/{config.js → config.ts} +0 -0
  33. /package/core/{context.js → context.ts} +0 -0
  34. /package/core/{dev.js → dev.ts} +0 -0
  35. /package/core/{loader.js → loader.ts} +0 -0
  36. /package/core/{router.js → router.ts} +0 -0
  37. /package/core/{utils.js → utils.ts} +0 -0
package/README.md CHANGED
@@ -2,24 +2,33 @@
2
2
  <img src="./assets/flexireact.webp" alt="FlexiReact Logo" width="400" />
3
3
  </p>
4
4
 
5
- <h1 align="center">FlexiReact</h1>
5
+ <h1 align="center">FlexiReact v2</h1>
6
6
 
7
7
  <p align="center">
8
8
  <strong>The Modern React Framework</strong>
9
9
  </p>
10
10
 
11
11
  <p align="center">
12
- A blazing-fast React framework with TypeScript, Tailwind CSS, SSR, SSG, Islands architecture, and file-based routing.<br/>
12
+ A blazing-fast React framework with TypeScript, Tailwind CSS v4, SSR, SSG, Islands architecture, and file-based routing.<br/>
13
13
  Inspired by Next.js, Remix, Astro, and TanStack Start — but simpler and lighter.
14
14
  </p>
15
15
 
16
16
  <p align="center">
17
17
  <a href="https://www.npmjs.com/package/@flexireact/core"><img src="https://img.shields.io/npm/v/@flexireact/core.svg" alt="npm version" /></a>
18
18
  <a href="https://opensource.org/licenses/MIT"><img src="https://img.shields.io/badge/License-MIT-yellow.svg" alt="License: MIT" /></a>
19
- <a href="#"><img src="https://img.shields.io/badge/TypeScript-Ready-blue.svg" alt="TypeScript Ready" /></a>
20
- <a href="#"><img src="https://img.shields.io/badge/Tailwind-CSS-38B2AC.svg" alt="Tailwind CSS" /></a>
19
+ <a href="#"><img src="https://img.shields.io/badge/TypeScript-Native-blue.svg" alt="TypeScript Native" /></a>
20
+ <a href="#"><img src="https://img.shields.io/badge/Tailwind-v4-38B2AC.svg" alt="Tailwind CSS v4" /></a>
21
21
  </p>
22
22
 
23
+ ## 🆕 What's New in v2
24
+
25
+ - **TypeScript Native** — Core rewritten in TypeScript for better DX
26
+ - **Tailwind CSS v4** — New `@import "tailwindcss"` and `@theme` syntax
27
+ - **Routes Directory** — New `routes/` directory with route groups, dynamic segments
28
+ - **Modern 404 Page** — Beautiful, interactive error pages
29
+ - **Enhanced DevTools** — Precise error messages with color-coded render times
30
+ - **Improved CLI** — TypeScript-based CLI with better templates
31
+
23
32
  ## ✨ Features
24
33
 
25
34
  ### 🏗️ Core Framework
@@ -156,51 +165,73 @@ npm run start
156
165
 
157
166
  Open http://localhost:3000
158
167
 
159
- ## 📁 Project Structure
168
+ ## 📁 Project Structure (v2)
169
+
170
+ FlexiReact v2 introduces a new `routes/` directory with enhanced routing capabilities:
160
171
 
161
172
  ```
162
173
  myapp/
163
- ├── app/ # App directory
164
- │ ├── components/ # Reusable components
165
- │ │ ├── Button.tsx # Button component
166
- │ │ ├── Card.tsx # Card component
167
- │ │ ├── Navbar.tsx # Navigation bar
168
- │ │ └── index.ts # Component exports
174
+ ├── app/ # App directory (layout, components, styles)
175
+ │ ├── components/
176
+ │ │ ├── ui/ # UI components (Button, Card, etc.)
177
+ │ │ └── layout/ # Layout components (Navbar, Footer)
169
178
  │ ├── styles/
170
- │ │ └── globals.css # Global styles + Tailwind
171
- └── layout.tsx # Root layout
172
- ├── pages/ # Routes (file-based)
173
- ├── index.tsx # /
174
- │ ├── about.tsx # /about
179
+ │ │ └── globals.css # Global styles + Tailwind v4
180
+ ├── providers/ # React context providers
181
+ │ └── layout.tsx # Root layout
182
+ ├── routes/ # FlexiReact v2 file-based routing
183
+ │ ├── (public)/ # Route groups (don't affect URL)
184
+ │ │ ├── home.tsx # → /
185
+ │ │ └── about.tsx # → /about
175
186
  │ ├── blog/
176
- │ │ ├── index.tsx # → /blog
177
- │ │ └── [slug].tsx # → /blog/:slug
187
+ │ │ ├── index.tsx # → /blog
188
+ │ │ └── [slug].tsx # → /blog/:slug
178
189
  │ └── api/
179
- │ └── hello.ts # → /api/hello
180
- ├── public/ # Static assets
181
- ├── tailwind.config.js # Tailwind configuration
182
- ├── tsconfig.json # TypeScript configuration
183
- ├── flexireact.config.ts # FlexiReact configuration
190
+ │ └── hello.ts # → /api/hello
191
+ ├── lib/ # Utilities
192
+ │ └── utils.ts
193
+ ├── public/ # Static assets
194
+ ├── tsconfig.json # TypeScript configuration
195
+ ├── flexireact.config.ts # FlexiReact configuration
184
196
  └── package.json
185
197
  ```
186
198
 
187
- ## 🛣️ Routing
199
+ ## 🛣️ Routing (v2)
200
+
201
+ FlexiReact v2 supports three routing conventions (in priority order):
202
+
203
+ ### 1. Routes Directory (Recommended)
204
+
205
+ | File | Route |
206
+ |------|-------|
207
+ | `routes/(public)/home.tsx` | `/` |
208
+ | `routes/(public)/about.tsx` | `/about` |
209
+ | `routes/blog/index.tsx` | `/blog` |
210
+ | `routes/blog/[slug].tsx` | `/blog/:slug` |
211
+ | `routes/[...path].tsx` | Catch-all route |
212
+ | `routes/api/hello.ts` | `/api/hello` |
213
+
214
+ ### 2. App Directory (Next.js style)
215
+
216
+ | File | Route |
217
+ |------|-------|
218
+ | `app/page.tsx` | `/` |
219
+ | `app/about/page.tsx` | `/about` |
220
+ | `app/blog/[slug]/page.tsx` | `/blog/:slug` |
188
221
 
189
- ### Page Routes
222
+ ### 3. Pages Directory (Legacy)
190
223
 
191
224
  | File | Route |
192
225
  |------|-------|
193
- | `pages/index.jsx` | `/` |
194
- | `pages/about.jsx` | `/about` |
195
- | `pages/blog/[slug].jsx` | `/blog/:slug` |
196
- | `pages/[...path].jsx` | Catch-all route |
226
+ | `pages/index.tsx` | `/` |
227
+ | `pages/about.tsx` | `/about` |
197
228
 
198
229
  ### Dynamic Routes
199
230
 
200
- ```jsx
201
- // pages/users/[id].jsx
202
- export default function User({ params }) {
203
- return <h1>User: {params.id}</h1>;
231
+ ```tsx
232
+ // routes/blog/[slug].tsx
233
+ export default function BlogPost({ params }: { params: { slug: string } }) {
234
+ return <h1>Post: {params.slug}</h1>;
204
235
  }
205
236
  ```
206
237
 
@@ -209,42 +240,62 @@ export default function User({ params }) {
209
240
  Use parentheses to group routes without affecting the URL:
210
241
 
211
242
  ```
212
- pages/
213
- (marketing)/
214
- about.jsx # → /about
215
- contact.jsx # → /contact
216
- (app)/
217
- dashboard.jsx # → /dashboard
243
+ routes/
244
+ (public)/
245
+ home.tsx # → /
246
+ about.tsx # → /about
247
+ (dashboard)/
248
+ settings.tsx # → /settings
218
249
  ```
219
250
 
220
251
  ## 📐 Layouts
221
252
 
222
- Create persistent layouts in `layouts/`:
253
+ Create layouts in `app/layout.tsx` or within route directories:
223
254
 
224
- ```jsx
225
- // layouts/root.jsx
226
- export default function RootLayout({ children }) {
255
+ ```tsx
256
+ // app/layout.tsx
257
+ import { Navbar } from './components/layout/Navbar';
258
+ import { Footer } from './components/layout/Footer';
259
+
260
+ export default function RootLayout({ children }: { children: React.ReactNode }) {
227
261
  return (
228
- <div>
229
- <header>My App</header>
230
- <main>{children}</main>
231
- <footer>© 2024</footer>
232
- </div>
262
+ <html lang="en" className="dark">
263
+ <head>
264
+ <link rel="stylesheet" href="/styles.css" />
265
+ </head>
266
+ <body className="bg-background text-foreground">
267
+ <Navbar />
268
+ <main>{children}</main>
269
+ <Footer />
270
+ </body>
271
+ </html>
233
272
  );
234
273
  }
235
274
  ```
236
275
 
237
276
  ## ⏳ Loading & Error States
238
277
 
239
- ```jsx
240
- // pages/loading.jsx
278
+ ```tsx
279
+ // routes/loading.tsx
241
280
  export default function Loading() {
242
- return <div>Loading...</div>;
281
+ return (
282
+ <div className="flex items-center justify-center min-h-screen">
283
+ <div className="w-8 h-8 border-2 border-primary border-t-transparent rounded-full animate-spin" />
284
+ </div>
285
+ );
243
286
  }
244
287
 
245
- // pages/error.jsx
246
- export default function Error({ error }) {
247
- return <div>Error: {error.message}</div>;
288
+ // routes/error.tsx
289
+ export default function Error({ error, reset }: { error: Error; reset: () => void }) {
290
+ return (
291
+ <div className="flex flex-col items-center justify-center min-h-screen">
292
+ <h1 className="text-4xl font-bold text-red-500">Something went wrong</h1>
293
+ <p className="text-gray-400 mt-4">{error.message}</p>
294
+ <button onClick={reset} className="mt-8 px-6 py-3 bg-primary text-black rounded-lg">
295
+ Try again
296
+ </button>
297
+ </div>
298
+ );
248
299
  }
249
300
  ```
250
301
 
@@ -480,63 +531,6 @@ Islands provide partial hydration:
480
531
  - **Better performance** — less code to parse/execute
481
532
  - **Selective loading** — hydrate on visibility, interaction, etc.
482
533
 
483
- ## 🎨 FlexiUI - Official UI Library
484
-
485
- FlexiReact comes with an official UI component library: **@flexireact/flexi-ui**
486
-
487
- ```bash
488
- npm install @flexireact/flexi-ui
489
- ```
490
-
491
- ### Features
492
- - 🌙 **Dark-first design** with neon emerald accents
493
- - ♿ **Fully accessible** (ARIA-compliant, Radix UI primitives)
494
- - 🎯 **TypeScript native** with full type safety
495
- - 🌳 **Tree-shakeable** — import only what you need
496
- - ⚡ **SSR ready** — works with FlexiReact SSR
497
-
498
- ### Quick Setup
499
-
500
- ```js
501
- // tailwind.config.js
502
- const { flexiUIPlugin } = require('@flexireact/flexi-ui/tailwind');
503
-
504
- module.exports = {
505
- darkMode: 'class',
506
- content: [
507
- './pages/**/*.{js,ts,jsx,tsx}',
508
- './node_modules/@flexireact/flexi-ui/dist/**/*.js',
509
- ],
510
- plugins: [flexiUIPlugin],
511
- };
512
- ```
513
-
514
- ### Usage
515
-
516
- ```jsx
517
- import { Button, Card, Badge, Input } from '@flexireact/flexi-ui';
518
-
519
- export default function MyPage() {
520
- return (
521
- <Card>
522
- <Badge variant="success">New</Badge>
523
- <h2>Welcome!</h2>
524
- <Input placeholder="Enter your email" />
525
- <Button>Get Started</Button>
526
- </Card>
527
- );
528
- }
529
- ```
530
-
531
- ### Available Components
532
- - **Core**: Button, Input, Textarea, Checkbox, Switch, Select
533
- - **Display**: Card, Badge, Avatar, Tooltip
534
- - **Feedback**: Alert, Toast, Spinner, Skeleton, Progress
535
- - **Overlay**: Modal, Drawer, Dropdown
536
- - **Layout**: Separator, Tabs
537
-
538
- 📖 [FlexiUI Documentation](https://github.com/flexireact/flexi-ui)
539
-
540
534
  ---
541
535
 
542
536
  ## 📋 Requirements
@@ -544,7 +538,13 @@ export default function MyPage() {
544
538
  - Node.js 18+
545
539
  - React 18+
546
540
 
541
+ ## 🔗 Links
542
+
543
+ - [GitHub Repository](https://github.com/flexireact/flexireact)
544
+ - [npm Package](https://www.npmjs.com/package/@flexireact/core)
545
+ - [Issues](https://github.com/flexireact/flexireact/issues)
546
+
547
547
  ## 📄 License
548
548
 
549
- MIT
549
+ MIT © [FlexiReact Team](https://github.com/flexireact)
550
550
 
@@ -0,0 +1,23 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * FlexiReact CLI Entry Point
5
+ * Uses tsx to run TypeScript CLI directly
6
+ */
7
+
8
+ import { spawnSync } from 'child_process';
9
+ import { fileURLToPath } from 'url';
10
+ import { dirname, join } from 'path';
11
+
12
+ const __filename = fileURLToPath(import.meta.url);
13
+ const __dirname = dirname(__filename);
14
+
15
+ const cliPath = join(__dirname, '..', 'cli', 'index.ts');
16
+
17
+ // Run the CLI with tsx
18
+ const result = spawnSync('npx', ['tsx', cliPath, ...process.argv.slice(2)], {
19
+ stdio: 'inherit',
20
+ shell: true,
21
+ });
22
+
23
+ process.exit(result.status ?? 0);
package/cli/index.ts CHANGED
@@ -112,7 +112,7 @@ async function createProject(projectName?: string): Promise<void> {
112
112
  name: 'projectName',
113
113
  message: 'Project name:',
114
114
  initial: 'my-flexi-app',
115
- validate: (value) => value.length > 0 || 'Project name is required'
115
+ validate: (value: string) => value.length > 0 || 'Project name is required'
116
116
  });
117
117
  name = response.projectName;
118
118
  if (!name) process.exit(1);
@@ -830,21 +830,15 @@ ${pc.cyan(' │')} ${pc.cyan('│')}
830
830
  ${pc.cyan(' ╰─────────────────────────────────────────╯')}
831
831
  `);
832
832
 
833
- const loaderPath = path.join(__dirname, '..', 'core', 'loader.js');
834
- const serverPath = path.join(__dirname, '..', 'core', 'server', 'index.js');
835
- const loaderUrl = pathToFileURL(loaderPath).href;
833
+ const startDevPath = path.join(__dirname, '..', 'core', 'start-dev.ts');
836
834
 
837
835
  const child = spawn(
838
- process.execPath,
839
- [
840
- '--import',
841
- `data:text/javascript,import { register } from 'node:module'; register('${loaderUrl}', import.meta.url);`,
842
- '-e',
843
- `import('${pathToFileURL(serverPath).href}').then(m => m.createServer({ mode: 'development' }))`
844
- ],
836
+ 'npx',
837
+ ['tsx', startDevPath],
845
838
  {
846
839
  stdio: 'inherit',
847
840
  cwd: process.cwd(),
841
+ shell: true,
848
842
  env: { ...process.env, NODE_ENV: 'development', FORCE_COLOR: '1' }
849
843
  }
850
844
  );
@@ -909,21 +903,15 @@ async function runStart(): Promise<void> {
909
903
  log.info('Starting production server...');
910
904
  log.blank();
911
905
 
912
- const loaderPath = path.join(__dirname, '..', 'core', 'loader.js');
913
- const serverPath = path.join(__dirname, '..', 'core', 'server', 'index.js');
914
- const loaderUrl = pathToFileURL(loaderPath).href;
906
+ const startProdPath = path.join(__dirname, '..', 'core', 'start-prod.ts');
915
907
 
916
908
  const child = spawn(
917
- process.execPath,
918
- [
919
- '--import',
920
- `data:text/javascript,import { register } from 'node:module'; register('${loaderUrl}', import.meta.url);`,
921
- '-e',
922
- `import('${pathToFileURL(serverPath).href}').then(m => m.createServer({ mode: 'production' }))`
923
- ],
909
+ 'npx',
910
+ ['tsx', startProdPath],
924
911
  {
925
912
  stdio: 'inherit',
926
913
  cwd: process.cwd(),
914
+ shell: true,
927
915
  env: { ...process.env, NODE_ENV: 'production' }
928
916
  }
929
917
  );
@@ -65,7 +65,13 @@ const box = {
65
65
  verticalLeft: '┤',
66
66
  };
67
67
 
68
- function createBox(content, options = {}) {
68
+ interface BoxOptions {
69
+ padding?: number;
70
+ borderColor?: (s: string) => string;
71
+ width?: number;
72
+ }
73
+
74
+ function createBox(content: string, options: BoxOptions = {}) {
69
75
  const {
70
76
  padding = 1,
71
77
  borderColor = colors.primary,
@@ -180,7 +186,7 @@ function pluginLoader(plugins = []) {
180
186
  // HTTP Request Logger
181
187
  // ============================================================================
182
188
 
183
- function request(method, path, statusCode, duration, options = {}) {
189
+ function request(method: string, path: string, statusCode: number, duration: number, options: { type?: string } = {}) {
184
190
  const { type = 'dynamic' } = options;
185
191
 
186
192
  // Method colors
@@ -6,6 +6,16 @@
6
6
  import React from 'react';
7
7
  import { hydrateRoot, createRoot } from 'react-dom/client';
8
8
 
9
+ // Extend Window interface for __FLEXI_DATA__
10
+ declare global {
11
+ interface Window {
12
+ __FLEXI_DATA__?: {
13
+ islands?: any[];
14
+ props?: Record<string, any>;
15
+ };
16
+ }
17
+ }
18
+
9
19
  /**
10
20
  * Hydrates a specific island component
11
21
  */
@@ -40,7 +40,12 @@ export function IslandBoundary({ children, fallback = null, name = 'island' }) {
40
40
  /**
41
41
  * Creates a lazy-loaded island
42
42
  */
43
- export function createClientIsland(loader, options = {}) {
43
+ interface LazyIslandOptions {
44
+ fallback?: React.ReactNode;
45
+ name?: string;
46
+ }
47
+
48
+ export function createClientIsland(loader: () => Promise<any>, options: LazyIslandOptions = {}) {
44
49
  const { fallback = null, name = 'lazy-island' } = options;
45
50
 
46
51
  return function LazyIsland(props) {
@@ -6,7 +6,10 @@
6
6
  import React from 'react';
7
7
 
8
8
  // Navigation state
9
- const navigationState = {
9
+ const navigationState: {
10
+ listeners: Set<(url: string) => void>;
11
+ prefetched: Set<string>;
12
+ } = {
10
13
  listeners: new Set(),
11
14
  prefetched: new Set()
12
15
  };
@@ -14,7 +17,12 @@ const navigationState = {
14
17
  /**
15
18
  * Navigates to a new URL
16
19
  */
17
- export function navigate(url, options = {}) {
20
+ interface NavigateOptions {
21
+ replace?: boolean;
22
+ scroll?: boolean;
23
+ }
24
+
25
+ export function navigate(url: string, options: NavigateOptions = {}) {
18
26
  const { replace = false, scroll = true } = options;
19
27
 
20
28
  if (replace) {
@@ -6,6 +6,22 @@
6
6
  import { hydrateAllIslands, setupProgressiveHydration } from './hydration.js';
7
7
  import { navigate, prefetch } from './navigation.js';
8
8
 
9
+ // Extend Window interface
10
+ declare global {
11
+ interface Window {
12
+ FlexiReact: {
13
+ navigate: typeof navigate;
14
+ prefetch: typeof prefetch;
15
+ hydrateAllIslands: typeof hydrateAllIslands;
16
+ setupProgressiveHydration: typeof setupProgressiveHydration;
17
+ };
18
+ __FLEXI_DATA__?: {
19
+ islands?: any[];
20
+ props?: Record<string, any>;
21
+ };
22
+ }
23
+ }
24
+
9
25
  // Expose to global scope
10
26
  window.FlexiReact = {
11
27
  navigate,
@@ -15,7 +15,8 @@ export { buildRouteTree, matchRoute, findRouteLayouts, RouteType } from './route
15
15
  export { renderPage, renderError, renderLoading } from './render/index.js';
16
16
 
17
17
  // Server
18
- export { createServer } from './server/index.js';
18
+ import { createServer } from './server/index.js';
19
+ export { createServer };
19
20
 
20
21
  // Build
21
22
  export { build, buildDev, BuildMode } from './build/index.js';
@@ -63,8 +63,13 @@ export function getRegisteredIslands() {
63
63
  /**
64
64
  * Creates an island component wrapper
65
65
  */
66
- export function createIsland(Component, options = {}) {
67
- const { name = Component.name || 'Island', clientPath } = options;
66
+ interface IslandOptions {
67
+ name?: string;
68
+ clientPath?: string;
69
+ }
70
+
71
+ export function createIsland(Component: React.ComponentType<any>, options: IslandOptions = {}) {
72
+ const { name = (Component as any).name || 'Island', clientPath } = options;
68
73
 
69
74
  function IslandWrapper(props) {
70
75
  return Island({
@@ -148,9 +153,16 @@ export const LoadStrategy = {
148
153
  /**
149
154
  * Creates a lazy island that hydrates based on strategy
150
155
  */
151
- export function createLazyIsland(Component, options = {}) {
156
+ interface LazyIslandOptions {
157
+ name?: string;
158
+ clientPath?: string;
159
+ strategy?: string;
160
+ media?: string | null;
161
+ }
162
+
163
+ export function createLazyIsland(Component: React.ComponentType<any>, options: LazyIslandOptions = {}) {
152
164
  const {
153
- name = Component.name || 'LazyIsland',
165
+ name = (Component as any).name || 'LazyIsland',
154
166
  clientPath,
155
167
  strategy = LoadStrategy.VISIBLE,
156
168
  media = null
@@ -122,7 +122,7 @@ export const logger = {
122
122
  },
123
123
 
124
124
  // HTTP request log - Compact single line like Next.js
125
- request(method, path, status, time, extra = {}) {
125
+ request(method: string, path: string, status: number, time: number, extra: { type?: string } = {}) {
126
126
  const methodColor = getMethodColor(method);
127
127
  const statusColor = getStatusColor(status);
128
128
  const timeStr = formatTime(time);
@@ -29,8 +29,22 @@ import { pathToFileURL } from 'url';
29
29
  /**
30
30
  * Middleware response helpers
31
31
  */
32
+ interface MiddlewareResponseOptions {
33
+ type?: string;
34
+ status?: number;
35
+ headers?: Record<string, string>;
36
+ body?: any;
37
+ url?: string | null;
38
+ }
39
+
32
40
  export class MiddlewareResponse {
33
- constructor(options = {}) {
41
+ type: string;
42
+ status: number;
43
+ headers: Map<string, string>;
44
+ body: any;
45
+ url: string | null;
46
+
47
+ constructor(options: MiddlewareResponseOptions = {}) {
34
48
  this.type = options.type || 'next';
35
49
  this.status = options.status || 200;
36
50
  this.headers = new Map(Object.entries(options.headers || {}));
@@ -69,7 +83,7 @@ export class MiddlewareResponse {
69
83
  /**
70
84
  * Return a JSON response
71
85
  */
72
- static json(data, options = {}) {
86
+ static json(data: any, options: { status?: number; headers?: Record<string, string> } = {}) {
73
87
  return new MiddlewareResponse({
74
88
  type: 'response',
75
89
  status: options.status || 200,
@@ -81,7 +95,7 @@ export class MiddlewareResponse {
81
95
  /**
82
96
  * Return an HTML response
83
97
  */
84
- static html(content, options = {}) {
98
+ static html(content: string, options: { status?: number; headers?: Record<string, string> } = {}) {
85
99
  return new MiddlewareResponse({
86
100
  type: 'response',
87
101
  status: options.status || 200,
@@ -95,7 +109,16 @@ export class MiddlewareResponse {
95
109
  * Middleware request wrapper
96
110
  */
97
111
  export class MiddlewareRequest {
98
- constructor(req) {
112
+ raw: any;
113
+ method: string;
114
+ url: string;
115
+ headers: Map<string, string>;
116
+ pathname: string;
117
+ searchParams: URLSearchParams;
118
+ query: Record<string, string>;
119
+ cookies: Map<string, string>;
120
+
121
+ constructor(req: any) {
99
122
  this.raw = req;
100
123
  this.method = req.method;
101
124
  this.url = req.url;
@@ -105,7 +128,7 @@ export class MiddlewareRequest {
105
128
  const parsedUrl = new URL(req.url, `http://${req.headers.host || 'localhost'}`);
106
129
  this.pathname = parsedUrl.pathname;
107
130
  this.searchParams = parsedUrl.searchParams;
108
- this.query = Object.fromEntries(parsedUrl.searchParams);
131
+ this.query = Object.fromEntries(parsedUrl.searchParams) as Record<string, string>;
109
132
 
110
133
  // Parse cookies
111
134
  this.cookies = this._parseCookies(req.headers.cookie || '');
@@ -274,7 +297,7 @@ export const middlewares = {
274
297
  /**
275
298
  * CORS middleware
276
299
  */
277
- cors(options = {}) {
300
+ cors(options: { origin?: string; methods?: string; headers?: string; credentials?: boolean } = {}) {
278
301
  const {
279
302
  origin = '*',
280
303
  methods = 'GET,HEAD,PUT,PATCH,POST,DELETE',
@@ -294,7 +317,7 @@ export const middlewares = {
294
317
 
295
318
  // Handle preflight
296
319
  if (request.method === 'OPTIONS') {
297
- return MiddlewareResponse.json({}, { status: 204, headers: response.headers });
320
+ return MiddlewareResponse.json({}, { status: 204, headers: Object.fromEntries(response.headers) });
298
321
  }
299
322
 
300
323
  return response;
@@ -330,7 +353,7 @@ export const middlewares = {
330
353
  /**
331
354
  * Rate limiting middleware
332
355
  */
333
- rateLimit(options = {}) {
356
+ rateLimit(options: { windowMs?: number; max?: number } = {}) {
334
357
  const { windowMs = 60000, max = 100 } = options;
335
358
  const requests = new Map();
336
359
 
@@ -369,7 +392,7 @@ export const middlewares = {
369
392
  /**
370
393
  * Logging middleware
371
394
  */
372
- logger(options = {}) {
395
+ logger(options: { format?: string } = {}) {
373
396
  const { format = 'combined' } = options;
374
397
 
375
398
  return (request) => {