@voiceinput/elevenlabs 0.1.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +114 -0
- package/dist/index.cjs +527 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +24 -0
- package/dist/index.d.cts.map +1 -0
- package/dist/index.d.ts +24 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +525 -0
- package/dist/index.js.map +1 -0
- package/dist/server.cjs +153 -0
- package/dist/server.cjs.map +1 -0
- package/dist/server.d.cts +34 -0
- package/dist/server.d.cts.map +1 -0
- package/dist/server.d.ts +34 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +152 -0
- package/dist/server.js.map +1 -0
- package/dist/session-config-D2GQf8TE.cjs +130 -0
- package/dist/session-config-D2GQf8TE.cjs.map +1 -0
- package/dist/session-config-DEaQLtZY.js +107 -0
- package/dist/session-config-DEaQLtZY.js.map +1 -0
- package/package.json +77 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../src/index.ts"],"sourcesContent":["import { sendWithBackpressure } from \"@voiceinput/provider/transport\";\nimport {\n VoiceInputError,\n type VoiceInputProviderV1,\n type VoiceInputProviderV1CallOptions,\n type VoiceInputProviderV1Session,\n type VoiceInputProviderV1StreamPart,\n type VoiceTranscriptionOptions,\n} from \"@voiceinput/provider\";\n\nimport {\n ELEVENLABS_DEFAULT_MODEL,\n ELEVENLABS_SAMPLE_RATE,\n createElevenLabsRealtimeUrl,\n validateElevenLabsConfiguration,\n type ElevenLabsRealtimeSettings,\n type ElevenLabsSessionConfiguration,\n} from \"./session-config.js\";\n\nconst DEFAULT_REALTIME_URL =\n \"wss://api.elevenlabs.io/v1/speech-to-text/realtime\";\nconst DEFAULT_FINISH_TIMEOUT_MS = 4_000;\nconst FINISH_DRAIN_MS = 250;\n\nexport interface ElevenLabsVoiceInputProviderOptions extends ElevenLabsRealtimeSettings {\n readonly tokenEndpoint: string | URL;\n readonly model?: string;\n readonly fetch?: typeof globalThis.fetch;\n readonly webSocket?: typeof globalThis.WebSocket;\n readonly realtimeUrl?: string;\n readonly finishTimeoutMs?: number;\n}\n\nexport function elevenlabs(\n options: ElevenLabsVoiceInputProviderOptions,\n): VoiceInputProviderV1 {\n const model = factoryString(\n options.model ?? ELEVENLABS_DEFAULT_MODEL,\n \"model\",\n );\n const tokenEndpoint = factoryString(\n String(options.tokenEndpoint),\n \"tokenEndpoint\",\n );\n const realtimeUrl = factoryString(\n options.realtimeUrl ?? DEFAULT_REALTIME_URL,\n \"realtimeUrl\",\n );\n const finishTimeoutMs = positiveInteger(\n options.finishTimeoutMs ?? DEFAULT_FINISH_TIMEOUT_MS,\n \"finishTimeoutMs\",\n );\n const providerSettings: ElevenLabsRealtimeSettings = {\n ...(options.vadThreshold === undefined\n ? {}\n : { vadThreshold: options.vadThreshold }),\n ...(options.minSpeechDurationMs === undefined\n ? {}\n : { minSpeechDurationMs: options.minSpeechDurationMs }),\n ...(options.minSilenceDurationMs === undefined\n ? {}\n : { minSilenceDurationMs: options.minSilenceDurationMs }),\n ...(options.noVerbatim === undefined\n ? {}\n : { noVerbatim: options.noVerbatim }),\n ...(options.filterBackgroundAudio === undefined\n ? {}\n : { filterBackgroundAudio: options.filterBackgroundAudio }),\n };\n const validateProviderOptions = (\n transcriptionOptions: VoiceTranscriptionOptions,\n ): void => {\n try {\n validateElevenLabsConfiguration({\n model,\n ...providerSettings,\n ...transcriptionOptions,\n });\n } catch (cause) {\n if (VoiceInputError.isInstance(cause)) {\n throw cause;\n }\n throw invalidConfiguration(cause);\n }\n };\n\n validateProviderOptions({});\n\n return Object.freeze({\n specificationVersion: \"v1\" as const,\n provider: \"elevenlabs\",\n modelId: model,\n sampleRate: ELEVENLABS_SAMPLE_RATE,\n validateOptions: validateProviderOptions,\n async doOpen(callOptions: VoiceInputProviderV1CallOptions) {\n validateProviderOptions(callOptions);\n const configuration = validateElevenLabsConfiguration({\n model,\n ...providerSettings,\n ...callOptions,\n });\n return await openSession({\n abortSignal: callOptions.abortSignal,\n configuration,\n fetchImplementation: options.fetch ?? globalThis.fetch,\n finishTimeoutMs,\n realtimeUrl,\n tokenEndpoint,\n WebSocketImplementation: options.webSocket ?? globalThis.WebSocket,\n });\n },\n });\n}\n\nasync function openSession(options: {\n abortSignal: AbortSignal;\n configuration: ElevenLabsSessionConfiguration;\n fetchImplementation: typeof globalThis.fetch;\n finishTimeoutMs: number;\n realtimeUrl: string;\n tokenEndpoint: string;\n WebSocketImplementation: typeof globalThis.WebSocket;\n}): Promise<VoiceInputProviderV1Session> {\n throwIfAborted(options.abortSignal);\n requireBrowserFunction(options.fetchImplementation, \"fetch\");\n requireBrowserFunction(options.WebSocketImplementation, \"WebSocket\");\n const token = await requestToken(\n options.fetchImplementation,\n options.tokenEndpoint,\n options.configuration.model,\n options.abortSignal,\n );\n throwIfAborted(options.abortSignal);\n const socket = new options.WebSocketImplementation(\n createElevenLabsRealtimeUrl(\n options.realtimeUrl,\n token,\n options.configuration,\n ),\n );\n return await createSession(\n socket,\n options.abortSignal,\n options.finishTimeoutMs,\n );\n}\n\nasync function requestToken(\n fetchImplementation: typeof globalThis.fetch,\n tokenEndpoint: string,\n model: string,\n abortSignal: AbortSignal,\n): Promise<string> {\n let response: Response;\n try {\n response = await fetchImplementation(tokenEndpoint, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ model }),\n credentials: \"same-origin\",\n signal: abortSignal,\n });\n } catch (cause) {\n throwIfAborted(abortSignal);\n throw new VoiceInputError({\n code: \"network-error\",\n message: \"Unable to reach the ElevenLabs token endpoint.\",\n provider: \"elevenlabs\",\n retryable: true,\n cause,\n });\n }\n if (!response.ok) {\n throw await tokenResponseError(response);\n }\n let value: unknown;\n try {\n value = await response.json();\n } catch (cause) {\n throw new VoiceInputError({\n code: \"token-error\",\n message: \"The ElevenLabs token endpoint returned invalid JSON.\",\n provider: \"elevenlabs\",\n cause,\n });\n }\n if (!isRecord(value) || !nonEmpty(value[\"token\"])) {\n throw new VoiceInputError({\n code: \"token-error\",\n message: \"The ElevenLabs token endpoint returned an invalid token.\",\n provider: \"elevenlabs\",\n });\n }\n return value[\"token\"];\n}\n\nasync function createSession(\n socket: WebSocket,\n abortSignal: AbortSignal,\n finishTimeoutMs: number,\n): Promise<VoiceInputProviderV1Session> {\n let controller:\n ReadableStreamDefaultController<VoiceInputProviderV1StreamPart> | undefined;\n const stream = new ReadableStream<VoiceInputProviderV1StreamPart>({\n start(value) {\n controller = value;\n },\n });\n if (controller === undefined) {\n throw new VoiceInputError({\n code: \"provider-error\",\n message: \"Unable to initialize the ElevenLabs transcript stream.\",\n provider: \"elevenlabs\",\n });\n }\n\n let hasAudio = false;\n let closed = false;\n let failed = false;\n let finishing = false;\n let finishDeferred: Deferred<void> | undefined;\n let finishSettled = false;\n let finishTimer: ReturnType<typeof setTimeout> | undefined;\n let finishDrainTimer: ReturnType<typeof setTimeout> | undefined;\n let lastInterim = \"\";\n let segmentSequence = 0;\n const segmentId = (): string => `commit:${segmentSequence}`;\n let speechActive = false;\n\n const closeSocket = (reason: \"aborted\" | \"finished\"): void => {\n if (socket.readyState === 0 || socket.readyState === 1) {\n socket.close(1000, reason);\n }\n };\n const closeStream = (): void => {\n if (closed) {\n return;\n }\n closed = true;\n abortSignal.removeEventListener(\"abort\", abort);\n controller?.close();\n };\n const settleFinish = (error?: unknown): void => {\n if (finishDeferred === undefined || finishSettled) {\n return;\n }\n finishSettled = true;\n if (finishTimer !== undefined) {\n clearTimeout(finishTimer);\n finishTimer = undefined;\n }\n if (finishDrainTimer !== undefined) {\n clearTimeout(finishDrainTimer);\n finishDrainTimer = undefined;\n }\n if (error === undefined) {\n finishDeferred.resolve(undefined);\n } else {\n finishDeferred.reject(error);\n }\n };\n const finishCleanly = (): void => {\n if (lastInterim.length > 0) {\n lastInterim = \"\";\n controller?.enqueue({\n type: \"interim\",\n text: \"\",\n segmentId: segmentId(),\n });\n }\n endSpeech();\n settleFinish();\n closeStream();\n closeSocket(\"finished\");\n };\n const scheduleFinishDrain = (): void => {\n if (!finishing || finishSettled) {\n return;\n }\n if (finishDrainTimer !== undefined) {\n clearTimeout(finishDrainTimer);\n }\n finishDrainTimer = setTimeout(finishCleanly, FINISH_DRAIN_MS);\n };\n const fail = (error: VoiceInputError): void => {\n if (closed || failed) {\n return;\n }\n failed = true;\n settleFinish(error);\n controller?.enqueue({ type: \"error\", error });\n closeStream();\n closeSocket(\"aborted\");\n };\n const startSpeech = (): void => {\n if (!speechActive) {\n speechActive = true;\n controller?.enqueue({ type: \"speech-start\" });\n }\n };\n const endSpeech = (): void => {\n if (speechActive) {\n speechActive = false;\n controller?.enqueue({ type: \"speech-end\" });\n }\n };\n const emitInterim = (text: string): void => {\n if (text.length === 0 || text === lastInterim) {\n return;\n }\n startSpeech();\n lastInterim = text;\n controller?.enqueue({ type: \"interim\", text, segmentId: segmentId() });\n };\n const commitTranscript = (text: string): void => {\n if (text.length > 0) startSpeech();\n controller?.enqueue({ type: \"final\", text, segmentId: segmentId() });\n segmentSequence += 1;\n lastInterim = \"\";\n if (!finishing) endSpeech();\n };\n const handleMessage = (event: MessageEvent): void => {\n if (closed) {\n return;\n }\n try {\n const value = JSON.parse(String(event.data)) as unknown;\n if (!isRecord(value) || !nonEmpty(value[\"message_type\"])) {\n throw new TypeError(\"ElevenLabs sent an invalid Realtime event.\");\n }\n const type = value[\"message_type\"];\n if (type === \"partial_transcript\") {\n emitInterim(readText(value));\n if (finishDrainTimer !== undefined) {\n scheduleFinishDrain();\n }\n } else if (type === \"final_transcript\") {\n // Informational only: committed_transcript closes the segment.\n if (finishDrainTimer !== undefined) {\n scheduleFinishDrain();\n }\n } else if (type === \"committed_transcript\") {\n commitTranscript(readText(value));\n if (finishing) {\n scheduleFinishDrain();\n }\n } else if (elevenLabsErrorTypes.has(type)) {\n fail(normalizeRealtimeError(value));\n }\n } catch (cause) {\n fail(\n new VoiceInputError({\n code: \"provider-error\",\n message: \"ElevenLabs sent an invalid Realtime event.\",\n provider: \"elevenlabs\",\n cause,\n }),\n );\n }\n };\n const handleClose = (event: CloseEvent): void => {\n if (closed) {\n return;\n }\n if (event.code === 1000) {\n if (finishing) {\n finishCleanly();\n return;\n }\n closeStream();\n } else {\n fail(\n new VoiceInputError({\n code: \"network-error\",\n message: \"The ElevenLabs Realtime connection closed unexpectedly.\",\n provider: \"elevenlabs\",\n retryable: true,\n cause: event,\n }),\n );\n }\n };\n function abort(): void {\n if (closed) {\n return;\n }\n settleFinish(\n abortSignal.reason ??\n new VoiceInputError({\n code: \"provider-error\",\n message: \"The ElevenLabs Realtime session was aborted.\",\n provider: \"elevenlabs\",\n }),\n );\n closeStream();\n closeSocket(\"aborted\");\n }\n\n socket.addEventListener(\"message\", handleMessage);\n socket.addEventListener(\"close\", handleClose);\n socket.addEventListener(\"error\", () => {\n fail(\n new VoiceInputError({\n code: \"network-error\",\n message: \"The ElevenLabs Realtime connection failed.\",\n provider: \"elevenlabs\",\n retryable: true,\n }),\n );\n });\n abortSignal.addEventListener(\"abort\", abort, { once: true });\n await waitForOpen(socket, abortSignal);\n\n return {\n stream,\n sendAudio(chunk) {\n if (closed || finishing || chunk.length === 0) {\n return;\n }\n hasAudio = true;\n return sendWithBackpressure(\n socket,\n Math.ceil((chunk.byteLength * 4) / 3) + 160,\n abortSignal,\n \"elevenlabs\",\n () =>\n sendJson(socket, {\n message_type: \"input_audio_chunk\",\n audio_base_64: encodePcm16(chunk),\n commit: false,\n sample_rate: ELEVENLABS_SAMPLE_RATE,\n }),\n );\n },\n finish() {\n if (finishDeferred !== undefined) {\n return finishDeferred.promise;\n }\n if (closed) {\n return;\n }\n finishing = true;\n finishDeferred = createDeferred<void>();\n if (!hasAudio) {\n finishCleanly();\n return finishDeferred.promise;\n }\n try {\n sendJson(socket, {\n message_type: \"input_audio_chunk\",\n audio_base_64: \"\",\n commit: true,\n sample_rate: ELEVENLABS_SAMPLE_RATE,\n });\n } catch (cause) {\n fail(\n VoiceInputError.isInstance(cause)\n ? cause\n : new VoiceInputError({\n code: \"network-error\",\n message: \"Unable to commit the ElevenLabs transcript.\",\n provider: \"elevenlabs\",\n retryable: true,\n cause,\n }),\n );\n return finishDeferred.promise;\n }\n finishTimer = setTimeout(() => {\n fail(\n new VoiceInputError({\n code: \"network-error\",\n message: `ElevenLabs did not commit the final transcript within ${finishTimeoutMs}ms.`,\n provider: \"elevenlabs\",\n retryable: true,\n }),\n );\n }, finishTimeoutMs);\n return finishDeferred.promise;\n },\n abort,\n };\n}\n\nconst elevenLabsErrorTypes = new Set([\n \"auth_error\",\n \"quota_exceeded\",\n \"transcriber_error\",\n \"input_error\",\n \"invalid_request\",\n \"error\",\n \"commit_throttled\",\n \"unaccepted_terms\",\n \"rate_limited\",\n \"queue_overflow\",\n \"resource_exhausted\",\n \"session_time_limit_exceeded\",\n \"chunk_size_exceeded\",\n \"insufficient_audio_activity\",\n]);\n\nfunction normalizeRealtimeError(\n value: Record<string, unknown>,\n): VoiceInputError {\n const type = String(value[\"message_type\"]);\n const rateLimited =\n type === \"rate_limited\" ||\n type === \"quota_exceeded\" ||\n type === \"commit_throttled\";\n const retryable =\n rateLimited || type === \"queue_overflow\" || type === \"resource_exhausted\";\n const code =\n type === \"auth_error\"\n ? \"unauthorized\"\n : rateLimited\n ? \"rate-limited\"\n : type === \"input_error\" || type === \"chunk_size_exceeded\"\n ? \"audio-error\"\n : \"provider-error\";\n return new VoiceInputError({\n code,\n message:\n typeof value[\"error\"] === \"string\"\n ? value[\"error\"]\n : `ElevenLabs Realtime reported ${type}.`,\n provider: \"elevenlabs\",\n retryable,\n cause: value,\n });\n}\n\nfunction invalidConfiguration(cause: unknown): VoiceInputError {\n return new VoiceInputError({\n code: \"invalid-configuration\",\n message:\n cause instanceof Error\n ? cause.message\n : \"Invalid ElevenLabs transcription options.\",\n provider: \"elevenlabs\",\n cause,\n });\n}\n\nfunction readText(value: Record<string, unknown>): string {\n if (typeof value[\"text\"] !== \"string\") {\n throw new TypeError(\"text must be a string.\");\n }\n return value[\"text\"];\n}\n\nfunction requireBrowserFunction(value: unknown, feature: string): void {\n if (typeof value !== \"function\") {\n throw new VoiceInputError({\n code: \"unsupported-browser\",\n message: `ElevenLabs voice input requires browser ${feature} support.`,\n provider: \"elevenlabs\",\n });\n }\n}\n\nfunction factoryString(value: string, name: string): string {\n if (value.trim().length === 0) {\n throw invalidConfiguration(new TypeError(`${name} must be non-empty.`));\n }\n return value;\n}\n\nfunction positiveInteger(value: number, name: string): number {\n if (!Number.isSafeInteger(value) || value <= 0) {\n throw invalidConfiguration(\n new TypeError(`${name} must be a positive safe integer.`),\n );\n }\n return value;\n}\n\ninterface Deferred<T> {\n readonly promise: Promise<T>;\n resolve(value: T | PromiseLike<T>): void;\n reject(reason?: unknown): void;\n}\n\nfunction createDeferred<T>(): Deferred<T> {\n let resolve: (value: T | PromiseLike<T>) => void = () => {};\n let reject: (reason?: unknown) => void = () => {};\n const promise = new Promise<T>((resolve_, reject_) => {\n resolve = resolve_;\n reject = reject_;\n });\n return { promise, resolve, reject };\n}\n\nasync function tokenResponseError(\n response: Response,\n): Promise<VoiceInputError> {\n const retryAfterMs = parseRetryAfter(response.headers.get(\"Retry-After\"));\n if (response.status === 401 || response.status === 403) {\n return new VoiceInputError({\n code: \"unauthorized\",\n message: \"The ElevenLabs token endpoint rejected this request.\",\n provider: \"elevenlabs\",\n });\n }\n if (response.status === 429) {\n return new VoiceInputError({\n code: \"rate-limited\",\n message: \"The ElevenLabs token endpoint rate limit was exceeded.\",\n provider: \"elevenlabs\",\n retryable: true,\n ...(retryAfterMs === undefined ? {} : { retryAfterMs }),\n });\n }\n const safeError = await readSafeTokenError(response);\n if (safeError !== undefined) {\n return new VoiceInputError({ ...safeError, provider: \"elevenlabs\" });\n }\n return new VoiceInputError({\n code: \"token-error\",\n message: \"The ElevenLabs token endpoint did not issue a token.\",\n provider: \"elevenlabs\",\n retryable: response.status >= 500,\n });\n}\n\nasync function readSafeTokenError(response: Response): Promise<\n | {\n code: \"invalid-configuration\" | \"unsupported-feature\";\n message: string;\n }\n | undefined\n> {\n if (\n response.status !== 400 ||\n response.headers.get(\"X-VoiceInput-Error\") !== \"1\" ||\n response.headers.get(\"Content-Type\")?.split(\";\", 1)[0]?.trim() !==\n \"application/json\"\n ) {\n return undefined;\n }\n const text = await readBoundedErrorText(response);\n if (text === undefined) return undefined;\n try {\n const value = JSON.parse(text) as unknown;\n const error = isRecord(value) ? value[\"error\"] : undefined;\n if (\n !isRecord(error) ||\n (error[\"code\"] !== \"invalid-configuration\" &&\n error[\"code\"] !== \"unsupported-feature\") ||\n typeof error[\"message\"] !== \"string\" ||\n error[\"message\"].length === 0 ||\n error[\"message\"].length > 1_000\n ) {\n return undefined;\n }\n return { code: error[\"code\"], message: error[\"message\"] };\n } catch {\n return undefined;\n }\n}\n\nasync function readBoundedErrorText(\n response: Response,\n): Promise<string | undefined> {\n const reader = response.body?.getReader();\n if (reader === undefined) return undefined;\n const decoder = new TextDecoder();\n let bytesRead = 0;\n let text = \"\";\n try {\n while (true) {\n const { done, value } = await reader.read();\n if (done) return text + decoder.decode();\n bytesRead += value.byteLength;\n if (bytesRead > 4_096) {\n await reader.cancel().catch(() => {});\n return undefined;\n }\n text += decoder.decode(value, { stream: true });\n }\n } catch {\n return undefined;\n }\n}\n\nfunction waitForOpen(\n socket: WebSocket,\n abortSignal: AbortSignal,\n): Promise<void> {\n if (socket.readyState === 1) {\n return Promise.resolve();\n }\n return new Promise((resolve, reject) => {\n const cleanup = (): void => {\n socket.removeEventListener(\"open\", handleOpen);\n socket.removeEventListener(\"error\", handleError);\n abortSignal.removeEventListener(\"abort\", handleAbort);\n };\n const handleOpen = (): void => {\n cleanup();\n resolve();\n };\n const handleError = (event: Event): void => {\n cleanup();\n reject(\n new VoiceInputError({\n code: \"network-error\",\n message: \"Unable to open the ElevenLabs Realtime connection.\",\n provider: \"elevenlabs\",\n retryable: true,\n cause: event,\n }),\n );\n };\n const handleAbort = (): void => {\n cleanup();\n reject(abortSignal.reason);\n };\n socket.addEventListener(\"open\", handleOpen, { once: true });\n socket.addEventListener(\"error\", handleError, { once: true });\n abortSignal.addEventListener(\"abort\", handleAbort, { once: true });\n });\n}\n\nfunction sendJson(socket: WebSocket, value: Record<string, unknown>): void {\n try {\n socket.send(JSON.stringify(value));\n } catch (cause) {\n throw new VoiceInputError({\n code: \"network-error\",\n message: \"Unable to send data to ElevenLabs Realtime.\",\n provider: \"elevenlabs\",\n retryable: true,\n cause,\n });\n }\n}\n\nfunction encodePcm16(chunk: Int16Array): string {\n const bytes = new Uint8Array(\n chunk.buffer,\n chunk.byteOffset,\n chunk.byteLength,\n );\n let binary = \"\";\n for (let offset = 0; offset < bytes.length; offset += 8_192) {\n binary += String.fromCharCode(...bytes.subarray(offset, offset + 8_192));\n }\n return btoa(binary);\n}\n\nfunction parseRetryAfter(value: string | null): number | undefined {\n if (value === null) {\n return undefined;\n }\n const seconds = Number(value);\n if (Number.isFinite(seconds) && seconds >= 0) {\n return Math.ceil(seconds * 1_000);\n }\n const date = Date.parse(value);\n return Number.isNaN(date) ? undefined : Math.max(0, date - Date.now());\n}\n\nfunction throwIfAborted(signal: AbortSignal): void {\n if (signal.aborted) {\n throw signal.reason;\n }\n}\n\nfunction nonEmpty(value: unknown): value is string {\n return typeof value === \"string\" && value.length > 0;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nexport { ELEVENLABS_DEFAULT_MODEL } from \"./session-config.js\";\n"],"mappings":"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"}
|
package/dist/server.cjs
ADDED
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
require("./session-config-D2GQf8TE.cjs");
|
|
3
|
+
//#region src/server.ts
|
|
4
|
+
const DEFAULT_TOKEN_URL = "https://api.elevenlabs.io/v1/single-use-token/realtime_scribe";
|
|
5
|
+
const MAX_TOKEN_REQUEST_BYTES = 16384;
|
|
6
|
+
function createElevenLabsTokenHandler(options) {
|
|
7
|
+
const defaultModel = nonEmpty(options.model ?? "scribe_v2_realtime", "model");
|
|
8
|
+
const allowedModels = new Set(options.allowedModels ?? [defaultModel]);
|
|
9
|
+
if (!allowedModels.has(defaultModel)) throw new TypeError("allowedModels must contain the default model.");
|
|
10
|
+
if (typeof options.authorize !== "function") throw new TypeError("authorize must be a function.");
|
|
11
|
+
const apiKey = nonEmpty(options.apiKey, "apiKey");
|
|
12
|
+
const fetchImplementation = options.fetch ?? globalThis.fetch;
|
|
13
|
+
const tokenUrl = options.tokenUrl ?? DEFAULT_TOKEN_URL;
|
|
14
|
+
return async (request) => {
|
|
15
|
+
if (request.method !== "POST") return jsonError(405, "invalid-request", "Method not allowed.", { Allow: "POST" });
|
|
16
|
+
try {
|
|
17
|
+
const requestBody = await readJsonBody(request);
|
|
18
|
+
const authorization = await options.authorize(copyRequest(request, requestBody));
|
|
19
|
+
if (authorization === null) return jsonError(401, "unauthorized", "Unauthorized.");
|
|
20
|
+
const subject = nonEmpty(authorization.subject, "subject");
|
|
21
|
+
const model = readModel(requestBody, defaultModel);
|
|
22
|
+
if (!allowedModels.has(model)) return jsonError(400, "invalid-configuration", "The requested ElevenLabs model is not allowed.");
|
|
23
|
+
const context = {
|
|
24
|
+
request: copyRequest(request, requestBody),
|
|
25
|
+
subject,
|
|
26
|
+
model
|
|
27
|
+
};
|
|
28
|
+
const limit = await options.rateLimit?.(context);
|
|
29
|
+
if (limit?.allowed === false) {
|
|
30
|
+
const retryAfter = normalizeRetryAfter(limit.retryAfterSeconds);
|
|
31
|
+
return jsonError(429, "rate-limited", "Rate limit exceeded.", retryAfter === void 0 ? {} : { "Retry-After": String(retryAfter) });
|
|
32
|
+
}
|
|
33
|
+
const response = await fetchImplementation(tokenUrl, {
|
|
34
|
+
method: "POST",
|
|
35
|
+
headers: { "xi-api-key": apiKey },
|
|
36
|
+
signal: request.signal
|
|
37
|
+
});
|
|
38
|
+
if (!response.ok) {
|
|
39
|
+
if (response.status === 429) return jsonError(429, "rate-limited", "ElevenLabs rate limit exceeded.", copyRetryAfter(response.headers));
|
|
40
|
+
return jsonError(502, "token-error", "ElevenLabs did not issue a single-use token.");
|
|
41
|
+
}
|
|
42
|
+
const token = validateToken(await response.json());
|
|
43
|
+
await options.onTokenIssued?.({
|
|
44
|
+
provider: "elevenlabs",
|
|
45
|
+
subject,
|
|
46
|
+
model
|
|
47
|
+
});
|
|
48
|
+
return Response.json(token, { headers: { "Cache-Control": "no-store" } });
|
|
49
|
+
} catch (error) {
|
|
50
|
+
if (request.signal.aborted) return jsonError(499, "network-error", "The request was cancelled.");
|
|
51
|
+
if (error instanceof InvalidTokenRequestError) return jsonError(error.status, error.code, error.message);
|
|
52
|
+
return jsonError(500, "token-error", "Unable to issue an ElevenLabs single-use token.");
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
function readModel(body, defaultModel) {
|
|
57
|
+
try {
|
|
58
|
+
if (body.length === 0) return defaultModel;
|
|
59
|
+
const value = JSON.parse(body);
|
|
60
|
+
if (!isRecord(value) || Object.keys(value).some((key) => key !== "model")) throw new TypeError("The token request must contain only model.");
|
|
61
|
+
return value["model"] === void 0 ? defaultModel : nonEmpty(value["model"], "model");
|
|
62
|
+
} catch (cause) {
|
|
63
|
+
if (cause instanceof SyntaxError || cause instanceof TypeError) throw new InvalidTokenRequestError(cause.message);
|
|
64
|
+
throw cause;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
function copyRequest(request, body) {
|
|
68
|
+
return new Request(request, {
|
|
69
|
+
method: "POST",
|
|
70
|
+
body,
|
|
71
|
+
referrer: request.referrer,
|
|
72
|
+
referrerPolicy: request.referrerPolicy
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
function validateToken(value) {
|
|
76
|
+
if (!isRecord(value) || typeof value["token"] !== "string" || value["token"].length === 0) throw new Error("ElevenLabs returned an invalid token.");
|
|
77
|
+
return { token: value["token"] };
|
|
78
|
+
}
|
|
79
|
+
var InvalidTokenRequestError = class extends Error {
|
|
80
|
+
code;
|
|
81
|
+
status;
|
|
82
|
+
constructor(message, code = "invalid-configuration", status = 400) {
|
|
83
|
+
super(message);
|
|
84
|
+
this.code = code;
|
|
85
|
+
this.status = status;
|
|
86
|
+
this.name = "InvalidTokenRequestError";
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
async function readJsonBody(request) {
|
|
90
|
+
if (request.headers.get("Content-Type")?.split(";", 1)[0]?.trim().toLowerCase() !== "application/json") throw new InvalidTokenRequestError("Content-Type must be application/json.", "invalid-request", 415);
|
|
91
|
+
if (Number(request.headers.get("Content-Length")) > MAX_TOKEN_REQUEST_BYTES) throw requestTooLarge();
|
|
92
|
+
const reader = request.body?.getReader();
|
|
93
|
+
if (reader === void 0) return "";
|
|
94
|
+
const decoder = new TextDecoder("utf-8", { fatal: true });
|
|
95
|
+
let bytesRead = 0;
|
|
96
|
+
let text = "";
|
|
97
|
+
while (true) {
|
|
98
|
+
const { done, value } = await reader.read();
|
|
99
|
+
if (done) break;
|
|
100
|
+
bytesRead += value.byteLength;
|
|
101
|
+
if (bytesRead > MAX_TOKEN_REQUEST_BYTES) {
|
|
102
|
+
await reader.cancel().catch(() => {});
|
|
103
|
+
throw requestTooLarge();
|
|
104
|
+
}
|
|
105
|
+
try {
|
|
106
|
+
text += decoder.decode(value, { stream: true });
|
|
107
|
+
} catch {
|
|
108
|
+
await reader.cancel().catch(() => {});
|
|
109
|
+
throw invalidUtf8();
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
try {
|
|
113
|
+
return text + decoder.decode();
|
|
114
|
+
} catch {
|
|
115
|
+
throw invalidUtf8();
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
function requestTooLarge() {
|
|
119
|
+
return new InvalidTokenRequestError(`Token request body exceeds ${MAX_TOKEN_REQUEST_BYTES} bytes.`, "invalid-request", 413);
|
|
120
|
+
}
|
|
121
|
+
function invalidUtf8() {
|
|
122
|
+
return new InvalidTokenRequestError("Token request body must be valid UTF-8.", "invalid-request");
|
|
123
|
+
}
|
|
124
|
+
function jsonError(status, code, message, headers = {}) {
|
|
125
|
+
const responseHeaders = new Headers(headers);
|
|
126
|
+
responseHeaders.set("Cache-Control", "no-store");
|
|
127
|
+
responseHeaders.set("X-VoiceInput-Error", "1");
|
|
128
|
+
return Response.json({ error: {
|
|
129
|
+
code,
|
|
130
|
+
message
|
|
131
|
+
} }, {
|
|
132
|
+
status,
|
|
133
|
+
headers: responseHeaders
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
function copyRetryAfter(headers) {
|
|
137
|
+
const value = headers.get("Retry-After");
|
|
138
|
+
return value === null ? {} : { "Retry-After": value };
|
|
139
|
+
}
|
|
140
|
+
function normalizeRetryAfter(value) {
|
|
141
|
+
return value === void 0 || !Number.isFinite(value) || value <= 0 ? void 0 : Math.ceil(value);
|
|
142
|
+
}
|
|
143
|
+
function nonEmpty(value, name) {
|
|
144
|
+
if (typeof value !== "string" || value.trim().length === 0) throw new TypeError(`${name} must be a non-empty string.`);
|
|
145
|
+
return value;
|
|
146
|
+
}
|
|
147
|
+
function isRecord(value) {
|
|
148
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
149
|
+
}
|
|
150
|
+
//#endregion
|
|
151
|
+
exports.createElevenLabsTokenHandler = createElevenLabsTokenHandler;
|
|
152
|
+
|
|
153
|
+
//# sourceMappingURL=server.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.cjs","names":[],"sources":["../src/server.ts"],"sourcesContent":["import { ELEVENLABS_DEFAULT_MODEL } from \"./session-config.js\";\n\nconst DEFAULT_TOKEN_URL =\n \"https://api.elevenlabs.io/v1/single-use-token/realtime_scribe\";\nconst MAX_TOKEN_REQUEST_BYTES = 16 * 1024;\n\nexport interface ElevenLabsAuthorization {\n readonly subject: string;\n}\n\nexport type ElevenLabsRateLimitResult =\n | { readonly allowed: true }\n | { readonly allowed: false; readonly retryAfterSeconds?: number };\n\nexport interface ElevenLabsTokenHandlerContext {\n readonly request: Request;\n readonly subject: string;\n readonly model: string;\n}\n\nexport interface ElevenLabsTokenIssuedMetadata {\n readonly provider: \"elevenlabs\";\n readonly subject: string;\n readonly model: string;\n}\n\nexport interface CreateElevenLabsTokenHandlerOptions {\n readonly apiKey: string;\n readonly authorize: (\n request: Request,\n ) =>\n | PromiseLike<ElevenLabsAuthorization | null>\n | ElevenLabsAuthorization\n | null;\n readonly model?: string;\n readonly allowedModels?: readonly string[];\n readonly rateLimit?: (\n context: ElevenLabsTokenHandlerContext,\n ) => PromiseLike<ElevenLabsRateLimitResult> | ElevenLabsRateLimitResult;\n readonly onTokenIssued?: (\n metadata: ElevenLabsTokenIssuedMetadata,\n ) => PromiseLike<void> | void;\n readonly fetch?: typeof globalThis.fetch;\n readonly tokenUrl?: string;\n}\n\nexport function createElevenLabsTokenHandler(\n options: CreateElevenLabsTokenHandlerOptions,\n): (request: Request) => Promise<Response> {\n const defaultModel = nonEmpty(\n options.model ?? ELEVENLABS_DEFAULT_MODEL,\n \"model\",\n );\n const allowedModels = new Set(options.allowedModels ?? [defaultModel]);\n if (!allowedModels.has(defaultModel)) {\n throw new TypeError(\"allowedModels must contain the default model.\");\n }\n if (typeof options.authorize !== \"function\") {\n throw new TypeError(\"authorize must be a function.\");\n }\n const apiKey = nonEmpty(options.apiKey, \"apiKey\");\n const fetchImplementation = options.fetch ?? globalThis.fetch;\n const tokenUrl = options.tokenUrl ?? DEFAULT_TOKEN_URL;\n\n return async (request) => {\n if (request.method !== \"POST\") {\n return jsonError(405, \"invalid-request\", \"Method not allowed.\", {\n Allow: \"POST\",\n });\n }\n try {\n const requestBody = await readJsonBody(request);\n const authorization = await options.authorize(\n copyRequest(request, requestBody),\n );\n if (authorization === null) {\n return jsonError(401, \"unauthorized\", \"Unauthorized.\");\n }\n const subject = nonEmpty(authorization.subject, \"subject\");\n const model = readModel(requestBody, defaultModel);\n if (!allowedModels.has(model)) {\n return jsonError(\n 400,\n \"invalid-configuration\",\n \"The requested ElevenLabs model is not allowed.\",\n );\n }\n const context: ElevenLabsTokenHandlerContext = {\n request: copyRequest(request, requestBody),\n subject,\n model,\n };\n const limit = await options.rateLimit?.(context);\n if (limit?.allowed === false) {\n const retryAfter = normalizeRetryAfter(limit.retryAfterSeconds);\n return jsonError(\n 429,\n \"rate-limited\",\n \"Rate limit exceeded.\",\n retryAfter === undefined ? {} : { \"Retry-After\": String(retryAfter) },\n );\n }\n const response = await fetchImplementation(tokenUrl, {\n method: \"POST\",\n headers: { \"xi-api-key\": apiKey },\n signal: request.signal,\n });\n if (!response.ok) {\n if (response.status === 429) {\n return jsonError(\n 429,\n \"rate-limited\",\n \"ElevenLabs rate limit exceeded.\",\n copyRetryAfter(response.headers),\n );\n }\n return jsonError(\n 502,\n \"token-error\",\n \"ElevenLabs did not issue a single-use token.\",\n );\n }\n const token = validateToken(await response.json());\n await options.onTokenIssued?.({\n provider: \"elevenlabs\",\n subject,\n model,\n });\n return Response.json(token, {\n headers: { \"Cache-Control\": \"no-store\" },\n });\n } catch (error) {\n if (request.signal.aborted) {\n return jsonError(499, \"network-error\", \"The request was cancelled.\");\n }\n if (error instanceof InvalidTokenRequestError) {\n return jsonError(error.status, error.code, error.message);\n }\n return jsonError(\n 500,\n \"token-error\",\n \"Unable to issue an ElevenLabs single-use token.\",\n );\n }\n };\n}\n\nfunction readModel(body: string, defaultModel: string): string {\n try {\n if (body.length === 0) {\n return defaultModel;\n }\n const value = JSON.parse(body) as unknown;\n if (!isRecord(value) || Object.keys(value).some((key) => key !== \"model\")) {\n throw new TypeError(\"The token request must contain only model.\");\n }\n return value[\"model\"] === undefined\n ? defaultModel\n : nonEmpty(value[\"model\"], \"model\");\n } catch (cause) {\n if (cause instanceof SyntaxError || cause instanceof TypeError) {\n throw new InvalidTokenRequestError(cause.message);\n }\n throw cause;\n }\n}\n\nfunction copyRequest(request: Request, body: string): Request {\n return new Request(request, {\n method: \"POST\",\n body,\n referrer: request.referrer,\n referrerPolicy: request.referrerPolicy,\n });\n}\n\nfunction validateToken(value: unknown): { token: string } {\n if (\n !isRecord(value) ||\n typeof value[\"token\"] !== \"string\" ||\n value[\"token\"].length === 0\n ) {\n throw new Error(\"ElevenLabs returned an invalid token.\");\n }\n return { token: value[\"token\"] };\n}\n\nclass InvalidTokenRequestError extends Error {\n constructor(\n message: string,\n readonly code:\n \"invalid-configuration\" | \"invalid-request\" = \"invalid-configuration\",\n readonly status = 400,\n ) {\n super(message);\n this.name = \"InvalidTokenRequestError\";\n }\n}\n\nasync function readJsonBody(request: Request): Promise<string> {\n const contentType = request.headers\n .get(\"Content-Type\")\n ?.split(\";\", 1)[0]\n ?.trim()\n .toLowerCase();\n if (contentType !== \"application/json\") {\n throw new InvalidTokenRequestError(\n \"Content-Type must be application/json.\",\n \"invalid-request\",\n 415,\n );\n }\n const declaredLength = Number(request.headers.get(\"Content-Length\"));\n if (declaredLength > MAX_TOKEN_REQUEST_BYTES) throw requestTooLarge();\n const reader = request.body?.getReader();\n if (reader === undefined) return \"\";\n const decoder = new TextDecoder(\"utf-8\", { fatal: true });\n let bytesRead = 0;\n let text = \"\";\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n bytesRead += value.byteLength;\n if (bytesRead > MAX_TOKEN_REQUEST_BYTES) {\n await reader.cancel().catch(() => {});\n throw requestTooLarge();\n }\n try {\n text += decoder.decode(value, { stream: true });\n } catch {\n await reader.cancel().catch(() => {});\n throw invalidUtf8();\n }\n }\n try {\n return text + decoder.decode();\n } catch {\n throw invalidUtf8();\n }\n}\n\nfunction requestTooLarge(): InvalidTokenRequestError {\n return new InvalidTokenRequestError(\n `Token request body exceeds ${MAX_TOKEN_REQUEST_BYTES} bytes.`,\n \"invalid-request\",\n 413,\n );\n}\n\nfunction invalidUtf8(): InvalidTokenRequestError {\n return new InvalidTokenRequestError(\n \"Token request body must be valid UTF-8.\",\n \"invalid-request\",\n );\n}\n\nfunction jsonError(\n status: number,\n code: string,\n message: string,\n headers: HeadersInit = {},\n): Response {\n const responseHeaders = new Headers(headers);\n responseHeaders.set(\"Cache-Control\", \"no-store\");\n responseHeaders.set(\"X-VoiceInput-Error\", \"1\");\n return Response.json(\n { error: { code, message } },\n { status, headers: responseHeaders },\n );\n}\n\nfunction copyRetryAfter(headers: Headers): HeadersInit {\n const value = headers.get(\"Retry-After\");\n return value === null ? {} : { \"Retry-After\": value };\n}\n\nfunction normalizeRetryAfter(value: number | undefined): number | undefined {\n return value === undefined || !Number.isFinite(value) || value <= 0\n ? undefined\n : Math.ceil(value);\n}\n\nfunction nonEmpty(value: unknown, name: string): string {\n if (typeof value !== \"string\" || value.trim().length === 0) {\n throw new TypeError(`${name} must be a non-empty string.`);\n }\n return value;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
//#region src/server.d.ts
|
|
2
|
+
interface ElevenLabsAuthorization {
|
|
3
|
+
readonly subject: string;
|
|
4
|
+
}
|
|
5
|
+
type ElevenLabsRateLimitResult = {
|
|
6
|
+
readonly allowed: true;
|
|
7
|
+
} | {
|
|
8
|
+
readonly allowed: false;
|
|
9
|
+
readonly retryAfterSeconds?: number;
|
|
10
|
+
};
|
|
11
|
+
interface ElevenLabsTokenHandlerContext {
|
|
12
|
+
readonly request: Request;
|
|
13
|
+
readonly subject: string;
|
|
14
|
+
readonly model: string;
|
|
15
|
+
}
|
|
16
|
+
interface ElevenLabsTokenIssuedMetadata {
|
|
17
|
+
readonly provider: "elevenlabs";
|
|
18
|
+
readonly subject: string;
|
|
19
|
+
readonly model: string;
|
|
20
|
+
}
|
|
21
|
+
interface CreateElevenLabsTokenHandlerOptions {
|
|
22
|
+
readonly apiKey: string;
|
|
23
|
+
readonly authorize: (request: Request) => PromiseLike<ElevenLabsAuthorization | null> | ElevenLabsAuthorization | null;
|
|
24
|
+
readonly model?: string;
|
|
25
|
+
readonly allowedModels?: readonly string[];
|
|
26
|
+
readonly rateLimit?: (context: ElevenLabsTokenHandlerContext) => PromiseLike<ElevenLabsRateLimitResult> | ElevenLabsRateLimitResult;
|
|
27
|
+
readonly onTokenIssued?: (metadata: ElevenLabsTokenIssuedMetadata) => PromiseLike<void> | void;
|
|
28
|
+
readonly fetch?: typeof globalThis.fetch;
|
|
29
|
+
readonly tokenUrl?: string;
|
|
30
|
+
}
|
|
31
|
+
declare function createElevenLabsTokenHandler(options: CreateElevenLabsTokenHandlerOptions): (request: Request) => Promise<Response>;
|
|
32
|
+
//#endregion
|
|
33
|
+
export { CreateElevenLabsTokenHandlerOptions, ElevenLabsAuthorization, ElevenLabsRateLimitResult, ElevenLabsTokenHandlerContext, ElevenLabsTokenIssuedMetadata, createElevenLabsTokenHandler };
|
|
34
|
+
//# sourceMappingURL=server.d.cts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.d.cts","names":[],"sources":["../src/server.ts"],"mappings":";UAMiB;WACN;;KAGC;WACG;;WACA;WAAyB;;UAEvB;WACN,SAAS;WACT;WACA;;UAGM;WACN;WACA;WACA;;UAGM;WACN;WACA,YACP,SAAS,YAEP,YAAY,kCACZ;WAEK;WACA;WACA,aACP,SAAS,kCACN,YAAY,6BAA6B;WACrC,iBACP,UAAU,kCACP;WACI,eAAe,WAAW;WAC1B;;iBAGK,6BACd,SAAS,uCACP,SAAS,YAAY,QAAQ"}
|
package/dist/server.d.ts
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
//#region src/server.d.ts
|
|
2
|
+
interface ElevenLabsAuthorization {
|
|
3
|
+
readonly subject: string;
|
|
4
|
+
}
|
|
5
|
+
type ElevenLabsRateLimitResult = {
|
|
6
|
+
readonly allowed: true;
|
|
7
|
+
} | {
|
|
8
|
+
readonly allowed: false;
|
|
9
|
+
readonly retryAfterSeconds?: number;
|
|
10
|
+
};
|
|
11
|
+
interface ElevenLabsTokenHandlerContext {
|
|
12
|
+
readonly request: Request;
|
|
13
|
+
readonly subject: string;
|
|
14
|
+
readonly model: string;
|
|
15
|
+
}
|
|
16
|
+
interface ElevenLabsTokenIssuedMetadata {
|
|
17
|
+
readonly provider: "elevenlabs";
|
|
18
|
+
readonly subject: string;
|
|
19
|
+
readonly model: string;
|
|
20
|
+
}
|
|
21
|
+
interface CreateElevenLabsTokenHandlerOptions {
|
|
22
|
+
readonly apiKey: string;
|
|
23
|
+
readonly authorize: (request: Request) => PromiseLike<ElevenLabsAuthorization | null> | ElevenLabsAuthorization | null;
|
|
24
|
+
readonly model?: string;
|
|
25
|
+
readonly allowedModels?: readonly string[];
|
|
26
|
+
readonly rateLimit?: (context: ElevenLabsTokenHandlerContext) => PromiseLike<ElevenLabsRateLimitResult> | ElevenLabsRateLimitResult;
|
|
27
|
+
readonly onTokenIssued?: (metadata: ElevenLabsTokenIssuedMetadata) => PromiseLike<void> | void;
|
|
28
|
+
readonly fetch?: typeof globalThis.fetch;
|
|
29
|
+
readonly tokenUrl?: string;
|
|
30
|
+
}
|
|
31
|
+
declare function createElevenLabsTokenHandler(options: CreateElevenLabsTokenHandlerOptions): (request: Request) => Promise<Response>;
|
|
32
|
+
//#endregion
|
|
33
|
+
export { CreateElevenLabsTokenHandlerOptions, ElevenLabsAuthorization, ElevenLabsRateLimitResult, ElevenLabsTokenHandlerContext, ElevenLabsTokenIssuedMetadata, createElevenLabsTokenHandler };
|
|
34
|
+
//# sourceMappingURL=server.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.d.ts","names":[],"sources":["../src/server.ts"],"mappings":";UAMiB;WACN;;KAGC;WACG;;WACA;WAAyB;;UAEvB;WACN,SAAS;WACT;WACA;;UAGM;WACN;WACA;WACA;;UAGM;WACN;WACA,YACP,SAAS,YAEP,YAAY,kCACZ;WAEK;WACA;WACA,aACP,SAAS,kCACN,YAAY,6BAA6B;WACrC,iBACP,UAAU,kCACP;WACI,eAAe,WAAW;WAC1B;;iBAGK,6BACd,SAAS,uCACP,SAAS,YAAY,QAAQ"}
|
package/dist/server.js
ADDED
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import "./session-config-DEaQLtZY.js";
|
|
2
|
+
//#region src/server.ts
|
|
3
|
+
const DEFAULT_TOKEN_URL = "https://api.elevenlabs.io/v1/single-use-token/realtime_scribe";
|
|
4
|
+
const MAX_TOKEN_REQUEST_BYTES = 16384;
|
|
5
|
+
function createElevenLabsTokenHandler(options) {
|
|
6
|
+
const defaultModel = nonEmpty(options.model ?? "scribe_v2_realtime", "model");
|
|
7
|
+
const allowedModels = new Set(options.allowedModels ?? [defaultModel]);
|
|
8
|
+
if (!allowedModels.has(defaultModel)) throw new TypeError("allowedModels must contain the default model.");
|
|
9
|
+
if (typeof options.authorize !== "function") throw new TypeError("authorize must be a function.");
|
|
10
|
+
const apiKey = nonEmpty(options.apiKey, "apiKey");
|
|
11
|
+
const fetchImplementation = options.fetch ?? globalThis.fetch;
|
|
12
|
+
const tokenUrl = options.tokenUrl ?? DEFAULT_TOKEN_URL;
|
|
13
|
+
return async (request) => {
|
|
14
|
+
if (request.method !== "POST") return jsonError(405, "invalid-request", "Method not allowed.", { Allow: "POST" });
|
|
15
|
+
try {
|
|
16
|
+
const requestBody = await readJsonBody(request);
|
|
17
|
+
const authorization = await options.authorize(copyRequest(request, requestBody));
|
|
18
|
+
if (authorization === null) return jsonError(401, "unauthorized", "Unauthorized.");
|
|
19
|
+
const subject = nonEmpty(authorization.subject, "subject");
|
|
20
|
+
const model = readModel(requestBody, defaultModel);
|
|
21
|
+
if (!allowedModels.has(model)) return jsonError(400, "invalid-configuration", "The requested ElevenLabs model is not allowed.");
|
|
22
|
+
const context = {
|
|
23
|
+
request: copyRequest(request, requestBody),
|
|
24
|
+
subject,
|
|
25
|
+
model
|
|
26
|
+
};
|
|
27
|
+
const limit = await options.rateLimit?.(context);
|
|
28
|
+
if (limit?.allowed === false) {
|
|
29
|
+
const retryAfter = normalizeRetryAfter(limit.retryAfterSeconds);
|
|
30
|
+
return jsonError(429, "rate-limited", "Rate limit exceeded.", retryAfter === void 0 ? {} : { "Retry-After": String(retryAfter) });
|
|
31
|
+
}
|
|
32
|
+
const response = await fetchImplementation(tokenUrl, {
|
|
33
|
+
method: "POST",
|
|
34
|
+
headers: { "xi-api-key": apiKey },
|
|
35
|
+
signal: request.signal
|
|
36
|
+
});
|
|
37
|
+
if (!response.ok) {
|
|
38
|
+
if (response.status === 429) return jsonError(429, "rate-limited", "ElevenLabs rate limit exceeded.", copyRetryAfter(response.headers));
|
|
39
|
+
return jsonError(502, "token-error", "ElevenLabs did not issue a single-use token.");
|
|
40
|
+
}
|
|
41
|
+
const token = validateToken(await response.json());
|
|
42
|
+
await options.onTokenIssued?.({
|
|
43
|
+
provider: "elevenlabs",
|
|
44
|
+
subject,
|
|
45
|
+
model
|
|
46
|
+
});
|
|
47
|
+
return Response.json(token, { headers: { "Cache-Control": "no-store" } });
|
|
48
|
+
} catch (error) {
|
|
49
|
+
if (request.signal.aborted) return jsonError(499, "network-error", "The request was cancelled.");
|
|
50
|
+
if (error instanceof InvalidTokenRequestError) return jsonError(error.status, error.code, error.message);
|
|
51
|
+
return jsonError(500, "token-error", "Unable to issue an ElevenLabs single-use token.");
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
function readModel(body, defaultModel) {
|
|
56
|
+
try {
|
|
57
|
+
if (body.length === 0) return defaultModel;
|
|
58
|
+
const value = JSON.parse(body);
|
|
59
|
+
if (!isRecord(value) || Object.keys(value).some((key) => key !== "model")) throw new TypeError("The token request must contain only model.");
|
|
60
|
+
return value["model"] === void 0 ? defaultModel : nonEmpty(value["model"], "model");
|
|
61
|
+
} catch (cause) {
|
|
62
|
+
if (cause instanceof SyntaxError || cause instanceof TypeError) throw new InvalidTokenRequestError(cause.message);
|
|
63
|
+
throw cause;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
function copyRequest(request, body) {
|
|
67
|
+
return new Request(request, {
|
|
68
|
+
method: "POST",
|
|
69
|
+
body,
|
|
70
|
+
referrer: request.referrer,
|
|
71
|
+
referrerPolicy: request.referrerPolicy
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
function validateToken(value) {
|
|
75
|
+
if (!isRecord(value) || typeof value["token"] !== "string" || value["token"].length === 0) throw new Error("ElevenLabs returned an invalid token.");
|
|
76
|
+
return { token: value["token"] };
|
|
77
|
+
}
|
|
78
|
+
var InvalidTokenRequestError = class extends Error {
|
|
79
|
+
code;
|
|
80
|
+
status;
|
|
81
|
+
constructor(message, code = "invalid-configuration", status = 400) {
|
|
82
|
+
super(message);
|
|
83
|
+
this.code = code;
|
|
84
|
+
this.status = status;
|
|
85
|
+
this.name = "InvalidTokenRequestError";
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
async function readJsonBody(request) {
|
|
89
|
+
if (request.headers.get("Content-Type")?.split(";", 1)[0]?.trim().toLowerCase() !== "application/json") throw new InvalidTokenRequestError("Content-Type must be application/json.", "invalid-request", 415);
|
|
90
|
+
if (Number(request.headers.get("Content-Length")) > MAX_TOKEN_REQUEST_BYTES) throw requestTooLarge();
|
|
91
|
+
const reader = request.body?.getReader();
|
|
92
|
+
if (reader === void 0) return "";
|
|
93
|
+
const decoder = new TextDecoder("utf-8", { fatal: true });
|
|
94
|
+
let bytesRead = 0;
|
|
95
|
+
let text = "";
|
|
96
|
+
while (true) {
|
|
97
|
+
const { done, value } = await reader.read();
|
|
98
|
+
if (done) break;
|
|
99
|
+
bytesRead += value.byteLength;
|
|
100
|
+
if (bytesRead > MAX_TOKEN_REQUEST_BYTES) {
|
|
101
|
+
await reader.cancel().catch(() => {});
|
|
102
|
+
throw requestTooLarge();
|
|
103
|
+
}
|
|
104
|
+
try {
|
|
105
|
+
text += decoder.decode(value, { stream: true });
|
|
106
|
+
} catch {
|
|
107
|
+
await reader.cancel().catch(() => {});
|
|
108
|
+
throw invalidUtf8();
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
try {
|
|
112
|
+
return text + decoder.decode();
|
|
113
|
+
} catch {
|
|
114
|
+
throw invalidUtf8();
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
function requestTooLarge() {
|
|
118
|
+
return new InvalidTokenRequestError(`Token request body exceeds ${MAX_TOKEN_REQUEST_BYTES} bytes.`, "invalid-request", 413);
|
|
119
|
+
}
|
|
120
|
+
function invalidUtf8() {
|
|
121
|
+
return new InvalidTokenRequestError("Token request body must be valid UTF-8.", "invalid-request");
|
|
122
|
+
}
|
|
123
|
+
function jsonError(status, code, message, headers = {}) {
|
|
124
|
+
const responseHeaders = new Headers(headers);
|
|
125
|
+
responseHeaders.set("Cache-Control", "no-store");
|
|
126
|
+
responseHeaders.set("X-VoiceInput-Error", "1");
|
|
127
|
+
return Response.json({ error: {
|
|
128
|
+
code,
|
|
129
|
+
message
|
|
130
|
+
} }, {
|
|
131
|
+
status,
|
|
132
|
+
headers: responseHeaders
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
function copyRetryAfter(headers) {
|
|
136
|
+
const value = headers.get("Retry-After");
|
|
137
|
+
return value === null ? {} : { "Retry-After": value };
|
|
138
|
+
}
|
|
139
|
+
function normalizeRetryAfter(value) {
|
|
140
|
+
return value === void 0 || !Number.isFinite(value) || value <= 0 ? void 0 : Math.ceil(value);
|
|
141
|
+
}
|
|
142
|
+
function nonEmpty(value, name) {
|
|
143
|
+
if (typeof value !== "string" || value.trim().length === 0) throw new TypeError(`${name} must be a non-empty string.`);
|
|
144
|
+
return value;
|
|
145
|
+
}
|
|
146
|
+
function isRecord(value) {
|
|
147
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
148
|
+
}
|
|
149
|
+
//#endregion
|
|
150
|
+
export { createElevenLabsTokenHandler };
|
|
151
|
+
|
|
152
|
+
//# sourceMappingURL=server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.js","names":[],"sources":["../src/server.ts"],"sourcesContent":["import { ELEVENLABS_DEFAULT_MODEL } from \"./session-config.js\";\n\nconst DEFAULT_TOKEN_URL =\n \"https://api.elevenlabs.io/v1/single-use-token/realtime_scribe\";\nconst MAX_TOKEN_REQUEST_BYTES = 16 * 1024;\n\nexport interface ElevenLabsAuthorization {\n readonly subject: string;\n}\n\nexport type ElevenLabsRateLimitResult =\n | { readonly allowed: true }\n | { readonly allowed: false; readonly retryAfterSeconds?: number };\n\nexport interface ElevenLabsTokenHandlerContext {\n readonly request: Request;\n readonly subject: string;\n readonly model: string;\n}\n\nexport interface ElevenLabsTokenIssuedMetadata {\n readonly provider: \"elevenlabs\";\n readonly subject: string;\n readonly model: string;\n}\n\nexport interface CreateElevenLabsTokenHandlerOptions {\n readonly apiKey: string;\n readonly authorize: (\n request: Request,\n ) =>\n | PromiseLike<ElevenLabsAuthorization | null>\n | ElevenLabsAuthorization\n | null;\n readonly model?: string;\n readonly allowedModels?: readonly string[];\n readonly rateLimit?: (\n context: ElevenLabsTokenHandlerContext,\n ) => PromiseLike<ElevenLabsRateLimitResult> | ElevenLabsRateLimitResult;\n readonly onTokenIssued?: (\n metadata: ElevenLabsTokenIssuedMetadata,\n ) => PromiseLike<void> | void;\n readonly fetch?: typeof globalThis.fetch;\n readonly tokenUrl?: string;\n}\n\nexport function createElevenLabsTokenHandler(\n options: CreateElevenLabsTokenHandlerOptions,\n): (request: Request) => Promise<Response> {\n const defaultModel = nonEmpty(\n options.model ?? ELEVENLABS_DEFAULT_MODEL,\n \"model\",\n );\n const allowedModels = new Set(options.allowedModels ?? [defaultModel]);\n if (!allowedModels.has(defaultModel)) {\n throw new TypeError(\"allowedModels must contain the default model.\");\n }\n if (typeof options.authorize !== \"function\") {\n throw new TypeError(\"authorize must be a function.\");\n }\n const apiKey = nonEmpty(options.apiKey, \"apiKey\");\n const fetchImplementation = options.fetch ?? globalThis.fetch;\n const tokenUrl = options.tokenUrl ?? DEFAULT_TOKEN_URL;\n\n return async (request) => {\n if (request.method !== \"POST\") {\n return jsonError(405, \"invalid-request\", \"Method not allowed.\", {\n Allow: \"POST\",\n });\n }\n try {\n const requestBody = await readJsonBody(request);\n const authorization = await options.authorize(\n copyRequest(request, requestBody),\n );\n if (authorization === null) {\n return jsonError(401, \"unauthorized\", \"Unauthorized.\");\n }\n const subject = nonEmpty(authorization.subject, \"subject\");\n const model = readModel(requestBody, defaultModel);\n if (!allowedModels.has(model)) {\n return jsonError(\n 400,\n \"invalid-configuration\",\n \"The requested ElevenLabs model is not allowed.\",\n );\n }\n const context: ElevenLabsTokenHandlerContext = {\n request: copyRequest(request, requestBody),\n subject,\n model,\n };\n const limit = await options.rateLimit?.(context);\n if (limit?.allowed === false) {\n const retryAfter = normalizeRetryAfter(limit.retryAfterSeconds);\n return jsonError(\n 429,\n \"rate-limited\",\n \"Rate limit exceeded.\",\n retryAfter === undefined ? {} : { \"Retry-After\": String(retryAfter) },\n );\n }\n const response = await fetchImplementation(tokenUrl, {\n method: \"POST\",\n headers: { \"xi-api-key\": apiKey },\n signal: request.signal,\n });\n if (!response.ok) {\n if (response.status === 429) {\n return jsonError(\n 429,\n \"rate-limited\",\n \"ElevenLabs rate limit exceeded.\",\n copyRetryAfter(response.headers),\n );\n }\n return jsonError(\n 502,\n \"token-error\",\n \"ElevenLabs did not issue a single-use token.\",\n );\n }\n const token = validateToken(await response.json());\n await options.onTokenIssued?.({\n provider: \"elevenlabs\",\n subject,\n model,\n });\n return Response.json(token, {\n headers: { \"Cache-Control\": \"no-store\" },\n });\n } catch (error) {\n if (request.signal.aborted) {\n return jsonError(499, \"network-error\", \"The request was cancelled.\");\n }\n if (error instanceof InvalidTokenRequestError) {\n return jsonError(error.status, error.code, error.message);\n }\n return jsonError(\n 500,\n \"token-error\",\n \"Unable to issue an ElevenLabs single-use token.\",\n );\n }\n };\n}\n\nfunction readModel(body: string, defaultModel: string): string {\n try {\n if (body.length === 0) {\n return defaultModel;\n }\n const value = JSON.parse(body) as unknown;\n if (!isRecord(value) || Object.keys(value).some((key) => key !== \"model\")) {\n throw new TypeError(\"The token request must contain only model.\");\n }\n return value[\"model\"] === undefined\n ? defaultModel\n : nonEmpty(value[\"model\"], \"model\");\n } catch (cause) {\n if (cause instanceof SyntaxError || cause instanceof TypeError) {\n throw new InvalidTokenRequestError(cause.message);\n }\n throw cause;\n }\n}\n\nfunction copyRequest(request: Request, body: string): Request {\n return new Request(request, {\n method: \"POST\",\n body,\n referrer: request.referrer,\n referrerPolicy: request.referrerPolicy,\n });\n}\n\nfunction validateToken(value: unknown): { token: string } {\n if (\n !isRecord(value) ||\n typeof value[\"token\"] !== \"string\" ||\n value[\"token\"].length === 0\n ) {\n throw new Error(\"ElevenLabs returned an invalid token.\");\n }\n return { token: value[\"token\"] };\n}\n\nclass InvalidTokenRequestError extends Error {\n constructor(\n message: string,\n readonly code:\n \"invalid-configuration\" | \"invalid-request\" = \"invalid-configuration\",\n readonly status = 400,\n ) {\n super(message);\n this.name = \"InvalidTokenRequestError\";\n }\n}\n\nasync function readJsonBody(request: Request): Promise<string> {\n const contentType = request.headers\n .get(\"Content-Type\")\n ?.split(\";\", 1)[0]\n ?.trim()\n .toLowerCase();\n if (contentType !== \"application/json\") {\n throw new InvalidTokenRequestError(\n \"Content-Type must be application/json.\",\n \"invalid-request\",\n 415,\n );\n }\n const declaredLength = Number(request.headers.get(\"Content-Length\"));\n if (declaredLength > MAX_TOKEN_REQUEST_BYTES) throw requestTooLarge();\n const reader = request.body?.getReader();\n if (reader === undefined) return \"\";\n const decoder = new TextDecoder(\"utf-8\", { fatal: true });\n let bytesRead = 0;\n let text = \"\";\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n bytesRead += value.byteLength;\n if (bytesRead > MAX_TOKEN_REQUEST_BYTES) {\n await reader.cancel().catch(() => {});\n throw requestTooLarge();\n }\n try {\n text += decoder.decode(value, { stream: true });\n } catch {\n await reader.cancel().catch(() => {});\n throw invalidUtf8();\n }\n }\n try {\n return text + decoder.decode();\n } catch {\n throw invalidUtf8();\n }\n}\n\nfunction requestTooLarge(): InvalidTokenRequestError {\n return new InvalidTokenRequestError(\n `Token request body exceeds ${MAX_TOKEN_REQUEST_BYTES} bytes.`,\n \"invalid-request\",\n 413,\n );\n}\n\nfunction invalidUtf8(): InvalidTokenRequestError {\n return new InvalidTokenRequestError(\n \"Token request body must be valid UTF-8.\",\n \"invalid-request\",\n );\n}\n\nfunction jsonError(\n status: number,\n code: string,\n message: string,\n headers: HeadersInit = {},\n): Response {\n const responseHeaders = new Headers(headers);\n responseHeaders.set(\"Cache-Control\", \"no-store\");\n responseHeaders.set(\"X-VoiceInput-Error\", \"1\");\n return Response.json(\n { error: { code, message } },\n { status, headers: responseHeaders },\n );\n}\n\nfunction copyRetryAfter(headers: Headers): HeadersInit {\n const value = headers.get(\"Retry-After\");\n return value === null ? {} : { \"Retry-After\": value };\n}\n\nfunction normalizeRetryAfter(value: number | undefined): number | undefined {\n return value === undefined || !Number.isFinite(value) || value <= 0\n ? undefined\n : Math.ceil(value);\n}\n\nfunction nonEmpty(value: unknown, name: string): string {\n if (typeof value !== \"string\" || value.trim().length === 0) {\n throw new TypeError(`${name} must be a non-empty string.`);\n }\n return value;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n"],"mappings":"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"}
|