Initial Commit
This commit is contained in:
110
openspec/changes/enterprise-browser-clipboard/tasks.md
Normal file
110
openspec/changes/enterprise-browser-clipboard/tasks.md
Normal file
@@ -0,0 +1,110 @@
|
||||
# Tasks: enterprise-browser-clipboard
|
||||
|
||||
**Change ID**: enterprise-browser-clipboard
|
||||
**Package**: `@suaveplan/clipboard`
|
||||
**Target path**: `browser/platform/clipboard`
|
||||
**Prerequisite packages on Verdaccio**: `@suaveplan/error`, `@suaveplan/logger`
|
||||
|
||||
---
|
||||
|
||||
## 0. Pre-flight (non-negotiable)
|
||||
|
||||
- [ ] 0.1 Confirm clean working tree (`git status` reports nothing) and no in-flight worktrees with uncommitted work (`git worktree list` then `git status` in each)
|
||||
- [ ] 0.2 If any worktrees exist under `.claude/worktrees/`, account for each one: still-active agent, work-pending-merge, or stale-and-prune. Stale worktrees MUST be removed (`git worktree remove`) before opening a new one — code that has been lost twice on main is the failure mode this guards against.
|
||||
- [ ] 0.3 Confirm the package set in `MANIFEST.md` matches `find packages -maxdepth 3 -name package.json` count. Drift here means a prior change archived without regenerating the manifest.
|
||||
|
||||
## 1. Scaffold
|
||||
|
||||
- [ ] Create directory tree per `proposal.md`.
|
||||
- [ ] `package.json` v0.0.0, `"sideEffects": false`.
|
||||
- `dependencies`: `@suaveplan/error workspace:^`, `@suaveplan/logger workspace:^`, `@suaveplan/schema-registry workspace:^`, `zod catalog:forms`, `@opentelemetry/api catalog:observability`, `tslib catalog:build`
|
||||
- `devDependencies`: `vitest catalog:testing`, `@vitest/coverage-istanbul catalog:testing`, `happy-dom catalog:testing`, `@playwright/test catalog:testing`, `@suaveplan/testing workspace:^`
|
||||
- Subpath exports: `"."`, `"./types"`
|
||||
- [ ] `tsconfig.json`, `vitest.config.ts` (pool: forks, happy-dom environment).
|
||||
|
||||
---
|
||||
|
||||
## 2. Error codes
|
||||
|
||||
- [ ] `CLIPBOARD_API_UNAVAILABLE`, `CLIPBOARD_PERMISSION_DENIED`, `CLIPBOARD_FALLBACK_FAILED`, `CLIPBOARD_INSECURE_CONTEXT`.
|
||||
- [ ] `registerClipboardErrorCodes()` — call eagerly.
|
||||
|
||||
---
|
||||
|
||||
## 3. Types (`src/types/types/`)
|
||||
|
||||
- [ ] `ClipboardData` (text, html, image, blobs), `ClipboardOptions`, `ClipboardResult`, `PermissionResult`, `ClipboardNavigator`.
|
||||
- [ ] Write `types.md`.
|
||||
|
||||
---
|
||||
|
||||
## 4. ClipboardOptions schema (`src/schema/clipboard-schema/`)
|
||||
|
||||
- [ ] Zod schema for `ClipboardOptions`; `registerClipboardSchema()`.
|
||||
- [ ] Write `clipboard-schema.md`.
|
||||
|
||||
---
|
||||
|
||||
## 5. Async queue (`src/clipboard/queue/`)
|
||||
|
||||
- [ ] `queue.ts` — `OperationQueue<T>` serializes concurrent async operations; rejects queued calls after configurable timeout.
|
||||
- [ ] Write `queue.test.ts` (sequential execution, timeout rejection). Write `queue.md`.
|
||||
|
||||
---
|
||||
|
||||
## 6. Fallback module (`src/clipboard/fallback/`)
|
||||
|
||||
- [ ] `fallback.ts` — `execCommandFallback(text)` for copy; `pasteEventFallback()` for read (focus + paste event capture).
|
||||
- [ ] `requireSecureContext()` — throws `CLIPBOARD_INSECURE_CONTEXT` on HTTP.
|
||||
- [ ] Focus-aware retry: on `Document is not focused`, wait for focus, retry once.
|
||||
- [ ] Write `fallback.test.ts` (execCommand success, paste capture, insecure context). Write `fallback.md`.
|
||||
|
||||
---
|
||||
|
||||
## 7. OTel metrics (`src/telemetry/metrics/`)
|
||||
|
||||
- [ ] `metrics.ts` — 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`.
|
||||
- [ ] Write `metrics.md`.
|
||||
|
||||
---
|
||||
|
||||
## 8. `ClipboardManager` (`src/clipboard/clipboard/`)
|
||||
|
||||
- [ ] `clipboard.ts` — `ClipboardManager` composing queue + fallback + metrics.
|
||||
- [ ] `copyText` / `readText` with secure-context guard + API → fallback path.
|
||||
- [ ] `copyRichContent` / `readRichContent` — `text/plain`, `text/html`, `image/png`, arbitrary MIME via `ClipboardItem`.
|
||||
- [ ] `checkPermission`, `onPermissionChange` via `PermissionStatus.onchange`.
|
||||
- [ ] Log deprecation metric when `execCommand` path fires.
|
||||
- [ ] Write `clipboard.test.ts` (all paths, image copy, permission change, queue serialization, insecure context). Write `clipboard.md`.
|
||||
|
||||
---
|
||||
|
||||
## 9. Integration test (`tests/integration.test.ts`)
|
||||
|
||||
- [ ] Plain text copy + read round-trip.
|
||||
- [ ] Rich content (text + html) round-trip.
|
||||
- [ ] Queue serializes 3 concurrent copy calls.
|
||||
- [ ] Insecure-context throws `CLIPBOARD_INSECURE_CONTEXT`.
|
||||
|
||||
---
|
||||
|
||||
## 10. Playwright E2E (`e2e/clipboard.spec.ts`)
|
||||
|
||||
- [ ] Chromium, WebKit, Firefox.
|
||||
- [ ] Copy and read plain text with real `clipboard-write` / `clipboard-read` permissions.
|
||||
- [ ] Copy and read PNG image in Chromium.
|
||||
- [ ] Permission-denied path returns `CLIPBOARD_PERMISSION_DENIED`.
|
||||
- [ ] `onPermissionChange` subscription fires on deny → grant transition.
|
||||
|
||||
---
|
||||
|
||||
## 11. Docs + verify
|
||||
|
||||
- [ ] Write `.md` per module (≥200 words, API reference, examples).
|
||||
- [ ] Write `README.md` with browser-matrix table, API guide, permission guide.
|
||||
- [ ] `bunx turbo lint typecheck test --filter=@suaveplan/clipboard` — green.
|
||||
- [ ] `bunx turbo test:coverage` — 100%.
|
||||
- [ ] No `@ts-expect-error` in `src/` or tests. No `vi.stubGlobal`.
|
||||
- [ ] Playwright E2E green on Chromium, WebKit, Firefox.
|
||||
- [ ] `bun scripts/publish.ts --package @suaveplan/clipboard`.
|
||||
- [ ] Archive to `openspec/archive/2026-04-24-enterprise-browser-clipboard/`.
|
||||
Reference in New Issue
Block a user