Move docs/ package mirror into document-navigator plugin
Relocate the ~6k-file docs tree from the repo root into plugins/document-navigator/docs/ so the plugin is self-contained and portable. docs/superpowers/ (specs and plans) stays at the repo root. Moved via cp+rm rather than git mv because directory rename() fails with EPERM on this WSL /mnt DrvFs mount; git rename-detection pairs the identical-content files at commit time. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,96 @@
|
||||
# `queue/queue.ts`
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
|
||||
## Purpose
|
||||
|
||||
`AsyncQueue` is a minimal FIFO asynchronous queue that serializes
|
||||
operations on a single tail promise. The `ClipboardManager` uses it to
|
||||
ensure concurrent `copyText` / `readText` / `copyRichContent` /
|
||||
`readRichContent` calls execute in arrival order — without it, the
|
||||
trailing call would win non-deterministically because the browser's
|
||||
Clipboard API does not provide ordering guarantees across overlapping
|
||||
requests. The queue is intentionally tiny (one promise reference plus a
|
||||
drained flag) so it adds no measurable overhead in the common single-call
|
||||
case.
|
||||
|
||||
## Features
|
||||
|
||||
- FIFO ordering — callbacks run strictly in arrival order.
|
||||
- Rejection-tolerant — a rejected operation does not stall later ones.
|
||||
- `drain()` closes the queue and resolves once the tail settles.
|
||||
- `reset()` re-opens a drained queue (useful for tests + dispose flows).
|
||||
- Zero dependencies; no `Promise.race`, no timers, no microtask leaks.
|
||||
|
||||
## Usage: Basic
|
||||
|
||||
```ts
|
||||
import { AsyncQueue } from "@suaveplan/clipboard";
|
||||
|
||||
const queue = new AsyncQueue();
|
||||
const results = await Promise.all([
|
||||
queue.enqueue(async () => "a"),
|
||||
queue.enqueue(async () => "b"),
|
||||
queue.enqueue(async () => "c"),
|
||||
]);
|
||||
// results === ["a", "b", "c"]
|
||||
```
|
||||
|
||||
## Usage: Advanced
|
||||
|
||||
```ts
|
||||
import { AsyncQueue } from "@suaveplan/clipboard";
|
||||
|
||||
const queue = new AsyncQueue();
|
||||
|
||||
// Rejection does not break the queue.
|
||||
queue.enqueue(async () => {
|
||||
throw new Error("boom");
|
||||
}).catch(() => undefined);
|
||||
const next = await queue.enqueue(async () => "still works");
|
||||
|
||||
// Close the queue when shutting down.
|
||||
await queue.drain();
|
||||
|
||||
// Re-open later for a new session.
|
||||
queue.reset();
|
||||
await queue.enqueue(async () => "round two");
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `new AsyncQueue()`
|
||||
|
||||
Constructs a queue with an already-resolved tail.
|
||||
|
||||
### `queue.enqueue<T>(fn: () => Promise<T>): Promise<T>`
|
||||
|
||||
Append `fn` to the queue. The returned promise settles with `fn`'s
|
||||
outcome once every previously enqueued callback has settled. If the
|
||||
queue has been drained, returns a rejected promise carrying `"AsyncQueue:
|
||||
enqueue after drain"`.
|
||||
|
||||
### `queue.drain(): Promise<void>`
|
||||
|
||||
Closes the queue. Subsequent `enqueue` calls reject. Returns a promise
|
||||
that resolves once the current tail settles (success or failure).
|
||||
|
||||
### `queue.reset(): void`
|
||||
|
||||
Re-opens a drained queue. Replaces the tail with a fresh
|
||||
`Promise.resolve()`.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
The tail promise chains through `then(onFulfilled, onRejected)` with both
|
||||
arms re-invoking `fn`. This means an operation always runs after the
|
||||
previous one regardless of how the previous resolved — the rejection does
|
||||
not propagate into the queue's tracked tail (we explicitly catch via
|
||||
`.then(() => undefined, () => undefined)`). Because the tail is `unknown`-
|
||||
typed internally, the generic `T` parameter on `enqueue` is preserved end
|
||||
to end. The drained flag is checked synchronously inside `enqueue` so
|
||||
post-drain calls reject without scheduling work. The queue is private to
|
||||
the manager but exported so advanced consumers can reuse it for
|
||||
caller-defined ordering (e.g. when composing with other browser APIs).
|
||||
Reference in New Issue
Block a user