> ## Documentation Index
> Fetch the complete documentation index at: https://connect-docs.supertab.co/llms.txt
> Use this file to discover all available pages before exploring further.

# TypeScript SDK

The Supertab Connect TypeScript SDK allows publishers to implement Really Simple Licensing (RSL) and the
Crawler Authentication Protocol (CAP) directly in their applications or at the CDN edge.

The SDK manages license token verification, bot detection, and licensing event recording with minimal configuration.

## Overview

Supertab Connect helps you manage how bots and automated systems access your content. It uses license tokens (JWTs) to verify that a caller has a valid license to access a specific resource.

### Key Features

* **Edge-Ready**: Optimized for Cloudflare Workers, Fastly Compute, and AWS CloudFront Lambda\@Edge.
* **Flexible Enforcement**: Observe and signal, or strictly block, unlicensed requests.
* **Plugin Bot Detection**: Built-in logic to identify common AI crawlers and headless browsers, customizable using signals from your WAF provider.
* **Analytics**: Opt in with `analyticsEnabled` to attempt per-request events for agent & bot classification.

## Installation

Install the SDK using your preferred package manager:

```bash theme={null}
npm install @getsupertab/supertab-connect-sdk
```

## Quickstart: Fastly Compute

The fastest way to get started is using one of the built-in CDN handlers.
For Fastly Compute, read your API key from the Secret Store and pass your origin backend name.

```typescript theme={null}
/// <reference types="@fastly/js-compute" />
import { SupertabConnect } from "@getsupertab/supertab-connect-sdk";
import { SecretStore } from "fastly:secret-store";

const secrets = new SecretStore("supertab_config");
const merchantApiKey = (await secrets.get("MERCHANT_API_KEY")).plaintext();

addEventListener("fetch", (event) => {
  event.respondWith(
    SupertabConnect.fastlyHandleRequests(
      event,
      merchantApiKey,
      "origin_backend_name",
      {
        enableRSL: true, // Optionally host /license.xml
        merchantSystemUrn: "your_website_urn",
        analyticsEnabled: true, // attempt events for bot classification
      }
    )
  );
});
```

## Initializing the Client

If you aren't using a convenience handler, you can initialize the `SupertabConnect` client manually. The client follows a singleton pattern.

```typescript theme={null}
import {
  SupertabConnect,
  EnforcementMode,
  defaultBotDetector,
} from "@getsupertab/supertab-connect-sdk";

const supertab = new SupertabConnect({
  apiKey: "stc_live_...", // API Keys should be read from environment variables or secrets management
  enforcement: EnforcementMode.OBSERVE, // Defaults to OBSERVE if not provided
  botDetector: defaultBotDetector, // Optional: define your own bot detection logic or use the SDK's basic bot detector
  analyticsEnabled: true, // attempt events for bot classification (off by default)
  debug: false // When enabled debug mode prints more logging information about token handling
});
```

### Configuration Options

