# `manager/manager.ts`
_(no submodules)_
## Purpose
`ClipboardManager` is the orchestrator class for `@suaveplan/clipboard`.
It composes the navigator + window seams, the FIFO `AsyncQueue`, the
typed error registry, the OpenTelemetry observability bundle, the
secure-context guard, the permission probe, and the fallback path into a
single ergonomic facade. Every public method records counters, the
operation latency histogram, and per-code error totals so production
deployments observe clipboard health without any caller-side wiring.
## Features
- `copyText`, `copyRichContent`, `readText`, `readRichContent` with
consistent `ClipboardResult` returns.
- `checkPermissions` + `onPermissionChange` for permission lifecycle.
- `isSupported`, `isSecure`, `dispose` for environment + lifecycle.
- Configurable serialization (`serialize: true` by default).
- Configurable secure-context guard (`requireSecureContext`).
- Optional sanitizer hook applied to inbound HTML.
- Optional pre-built observability bundle.
- Focus-aware retry on `NotAllowedError: Document is not focused`.
## Usage: Basic
```ts
import { ClipboardManager } from "@suaveplan/clipboard";
const clipboard = new ClipboardManager();
const result = await clipboard.copyText("hello");
if (!result.success) {
console.warn("copy failed:", result.code, result.error);
}
```
## Usage: Advanced
```ts
import {
ClipboardManager,
createClipboardObservability,
createTagAllowlistSanitizer,
} from "@suaveplan/clipboard";
const observability = createClipboardObservability();
const clipboard = new ClipboardManager({
useFallback: true,
requireSecureContext: true,
serialize: true,
focusRetry: true,
sanitizer: createTagAllowlistSanitizer(["p", "b", "i", "br"]),
pasteTimeoutMs: 2000,
observability,
});
const png = await loadPngBlob();
await clipboard.copyRichContent({
text: "fallback text",
html: "bold",
image: png,
});
const unsub = await clipboard.onPermissionChange((snapshot, name) => {
console.info("permission change", name, snapshot);
});
// later: unsub(); await clipboard.dispose();
```
## API Reference
### `new ClipboardManager(options?, nav?, win?)`
Construct a manager. `options` is merged on top of
`DEFAULT_CLIPBOARD_OPTIONS`. `nav` and `win` default to the global
`navigator` and `window`; either may be supplied as a structural shape
for SSR or unit tests.
### `copyText(text): Promise>`
Copy plain text. Uses the async API when available; falls back to the
hidden-textarea path on rejection or absence when `useFallback` is true.
Short-circuits with `CLIPBOARD.INSECURE_CONTEXT` when
`requireSecureContext` is set and the bound window is insecure.
### `copyRichContent(data): Promise>`
Copy rich content (text / html / image / blobs) via `ClipboardItem`. The
fallback path requires `data.text`; rich-only payloads without text
short-circuit with `CLIPBOARD.NO_CONTENT`. Empty payloads also surface
`CLIPBOARD.NO_CONTENT`.
### `readText(): Promise>`
Read plain text. Falls back to the paste-event capture when the API is
unavailable or rejects (and `useFallback` is true).
### `readRichContent(): Promise>`
Read rich content. Returns `CLIPBOARD.API_UNAVAILABLE` when no async API
is present (rich content has no usable fallback). Applies the optional
sanitizer to inbound HTML.
### `checkPermissions(): Promise`
Snapshot of `clipboard-read` + `clipboard-write` permission state.
### `onPermissionChange(cb): Promise<() => void>`
Subscribe to permission changes. Returns an unsubscribe function.
### `isSupported(): boolean`
True when `navigator.clipboard` is defined on the bound navigator.
### `isSecure(): boolean`
True when the bound window does not flag `isSecureContext: false`.
### `dispose(): Promise`
Drain the internal queue. After dispose, queued operations resolve with
their current outcomes; new enqueues reject.
## Implementation Notes
The manager keeps a single `OperationContext` reference (navigator,
window, options, observability) shared with the pure operation functions
in `operations.ts`. This split keeps the class under the 300-line file
budget while preserving the public API. The internal `run` method wraps
every operation in a latency-measuring closure and either enqueues it
via the `AsyncQueue` (when `serialize: true`) or invokes it directly. The
`recordResult` helper updates counters per-call and per-error-code so
the OTel pipeline measures both the macro-shape (copy vs. read) and the
micro-shape (which `CLIPBOARD.*` code fired). The constructor is SSR-
safe: missing globals collapse to a fully-typed empty navigator and an
undefined window so import-time evaluation never throws on a server.