# Kernstra

The SDK entry point: all members are static. `Kernstra.init()` verifies the license and loads the engine binary; call it once, before creating any viewer, and `await` it before `PDFDocument.fromUrl()`, `fromFile()`, `fromBytes()` or `fromUrlProgressive()` — all four require the engine to be loaded.

A valid license, development or production, is **required**: without one `init()` shows a "License required" popup, rejects with `LICENSE_REQUIRED` and never downloads the engine. Pass the token to `init`, typically from your build's own variable, or set it where the SDK looks when none is passed: a `globalThis.KERNSTRA_LICENSE` string, a `<meta name="kernstra-license" content="…">` tag, or, in Node, `process.env.KERNSTRA_LICENSE`. The SDK ships prebuilt, so a bundler cannot substitute an environment variable into it; read the variable in your code. The license chapter ([Licensing](https://kernstra.com/docs/web-sdk/guides/licensing)) covers origins, the secure-context rule, the status check, outages and which feature each tool and API member needs.

- Kind: static class
- Access: `Kernstra`

## Usage

```ts
import { Kernstra, PDFDocument } from '@kernstra/pdf-web-sdk';

// Application startup (once)
await Kernstra.init({ license: import.meta.env.KERNSTRA_LICENSE });

// Then load documents as needed
const doc = await PDFDocument.fromUrl('/my-file.pdf');
```

## Lifecycle

### `init`

```ts
static init(options?: SDKInitOptions): Promise<void>
```

Initialize the SDK — verifies the license and loads the engine binary. Call before creating any viewer; resolves when the engine is ready.

