5.3 KiB
OpenSpec Change: enterprise-browser-clipboard
- Change ID: enterprise-browser-clipboard
- Package:
@suaveplan/clipboard - Category: browser
- Target path:
browser/platform/clipboard - Status: IN PROGRESS
- Date: 2026-04-24
- Reference spec:
/mnt/s/web/suaveplan/enterprise/clipboard.md
Summary
Deliver @suaveplan/clipboard on the genesis branch at enterprise quality. The package provides ClipboardManager with injectable ClipboardNavigator seam, plain-text and rich copy/read, permission probing, and execCommand fallback. Genesis adds image and arbitrary-blob clipboard I/O, typed errors via @suaveplan/error, secure-context guard, paste-event fallback, permission-change subscription, async queue for concurrent operations, OTel counters/histograms, and Playwright E2E across Chromium/WebKit/Firefox.
Current-state audit (genesis)
Fresh build — does not exist on genesis yet.
Available workspace deps on genesis
| Package | Role |
|---|---|
@suaveplan/error (workspace:^) |
CLIPBOARD_API_UNAVAILABLE, CLIPBOARD_PERMISSION_DENIED, etc. |
@suaveplan/logger (workspace:^) |
Structured logging |
@suaveplan/schema-registry (workspace:^) |
ClipboardOptions schema |
zod (catalog:forms) |
ClipboardOptions Zod schema |
@opentelemetry/api (catalog:observability) |
Copy/read/fallback metrics |
tslib (catalog:build) |
Runtime helpers |
Dev: vitest, @vitest/coverage-istanbul, happy-dom, @playwright/test, @suaveplan/testing.
Preserved public API surface
class ClipboardManager {
constructor(navigator?: ClipboardNavigator)
copyText(text: string, options?: ClipboardOptions): Promise<ClipboardResult>
readText(options?: ClipboardOptions): Promise<ClipboardResult>
copyRichContent(data: ClipboardData, options?: ClipboardOptions): Promise<ClipboardResult>
readRichContent(options?: ClipboardOptions): Promise<ClipboardResult>
checkPermission(name: 'clipboard-read' | 'clipboard-write'): Promise<PermissionResult>
onPermissionChange(name: 'clipboard-read' | 'clipboard-write', cb: (s: PermissionState) => void): () => void
}
// Types
interface ClipboardData {
text?: string
html?: string
image?: Blob // new
blobs?: Record<string, Blob> // new — arbitrary MIME
}
interface ClipboardResult { success: boolean; method: 'api' | 'fallback'; payload?: ClipboardData; error?: SuaveplanError }
interface PermissionResult { state: PermissionState; supported: boolean }
Subpath exports: ".", "./types".
New additions for genesis
1. Image and arbitrary-blob clipboard I/O
copyRichContent and readRichContent accept/return image?: Blob and blobs?: Record<string, Blob> via ClipboardItem for modern Clipboard API.
2. Typed errors
CLIPBOARD_API_UNAVAILABLE, CLIPBOARD_PERMISSION_DENIED, CLIPBOARD_FALLBACK_FAILED, CLIPBOARD_INSECURE_CONTEXT — all extend @suaveplan/error.
3. Secure-context guard
requireSecureContext() probes window.isSecureContext; throws CLIPBOARD_INSECURE_CONTEXT outside HTTPS/localhost before attempting clipboard access.
4. Paste-event fallback
readText in non-API contexts orchestrates focus + paste event capture as best-effort fallback.
5. Permission-change subscription
onPermissionChange(name, cb) using PermissionStatus.onchange; returns unsubscribe function.
6. Async operation queue
Concurrent copy/read calls are serialized via an internal queue to prevent "Document is not focused" races.
7. Focus-aware retry
When Document is not focused error fires, waits for the next focus event and retries once.
8. Error codes
CLIPBOARD_API_UNAVAILABLE, CLIPBOARD_PERMISSION_DENIED, CLIPBOARD_FALLBACK_FAILED, CLIPBOARD_INSECURE_CONTEXT.
9. OTel metrics
Counter clipboard.copy_total{method}, counter clipboard.read_total{method}, counter clipboard.fallback_used_total, counter clipboard.error_total{code}, histogram clipboard.op_latency_ms.
File layout
browser/platform/clipboard/
package.json
tsconfig.json
vitest.config.ts
src/
index.ts
error-codes.ts
clipboard/
clipboard/
clipboard.ts (ClipboardManager, ≤250 lines)
clipboard.test.ts
clipboard.md
clipboard.types.ts
index.ts
queue/
queue.ts (async operation serializer)
queue.test.ts
queue.md
index.ts
fallback/
fallback.ts (execCommand + paste-event fallback)
fallback.test.ts
fallback.md
index.ts
telemetry/
metrics/
metrics.ts
metrics.md
index.ts
types/
types/
types.ts
types.md
index.ts
schema/
clipboard-schema/
clipboard-schema.ts
clipboard-schema.md
index.ts
constants/
index.ts
tests/
integration.test.ts
e2e/
clipboard.spec.ts (Chromium/WebKit/Firefox: text, image, permissions)
Impact
Downstream: any Suaveplan browser app using clipboard operations.
Breaking changes: None — all additions are additive. Error types changed from bare strings to SuaveplanError (structured), but result shape is unchanged.
Prerequisites: @suaveplan/error, @suaveplan/logger on Verdaccio.