first commit
This commit is contained in:
108
docs/media/audio/third-party-elevenlabs/src/client/socket.md
Normal file
108
docs/media/audio/third-party-elevenlabs/src/client/socket.md
Normal file
@@ -0,0 +1,108 @@
|
||||
# `socket`
|
||||
|
||||
Wrapper around any `WebSocketLike` (the browser global, `ws` from Node,
|
||||
or a fake from tests). Exposes a typed frame-level API and a
|
||||
`Promise<void>` for the open handshake.
|
||||
|
||||
## Purpose
|
||||
|
||||
Raw WebSocket APIs are awkward — open / message / close / error are
|
||||
delivered as untyped events, binary frames arrive as `Blob` /
|
||||
`ArrayBuffer` / typed-array views depending on platform, and there's no
|
||||
built-in way to await the open handshake. `ElevenLabsSocket` papers over
|
||||
all of that so adapters that need WebSocket transport (e.g. the
|
||||
streaming-input TTS endpoint) can `await sock.waitOpen()` and then send
|
||||
typed `SocketFrame` values without touching the raw event surface.
|
||||
|
||||
## Features
|
||||
|
||||
- `waitOpen()` — promise that resolves on `open` and rejects on `error`
|
||||
or caller-signal abort.
|
||||
- `send(frame: SocketFrame)` — typed text or binary send; binary frames
|
||||
are copied into a fresh `ArrayBuffer` so the underlying WS impl never
|
||||
sees a SharedArrayBuffer-backed view by accident.
|
||||
- `onMessage` / `onClose` / `onError` — listeners returning unsubscribe
|
||||
functions.
|
||||
- Accepts `Uint8Array`, `ArrayBuffer`, or any `ArrayBufferView` on the
|
||||
wire and normalises them to `Uint8Array` before dispatching.
|
||||
- Sends on a closed socket throw `WebSocketProtocolError` instead of
|
||||
silently dropping the frame.
|
||||
- Pluggable `WebSocketConstructor` so tests inject a fake.
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```ts
|
||||
import { ElevenLabsClient } from "@suaveplan/third-party-elevenlabs";
|
||||
|
||||
const client = new ElevenLabsClient({ apiKey: process.env.KEY! });
|
||||
const sock = client.openSocket({
|
||||
path: "/v1/text-to-speech/voice-id/stream-input",
|
||||
feature: "tts",
|
||||
operation: "stream_input",
|
||||
query: { model_id: "eleven_flash_v2" },
|
||||
});
|
||||
await sock.waitOpen();
|
||||
sock.onMessage((f) => process.stdout.write(f.data));
|
||||
sock.send({ kind: "text", data: JSON.stringify({ text: "hello" }) });
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```ts
|
||||
import { ElevenLabsClient } from "@suaveplan/third-party-elevenlabs";
|
||||
|
||||
const ctrl = new AbortController();
|
||||
const client = new ElevenLabsClient({
|
||||
apiKey: KEY,
|
||||
webSocketImpl: WS, // 'ws' on Node
|
||||
});
|
||||
const sock = client.openSocket({
|
||||
path: "/v1/text-to-speech/abc/stream-input",
|
||||
feature: "tts",
|
||||
operation: "stream_input",
|
||||
});
|
||||
const unsubMsg = sock.onMessage((f) => sink.write(f.data));
|
||||
const unsubClose = sock.onClose((e) =>
|
||||
log.info("closed", { code: e.code, clean: e.wasClean })
|
||||
);
|
||||
sock.onError((err) => log.warn("ws error", { err }));
|
||||
try {
|
||||
await sock.waitOpen();
|
||||
for (const chunk of corpus) {
|
||||
sock.send({ kind: "text", data: JSON.stringify({ text: chunk }) });
|
||||
}
|
||||
} finally {
|
||||
unsubMsg();
|
||||
unsubClose();
|
||||
sock.close(1000, "done");
|
||||
}
|
||||
ctrl.abort();
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
- `new ElevenLabsSocket(ws: WebSocketLike, endpoint: string, signal?: AbortSignal)`
|
||||
- `waitOpen(): Promise<void>`
|
||||
- `readyState: number` — 0 connecting / 1 open / 2 closing / 3 closed.
|
||||
- `send(frame: SocketFrame): void` — `{ kind: "text" | "binary", data }`
|
||||
- `close(code?: number, reason?: string): void`
|
||||
- `onMessage(listener): () => void`
|
||||
- `onClose(listener): () => void`
|
||||
- `onError(listener): () => void`
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
- The `xi-api-key` is sent as a query parameter (browsers can't set
|
||||
headers on the WS upgrade) — that's done in `ElevenLabsClient.openSocket`,
|
||||
not here.
|
||||
- Binary `send` always copies into a fresh `ArrayBuffer` to defend
|
||||
against SharedArrayBuffer leakage; the cost is one heap allocation per
|
||||
outbound binary frame.
|
||||
- The internal `#wireOpenPromise` removes its `error` listener once the
|
||||
promise settles, so post-open transport errors flow only into the
|
||||
user-installed `onError` listeners — they don't reject `waitOpen()`
|
||||
twice.
|
||||
- `bytesFrom` rejects unsupported binary payload types with
|
||||
`WebSocketProtocolError`. The error is dispatched to `onError` rather
|
||||
than thrown synchronously, since it originates inside an event handler
|
||||
callback.
|
||||
Reference in New Issue
Block a user