npm package: @gesher/sdk
Description: Browser SDK for Gesher push notification service
Latest version: 0.1.1

# @gesher/sdk

[Gesher](https://gesher.pro) is Otomator’s hosted web push service: your backend sends notifications; this SDK helps browsers subscribe and receive them—without exposing API keys in the client.

## Install

```bash
npm install @gesher/sdk
```

## Quick start

1. Copy `node_modules/@gesher/sdk/gesher-sw.js` (or import path `@gesher/sdk/sw` for bundler resolution of the file) to your site’s **public** root, e.g. `public/gesher-sw.js`, so it is served at `/gesher-sw.js`.

2. Point the SDK at **your** backend URLs; your server proxies to `api.gesher.pro` with the Bearer API key.

```typescript
import { GesherClient } from '@gesher/sdk'

const gesher = new GesherClient({
  subscribeEndpoint: '/api/push/subscribe',
  unsubscribeEndpoint: '/api/push/unsubscribe',
  // Optional: omit to fetch from https://api.gesher.pro/vapid-key
  // vapidPublicKey: '...',
})

const result = await gesher.subscribe('user-123')
if (!result.ok) console.error(result.error)

if (await gesher.isSubscribed()) {
  // ...
}

await gesher.unsubscribe() // or unsubscribe('user-123')
```

## PWA install (optional)

If your app is a Progressive Web App with a web app manifest, you can use the SDK helpers to capture `beforeinstallprompt` and show an install button.

```typescript
const gesher = new GesherClient({
  subscribeEndpoint: '/api/push/subscribe',
  unsubscribeEndpoint: '/api/push/unsubscribe',
})

// Call once at app startup
gesher.watchInstall()

// Show an "Install App" button when ready
gesher.onInstallStateChange((state) => {
  if (state === 'available') showInstallButton()
  if (state === 'installed') hideInstallButton()
})

// On button click
document.getElementById('install-btn')!.onclick = async () => {
  const result = await gesher.install()
  if (result.accepted) console.log('App installed!')
}
```

## Backend proxy (Express)

Your route receives the JSON body from the SDK and forwards it to Gesher with the secret key:

```javascript
app.post('/api/push/subscribe', async (req, res) => {
  const r = await fetch('https://api.gesher.pro/subscribe', {
    method: 'POST',
    headers: {
      Authorization: `Bearer ${process.env.GESHER_API_KEY}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify(req.body),
  })
  res.status(r.status).send(await r.text())
})
```

Unsubscribe uses the same JSON body (`userId`, `endpoint`) with `DELETE`:

```javascript
app.post('/api/push/unsubscribe', async (req, res) => {
  const r = await fetch('https://api.gesher.pro/unsubscribe', {
    method: 'DELETE',
    headers: {
      Authorization: `Bearer ${process.env.GESHER_API_KEY}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify(req.body),
  })
  res.status(r.status).send(await r.text())
})
```

## Configuration

| Option | Description |
|--------|-------------|
| `subscribeEndpoint` | Your backend URL; SDK POSTs `{ userId, subscription }`. |
| `unsubscribeEndpoint` | Your backend URL; SDK POSTs `{ userId, endpoint }`. |
| `vapidPublicKey` | Optional. URL-safe base64 public key; if omitted, fetched from `vapidKeyEndpoint`. |
| `vapidKeyEndpoint` | Optional. Defaults to `https://api.gesher.pro/vapid-key`. |
| `serviceWorkerPath` | Optional. Default `/gesher-sw.js`. |
| `serviceWorkerScope` | Optional. Default `/`. |

## TypeScript

Types for config and payloads are exported from `@gesher/sdk`.

## Docs

More detail: [https://gesher.pro/docs](https://gesher.pro/docs)