Files
suaveplan/openspec/changes/enterprise-browser-clipboard/proposal.md
2026-05-27 23:29:53 +01:00

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.