# @dabble/local-dev-setup

Shared local development setup for Dabble projects. Installs and configures [Caddy](https://caddyserver.com/) as an HTTPS reverse proxy so all Dabble services are reachable at `https://*.local.dabblewriter.com` on port 443.

## What it does

When you run `npm run dev` in any Dabble repo, this package runs first and ensures the local environment is ready:

1. **Install Caddy** — prompts to install via Homebrew (macOS), apt (Linux), or winget/choco (Windows) if not found.
2. **Configure Caddyfile** — appends or updates the `# dabble-local-dev` block with proxy entries for all Dabble services.
3. **DNS setup** — configures dnsmasq (macOS) or `/etc/hosts` (Linux/Windows) so `*.local.dabblewriter.com` resolves to `127.0.0.1`.
4. **Start Caddy** — starts or reloads the Caddy service, and trusts its local CA certificate on first run.

### Fast path

If everything is already configured, the script exits silently in under 500ms so it doesn't slow down `npm run dev`.

### Caddy reload tracking

A hash of the Caddyfile is stored in `$TMPDIR/.dabble-caddy-config-hash` after each reload. If the Caddyfile is modified externally (e.g. by another project's setup), the next run detects the drift and reloads Caddy automatically.

## Proxy configuration

| Hostname | Upstream | Notes |
|---|---|---|
| `local.dabblewriter.com` | `localhost:5180` | Writer app (canonical Vite), retries for 10s |
| `two.local.dabblewriter.com` | `localhost:8080` | Legacy alt slot, retains for backwards compat |
| `*.local.dabblewriter.com` | (dynamic) | Per-worktree subdomains, see below |
| `accounts.local.dabblewriter.com` | `localhost:5181` | Accounts (SvelteKit) |
| `admin.local.dabblewriter.com` | `localhost:5182` | Admin (Vite) |
| `rest.local.dabblewriter.com` | `localhost:5184` | REST API (Wrangler) |
| `pup.local.dabblewriter.com` | `localhost:5183` | Pup service |

The canonical writer proxy uses `lb_try_duration 10s` to retry connections to Vite and returns non-cacheable 503 errors instead of falling back to production. Backend service proxies fall back to their respective test servers on failure. Unregistered `*.local.dabblewriter.com` subdomains fall through to a 503 with a hint to run `npm run dev` in the matching worktree.

## Per-worktree dev subdomains (1.3.0+)

When the dev script is invoked as `dabble-local-dev-setup vite` (rather than `dabble-local-dev-setup && vite`), the wrapper:

1. Detects whether the current `cwd` is a non-main `git worktree`.
2. Picks a subdomain from the directory name (with the main worktree's basename + `-` stripped) — e.g. `dabble-writer-3.0-cover-image-fix` -> `cover-image-fix.local.dabblewriter.com`.
3. Allocates a port deterministically in `[8100, 8200)` (persisted in `$TMPDIR/dabble-worktree-ports.json` so a worktree always gets the same port).
4. Registers `<host> -> localhost:<port>` in Caddy via its admin API (`localhost:2019`); the route is inserted ahead of the wildcard catch-all.
5. Spawns the given command (e.g. `vite`) with `VITE_DEV_HOST` and `VITE_DEV_PORT` injected.
6. Removes the route on exit (SIGINT / SIGTERM / process exit).

The main worktree (where the script is run from `cwd === git worktree list --porcelain`'s first entry) keeps using the canonical `local.dabblewriter.com` static block on port 5180.

### Override host / port

Resolution order: **real shell env vars** > `<cwd>/.env.local` > `<cwd>/.env` > auto-detect.

```bash
# dabble-writer-3.0-cover-image-fix/.env.local (optional)
DABBLE_DEV_HOST=feature-x.local.dabblewriter.com
DABBLE_DEV_PORT=8120
```

Or one-off: `DABBLE_DEV_HOST=foo.local.dabblewriter.com npm run dev`.

Vite still loads the rest of `.env.local` itself; only `DABBLE_DEV_HOST` and `DABBLE_DEV_PORT` are read here.

### Notes & caveats

- **Duplicate-instance protection (1.3.1+):** if you accidentally run `npm run dev` for the same worktree in two terminals, the second invocation refuses early with a clear error instead of clobbering the first instance's Caddy route on cleanup.
- Routes are stored in Caddy's running config and survive Caddyfile reloads, but a full Caddy restart drops them; just re-run `npm run dev` in the affected worktree to re-register.
- All worktrees still share the same `accounts` / `rest` / `pup` backends — server-state changes are visible across instances.
- Each subdomain is its own browser origin: separate IndexedDB, service worker, and login session.
- Wildcard DNS for `*.local.dabblewriter.com` requires `dnsmasq` (auto-set up on macOS). On Linux/Windows the static `/etc/hosts` entries don't cover ad-hoc subdomains; install dnsmasq or add the specific worktree subdomain manually.

## Usage

Add as a devDependency:

```bash
npm install --save-dev @dabble/local-dev-setup
```

Reference in your `dev` script. Two forms:

```json
{
  "scripts": {
    // Backwards-compatible: just runs setup, then exits. Vite is invoked separately.
    "dev:legacy": "dabble-local-dev-setup && vite --port 5180",

    // Preferred (1.3.0+): wrapper handles per-worktree subdomains + ports automatically.
    "dev": "dabble-local-dev-setup vite"
  }
}
```

The `dabble-local-dev-setup` bin is resolved automatically from `node_modules/.bin` when run inside an npm script. The wrapper form (any positional command after the script name) injects `VITE_DEV_HOST` / `VITE_DEV_PORT` into the child process; consume them in `vite.config.ts`:

```ts
server: {
  port: Number(process.env.VITE_DEV_PORT) || 5180,
  strictPort: true,
  hmr: {
    host: process.env.VITE_DEV_HOST || 'local.dabblewriter.com',
    protocol: 'wss',
    clientPort: 443,
  },
},
```

## Requirements

- Node.js >= 18
- An `.npmrc` with `//registry.npmjs.org/:_authToken=${NPM_TOKEN}` to access the `@dabble` scope