Idempotent: a second call with the same options gets the same promise (React's StrictMode needs no guard), one with other options rejects with `INVALID_ARGUMENT`, and a failed call starts over on the next. An empty or missing `license` means "no license given"; only `setLicense('')` signs out and forgets the cached token.

The token is verified locally in milliseconds; its status is confirmed with the licensing API once per load. A load that holds a recent good answer never waits; a load without one (the first on a browser, site data cleared, a frame without storage, an answer older than 48 hours, or a paused license) waits for the check, at most 8 seconds. Nothing runs that we did not publish: the engine, the shaping and subsetting binaries and every bundled font are downloaded from the asset store your license names, by digest, verified and cached in the browser.

- Kind: static method
- Since: 1.0.0

| Parameter | Type | Description |
| --- | --- | --- |
| `options?` | `SDKInitOptions` | `license` (your license token), `engineUrl`, `shaperUrl` and `subsetterUrl` (self-hosted copies of the binaries — self-hosting licenses only), `workerUrl` (the render worker), `styleNonce` (the page's CSP nonce, carried by every stylesheet the SDK injects) and `licensePopup` (the "License required" popup in your own words). See [Initialization](https://kernstra.com/docs/web-sdk/config/init). |

Returns `Promise<void>`: Resolves when the engine is ready.

Throws:

- `LICENSE_REQUIRED` — no valid license, or not a secure context (the SDK runs only over HTTPS or on `localhost`; on a plain-HTTP origin it refuses to start with reason `insecure-context`).
- `FEATURE_NOT_LICENSED` — `engineUrl`, `shaperUrl` or `subsetterUrl` given without a self-hosting license.
- `ENGINE_LOAD_FAILED` — the binary was not downloaded, not verified, or not started.
- `INVALID_ARGUMENT` — a second call with other options, a `styleNonce` that is not base64 or base64url, or a `licensePopup` part that is not a string.
- `INVALID_STATE` — called outside a browser, where there is no `window` or `document`: server rendering.

```ts
await Kernstra.init({ license: import.meta.env.KERNSTRA_LICENSE });
```

Page: https://kernstra.com/docs/web-sdk/api/kernstra/init

### `isInitialized`

```ts
static isInitialized(): boolean
```

Returns `true` if the SDK has been initialized.

- Kind: static method
- Since: 1.0.0

Returns `boolean`

```ts
if (!Kernstra.isInitialized()) {
  await Kernstra.init({ license: import.meta.env.KERNSTRA_LICENSE });
}
```

Page: https://kernstra.com/docs/web-sdk/api/kernstra/isinitialized

### `destroy`

```ts
static destroy(): void
```

Tear down the SDK and free its resources. Destroy your viewers and close your documents first (each viewer terminates its render worker): with a document still open it throws `INVALID_STATE` rather than free them from under it.

After an `ENGINE_FAILED` fault it tears down whatever is open — that is the recovery — and a document closed after it is already released, so its `close()` does nothing. `destroy()` removes the `onLicenseChange` subscriptions, like every other SDK listener; `Kernstra.events` subscriptions are kept across it.

- Kind: static method
- Since: 1.0.0

Returns `void`

Throws:

- `INVALID_STATE` — a document is still open.

```ts
viewer.destroy();
await doc.close();
Kernstra.destroy();
```

Page: https://kernstra.com/docs/web-sdk/api/kernstra/destroy

### `version`

```ts
static get version(): string
```

SDK version string, matching the published package.

- Kind: static property
- Since: 1.0.0

Returns `string`

```ts
console.log('Kernstra Web SDK', Kernstra.version);
```

Page: https://kernstra.com/docs/web-sdk/api/kernstra/version

## License

### `setLicense`

```ts
static setLicense(token: string): Promise<LicenseState>
```

Replaces the license at runtime; resolves to the new license state. A `setLicense` is a load: the licensing API confirms the token's status, and a license that does not load (revoked, paused, expired, a copied token, a host it does not name, a malformed or forged key) shows the "license required" popup — signing out with `''` stops nothing that is open. Presenting the same key again changes neither a pause nor a revocation.

A verdict that adds rights reaches open documents at once; a viewer whose document's rights grow rebuilds its toolbar. A verdict that takes rights away reaches only the next load: each document keeps the rights it was opened with until it is closed.

- Kind: static method
- Since: 1.0.0

| Parameter | Type | Description |
| --- | --- | --- |
| `token` | `string` | The new license token. `''` signs out: it forgets the cached token, but not a stop — only a license that loads lifts a pause or a revocation. |

Returns `Promise<LicenseState>`: Resolves to the new license state.

```ts
const state = await Kernstra.setLicense(newToken);
if (state.valid && state.features.includes('export')) enableDownload();
```

Page: https://kernstra.com/docs/web-sdk/api/kernstra/setlicense

### `getLicense`

```ts
static getLicense(): LicenseState
```

The current license state, as the exported `LicenseState`. `if (state.valid) state.features.includes('export')` type-checks: the valid half has every field, the invalid half the reason.

An invalid state's `reason` is one of `no-license`, `insecure-context` (plain HTTP), `malformed`, `unsupported-algorithm`, `invalid-signature`, `not-yet-valid`, `expired`, `origin-mismatch`, `wrong-holder`, `revoked`, `paused` or `stale` (the licensing API has not confirmed the license for more than 48 hours, or never on this browser and the check could not get through; the SDK loads, watermarked and without `extract`, and asks again whenever a document is opened). A `paused` state carries `pauseReason`: `payment`, `suspended`, `term_ended`, `trial_ended` or `other`. During a licensing-API outage `getLicense()` says why output is watermarked. It reports a verdict as soon as it arrives; documents already open keep the rights they were opened with.

- Kind: static method
- Since: 1.0.0

Returns `LicenseState`: A union narrowed on `valid`: the valid half carries `watermark`, `env`, `plan`, `features`, `offline` and, on a license with an expiry date, `expiresAt`; the invalid half carries `watermark: true`, a `reason` and, when the reason is `paused`, a `pauseReason`.

```ts
const state = Kernstra.getLicense();
if (!state.valid) console.warn('License not valid:', state.reason);
else if (state.watermark) console.warn('Output is watermarked (development token)');
```

Page: https://kernstra.com/docs/web-sdk/api/kernstra/getlicense

### `onLicenseChange`

```ts
static onLicenseChange(callback: (state: LicenseState) => void): () => void
```

Calls `callback` with each new license state; returns a function that unsubscribes. The same changes arrive as the [`licenseChanged`](https://kernstra.com/docs/web-sdk/api/events#ev-licensechanged) event on `Kernstra.events`. A verdict that takes rights away — revoked, paused, fewer features, the outage grace period run out — is reported at once here but reaches only the **next load** (`init`, `setLicense`, or opening a document): whoever is exporting, printing or filling a form in an open document finishes. Listen here if your app should react sooner.

`Kernstra.destroy()` removes these subscriptions; one made before an `init()` refused for want of a license stays, so it sees the retry.

- Kind: static method
- Since: 1.0.0

| Parameter | Type | Description |
| --- | --- | --- |
| `callback` | `(state: LicenseState) => void` | Called with each new license state. |

Returns `() => void`: A function that unsubscribes.

```ts
const stop = Kernstra.onLicenseChange((state) => {
  if (!state.valid) showBanner('License problem: ' + state.reason);
});
// later
stop();
```

Page: https://kernstra.com/docs/web-sdk/api/kernstra/onlicensechange

## Events and assets

### `events`

```ts
static get events(): GlobalEvents
```

Global `GlobalEvents` — `on()` and `off()` for `GlobalEventMap`; see the [Event System](https://kernstra.com/docs/web-sdk/api/events). Only the SDK emits on it. Kept across `destroy()`: subscribe once, at module level if you like.

- Kind: static property
- Since: 1.0.0

Returns `GlobalEvents`: `on()` and `off()` for `GlobalEventMap`.

```ts
Kernstra.events.on('licenseChanged', (state) => {
  console.log('license', state.valid ? state.plan : state.reason);
});
```

Page: https://kernstra.com/docs/web-sdk/api/kernstra/events

### `assetDigests`

```ts
static readonly assetDigests: Readonly<{ engine: string; shaper: string; subset: string; fonts: Readonly<Record<string, string>> }>
```

The SHA-256 of every file this version downloads: `{ engine, shaper, subset, fonts }`, `fonts` keyed by font id (`BUILTIN_FONTS[i].id`), for scripting a self-hosted deployment; the `subset` digest's folder in the store is `subsetter`. See [Engine, binaries and fonts](https://kernstra.com/docs/web-sdk/guides/engine-assets).

- Kind: static property
- Since: 1.0.0

Returns `{ engine, shaper, subset, fonts }`

```ts
const { engine, shaper, subset, fonts } = Kernstra.assetDigests;
console.log(engine, Object.keys(fonts).length, 'fonts');
```

Page: https://kernstra.com/docs/web-sdk/api/kernstra/assetdigests

Web page: https://kernstra.com/docs/web-sdk/api/kernstra