| Property           | Type              | Description                                                                                                                |
| :----------------- | :---------------- | :------------------------------------------------------------------------------------------------------------------------- |
| `apiKey`           | `string`          | **Required.** Your Supertab Merchant API Key.                                                                              |
| `enforcement`      | `EnforcementMode` | Controls how unlicensed requests are handled. Defaults to `OBSERVE`. See [`EnforcementMode`](#enforcementmode).            |
| `analyticsEnabled` | `boolean`         | Attempt per-request events for agent & bot classification. Defaults to `false`.                                            |
| `botDetector`      | `BotDetector`     | Optional bot-detection function. Use `defaultBotDetector` for the SDK's built-in heuristics, or provide a custom detector. |
| `debug`            | `boolean`         | Enables verbose logging to the console.                                                                                    |

## Common Workflows

### Edge Integration (CDN Handlers)

The SDK provides static methods that handle the entire request/response lifecycle for specific platforms. These handlers:

1. Extract tokens from the `Authorization: License <token>` header.
2. Verify the token against the Supertab JWKS.
3. Attempt a license-usage event, plus an analytics event when `analyticsEnabled` is set.
4. If no token is present, run bot detection and apply the enforcement mode.

#### Fastly Compute

```typescript theme={null}
import { SupertabConnect } from "@getsupertab/supertab-connect-sdk";

addEventListener("fetch", (event) => {
  event.respondWith(
    SupertabConnect.fastlyHandleRequests(
      event,
      "YOUR_MERCHANT_API_KEY",
      "your-origin-backend-name",
      {
        enableRSL: true, // Automatically hosts /license.xml
        merchantSystemUrn: "urn:stc:merchant:system:..."
      }
    )
  );
});
```

#### Cloudflare Workers

Always pass `ctx` — the SDK uses its `waitUntil` to send events (license-usage, plus analytics when enabled) in the background without blocking the response. The API key is read from the `env` object (`MERCHANT_API_KEY` secret).

```typescript theme={null}
import { SupertabConnect, Env } from "@getsupertab/supertab-connect-sdk";

export default {
  async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> {
    return SupertabConnect.cloudflareHandleRequests(request, env, ctx, {
      analyticsEnabled: true,
    });
  },
};
```

#### AWS CloudFront (Lambda\@Edge)

Attach the function at **viewer request**, where it runs pre-cache on every request. The SDK reads the trigger
from the event: at `viewer-request` it processes all traffic; at `origin-request` it only processes requests a
CloudFront Function stamped with `x-license-auth`. Lambda\@Edge has no `waitUntil`, so analytics and usage
recording are awaited before the response returns. The SDK uses a 2-second background-work budget by default;
`backgroundWorkTimeoutMs` changes that budget but does not cover token verification or JWKS fetching.

```typescript theme={null}
import {
  SupertabConnect,
  EnforcementMode,
  defaultBotDetector,
} from "@getsupertab/supertab-connect-sdk";
import type { CloudFrontRequestEvent } from "aws-lambda";

export async function handler(event: CloudFrontRequestEvent) {
  const { config, request } = event.Records[0].cf;
  if (config.eventType === "viewer-request") {
    delete request.headers["x-original-request-url"];
  }

  return SupertabConnect.cloudfrontHandleRequests(event, {
    apiKey: "YOUR_MERCHANT_API_KEY",
    enforcement: EnforcementMode.OBSERVE,
    botDetector: defaultBotDetector,
    analyticsEnabled: true,
    backgroundWorkTimeoutMs: 2000,
  });
}
```

### Manual Verification

Use `verifyAndRecord` when you need granular control or are running in a standard Node.js/Bun/Deno backend.

```typescript theme={null}
const result = await supertab.verifyAndRecord({
  token: "...", // Extracted from header or other source
  resourceUrl: "https://example.com/premium-article",
  userAgent: request.headers.get("User-Agent"),
  ctx: ctx // Optional: Pass context to use waitUntil for non-blocking analytics
});

if (result.valid) {
  // Allow access to content
} else {
  // Handle invalid license (e.g., return 401)
  console.error(result.error);
}
```

### Obtaining a License Token

If you are building a client that needs to access protected resources, use `obtainLicenseToken` to get a license token.
The SDK handles retrieval of the licensing details and automatically refreshes the token when needed.
Whenever a usage type is specified and a token is not required
(the matched content rule permits the intended usage without a license), the method returns no token (`undefined`).
You should call `obtainLicenseToken` before every request, the SDK will handle caching and expiration.

```typescript theme={null}
const token = await SupertabConnect.obtainLicenseToken({
  clientId: "your_client_id",
  clientSecret: "your_client_secret",
  resourceUrl: "https://example.com/protected-resource"
});

if (token) {
  const headers = { Authorization: `License ${token}` };
  // Use these headers on the resource request.
}
```

When you know the intended content usage type, pass `usage`. If there is a matching `<content>` rule with the license
explicitly permitting that usage without requiring a license token, the SDK returns `undefined`.
This allows you to treat `undefined` as "no token needed" rather than an error.

```typescript theme={null}
import { SupertabConnect, UsageType } from "@getsupertab/supertab-connect-sdk";

const token = await SupertabConnect.obtainLicenseToken({
  clientId: "your_client_id",
  clientSecret: "your_client_secret",
  resourceUrl: "https://example.com/public-resource",
  usage: UsageType.SEARCH
});

if (token) {
  const headers = { Authorization: `License ${token}` };
  // Use these headers on the resource request.
}
```

## Important Types

### `EnforcementMode`

Enforcement modes determine what happens to a **bot** request. Non-bot traffic is always allowed.

* `DISABLED`: No verification — every request passes through untouched.
* `OBSERVE` (Default): Tokens are verified and outcome recording is attempted. A bot with **no** token is allowed through with RSL signaling headers (`Link`, `X-RSL-Status`) indicating a license is required; a bot presenting an **invalid** token is still blocked.
* `ENFORCE`: Blocks any bot without a valid token — `401 Unauthorized` (missing or invalid token) or `403 Forbidden` (token valid but wrong audience).

Invalid tokens are always blocked except in `DISABLED` mode.

### Handler Result

When calling `handleRequest` manually, you receive a `HandlerResult`:

* `{ action: "allow", headers?: ... }`: The request should proceed.
* `{ action: "block", status: number, body: string, headers: ... }`: The request should be rejected with the provided response.

## Error Handling

The SDK provides clear error reasons when a license is invalid. Common reasons include:

* `missing_license_token`: No license was provided in the headers.
* `license_token_expired`: The JWT `exp` claim is in the past.
* `invalid_license_audience`: The token is valid but not for the requested URL.
* `license_signature_verification_failed`: The token was tampered with or signed by an untrusted issuer.

## Tips and Pitfalls

* **Performance**: When using Cloudflare Workers, always pass the `ExecutionContext` (`ctx`) so event delivery can continue in the background. For CloudFront analytics and usage delivery, see [CloudFront](/reference/cloudfront#enforcement-modes-and-analytics).
* **Singleton Pattern**: The `SupertabConnect` constructor returns the existing instance if one was already created with the same API key. Use `SupertabConnect.resetInstance()` if you need to change configurations dynamically.
* **Custom Bot Detection**: If you have specific traffic patterns (e.g., a known internal scraper), provide a custom `botDetector` function to prevent false positives.
* **No token required**: `obtainLicenseToken` returning `undefined` is valid when `usage` matches content without server URL that permits that usage. Treat it as "no token needed", not as an authentication failure.
* **Cache behavior**: `obtainLicenseToken` caches `license.xml` by origin for 15 minutes and license tokens by client, token server, and matched URL pattern. Process restarts clear that cache.

```typescript theme={null}
import { defaultBotDetector } from "@getsupertab/supertab-connect-sdk";

const customDetector = (request: Request) => {
  const ua = request.headers.get("User-Agent");
  return ua?.includes("MyInternalBot") ? false : defaultBotDetector(request);
};
```

## API Reference

### Static Methods

* `cloudflareHandleRequests(request, env, ctx, options?)`: Cloudflare-specific handler.
* `fastlyHandleRequests(event, apiKey, backend, options?)`: Fastly-specific handler. Takes the Fastly `FetchEvent`, not `event.request`.
* `cloudfrontHandleRequests(event, options)`: CloudFront Lambda\@Edge handler. Attach at viewer request; the trigger is auto-detected. `options` takes `backgroundWorkTimeoutMs` — see [CloudFront](/reference/cloudfront#enforcement-modes-and-analytics).
* `verify(options)`: Pure token verification (no event recording).
* `obtainLicenseToken(options)`: Client-side token acquisition.

### Instance Methods

* `handleRequest(request, context?)`: The core logic used by CDN handlers. `context` is a `HandleRequestContext` object (`{ ctx?, sourceCdn?, clientIp?, ... }`), not a bare execution context.
* `verifyAndRecord(options)`: Verifies a token and records the usage event. Returns `{ valid, error? }`.
