npm package: @holix/router
Description: A lightweight and fast router for Node.js with middleware support
Author: zhaogongchengsi
Latest version: 0.0.3

# @holix/router

Custom protocol router for Holix applications. Handles HTTP-like routing for Electron custom protocols (e.g., `app://`).

## Installation

```bash
pnpm add @holix/router
```

## Features

- 🎯 **Custom Protocol Support**: Handle `app://`, `holix://` or any custom protocol
- 🚀 **Radix3 Based**: Fast route matching using radix tree
- 🔧 **Middleware Support**: Onion-style middleware chain
- 📦 **Type-safe**: Full TypeScript support
- 🌐 **HTTP-like API**: Familiar request/response pattern
- 🛠️ **HTML Builder**: Built-in HTML generation utilities

## Usage

### Basic Setup

```typescript
import { HolixProtocolRouter } from '@holix/router'

// Create a router for custom protocol
const router = new HolixProtocolRouter()

// Register the protocol handler in Electron main process
protocol.handle('app', async (request) => {
  return await router.fetch(request)
})
```

### Defining Routes

```typescript
// Simple route
router.get('/app/home', async (ctx) => {
  ctx.html('<h1>Home Page</h1>')
})

// Route with parameters
router.get('/app/users/:id', async (ctx) => {
  const { id } = ctx.params
  ctx.json({ userId: id })
})

// Multiple HTTP methods
router.post('/app/api/data', async (ctx) => {
  ctx.json({ message: 'Data received' })
})

// All methods
router.all('/app/health', async (ctx) => {
  ctx.text('OK')
})
```

### Context API

The route handler receives a context object with convenient methods:

```typescript
router.get('/app/example', async (ctx) => {
  // Request information
  ctx.url        // URL object
  ctx.params     // Route parameters
  ctx.method     // HTTP method
  ctx.scheme     // Protocol scheme (e.g., 'app')

  // Response methods
  ctx.status(200)                    // Set status code
  ctx.header('X-Custom', 'value')    // Set header
  ctx.headers({ ... })               // Set multiple headers

  // Send response
  ctx.text('Plain text')             // Plain text response
  ctx.json({ data: 'value' })        // JSON response
  ctx.html('<h1>HTML</h1>')          // HTML response
  ctx.buffer(Buffer.from('data'))    // Binary response
  ctx.stream(readableStream)         // Stream response
  ctx.redirect('/app/other')         // Redirect
})
```

### HTML Builder

Built-in HTML generation utilities:

```typescript
import { html } from '@holix/router'

router.get('/app/page', async (ctx) => {
  const page = ctx.createHtml()
    .tag('html', { lang: 'en' })
    .tag('head')
    .tag('title')
    .text('My Page')
    .close()
    .tag('meta', { charset: 'utf-8' })
    .close() // close head
    .tag('body')
    .tag('h1')
    .text('Welcome')
    .close()
    .tag('p')
    .text('This is a page')
    .close()
    .close() // close body
    .close() // close html

  ctx.html(page.toString())
})

// Or use the helper function
router.get('/app/simple', async (ctx) => {
  const content = html('div', { class: 'container' })
    .tag('h1')
    .text('Title')
    .close()
    .tag('p')
    .text('Content')
    .close()

  ctx.html(content.toString())
})
```

### Middleware

Use middleware for cross-cutting concerns:

```typescript
// Logger middleware
router.use('/app/*', async (ctx, next) => {
  console.log(`${ctx.method} ${ctx.url.pathname}`)
  await next()
})

// Auth middleware
router.use('/app/admin/*', async (ctx, next) => {
  if (!isAuthenticated(ctx)) {
    ctx.status(401).text('Unauthorized')
    return
  }
  await next()
})

// Error handling
router.use('/app/*', async (ctx, next) => {
  try {
    await next()
  }
  catch (error) {
    console.error(error)
    ctx.status(500).text('Internal Server Error')
  }
})
```

### Complete Example

```typescript
import { HolixProtocolRouter } from '@holix/router'
import { app, protocol } from 'electron'

app.whenReady().then(() => {
  const router = new HolixProtocolRouter()

  // Home page
  router.get('/app/index.html', async (ctx) => {
    const page = ctx.createHtml()
      .tag('html')
      .tag('head')
      .tag('title')
      .text('My App')
      .close()
      .close()
      .tag('body')
      .tag('h1')
      .text('Welcome to My App')
      .close()
      .close()
      .close()

    ctx.html(page.toString())
  })

  // API endpoint
  router.get('/app/api/users/:id', async (ctx) => {
    const user = await getUserById(ctx.params.id)
    ctx.json(user)
  })

  // Static file fallback
  router.get('/app/*', async (ctx) => {
    // Serve static files
    const filePath = ctx.url.pathname.replace('/app', '')
    const file = await readFile(filePath)
    ctx.buffer(file)
  })

  // Register protocol
  protocol.handle('app', async (request) => {
    return await router.fetch(request)
  })
})
```

## API Reference

### `HolixProtocolRouter`

Main router class for handling custom protocol requests.

#### Methods

- `get(path, handler)` - Register GET route
- `post(path, handler)` - Register POST route
- `put(path, handler)` - Register PUT route
- `delete(path, handler)` - Register DELETE route
- `patch(path, handler)` - Register PATCH route
- `all(path, handler)` - Register route for all methods
- `use(path, handler)` - Register middleware
- `fetch(request)` - Handle incoming request

### `RouteContext`

Context object passed to route handlers.

```typescript
interface RouteContext {
  url: URL
  params: Record<string, string>
  req: Request
  method: string
  scheme: string
  domain?: string
}
```

### `ExtendedRouteContext`

Extended context with response methods.

```typescript
interface ExtendedRouteContext extends RouteContext {
  status: (code: number) => this
  header: (name: string, value: string) => this
  headers: (headers: Record<string, string>) => this
  text: (body: string, statusCode?: number) => void
  json: (data: any, statusCode?: number) => void
  html: (body: string, statusCode?: number) => void
  buffer: (body: Buffer, statusCode?: number) => void
  stream: (body: Readable, statusCode?: number) => void
  redirect: (url: string, statusCode?: number) => void
  createHtml: () => HtmlBuilder
}
```

## Note

This package provides the routing infrastructure for custom protocols. File-based routing functionality is not yet implemented. For now, you need to manually define routes using the router API.

## License

MIT