npm package: @superpilot/pages
Description: Middleware to add Superpilot pages to Node.js servers
Author: Superpilot
Latest version: 0.0.1-alpha.1

# `@superpilot/pages`

Node.js middleware for adding Superpilot pages to your e-commerce website.

## Install

```bash
npm install @superpilot/pages
```

## Quick Start

### With Express.js

```typescript
import { createExpressProxy } from '@superpilot/pages';
import express from 'express';

const superpilot = createExpressProxy({
  origin: 'https://acme-prd.pages.superpilot.net',
});

const app = express();
// /products/* → proxies to → https://acme-prd.pages.superpilot.net/products/*
app.use('/products/*', superpilot.proxy);

app.listen(3000);
```

### With Native Node.js http

```typescript
import { createProxy } from '@superpilot/pages';
import http from 'http';

const superpilot = createProxy({
  origin: 'https://acme-prd.pages.superpilot.net',
});

const server = http.createServer((req, res) => {
  superpilot.proxy({
    req,
    res,
    next: (err) => {
      if (err) {
        res.statusCode = 500;
        res.end(err.message);
      }
    },
  });
});

server.listen(3000);
```

## Usage Patterns

### 1. Proxy All Requests (Dynamic Paths)

The `proxy` function forwards requests with their original path:

```typescript
const superpilot = createExpressProxy({
  origin: 'https://acme-prd.pages.superpilot.net',
});

app.use('/products/*', superpilot.proxy);
```

Request to `/products/sale?color=red` → proxies to `https://acme-prd.pages.superpilot.net/products/sale?color=red`

### 2. Proxy to a Fixed Page

The `proxyTo` function always proxies to the same Superpilot page, regardless of the incoming path:

```typescript
const superpilot = createExpressProxy({
  origin: 'https://acme-prd.pages.superpilot.net',
});

// Both routes serve the same Superpilot page
app.use('/landing', superpilot.proxyTo('/products/promo'));
app.use('/special-offer', superpilot.proxyTo('/products/promo'));
```

Both `/landing` and `/special-offer` → proxy to `https://acme-prd.pages.superpilot.net/products/promo`

### 3. Multiple Superpilot Origins

```typescript
const staging = createExpressProxy({
  origin: 'https://acme-staging.pages.superpilot.net',
});

const production = createExpressProxy({
  origin: 'https://acme-prd.pages.superpilot.net',
});

app.use('/preview/*', staging.proxy);
app.use('/products/*', production.proxy);
```

### 4. Content Security Policy (CSP)

Superpilot pages require specific CSP directives:

```typescript
const superpilot = createExpressProxy({
  origin: 'https://acme-prd.pages.superpilot.net',
});

const baseCSP = {
  'default-src': ["'self'"],
  'style-src': ["'self'", "'unsafe-inline'"],
};

const mergedCSP = superpilot.mergeCSP(baseCSP);

app.use((req, res, next) => {
  const cspString = Object.entries(mergedCSP)
    .map(([key, values]) => `${key} ${values.join(' ')}`)
    .join('; ');
  res.setHeader('Content-Security-Policy', cspString);
  next();
});
```

## Configuration

### Environment Variables

- `SUPERPILOT_ORIGIN` - Superpilot pages URL (overrides `origin` option)

**Priority:** `SUPERPILOT_ORIGIN` env var > `origin` option

## Advanced Usage

### Custom Fetch

```typescript
import { createExpressProxy } from '@superpilot/pages';

async function fetchWithRetry(url: string, options: RequestInit): Promise<Response> {
  const maxRetries = 3;
  for (let i = 0; i < maxRetries; i++) {
    try {
      const response = await fetch(url, options);
      if (response.ok || i === maxRetries - 1) return response;
      if (response.status >= 500) {
        continue;
      }
      return response;
    } catch (err) {
      if (i === maxRetries - 1) throw err;
    }
  }
  throw new Error('Max retries reached');
}

const superpilot = createExpressProxy({
  origin: 'https://acme.pages.superpilot.net',
  fetch: fetchWithRetry,
});
```

### Custom Framework Adapters

For frameworks other than Express or Node.js http, provide custom adapters:

```typescript
import { createProxy } from '@superpilot/pages';

// Example: Fastify adapters
const fastifyAdapters = {
  request: {
    getUrl: ({ req }) => req.url,
    getMethod: ({ req }) => req.method,
    getHeader: ({ req, name }) => req.headers[name],
    getBody: ({ req }) => req.raw,
  },
  response: {
    setStatus: ({ reply, status }) => reply.status(status),
    setHeader: ({ reply, name, value }) => reply.header(name, value),
    // ... other methods
  },
};

const superpilot = createProxy({
  origin: 'https://acme-prd.pages.superpilot.net',
  requestAdapter: fastifyAdapters.request,
  responseAdapter: fastifyAdapters.response,
});
```

## Development

```bash
pnpm build
pnpm test
pnpm typecheck
pnpm lint
pnpm test:proxy -- --origin https://example.com
```

## License

MIT