npm package: @s4e/jsentinel-cli
Description: JSentinel CLI: framework-agnostic manifest generator
Latest version: 0.3.3

## @s4e/jsentinel-cli

Manifest generator for JSentinel. Scans your production build and writes a single `manifest.json` describing scripts your pages load.

- same‑origin JS/CSS with SRI hashes
- inline scripts, classified as stable or volatile (stable are hashed)
- external scripts from build outputs

### Install

```bash
npm i -D @s4e/jsentinel-cli
```

### Configure

Create a single configuration file at your project root. Use `JSENTINEL_ENV=production` environment variable for production mode (defaults to development if not set). Obtain your S4E dashboard API token (`s4eToken`) via sales@s4e.io.

**jsentinel.config.json:**

```json
{
  "$schema": "./node_modules/@s4e/jsentinel-cli/schema/jsentinel.config.schema.json",
  "baseUrl": "/",
  "inputs": ["dist", ".next/static", "public"],
  "output": "public/manifest.json",
  "hashAlgo": "sha256",
  "exclude": ["**/*.map"],
  "s4eToken": "<YOUR_S4E_TOKEN>"
}
```

**Environment Variables:**

- **JSENTINEL_ENV**: Set to `production` for production mode. Defaults to development mode if not set.

#### Development vs Production Modes

**Development Mode (Default):**

- ✅ **Host validation**: License host check is **skipped** (allows localhost, any domain)
- ✅ **S4E delivery**: **Skipped** - manifest file is written locally only
- ✅ **Use case**: Local development, testing, CI/CD without S4E integration

**Production Mode (`JSENTINEL_ENV=production`):**

- ✅ **Host validation**: License host check is **enforced** (must match license host)
- ✅ **S4E delivery**: **Enabled** - manifest is sent to S4E dashboard
- ✅ **Use case**: Production deployments with full security enforcement

#### Configuration properties

All keys go inside your `jsentinel.config.json` root object:

- **$schema** (string): JSON Schema reference for editor IntelliSense. Do not modify.

- **baseUrl** (string, default: "/"): Base URL prefix used for same‑origin keys. If missing trailing `/`, it will be added. For `.next/static` inputs, the web path is normalized to `/_next/static/`.

- **inputs** (string[], default: `["dist","build","public",".next/static","out"]`): Directories to scan for built assets (JS/CSS/HTML). CLI positionals override this list.

- **output** (string, default: `"public/manifest.json"`): Destination path for the generated manifest JSON file.

- **hashAlgo** ("sha256" | "sha384" | "sha512", default: "sha256"): Hash algorithm for integrity (SRI) values in the manifest.

- **exclude** (string[], default: `[]`): Glob patterns to exclude during scanning (applied relative to each input dir).

- **post** (string, default: ""): When set, the CLI will POST the manifest JSON to this URL after generation (separate from S4E collector).

- **header** (string[], default: `[]`): Additional request headers for POST, expressed as `key:value` strings (e.g., `x-custom: foo`).

- **token** (string, default: ""): Bearer token used to set the `authorization: Bearer <token>` header when posting to your `--post` URL.

- **signSecret** (string, default: ""): HMAC‑SHA256 secret for signing the POST body. Adds header `x-signature: sha256=<base64>`.

- **idempotencyKey** (string, default: ""): Value for the `idempotency-key` header to ensure idempotent delivery to your `--post` endpoint.

- **timeout** (number ≥ 0, default: `10000`): HTTP timeout in milliseconds for headers and body when posting the manifest.

- **retries** (number ≥ 0, default: `2`): Retry count for 5xx or network errors when posting the manifest (exponential backoff).

- **insecure** (boolean, default: `false`): If `true`, disables TLS verification for HTTPS requests (not recommended).

- **verbose** (boolean, default: `false`): If `true`, prints additional logs (headers, sizes, attempts).

- **dumpResponse** (boolean, default: `false`): If `true`, prints the first 2KB of the successful POST response body.

- **dryRun** (boolean, default: `false`): If `true`, performs all steps except the final POST (no network call).

- **s4eToken** (string, REQUIRED): JWT used for sending payloads to the S4E dashboard collector. This is separate from `token`, which is only used for your own `--post` uploads.

### Generate

**Production:**

```bash
JSENTINEL_ENV=production jsentinel manifest -c jsentinel.config.json
```

**Development (default):**

```bash
jsentinel manifest -c jsentinel.config.json
```

**Package.json scripts:**

```json
{
  "scripts": {
    "jsentinel": "jsentinel manifest -c jsentinel.config.json",
    "jsentinel:prod": "JSENTINEL_ENV=production jsentinel manifest -c jsentinel.config.json",
    "build": "your-build-command && npm run jsentinel",
    "build:prod": "your-build-command && npm run jsentinel:prod"
  }
}
```

**Common examples:**

- Vite: `vite build && npm run jsentinel` (dev) / `vite build && npm run jsentinel:prod` (prod)
- Next.js: `next build && npm run jsentinel` (dev) / `next build && npm run jsentinel:prod` (prod)
- Vue CLI: `vue-cli-service build && npm run jsentinel` (dev) / `vue-cli-service build && npm run jsentinel:prod` (prod)

### Output

The CLI writes `manifest.json` (example):

```json
{
  "version": "2024-07-01T12:34:56.000Z",
  "hashAlgo": "sha256",
  "scripts": [
    {
      "src": "/assets/app-abc123.js",
      "type": "same-origin",
      "hash": "sha256-..."
    },
    {
      "inline": "console.log('hi')",
      "type": "inline-stable",
      "hash": "sha256-..."
    },
    { "inline": "window.__NEXT_DATA__=...", "type": "inline-volatile" },
    {
      "src": "https://cdn.example.com/pkg@1.4.2/index.min.js",
      "type": "versioned-cdn"
    },
    {
      "src": "https://example.com/loader.js?v=1",
      "type": "dynamic-loader",
      "host": "example.com",
      "path": "/loader.js",
      "query": "v=1"
    }
  ]
}
```

### Support

License/dashboard tokens: sales@s4e.io