npm package: @obul.ai/sdk
Description: Single-package Obul BFF SDK facade with browser client, server core, and framework adapters.
Latest version: 0.2.13

# @obul.ai/sdk

Single-package Obul BFF SDK facade with browser client, server core, and framework adapters.

## What is a BFF?
BFF stands for **Backend For Frontend**. It's a small server that sits between your browser app and Obul's APIs. The browser only talks to your BFF, and the BFF handles OAuth, token storage, session cookies, and proxying API calls. This keeps secrets and tokens off the client and lets you enforce auth, rate limits, and request policies in one place.

## Install
```bash
npm install @obul.ai/sdk
```

## Browser via CDN
Module (recommended):
```html
<script type="module">
  import { createObulClient } from "https://cdn.obul.ai/sdk/v0.1.1/obul-sdk.min.mjs"

  const obul = createObulClient({ baseUrl: "" })
  obul.signIn()
</script>
```

Script tag (global `ObulSDK`):
```html
<script src="https://cdn.obul.ai/sdk/v0.1.1/obul-sdk.min.js"></script>
<script>
  const obul = ObulSDK.createObulClient({ baseUrl: "" })
  obul.signIn()
</script>
```

## Drop-in browser snippet (no build)

ESM (recommended):
```html
<script type="module">
  import { initObul } from "https://cdn.obul.ai/sdk/obul-sdk.min.mjs"

  const obul = initObul({
    issuerUrl: "https://issuer.example.com",
    authEndpoint: "/oauth/authorize",
    tokenEndpoint: "/oauth/token",
    apiBaseUrl: "https://api.example.com",
    clientId: "client_123",
    scopes: ["openid", "profile", "email", "offline_access"]
  })

  document.getElementById("login")?.addEventListener("click", () => obul.signIn())
  document.getElementById("call-api")?.addEventListener("click", () => obul.api.get("/v1/limits"))
</script>
```

Script tag (global):
```html
<script src="https://cdn.obul.ai/sdk/obul-sdk.min.js"></script>
<script>
  const obul = ObulSDK.initObul({
    issuerUrl: "https://issuer.example.com",
    authEndpoint: "/oauth/authorize",
    tokenEndpoint: "/oauth/token",
    apiBaseUrl: "https://api.example.com",
    clientId: "client_123",
    scopes: ["openid", "profile", "email", "offline_access"]
  })
</script>
```

## Exports
- `@obul.ai/sdk/browser` (browser client)
- `@obul.ai/sdk/server` (server core)
- `@obul.ai/sdk/adapters/express`
- `@obul.ai/sdk/adapters/next`
- `@obul.ai/sdk/adapters/hono`
- `@obul.ai/sdk/adapters/cloudflare`

## Example integrations

### Browser-only OAuth (PKCE, no BFF)
```ts
import { createObulBrowserAuthClient } from "@obul.ai/sdk/browser"

const obul = createObulBrowserAuthClient({
  issuerUrl: "https://issuer.example.com",
  authEndpoint: "/oauth/authorize",
  tokenEndpoint: "/oauth/token",
  apiBaseUrl: "https://api.example.com",
  clientId: "client_123",
  redirectUri: `${window.location.origin}/callback`,
  scopes: ["openid", "profile", "email", "offline_access"]
})

// Start login
obul.signIn()

// On /callback
obul.handleCallback()
```

CDN (no bundler):
```html
<script type="module">
  import { createObulBrowserAuthClient } from "https://cdn.obul.ai/sdk/obul-sdk.min.mjs"
  const obul = createObulBrowserAuthClient({
    issuerUrl: "https://issuer.example.com",
    authEndpoint: "/oauth/authorize",
    tokenEndpoint: "/oauth/token",
    apiBaseUrl: "https://api.example.com",
    clientId: "client_123",
    redirectUri: `${window.location.origin}/callback`,
    scopes: ["openid", "profile", "email", "offline_access"]
  })

  document.getElementById("login")?.addEventListener("click", () => obul.signIn())

  if (window.location.pathname === "/callback") {
    obul.handleCallback()
  }

  // Example API call with JWT (Bearer token)
  document.getElementById("call-api")?.addEventListener("click", async () => {
    const res = await obul.api.get("/v1/limits")
    console.log(await res.json())
  })
</script>
```

### Express (BFF server)
```js
import express from "express"
import { createObulExpressRouter } from "@obul.ai/sdk/adapters/express"

const app = express()
const router = createObulExpressRouter({
  issuerUrl: process.env.OBUL_ISSUER_URL,
  authEndpoint: process.env.OBUL_AUTH_ENDPOINT,
  tokenEndpoint: process.env.OBUL_TOKEN_ENDPOINT,
  revocationEndpoint: process.env.OBUL_REVOCATION_ENDPOINT,
  apiBaseUrl: process.env.OBUL_API_BASE_URL,
  clientId: process.env.OBUL_CLIENT_ID,
  clientSecret: process.env.OBUL_CLIENT_SECRET,
  redirectUri: process.env.OBUL_REDIRECT_URI,
  scopes: (process.env.OBUL_SCOPES ?? "openid profile email offline_access").split(" ")
})

app.use(router)
app.listen(3000)
```

### React / Vite (browser client)
```ts
import { createObulClient } from "@obul.ai/sdk/browser"

const obul = createObulClient({
  baseUrl: "", // BFF origin, if different from the browser origin
  postLoginRedirect: window.location.href,
  postLogoutRedirect: "/",
  sessionCheckIntervalMs: 10_000
})

// Example: trigger login
obul.signIn()
```

### Next.js (App Router, client component)
```tsx
"use client"

import { useEffect, useMemo } from "react"
import { createObulClient } from "@obul.ai/sdk/browser"

export function ObulLoginButton() {
  const obul = useMemo(() => createObulClient({ baseUrl: "" }), [])

  useEffect(() => {
    obul.getSession().catch(() => {})
  }, [obul])

  return <button onClick={() => obul.signIn()}>Sign in</button>
}
```

## Repo
See the repo for the Express example and additional docs.