@pie-players/tts-client-server 0.3.44 → 0.3.45

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/README.md CHANGED
@@ -2,6 +2,10 @@
2
2
 
3
3
  Client-side TTS provider that calls a server API for synthesis with speech marks support.
4
4
 
5
+ For the cross-package TTS architecture and provider layering, see
6
+ [TTS Architecture](../../docs/accessibility/tts-architecture.md). This README
7
+ focuses on the browser-side server provider API.
8
+
5
9
  ## Overview
6
10
 
7
11
  This package provides a browser-side TTS provider that offloads synthesis to a server API. The server handles provider selection (AWS Polly, Google Cloud TTS, etc.) and credential management, while the client plays audio and coordinates word highlighting.
@@ -94,6 +94,24 @@ const MAX_TEXT_LENGTH_BY_MODE = {
94
94
  pie: 3000,
95
95
  custom: 3000,
96
96
  };
97
+ /**
98
+ * Server providers whose SSML support is reliable enough to receive generated
99
+ * SSML (AWS Polly, Google Cloud TTS). This is an allow-list rather than a
100
+ * deny-list: the `custom` transport (and unknown providers) report
101
+ * `supportsSSML: false` so the toolkit falls back to plain text. Some custom
102
+ * backends advertise SSML support server-side but mis-voice or ignore parts of
103
+ * it, so we stay conservative at the client boundary.
104
+ */
105
+ const SSML_CAPABLE_PIE_PROVIDERS = new Set(["polly", "google"]);
106
+ const resolveSupportsSSML = (config) => {
107
+ if (!config)
108
+ return false;
109
+ if (resolveTransportMode(config) !== "pie")
110
+ return false;
111
+ // The pie transport defaults to Polly when no provider is named.
112
+ const provider = (config.provider || "polly").toLowerCase();
113
+ return SSML_CAPABLE_PIE_PROVIDERS.has(provider);
114
+ };
97
115
  const trimTrailingSlash = (value) => value.replace(/\/+$/, "");
98
116
  const resolveVoicesValidationUrl = (config) => {
99
117
  const base = trimTrailingSlash(config.apiEndpoint);
@@ -369,21 +387,11 @@ class ServerTTSProviderImpl {
369
387
  URL.revokeObjectURL(audioUrl);
370
388
  return;
371
389
  }
372
- // Adjust word timing for playback rate
373
- // Speech marks are at 1.0x speed, so we need to scale them
374
- const playbackRate = this.config.rate || 1.0;
375
- this.wordTimings = wordTimings.map((timing) => ({
376
- ...timing,
377
- time: timing.time / playbackRate,
378
- }));
390
+ this.wordTimings = wordTimings;
379
391
  return new Promise((resolve, reject) => {
380
392
  // Create audio element
381
393
  const audio = new Audio(audioUrl);
382
394
  this.currentAudio = audio;
383
- // Apply rate from config
384
- if (this.config.rate) {
385
- audio.playbackRate = Math.max(0.25, Math.min(4.0, this.config.rate));
386
- }
387
395
  // Apply volume from config
388
396
  if (this.config.volume !== undefined) {
389
397
  audio.volume = Math.max(0, Math.min(1, this.config.volume));
@@ -468,9 +476,27 @@ class ServerTTSProviderImpl {
468
476
  }
469
477
  })();
470
478
  if (!response.ok) {
471
- const errorData = await response.json().catch(() => ({}));
472
- const errorMessage = errorData.message ||
473
- errorData.error?.message ||
479
+ const rawBody = await response.text().catch(() => "");
480
+ let errorData = {};
481
+ if (rawBody) {
482
+ try {
483
+ errorData = JSON.parse(rawBody);
484
+ }
485
+ catch (parseError) {
486
+ console.warn("[ServerTTSProvider] non-OK response body was not JSON", {
487
+ status: response.status,
488
+ url: synthUrl,
489
+ rawBodyPreview: rawBody.slice(0, 500),
490
+ parseError: parseError instanceof Error
491
+ ? parseError.message
492
+ : String(parseError),
493
+ });
494
+ }
495
+ }
496
+ const errorMessage = (typeof errorData.message === "string" && errorData.message) ||
497
+ (typeof errorData.error?.message === "string"
498
+ ? errorData.error.message
499
+ : undefined) ||
474
500
  `Server returned ${response.status}`;
475
501
  await this.emitTelemetry("pie-tool-backend-call-error", {
476
502
  toolId: "tts",
@@ -481,6 +507,16 @@ class ServerTTSProviderImpl {
481
507
  errorType: "TTSBackendRequestError",
482
508
  message: errorMessage,
483
509
  });
510
+ const textPreview = text.replace(/\s+/g, " ").trim().slice(0, 120);
511
+ console.error("[ServerTTSProvider] synthesize request failed", {
512
+ status: response.status,
513
+ url: synthUrl,
514
+ backend: this.config.provider || "server",
515
+ message: errorMessage,
516
+ responseBody: errorData,
517
+ textLength: text.length,
518
+ textPreview: textPreview.length < text.length ? `${textPreview}…` : textPreview,
519
+ });
484
520
  throw new Error(errorMessage);
485
521
  }
486
522
  const normalized = await this.adapter.parseResponse(response, this.config, headers, signal);
@@ -594,6 +630,7 @@ class ServerTTSProviderImpl {
594
630
  wordIndex: index,
595
631
  charIndex: mark.start,
596
632
  length: mark.end - mark.start,
633
+ word: mark.value,
597
634
  }));
598
635
  }
599
636
  /**
@@ -630,8 +667,7 @@ class ServerTTSProviderImpl {
630
667
  // Fire word boundary callback
631
668
  if (this.onWordBoundary) {
632
669
  console.log("[ServerTTSProvider] Highlighting word at charIndex:", timing.charIndex, "length:", timing.length, "time:", timing.time, "currentTime:", currentTime);
633
- // Pass the length as the "word" parameter so TTSService can use it
634
- this.onWordBoundary("", timing.charIndex, timing.length);
670
+ this.onWordBoundary(timing.word, timing.charIndex, timing.length);
635
671
  }
636
672
  lastWordIndex = i;
637
673
  break;
@@ -700,10 +736,6 @@ class ServerTTSProviderImpl {
700
736
  // Update config
701
737
  if (settings.rate !== undefined) {
702
738
  this.config.rate = settings.rate;
703
- // Apply rate immediately to current playback if active
704
- if (this.currentAudio) {
705
- this.currentAudio.playbackRate = Math.max(0.25, Math.min(4.0, settings.rate));
706
- }
707
739
  }
708
740
  if (settings.pitch !== undefined) {
709
741
  // Server-side pitch is baked into audio, so this only affects next speak()
@@ -850,6 +882,7 @@ export class ServerTTSProvider {
850
882
  supportsVoiceSelection: true,
851
883
  supportsRateControl: true,
852
884
  supportsPitchControl: false, // Depends on server provider
885
+ supportsSSML: resolveSupportsSSML(this.config),
853
886
  maxTextLength: MAX_TEXT_LENGTH_BY_MODE[mode],
854
887
  };
855
888
  }
@@ -1 +1 @@
1
- {"version":3,"file":"ServerTTSProvider.js","sourceRoot":"","sources":["../src/ServerTTSProvider.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * ServerTTSProvider - Client-side TTS provider that calls server API\n *\n * Provides high-quality TTS by calling a server-side API that uses\n * providers like AWS Polly, Google Cloud TTS, etc.\n * Returns audio with precise word-level timing (speech marks).\n */\n\nimport type {\n\tITTSProvider,\n\tITTSProviderImplementation,\n\tTTSConfig,\n\tTTSFeature,\n\tTTSProviderCapabilities,\n} from \"@pie-players/pie-tts\";\n\n/**\n * Configuration for ServerTTSProvider\n */\nexport interface ServerTTSProviderConfig extends TTSConfig {\n\t/** API endpoint base URL (e.g., '/api/tts' or 'https://api.example.com/tts') */\n\tapiEndpoint: string;\n\n\t/** Provider to use on server ('polly', 'google', 'elevenlabs', etc.) */\n\tprovider?: string;\n\n\t/** Authentication token or API key */\n\tauthToken?: string;\n\n\t/** Custom headers for API requests */\n\theaders?: Record<string, string>;\n\n\t/** Language code */\n\tlanguage?: string;\n\n\t/** Polly engine selection when provider is AWS Polly */\n\tengine?: \"standard\" | \"neural\";\n\n\t/** Volume level 0-1 */\n\tvolume?: number;\n\n\t/**\n\t * Transport mode determines request/response translation strategy.\n\t * - pie: POST {apiEndpoint}/synthesize, expects inline base64 audio + speech marks\n\t * - custom: POST root endpoint, expects audioContent URL + JSONL speech mark URL\n\t */\n\ttransportMode?: \"pie\" | \"custom\";\n\n\t/**\n\t * Endpoint mode for synthesis requests.\n\t * - synthesizePath: append /synthesize to apiEndpoint\n\t * - rootPost: POST directly to apiEndpoint\n\t */\n\tendpointMode?: \"synthesizePath\" | \"rootPost\";\n\n\t/**\n\t * Endpoint validation mode used during initialize(validateEndpoint=true).\n\t * - voices: probe {apiEndpoint}/voices\n\t * - endpoint: probe resolved synthesis endpoint\n\t * - none: skip endpoint probe\n\t */\n\tendpointValidationMode?: \"voices\" | \"endpoint\" | \"none\";\n\n\t/**\n\t * Include auth headers when fetching custom-transport speech marks and audio URLs.\n\t * Defaults to false for compatibility.\n\t */\n\tincludeAuthOnAssetFetch?: boolean;\n\n\t/**\n\t * Origins that are trusted to receive the provider's `Authorization`\n\t * header (and any `authToken`-derived credentials). When an asset URL\n\t * returned by the TTS server falls outside this list, auth is scrubbed\n\t * before the fetch. Malformed or non-http(s) URLs are always rejected\n\t * and the fetch is skipped.\n\t *\n\t * Defaults to the origin of `apiEndpoint` so same-origin / same-host\n\t * responses keep working without explicit configuration.\n\t */\n\tassetOrigins?: string[];\n\n\t/**\n\t * Validate API endpoint availability during initialization (slower but safer)\n\t *\n\t * @extension Performance vs safety tradeoff\n\t * @default false (fast initialization, fail on first synthesis if unavailable)\n\t * @note When true, adds 100-500ms to initialization time\n\t */\n\tvalidateEndpoint?: boolean;\n}\n\ntype TelemetryReporter = (\n\teventName: string,\n\tpayload?: Record<string, unknown>,\n) => void | Promise<void>;\n\n/**\n * Parse a TTS asset URL (speech marks or audio). Rejects anything that is\n * not an absolute http(s) URL; relative URLs are resolved against\n * `apiEndpoint` when it is itself absolute.\n */\nfunction parseAssetUrl(\n\trawUrl: string,\n\tconfig: ServerTTSProviderConfig,\n): URL | null {\n\tif (typeof rawUrl !== \"string\" || rawUrl.length === 0) {\n\t\treturn null;\n\t}\n\ttry {\n\t\tconst base =\n\t\t\ttypeof config.apiEndpoint === \"string\" && /^https?:\\/\\//i.test(config.apiEndpoint)\n\t\t\t\t? config.apiEndpoint\n\t\t\t\t: undefined;\n\t\tconst parsed = base ? new URL(rawUrl, base) : new URL(rawUrl);\n\t\tif (parsed.protocol !== \"http:\" && parsed.protocol !== \"https:\") {\n\t\t\treturn null;\n\t\t}\n\t\treturn parsed;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\n/**\n * Decide whether a parsed asset URL is allowed to receive the provider's\n * `Authorization` header. Defaults to the origin of `apiEndpoint` when\n * `assetOrigins` is not configured.\n */\nfunction isOriginTrustedForAuth(\n\turl: URL,\n\tconfig: ServerTTSProviderConfig,\n): boolean {\n\tconst configured = Array.isArray(config.assetOrigins)\n\t\t? config.assetOrigins\n\t\t\t\t.filter((value): value is string => typeof value === \"string\" && value.length > 0)\n\t\t\t\t.map((value) => {\n\t\t\t\t\ttry {\n\t\t\t\t\t\treturn new URL(value).origin;\n\t\t\t\t\t} catch {\n\t\t\t\t\t\treturn value;\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t: [];\n\tif (configured.length === 0) {\n\t\tif (\n\t\t\ttypeof config.apiEndpoint === \"string\" &&\n\t\t\t/^https?:\\/\\//i.test(config.apiEndpoint)\n\t\t) {\n\t\t\ttry {\n\t\t\t\treturn new URL(config.apiEndpoint).origin === url.origin;\n\t\t\t} catch {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t\t// Relative apiEndpoint (e.g. \"/api/tts\") implies same-origin usage;\n\t\t// only same-origin assets should carry auth.\n\t\treturn (\n\t\t\ttypeof window !== \"undefined\" &&\n\t\t\ttypeof window.location?.origin === \"string\" &&\n\t\t\twindow.location.origin === url.origin\n\t\t);\n\t}\n\treturn configured.includes(url.origin);\n}\n\n/**\n * Strip auth-carrying headers when the target origin is not trusted.\n */\nfunction scrubAuthHeaders(\n\theaders: Record<string, string>,\n): Record<string, string> {\n\tconst cleaned: Record<string, string> = {};\n\tfor (const [key, value] of Object.entries(headers)) {\n\t\tconst lower = key.toLowerCase();\n\t\tif (\n\t\t\tlower === \"authorization\" ||\n\t\t\tlower === \"proxy-authorization\" ||\n\t\t\tlower === \"cookie\" ||\n\t\t\tlower.startsWith(\"x-auth-\")\n\t\t) {\n\t\t\tcontinue;\n\t\t}\n\t\tcleaned[key] = value;\n\t}\n\treturn cleaned;\n}\n\nconst getTelemetryReporter = (\n\tconfig: ServerTTSProviderConfig,\n): TelemetryReporter | undefined => {\n\tconst providerOptions =\n\t\tconfig.providerOptions && typeof config.providerOptions === \"object\"\n\t\t\t? (config.providerOptions as Record<string, unknown>)\n\t\t\t: {};\n\tconst reporter = providerOptions.__pieTelemetry;\n\treturn typeof reporter === \"function\"\n\t\t? (reporter as TelemetryReporter)\n\t\t: undefined;\n};\n\n/**\n * Word timing from speech marks\n */\ninterface WordTiming {\n\ttime: number; // Milliseconds from audio start\n\twordIndex: number;\n\tcharIndex: number; // Character position in text\n\tlength: number; // Word length in characters\n}\n\n/**\n * Server API response for synthesis\n */\ninterface SynthesizeAPIResponse {\n\taudio: string; // Base64 encoded audio\n\tcontentType: string;\n\tspeechMarks: Array<{\n\t\ttime: number;\n\t\ttype: string;\n\t\tstart: number;\n\t\tend: number;\n\t\tvalue: string;\n\t}>;\n\tmetadata: {\n\t\tproviderId: string;\n\t\tvoice: string;\n\t\tduration: number;\n\t\tcharCount: number;\n\t\tcached: boolean;\n\t};\n}\n\ninterface CustomTransportResponse {\n\taudioContent: string;\n\tword?: string;\n\tspeechMarks?: Array<{\n\t\ttime?: number;\n\t\ttype?: string;\n\t\tstart?: number;\n\t\tend?: number;\n\t\tvalue?: string;\n\t}>;\n}\n\ntype NormalizedAudioSource =\n\t| {\n\t\t\tkind: \"base64\";\n\t\t\tdata: string;\n\t\t\tcontentType: string;\n\t }\n\t| {\n\t\t\tkind: \"url\";\n\t\t\turl: string;\n\t };\n\ninterface NormalizedSynthesisResult {\n\taudio: NormalizedAudioSource;\n\tspeechMarks: Array<{\n\t\ttime: number;\n\t\ttype: string;\n\t\tstart: number;\n\t\tend: number;\n\t\tvalue: string;\n\t}>;\n}\n\ninterface TransportAdapter {\n\tid: \"pie\" | \"custom\";\n\tresolveSynthesisUrl: (config: ServerTTSProviderConfig) => string;\n\tbuildRequestBody: (text: string, config: ServerTTSProviderConfig) => unknown;\n\tparseResponse: (\n\t\tresponse: Response,\n\t\tconfig: ServerTTSProviderConfig,\n\t\theaders: Record<string, string>,\n\t\tsignal: AbortSignal,\n\t) => Promise<NormalizedSynthesisResult>;\n}\n\nconst MAX_TEXT_LENGTH_BY_MODE: Record<TransportAdapter[\"id\"], number> = {\n\tpie: 3000,\n\tcustom: 3000,\n};\n\nconst trimTrailingSlash = (value: string): string => value.replace(/\\/+$/, \"\");\n\nconst resolveVoicesValidationUrl = (\n\tconfig: ServerTTSProviderConfig,\n): string => {\n\tconst base = trimTrailingSlash(config.apiEndpoint);\n\tconst provider = (config.provider || \"\").toLowerCase();\n\tif (provider === \"polly\" || provider === \"google\") {\n\t\treturn `${base}/${provider}/voices`;\n\t}\n\treturn `${base}/voices`;\n};\n\nconst resolveTransportMode = (\n\tconfig: ServerTTSProviderConfig,\n): TransportAdapter[\"id\"] => {\n\tif (config.transportMode === \"custom\") return \"custom\";\n\tif (config.transportMode === \"pie\") return \"pie\";\n\treturn config.provider === \"custom\" ? \"custom\" : \"pie\";\n};\n\nconst resolveEndpointMode = (\n\tconfig: ServerTTSProviderConfig,\n\tmode: TransportAdapter[\"id\"],\n): NonNullable<ServerTTSProviderConfig[\"endpointMode\"]> => {\n\tif (config.endpointMode) return config.endpointMode;\n\treturn mode === \"custom\" ? \"rootPost\" : \"synthesizePath\";\n};\n\nconst resolveValidationMode = (\n\tconfig: ServerTTSProviderConfig,\n\tmode: TransportAdapter[\"id\"],\n): NonNullable<ServerTTSProviderConfig[\"endpointValidationMode\"]> => {\n\tif (config.endpointValidationMode) return config.endpointValidationMode;\n\treturn mode === \"custom\" ? \"none\" : \"voices\";\n};\n\nconst resolveSpeedRate = (config: ServerTTSProviderConfig): string => {\n\tconst providerOptions = (config.providerOptions || {}) as Record<string, unknown>;\n\tif (typeof providerOptions.speedRate === \"string\") {\n\t\treturn providerOptions.speedRate;\n\t}\n\tconst rate = Number(config.rate ?? 1);\n\tif (!Number.isFinite(rate) || rate <= 0.95) return \"slow\";\n\tif (rate >= 1.5) return \"fast\";\n\treturn \"medium\";\n};\n\nconst parseJSONLSpeechMarks = (raw: string): NormalizedSynthesisResult[\"speechMarks\"] => {\n\tconst marks: NormalizedSynthesisResult[\"speechMarks\"] = [];\n\tlet fallbackIndex = 0;\n\tconst lines = raw\n\t\t.split(\"\\n\")\n\t\t.map((line) => line.trim())\n\t\t.filter(Boolean);\n\tfor (const line of lines) {\n\t\ttry {\n\t\t\tconst parsed = JSON.parse(line) as Record<string, unknown>;\n\t\t\tconst type = typeof parsed.type === \"string\" ? parsed.type : \"word\";\n\t\t\tconst time =\n\t\t\t\ttypeof parsed.time === \"number\" && Number.isFinite(parsed.time)\n\t\t\t\t\t? parsed.time\n\t\t\t\t\t: 0;\n\t\t\tconst value = typeof parsed.value === \"string\" ? parsed.value : \"\";\n\t\t\tconst explicitStart =\n\t\t\t\ttypeof parsed.start === \"number\" && Number.isFinite(parsed.start)\n\t\t\t\t\t? parsed.start\n\t\t\t\t\t: null;\n\t\t\tconst explicitEnd =\n\t\t\t\ttypeof parsed.end === \"number\" && Number.isFinite(parsed.end)\n\t\t\t\t\t? parsed.end\n\t\t\t\t\t: null;\n\t\t\tconst start = explicitStart ?? fallbackIndex;\n\t\t\tconst computedEnd =\n\t\t\t\texplicitEnd ??\n\t\t\t\t(start + Math.max(1, value.length || String(parsed.value || \"\").length));\n\t\t\tfallbackIndex = Math.max(computedEnd + 1, fallbackIndex);\n\t\t\tmarks.push({\n\t\t\t\ttime,\n\t\t\t\ttype,\n\t\t\t\tstart,\n\t\t\t\tend: computedEnd,\n\t\t\t\tvalue,\n\t\t\t});\n\t\t} catch {\n\t\t\t// Ignore malformed lines and keep parsing valid marks.\n\t\t}\n\t}\n\treturn marks;\n};\n\nconst parseInlineSpeechMarks = (\n\tinput: CustomTransportResponse[\"speechMarks\"],\n): NormalizedSynthesisResult[\"speechMarks\"] => {\n\tif (!Array.isArray(input)) return [];\n\tconst marks: NormalizedSynthesisResult[\"speechMarks\"] = [];\n\tlet fallbackIndex = 0;\n\tfor (const entry of input) {\n\t\tif (!entry || typeof entry !== \"object\") continue;\n\t\tconst type = typeof entry.type === \"string\" ? entry.type : \"word\";\n\t\tconst time =\n\t\t\ttypeof entry.time === \"number\" && Number.isFinite(entry.time)\n\t\t\t\t? entry.time\n\t\t\t\t: 0;\n\t\tconst value = typeof entry.value === \"string\" ? entry.value : \"\";\n\t\tconst explicitStart =\n\t\t\ttypeof entry.start === \"number\" && Number.isFinite(entry.start)\n\t\t\t\t? entry.start\n\t\t\t\t: null;\n\t\tconst explicitEnd =\n\t\t\ttypeof entry.end === \"number\" && Number.isFinite(entry.end)\n\t\t\t\t? entry.end\n\t\t\t\t: null;\n\t\tconst start = explicitStart ?? fallbackIndex;\n\t\tconst end =\n\t\t\texplicitEnd ?? (start + Math.max(1, value.length || 1));\n\t\tfallbackIndex = Math.max(fallbackIndex, end + 1);\n\t\tmarks.push({ time, type, start, end, value });\n\t}\n\treturn marks.sort((left, right) => {\n\t\tif (left.time !== right.time) return left.time - right.time;\n\t\tif (left.start !== right.start) return left.start - right.start;\n\t\treturn left.end - right.end;\n\t});\n};\n\nconst pieAdapter: TransportAdapter = {\n\tid: \"pie\",\n\tresolveSynthesisUrl: (config) => {\n\t\tconst endpointMode = resolveEndpointMode(config, \"pie\");\n\t\tconst base = trimTrailingSlash(config.apiEndpoint);\n\t\treturn endpointMode === \"rootPost\" ? base : `${base}/synthesize`;\n\t},\n\tbuildRequestBody: (text, config) => {\n\t\tconst providerOptions = (config.providerOptions ||\n\t\t\t{}) as Record<string, unknown>;\n\t\tconst engine =\n\t\t\ttypeof config.engine === \"string\"\n\t\t\t\t? config.engine\n\t\t\t\t: typeof providerOptions.engine === \"string\"\n\t\t\t\t\t? providerOptions.engine\n\t\t\t\t\t: undefined;\n\t\tconst sampleRate =\n\t\t\ttypeof providerOptions.sampleRate === \"number\" &&\n\t\t\tNumber.isFinite(providerOptions.sampleRate)\n\t\t\t\t? providerOptions.sampleRate\n\t\t\t\t: undefined;\n\t\tconst format =\n\t\t\tproviderOptions.format === \"mp3\" ||\n\t\t\tproviderOptions.format === \"ogg\" ||\n\t\t\tproviderOptions.format === \"pcm\"\n\t\t\t\t? providerOptions.format\n\t\t\t\t: undefined;\n\t\tconst speechMarkTypes = Array.isArray(providerOptions.speechMarkTypes)\n\t\t\t? providerOptions.speechMarkTypes.filter(\n\t\t\t\t\t(entry): entry is \"word\" | \"sentence\" | \"ssml\" =>\n\t\t\t\t\t\tentry === \"word\" || entry === \"sentence\" || entry === \"ssml\",\n\t\t\t\t)\n\t\t\t: undefined;\n\t\treturn {\n\t\t\ttext,\n\t\t\tprovider: config.provider || \"polly\",\n\t\t\tvoice: config.voice,\n\t\t\tlanguage: config.language,\n\t\t\trate: config.rate,\n\t\t\tengine,\n\t\t\tsampleRate,\n\t\t\tformat,\n\t\t\tspeechMarkTypes,\n\t\t\tincludeSpeechMarks: true,\n\t\t};\n\t},\n\tparseResponse: async (response) => {\n\t\tconst data: SynthesizeAPIResponse = await response.json();\n\t\treturn {\n\t\t\taudio: {\n\t\t\t\tkind: \"base64\",\n\t\t\t\tdata: data.audio,\n\t\t\t\tcontentType: data.contentType,\n\t\t\t},\n\t\t\tspeechMarks: Array.isArray(data.speechMarks) ? data.speechMarks : [],\n\t\t};\n\t},\n};\n\nconst customAdapter: TransportAdapter = {\n\tid: \"custom\",\n\tresolveSynthesisUrl: (config) => {\n\t\tconst endpointMode = resolveEndpointMode(config, \"custom\");\n\t\tconst base = trimTrailingSlash(config.apiEndpoint);\n\t\treturn endpointMode === \"synthesizePath\" ? `${base}/synthesize` : base;\n\t},\n\tbuildRequestBody: (text, config) => {\n\t\tconst providerOptions = (config.providerOptions || {}) as Record<string, unknown>;\n\t\tconst langId =\n\t\t\ttypeof providerOptions.lang_id === \"string\"\n\t\t\t\t? providerOptions.lang_id\n\t\t\t\t: config.language || \"en-US\";\n\t\tconst cache =\n\t\t\ttypeof providerOptions.cache === \"boolean\" ? providerOptions.cache : true;\n\t\treturn {\n\t\t\ttext,\n\t\t\tspeedRate: resolveSpeedRate(config),\n\t\t\tlang_id: langId,\n\t\t\tcache,\n\t\t};\n\t},\n\tparseResponse: async (response, config, headers, signal) => {\n\t\tconst data: CustomTransportResponse = await response.json();\n\t\tconst marksHeaders: Record<string, string> = {};\n\t\tif (config.includeAuthOnAssetFetch) {\n\t\t\tfor (const [key, value] of Object.entries(headers)) {\n\t\t\t\tif (key.toLowerCase() === \"authorization\") {\n\t\t\t\t\tmarksHeaders[key] = value;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tlet speechMarks: NormalizedSynthesisResult[\"speechMarks\"] = [];\n\t\tconst inlineSpeechMarks = parseInlineSpeechMarks(data.speechMarks);\n\t\tif (inlineSpeechMarks.length > 0) {\n\t\t\tspeechMarks = inlineSpeechMarks;\n\t\t} else if (typeof data.word === \"string\" && data.word.length > 0) {\n\t\t\tconst marksUrl = parseAssetUrl(data.word, config);\n\t\t\tif (marksUrl !== null) {\n\t\t\t\tconst effectiveMarksHeaders = isOriginTrustedForAuth(marksUrl, config)\n\t\t\t\t\t? marksHeaders\n\t\t\t\t\t: scrubAuthHeaders(marksHeaders);\n\t\t\t\tconst marksResponse = await fetch(marksUrl.toString(), {\n\t\t\t\t\theaders: effectiveMarksHeaders,\n\t\t\t\t\tsignal,\n\t\t\t\t\t// Fail loud on redirects; the origin allow-list decision\n\t\t\t\t\t// is made pre-redirect and a silent hop could leak auth\n\t\t\t\t\t// to an unlisted origin.\n\t\t\t\t\tredirect: \"error\",\n\t\t\t\t});\n\t\t\t\tif (marksResponse.ok) {\n\t\t\t\t\tconst marksRaw = await marksResponse.text();\n\t\t\t\t\tspeechMarks = parseJSONLSpeechMarks(marksRaw);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tconst parsedAudioUrl = parseAssetUrl(data.audioContent, config);\n\t\tif (parsedAudioUrl === null) {\n\t\t\tthrow new Error(\"TTS server returned an invalid audio URL\");\n\t\t}\n\t\treturn {\n\t\t\taudio: {\n\t\t\t\tkind: \"url\",\n\t\t\t\turl: parsedAudioUrl.toString(),\n\t\t\t},\n\t\t\tspeechMarks,\n\t\t};\n\t},\n};\n\nconst ADAPTERS: Record<TransportAdapter[\"id\"], TransportAdapter> = {\n\tpie: pieAdapter,\n\tcustom: customAdapter,\n};\n\n/**\n * Provider implementation that handles audio playback\n */\nclass ServerTTSProviderImpl implements ITTSProviderImplementation {\n\tprivate config: ServerTTSProviderConfig;\n\tprivate adapter: TransportAdapter;\n\tprivate currentAudio: HTMLAudioElement | null = null;\n\tprivate pausedState = false;\n\tprivate wordTimings: WordTiming[] = [];\n\tprivate highlightInterval: number | null = null;\n\tprivate intentionallyStopped = false;\n\tprivate activeSynthesisController: AbortController | null = null;\n\tprivate synthesisRunId = 0;\n\tprivate readonly telemetryReporter: TelemetryReporter | undefined;\n\n\tpublic onWordBoundary?: (\n\t\tword: string,\n\t\tposition: number,\n\t\tlength?: number,\n\t) => void;\n\n\tconstructor(config: ServerTTSProviderConfig, adapter: TransportAdapter) {\n\t\tthis.config = config;\n\t\tthis.adapter = adapter;\n\t\tthis.telemetryReporter = getTelemetryReporter(config);\n\t}\n\n\tprivate async emitTelemetry(\n\t\teventName: string,\n\t\tpayload?: Record<string, unknown>,\n\t): Promise<void> {\n\t\ttry {\n\t\t\tawait this.telemetryReporter?.(eventName, payload);\n\t\t} catch (error) {\n\t\t\tconsole.warn(\"[ServerTTSProvider] telemetry callback failed:\", error);\n\t\t}\n\t}\n\n\tasync speak(text: string): Promise<void> {\n\t\t// Stop any current playback\n\t\tthis.stop();\n\n\t\t// Reset intentionally stopped flag for new playback\n\t\tthis.intentionallyStopped = false;\n\t\tconst runId = ++this.synthesisRunId;\n\t\tconst synthesisController = new AbortController();\n\t\tthis.activeSynthesisController = synthesisController;\n\n\t\t// Call server API to synthesize speech\n\t\tconst { audioUrl, wordTimings } = await this.synthesizeSpeech(\n\t\t\ttext,\n\t\t\tsynthesisController.signal,\n\t\t\trunId,\n\t\t);\n\t\tif (runId !== this.synthesisRunId) {\n\t\t\tURL.revokeObjectURL(audioUrl);\n\t\t\treturn;\n\t\t}\n\n\t\t// Adjust word timing for playback rate\n\t\t// Speech marks are at 1.0x speed, so we need to scale them\n\t\tconst playbackRate = this.config.rate || 1.0;\n\t\tthis.wordTimings = wordTimings.map((timing) => ({\n\t\t\t...timing,\n\t\t\ttime: timing.time / playbackRate,\n\t\t}));\n\n\t\treturn new Promise((resolve, reject) => {\n\t\t\t// Create audio element\n\t\t\tconst audio = new Audio(audioUrl);\n\t\t\tthis.currentAudio = audio;\n\n\t\t\t// Apply rate from config\n\t\t\tif (this.config.rate) {\n\t\t\t\taudio.playbackRate = Math.max(0.25, Math.min(4.0, this.config.rate));\n\t\t\t}\n\n\t\t\t// Apply volume from config\n\t\t\tif (this.config.volume !== undefined) {\n\t\t\t\taudio.volume = Math.max(0, Math.min(1, this.config.volume));\n\t\t\t}\n\n\t\t\t// Setup event handlers\n\t\t\taudio.onplay = () => {\n\t\t\t\tthis.pausedState = false;\n\n\t\t\t\t// Start word highlighting\n\t\t\t\tif (this.onWordBoundary && this.wordTimings.length > 0) {\n\t\t\t\t\tthis.startWordHighlighting();\n\t\t\t\t}\n\t\t\t};\n\n\t\t\taudio.onended = () => {\n\t\t\t\tthis.stopWordHighlighting();\n\t\t\t\tURL.revokeObjectURL(audioUrl);\n\t\t\t\tthis.currentAudio = null;\n\t\t\t\tthis.wordTimings = [];\n\t\t\t\tresolve();\n\t\t\t};\n\n\t\t\taudio.onerror = (event) => {\n\t\t\t\tthis.stopWordHighlighting();\n\t\t\t\tURL.revokeObjectURL(audioUrl);\n\t\t\t\tthis.currentAudio = null;\n\t\t\t\tthis.wordTimings = [];\n\t\t\t\tvoid event;\n\t\t\t\t// Only reject if this wasn't an intentional stop\n\t\t\t\tif (!this.intentionallyStopped) {\n\t\t\t\t\treject(new Error(\"Failed to play audio from server\"));\n\t\t\t\t} else {\n\t\t\t\t\t// Intentional stop, resolve normally\n\t\t\t\t\tresolve();\n\t\t\t\t}\n\t\t\t};\n\n\t\t\taudio.onpause = () => {\n\t\t\t\tthis.stopWordHighlighting();\n\t\t\t\tthis.pausedState = true;\n\t\t\t};\n\n\t\t\t// Start playback\n\t\t\taudio.play().catch(reject);\n\t\t});\n\t}\n\n\t/**\n\t * Call server API to synthesize speech\n\t */\n\tprivate async synthesizeSpeech(\n\t\ttext: string,\n\t\tsignal: AbortSignal,\n\t\trunId: number,\n\t): Promise<{ audioUrl: string; wordTimings: WordTiming[] }> {\n\t\tconst synthStartedAt = Date.now();\n\t\tawait this.emitTelemetry(\"pie-tool-backend-call-start\", {\n\t\t\ttoolId: \"tts\",\n\t\t\tbackend: this.config.provider || \"server\",\n\t\t\toperation: \"synthesize-speech\",\n\t\t});\n\t\tconst headers: Record<string, string> = {\n\t\t\t\"Content-Type\": \"application/json\",\n\t\t\t...this.config.headers,\n\t\t};\n\n\t\t// Add authentication if provided\n\t\tif (this.config.authToken) {\n\t\t\theaders[\"Authorization\"] = `Bearer ${this.config.authToken}`;\n\t\t}\n\n\t\tconst synthUrl = this.adapter.resolveSynthesisUrl(this.config);\n\t\tconst requestBody = this.adapter.buildRequestBody(text, this.config);\n\t\tconst response = await (async () => {\n\t\t\ttry {\n\t\t\t\treturn await fetch(synthUrl, {\n\t\t\t\t\tmethod: \"POST\",\n\t\t\t\t\theaders,\n\t\t\t\t\tbody: JSON.stringify(requestBody),\n\t\t\t\t\tsignal,\n\t\t\t\t});\n\t\t\t} catch (error) {\n\t\t\t\tawait this.emitTelemetry(\"pie-tool-backend-call-error\", {\n\t\t\t\t\ttoolId: \"tts\",\n\t\t\t\t\tbackend: this.config.provider || \"server\",\n\t\t\t\t\toperation: \"synthesize-speech\",\n\t\t\t\t\tduration: Date.now() - synthStartedAt,\n\t\t\t\t\terrorType: \"TTSBackendNetworkError\",\n\t\t\t\t\tmessage: error instanceof Error ? error.message : String(error),\n\t\t\t\t});\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t})();\n\n\t\tif (!response.ok) {\n\t\t\tconst errorData = await response.json().catch(() => ({}));\n\t\t\tconst errorMessage =\n\t\t\t\terrorData.message ||\n\t\t\t\terrorData.error?.message ||\n\t\t\t\t`Server returned ${response.status}`;\n\t\t\tawait this.emitTelemetry(\"pie-tool-backend-call-error\", {\n\t\t\t\ttoolId: \"tts\",\n\t\t\t\tbackend: this.config.provider || \"server\",\n\t\t\t\toperation: \"synthesize-speech\",\n\t\t\t\tduration: Date.now() - synthStartedAt,\n\t\t\t\tstatusCode: response.status,\n\t\t\t\terrorType: \"TTSBackendRequestError\",\n\t\t\t\tmessage: errorMessage,\n\t\t\t});\n\t\t\tthrow new Error(errorMessage);\n\t\t}\n\n\t\tconst normalized = await this.adapter.parseResponse(\n\t\t\tresponse,\n\t\t\tthis.config,\n\t\t\theaders,\n\t\t\tsignal,\n\t\t);\n\t\tif (runId !== this.synthesisRunId || signal.aborted) {\n\t\t\tthrow new Error(\"Synthesis superseded by a newer request\");\n\t\t}\n\n\t\tlet audioBlob: Blob;\n\t\tif (normalized.audio.kind === \"base64\") {\n\t\t\taudioBlob = this.base64ToBlob(\n\t\t\t\tnormalized.audio.data,\n\t\t\t\tnormalized.audio.contentType,\n\t\t\t);\n\t\t} else {\n\t\t\tconst audioAssetUrl = normalized.audio.url;\n\t\t\tconst assetFetchStartedAt = Date.now();\n\t\t\tawait this.emitTelemetry(\"pie-tool-backend-call-start\", {\n\t\t\t\ttoolId: \"tts\",\n\t\t\t\tbackend: this.config.provider || \"server\",\n\t\t\t\toperation: \"fetch-synthesized-audio-asset\",\n\t\t\t});\n\t\t\tconst assetHeaders: Record<string, string> = {};\n\t\t\tif (this.config.includeAuthOnAssetFetch) {\n\t\t\t\tif (this.config.authToken) {\n\t\t\t\t\tassetHeaders[\"Authorization\"] = `Bearer ${this.config.authToken}`;\n\t\t\t\t}\n\t\t\t}\n\t\t\tconst parsedAssetUrl = parseAssetUrl(audioAssetUrl, this.config);\n\t\t\tif (parsedAssetUrl === null) {\n\t\t\t\tawait this.emitTelemetry(\"pie-tool-backend-call-error\", {\n\t\t\t\t\ttoolId: \"tts\",\n\t\t\t\t\tbackend: this.config.provider || \"server\",\n\t\t\t\t\toperation: \"fetch-synthesized-audio-asset\",\n\t\t\t\t\tduration: Date.now() - assetFetchStartedAt,\n\t\t\t\t\terrorType: \"TTSAssetInvalidUrl\",\n\t\t\t\t\tmessage: \"TTS asset URL rejected (non-http(s) or malformed)\",\n\t\t\t\t});\n\t\t\t\tthrow new Error(\"TTS asset URL rejected (non-http(s) or malformed)\");\n\t\t\t}\n\t\t\tconst effectiveAssetHeaders = isOriginTrustedForAuth(\n\t\t\t\tparsedAssetUrl,\n\t\t\t\tthis.config,\n\t\t\t)\n\t\t\t\t? assetHeaders\n\t\t\t\t: scrubAuthHeaders(assetHeaders);\n\t\t\tconst audioResponse = await (async () => {\n\t\t\t\ttry {\n\t\t\t\t\treturn await fetch(parsedAssetUrl.toString(), {\n\t\t\t\t\t\theaders: effectiveAssetHeaders,\n\t\t\t\t\t\tsignal,\n\t\t\t\t\t\tredirect: \"error\",\n\t\t\t\t\t});\n\t\t\t\t} catch (error) {\n\t\t\t\t\tawait this.emitTelemetry(\"pie-tool-backend-call-error\", {\n\t\t\t\t\t\ttoolId: \"tts\",\n\t\t\t\t\t\tbackend: this.config.provider || \"server\",\n\t\t\t\t\t\toperation: \"fetch-synthesized-audio-asset\",\n\t\t\t\t\t\tduration: Date.now() - assetFetchStartedAt,\n\t\t\t\t\t\terrorType: \"TTSAssetNetworkError\",\n\t\t\t\t\t\tmessage: error instanceof Error ? error.message : String(error),\n\t\t\t\t\t});\n\t\t\t\t\tthrow error;\n\t\t\t\t}\n\t\t\t})();\n\t\t\tif (!audioResponse.ok) {\n\t\t\t\tawait this.emitTelemetry(\"pie-tool-backend-call-error\", {\n\t\t\t\t\ttoolId: \"tts\",\n\t\t\t\t\tbackend: this.config.provider || \"server\",\n\t\t\t\t\toperation: \"fetch-synthesized-audio-asset\",\n\t\t\t\t\tduration: Date.now() - assetFetchStartedAt,\n\t\t\t\t\tstatusCode: audioResponse.status,\n\t\t\t\t\terrorType: \"TTSAssetFetchError\",\n\t\t\t\t\tmessage: `Failed to download synthesized audio (${audioResponse.status})`,\n\t\t\t\t});\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`Failed to download synthesized audio (${audioResponse.status})`,\n\t\t\t\t);\n\t\t\t}\n\t\t\taudioBlob = await audioResponse.blob();\n\t\t\tawait this.emitTelemetry(\"pie-tool-backend-call-success\", {\n\t\t\t\ttoolId: \"tts\",\n\t\t\t\tbackend: this.config.provider || \"server\",\n\t\t\t\toperation: \"fetch-synthesized-audio-asset\",\n\t\t\t\tduration: Date.now() - assetFetchStartedAt,\n\t\t\t});\n\t\t}\n\t\tconst audioUrl = URL.createObjectURL(audioBlob);\n\n\t\t// Convert speech marks to word timings\n\t\tconst wordTimings = this.parseSpeechMarks(normalized.speechMarks);\n\t\tawait this.emitTelemetry(\"pie-tool-backend-call-success\", {\n\t\t\ttoolId: \"tts\",\n\t\t\tbackend: this.config.provider || \"server\",\n\t\t\toperation: \"synthesize-speech\",\n\t\t\tduration: Date.now() - synthStartedAt,\n\t\t});\n\n\t\treturn { audioUrl, wordTimings };\n\t}\n\n\t/**\n\t * Convert base64 to Blob\n\t */\n\tprivate base64ToBlob(base64: string, contentType: string): Blob {\n\t\tconst byteCharacters = atob(base64);\n\t\tconst byteNumbers = new Array(byteCharacters.length);\n\n\t\tfor (let i = 0; i < byteCharacters.length; i++) {\n\t\t\tbyteNumbers[i] = byteCharacters.charCodeAt(i);\n\t\t}\n\n\t\tconst byteArray = new Uint8Array(byteNumbers);\n\t\treturn new Blob([byteArray], { type: contentType });\n\t}\n\n\t/**\n\t * Parse speech marks into word timings\n\t */\n\tprivate parseSpeechMarks(\n\t\tmarks: SynthesizeAPIResponse[\"speechMarks\"],\n\t): WordTiming[] {\n\t\treturn marks\n\t\t\t.filter((mark) => mark.type === \"word\")\n\t\t\t.map((mark, index) => ({\n\t\t\t\ttime: mark.time,\n\t\t\t\twordIndex: index,\n\t\t\t\tcharIndex: mark.start,\n\t\t\t\tlength: mark.end - mark.start,\n\t\t\t}));\n\t}\n\n\t/**\n\t * Start word highlighting synchronized with audio playback\n\t */\n\tprivate startWordHighlighting(): void {\n\t\tthis.stopWordHighlighting();\n\n\t\tif (\n\t\t\t!this.currentAudio ||\n\t\t\t!this.onWordBoundary ||\n\t\t\tthis.wordTimings.length === 0\n\t\t) {\n\t\t\tconsole.log(\"[ServerTTSProvider] Cannot start highlighting:\", {\n\t\t\t\thasAudio: !!this.currentAudio,\n\t\t\t\thasCallback: !!this.onWordBoundary,\n\t\t\t\twordTimingsCount: this.wordTimings.length,\n\t\t\t});\n\t\t\treturn;\n\t\t}\n\n\t\tconsole.log(\n\t\t\t\"[ServerTTSProvider] Starting word highlighting with\",\n\t\t\tthis.wordTimings.length,\n\t\t\t\"word timings\",\n\t\t);\n\t\tconsole.log(\n\t\t\t\"[ServerTTSProvider] Playback rate:\",\n\t\t\tthis.currentAudio.playbackRate,\n\t\t);\n\t\tconsole.log(\n\t\t\t\"[ServerTTSProvider] First 3 timings:\",\n\t\t\tthis.wordTimings.slice(0, 3),\n\t\t);\n\n\t\tlet lastWordIndex = -1;\n\n\t\t// Poll every 50ms to check current playback time\n\t\tthis.highlightInterval = window.setInterval(() => {\n\t\t\tif (!this.currentAudio) {\n\t\t\t\tthis.stopWordHighlighting();\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Get current playback time in milliseconds\n\t\t\tconst currentTime = this.currentAudio.currentTime * 1000;\n\n\t\t\t// Find words that should be highlighted at current time\n\t\t\tfor (let i = 0; i < this.wordTimings.length; i++) {\n\t\t\t\tconst timing = this.wordTimings[i];\n\n\t\t\t\tif (currentTime >= timing.time && i > lastWordIndex) {\n\t\t\t\t\t// Fire word boundary callback\n\t\t\t\t\tif (this.onWordBoundary) {\n\t\t\t\t\t\tconsole.log(\n\t\t\t\t\t\t\t\"[ServerTTSProvider] Highlighting word at charIndex:\",\n\t\t\t\t\t\t\ttiming.charIndex,\n\t\t\t\t\t\t\t\"length:\",\n\t\t\t\t\t\t\ttiming.length,\n\t\t\t\t\t\t\t\"time:\",\n\t\t\t\t\t\t\ttiming.time,\n\t\t\t\t\t\t\t\"currentTime:\",\n\t\t\t\t\t\t\tcurrentTime,\n\t\t\t\t\t\t);\n\t\t\t\t\t\t// Pass the length as the \"word\" parameter so TTSService can use it\n\t\t\t\t\t\tthis.onWordBoundary(\"\", timing.charIndex, timing.length);\n\t\t\t\t\t}\n\t\t\t\t\tlastWordIndex = i;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}, 50); // 50ms polling = 20 times per second\n\t}\n\n\t/**\n\t * Stop word highlighting\n\t */\n\tprivate stopWordHighlighting(): void {\n\t\tif (this.highlightInterval !== null) {\n\t\t\tclearInterval(this.highlightInterval);\n\t\t\tthis.highlightInterval = null;\n\t\t}\n\t}\n\n\tpause(): void {\n\t\tif (this.currentAudio && !this.pausedState) {\n\t\t\tthis.currentAudio.pause();\n\t\t\tthis.stopWordHighlighting();\n\t\t\tthis.pausedState = true;\n\t\t}\n\t}\n\n\tresume(): void {\n\t\tif (this.currentAudio && this.pausedState) {\n\t\t\tthis.currentAudio.play();\n\t\t\tthis.pausedState = false;\n\n\t\t\t// Resume word highlighting\n\t\t\tif (this.onWordBoundary && this.wordTimings.length > 0) {\n\t\t\t\tthis.startWordHighlighting();\n\t\t\t}\n\t\t}\n\t}\n\n\tstop(): void {\n\t\tthis.synthesisRunId += 1;\n\t\tif (this.activeSynthesisController) {\n\t\t\tthis.activeSynthesisController.abort();\n\t\t\tthis.activeSynthesisController = null;\n\t\t}\n\t\tthis.stopWordHighlighting();\n\n\t\tif (this.currentAudio) {\n\t\t\t// Mark as intentionally stopped to prevent error handler from rejecting\n\t\t\tthis.intentionallyStopped = true;\n\t\t\tthis.currentAudio.pause();\n\t\t\tif (this.currentAudio.src) {\n\t\t\t\tURL.revokeObjectURL(this.currentAudio.src);\n\t\t\t}\n\t\t\tthis.currentAudio.src = \"\";\n\t\t\tthis.currentAudio = null;\n\t\t}\n\n\t\tthis.pausedState = false;\n\t\tthis.wordTimings = [];\n\t}\n\n\tisPlaying(): boolean {\n\t\treturn this.currentAudio !== null && !this.pausedState;\n\t}\n\n\tisPaused(): boolean {\n\t\treturn this.pausedState;\n\t}\n\n\t/**\n\t * Update settings dynamically (rate, pitch, voice)\n\t * Note: Voice changes require resynthesis, so voice updates are stored but\n\t * take effect on the next speak() call. Rate can be applied to current playback.\n\t */\n\tupdateSettings(settings: Partial<ServerTTSProviderConfig>): void {\n\t\t// Update config\n\t\tif (settings.rate !== undefined) {\n\t\t\tthis.config.rate = settings.rate;\n\t\t\t// Apply rate immediately to current playback if active\n\t\t\tif (this.currentAudio) {\n\t\t\t\tthis.currentAudio.playbackRate = Math.max(\n\t\t\t\t\t0.25,\n\t\t\t\t\tMath.min(4.0, settings.rate),\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t\tif (settings.pitch !== undefined) {\n\t\t\t// Server-side pitch is baked into audio, so this only affects next speak()\n\t\t\tthis.config.pitch = settings.pitch;\n\t\t}\n\t\tif (settings.voice !== undefined) {\n\t\t\t// Voice change requires resynthesis, affects next speak()\n\t\t\tthis.config.voice = settings.voice;\n\t\t}\n\t}\n}\n\n/**\n * Server TTS Provider\n *\n * Client-side provider that calls a server API for TTS synthesis.\n * The server handles provider selection (Polly, Google, etc.) and credential management.\n */\nexport class ServerTTSProvider implements ITTSProvider {\n\treadonly providerId = \"server-tts\";\n\treadonly providerName = \"Server TTS\";\n\treadonly version = \"1.0.0\";\n\n\tprivate config: ServerTTSProviderConfig | null = null;\n\tprivate adapter: TransportAdapter | null = null;\n\tprivate telemetryReporter: TelemetryReporter | undefined;\n\n\tprivate async emitTelemetry(\n\t\teventName: string,\n\t\tpayload?: Record<string, unknown>,\n\t): Promise<void> {\n\t\ttry {\n\t\t\tawait this.telemetryReporter?.(eventName, payload);\n\t\t} catch (error) {\n\t\t\tconsole.warn(\"[ServerTTSProvider] telemetry callback failed:\", error);\n\t\t}\n\t}\n\n\t/**\n\t * Initialize the server TTS provider.\n\t *\n\t * This is designed to be fast by default (no API calls).\n\t * Set validateEndpoint: true in config to test API availability during initialization.\n\t *\n\t * @performance Default: <10ms, With validation: 100-500ms\n\t */\n\tasync initialize(config: TTSConfig): Promise<ITTSProviderImplementation> {\n\t\tconst serverConfig = config as ServerTTSProviderConfig;\n\n\t\tif (!serverConfig.apiEndpoint) {\n\t\t\tthrow new Error(\"apiEndpoint is required for ServerTTSProvider\");\n\t\t}\n\n\t\tthis.config = serverConfig;\n\t\tthis.telemetryReporter = getTelemetryReporter(serverConfig);\n\t\tconst transportMode = resolveTransportMode(serverConfig);\n\t\tthis.adapter = ADAPTERS[transportMode];\n\n\t\t// Only test API availability if explicitly requested (slower but safer)\n\t\tif (serverConfig.validateEndpoint) {\n\t\t\tconst validationStartedAt = Date.now();\n\t\t\tawait this.emitTelemetry(\"pie-tool-backend-call-start\", {\n\t\t\t\ttoolId: \"tts\",\n\t\t\t\tbackend: serverConfig.provider || \"server\",\n\t\t\t\toperation: \"validate-endpoint\",\n\t\t\t});\n\t\t\tconst available = await this.testAPIAvailability();\n\t\t\tif (!available) {\n\t\t\t\tawait this.emitTelemetry(\"pie-tool-backend-call-error\", {\n\t\t\t\t\ttoolId: \"tts\",\n\t\t\t\t\tbackend: serverConfig.provider || \"server\",\n\t\t\t\t\toperation: \"validate-endpoint\",\n\t\t\t\t\tduration: Date.now() - validationStartedAt,\n\t\t\t\t\terrorType: \"TTSEndpointValidationError\",\n\t\t\t\t\tmessage: `Server TTS API not available at ${serverConfig.apiEndpoint}`,\n\t\t\t\t});\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`Server TTS API not available at ${serverConfig.apiEndpoint}`,\n\t\t\t\t);\n\t\t\t}\n\t\t\tawait this.emitTelemetry(\"pie-tool-backend-call-success\", {\n\t\t\t\ttoolId: \"tts\",\n\t\t\t\tbackend: serverConfig.provider || \"server\",\n\t\t\t\toperation: \"validate-endpoint\",\n\t\t\t\tduration: Date.now() - validationStartedAt,\n\t\t\t});\n\t\t}\n\n\t\treturn new ServerTTSProviderImpl(serverConfig, this.adapter);\n\t}\n\n\t/**\n\t * Test if API endpoint is available (with timeout).\n\t *\n\t * @performance 100-500ms depending on network\n\t */\n\tprivate async testAPIAvailability(): Promise<boolean> {\n\t\tif (!this.config || !this.adapter) return false;\n\n\t\ttry {\n\t\t\tconst headers: Record<string, string> = { ...this.config.headers };\n\n\t\t\tif (this.config.authToken) {\n\t\t\t\theaders[\"Authorization\"] = `Bearer ${this.config.authToken}`;\n\t\t\t}\n\n\t\t\t// Create abort controller for timeout\n\t\t\tconst controller = new AbortController();\n\t\t\tconst timeoutId = setTimeout(() => controller.abort(), 5000); // 5 second timeout\n\n\t\t\tconst mode = resolveValidationMode(this.config, this.adapter.id);\n\t\t\tif (mode === \"none\") {\n\t\t\t\tclearTimeout(timeoutId);\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tconst validationUrl =\n\t\t\t\tmode === \"voices\"\n\t\t\t\t\t? resolveVoicesValidationUrl(this.config)\n\t\t\t\t\t: this.adapter.resolveSynthesisUrl(this.config);\n\t\t\tconst method = mode === \"voices\" ? \"GET\" : \"OPTIONS\";\n\t\t\ttry {\n\t\t\t\tconst response = await fetch(validationUrl, {\n\t\t\t\t\tmethod,\n\t\t\t\t\theaders,\n\t\t\t\t\tsignal: controller.signal,\n\t\t\t\t});\n\t\t\t\tclearTimeout(timeoutId);\n\t\t\t\t// Some endpoints may not accept OPTIONS; treat 405 as reachable.\n\t\t\t\treturn response.ok || response.status === 405;\n\t\t\t} catch {\n\t\t\t\tclearTimeout(timeoutId);\n\t\t\t\treturn false;\n\t\t\t}\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tsupportsFeature(feature: TTSFeature): boolean {\n\t\tswitch (feature) {\n\t\t\tcase \"pause\":\n\t\t\tcase \"resume\":\n\t\t\tcase \"wordBoundary\":\n\t\t\tcase \"voiceSelection\":\n\t\t\tcase \"rateControl\":\n\t\t\t\treturn true;\n\t\t\tcase \"pitchControl\":\n\t\t\t\t// Depends on server provider, assume no for safety\n\t\t\t\treturn false;\n\t\t\tdefault:\n\t\t\t\treturn false;\n\t\t}\n\t}\n\n\tgetCapabilities(): TTSProviderCapabilities {\n\t\tconst mode = this.config ? resolveTransportMode(this.config) : \"pie\";\n\t\treturn {\n\t\t\tsupportsPause: true,\n\t\t\tsupportsResume: true,\n\t\t\tsupportsWordBoundary: true, // ✅ Via speech marks from server\n\t\t\tsupportsVoiceSelection: true,\n\t\t\tsupportsRateControl: true,\n\t\t\tsupportsPitchControl: false, // Depends on server provider\n\t\t\tmaxTextLength: MAX_TEXT_LENGTH_BY_MODE[mode],\n\t\t};\n\t}\n\n\tdestroy(): void {\n\t\tthis.config = null;\n\t\tthis.adapter = null;\n\t\tthis.telemetryReporter = undefined;\n\t}\n}\n"]}
1
+ {"version":3,"file":"ServerTTSProvider.js","sourceRoot":"","sources":["../src/ServerTTSProvider.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * ServerTTSProvider - Client-side TTS provider that calls server API\n *\n * Provides high-quality TTS by calling a server-side API that uses\n * providers like AWS Polly, Google Cloud TTS, etc.\n * Returns audio with precise word-level timing (speech marks).\n */\n\nimport type {\n\tITTSProvider,\n\tITTSProviderImplementation,\n\tTTSConfig,\n\tTTSFeature,\n\tTTSProviderCapabilities,\n} from \"@pie-players/pie-tts\";\n\n/**\n * Configuration for ServerTTSProvider\n */\nexport interface ServerTTSProviderConfig extends TTSConfig {\n\t/** API endpoint base URL (e.g., '/api/tts' or 'https://api.example.com/tts') */\n\tapiEndpoint: string;\n\n\t/** Provider to use on server ('polly', 'google', 'elevenlabs', etc.) */\n\tprovider?: string;\n\n\t/** Authentication token or API key */\n\tauthToken?: string;\n\n\t/** Custom headers for API requests */\n\theaders?: Record<string, string>;\n\n\t/** Language code */\n\tlanguage?: string;\n\n\t/** Polly engine selection when provider is AWS Polly */\n\tengine?: \"standard\" | \"neural\";\n\n\t/** Volume level 0-1 */\n\tvolume?: number;\n\n\t/**\n\t * Transport mode determines request/response translation strategy.\n\t * - pie: POST {apiEndpoint}/synthesize, expects inline base64 audio + speech marks\n\t * - custom: POST root endpoint, expects audioContent URL + JSONL speech mark URL\n\t */\n\ttransportMode?: \"pie\" | \"custom\";\n\n\t/**\n\t * Endpoint mode for synthesis requests.\n\t * - synthesizePath: append /synthesize to apiEndpoint\n\t * - rootPost: POST directly to apiEndpoint\n\t */\n\tendpointMode?: \"synthesizePath\" | \"rootPost\";\n\n\t/**\n\t * Endpoint validation mode used during initialize(validateEndpoint=true).\n\t * - voices: probe {apiEndpoint}/voices\n\t * - endpoint: probe resolved synthesis endpoint\n\t * - none: skip endpoint probe\n\t */\n\tendpointValidationMode?: \"voices\" | \"endpoint\" | \"none\";\n\n\t/**\n\t * Include auth headers when fetching custom-transport speech marks and audio URLs.\n\t * Defaults to false for compatibility.\n\t */\n\tincludeAuthOnAssetFetch?: boolean;\n\n\t/**\n\t * Origins that are trusted to receive the provider's `Authorization`\n\t * header (and any `authToken`-derived credentials). When an asset URL\n\t * returned by the TTS server falls outside this list, auth is scrubbed\n\t * before the fetch. Malformed or non-http(s) URLs are always rejected\n\t * and the fetch is skipped.\n\t *\n\t * Defaults to the origin of `apiEndpoint` so same-origin / same-host\n\t * responses keep working without explicit configuration.\n\t */\n\tassetOrigins?: string[];\n\n\t/**\n\t * Validate API endpoint availability during initialization (slower but safer)\n\t *\n\t * @extension Performance vs safety tradeoff\n\t * @default false (fast initialization, fail on first synthesis if unavailable)\n\t * @note When true, adds 100-500ms to initialization time\n\t */\n\tvalidateEndpoint?: boolean;\n}\n\ntype TelemetryReporter = (\n\teventName: string,\n\tpayload?: Record<string, unknown>,\n) => void | Promise<void>;\n\n/**\n * Parse a TTS asset URL (speech marks or audio). Rejects anything that is\n * not an absolute http(s) URL; relative URLs are resolved against\n * `apiEndpoint` when it is itself absolute.\n */\nfunction parseAssetUrl(\n\trawUrl: string,\n\tconfig: ServerTTSProviderConfig,\n): URL | null {\n\tif (typeof rawUrl !== \"string\" || rawUrl.length === 0) {\n\t\treturn null;\n\t}\n\ttry {\n\t\tconst base =\n\t\t\ttypeof config.apiEndpoint === \"string\" && /^https?:\\/\\//i.test(config.apiEndpoint)\n\t\t\t\t? config.apiEndpoint\n\t\t\t\t: undefined;\n\t\tconst parsed = base ? new URL(rawUrl, base) : new URL(rawUrl);\n\t\tif (parsed.protocol !== \"http:\" && parsed.protocol !== \"https:\") {\n\t\t\treturn null;\n\t\t}\n\t\treturn parsed;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\n/**\n * Decide whether a parsed asset URL is allowed to receive the provider's\n * `Authorization` header. Defaults to the origin of `apiEndpoint` when\n * `assetOrigins` is not configured.\n */\nfunction isOriginTrustedForAuth(\n\turl: URL,\n\tconfig: ServerTTSProviderConfig,\n): boolean {\n\tconst configured = Array.isArray(config.assetOrigins)\n\t\t? config.assetOrigins\n\t\t\t\t.filter((value): value is string => typeof value === \"string\" && value.length > 0)\n\t\t\t\t.map((value) => {\n\t\t\t\t\ttry {\n\t\t\t\t\t\treturn new URL(value).origin;\n\t\t\t\t\t} catch {\n\t\t\t\t\t\treturn value;\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t: [];\n\tif (configured.length === 0) {\n\t\tif (\n\t\t\ttypeof config.apiEndpoint === \"string\" &&\n\t\t\t/^https?:\\/\\//i.test(config.apiEndpoint)\n\t\t) {\n\t\t\ttry {\n\t\t\t\treturn new URL(config.apiEndpoint).origin === url.origin;\n\t\t\t} catch {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t\t// Relative apiEndpoint (e.g. \"/api/tts\") implies same-origin usage;\n\t\t// only same-origin assets should carry auth.\n\t\treturn (\n\t\t\ttypeof window !== \"undefined\" &&\n\t\t\ttypeof window.location?.origin === \"string\" &&\n\t\t\twindow.location.origin === url.origin\n\t\t);\n\t}\n\treturn configured.includes(url.origin);\n}\n\n/**\n * Strip auth-carrying headers when the target origin is not trusted.\n */\nfunction scrubAuthHeaders(\n\theaders: Record<string, string>,\n): Record<string, string> {\n\tconst cleaned: Record<string, string> = {};\n\tfor (const [key, value] of Object.entries(headers)) {\n\t\tconst lower = key.toLowerCase();\n\t\tif (\n\t\t\tlower === \"authorization\" ||\n\t\t\tlower === \"proxy-authorization\" ||\n\t\t\tlower === \"cookie\" ||\n\t\t\tlower.startsWith(\"x-auth-\")\n\t\t) {\n\t\t\tcontinue;\n\t\t}\n\t\tcleaned[key] = value;\n\t}\n\treturn cleaned;\n}\n\nconst getTelemetryReporter = (\n\tconfig: ServerTTSProviderConfig,\n): TelemetryReporter | undefined => {\n\tconst providerOptions =\n\t\tconfig.providerOptions && typeof config.providerOptions === \"object\"\n\t\t\t? (config.providerOptions as Record<string, unknown>)\n\t\t\t: {};\n\tconst reporter = providerOptions.__pieTelemetry;\n\treturn typeof reporter === \"function\"\n\t\t? (reporter as TelemetryReporter)\n\t\t: undefined;\n};\n\n/**\n * Word timing from speech marks\n */\ninterface WordTiming {\n\ttime: number; // Milliseconds from audio start\n\twordIndex: number;\n\tcharIndex: number; // Character position in text\n\tlength: number; // Word length in characters\n\tword: string;\n}\n\n/**\n * Server API response for synthesis\n */\ninterface SynthesizeAPIResponse {\n\taudio: string; // Base64 encoded audio\n\tcontentType: string;\n\tspeechMarks: Array<{\n\t\ttime: number;\n\t\ttype: string;\n\t\tstart: number;\n\t\tend: number;\n\t\tvalue: string;\n\t}>;\n\tmetadata: {\n\t\tproviderId: string;\n\t\tvoice: string;\n\t\tduration: number;\n\t\tcharCount: number;\n\t\tcached: boolean;\n\t};\n}\n\ninterface CustomTransportResponse {\n\taudioContent: string;\n\tword?: string;\n\tspeechMarks?: Array<{\n\t\ttime?: number;\n\t\ttype?: string;\n\t\tstart?: number;\n\t\tend?: number;\n\t\tvalue?: string;\n\t}>;\n}\n\ntype NormalizedAudioSource =\n\t| {\n\t\t\tkind: \"base64\";\n\t\t\tdata: string;\n\t\t\tcontentType: string;\n\t }\n\t| {\n\t\t\tkind: \"url\";\n\t\t\turl: string;\n\t };\n\ninterface NormalizedSynthesisResult {\n\taudio: NormalizedAudioSource;\n\tspeechMarks: Array<{\n\t\ttime: number;\n\t\ttype: string;\n\t\tstart: number;\n\t\tend: number;\n\t\tvalue: string;\n\t}>;\n}\n\ninterface TransportAdapter {\n\tid: \"pie\" | \"custom\";\n\tresolveSynthesisUrl: (config: ServerTTSProviderConfig) => string;\n\tbuildRequestBody: (text: string, config: ServerTTSProviderConfig) => unknown;\n\tparseResponse: (\n\t\tresponse: Response,\n\t\tconfig: ServerTTSProviderConfig,\n\t\theaders: Record<string, string>,\n\t\tsignal: AbortSignal,\n\t) => Promise<NormalizedSynthesisResult>;\n}\n\nconst MAX_TEXT_LENGTH_BY_MODE: Record<TransportAdapter[\"id\"], number> = {\n\tpie: 3000,\n\tcustom: 3000,\n};\n\n/**\n * Server providers whose SSML support is reliable enough to receive generated\n * SSML (AWS Polly, Google Cloud TTS). This is an allow-list rather than a\n * deny-list: the `custom` transport (and unknown providers) report\n * `supportsSSML: false` so the toolkit falls back to plain text. Some custom\n * backends advertise SSML support server-side but mis-voice or ignore parts of\n * it, so we stay conservative at the client boundary.\n */\nconst SSML_CAPABLE_PIE_PROVIDERS = new Set([\"polly\", \"google\"]);\n\nconst resolveSupportsSSML = (\n\tconfig: ServerTTSProviderConfig | null,\n): boolean => {\n\tif (!config) return false;\n\tif (resolveTransportMode(config) !== \"pie\") return false;\n\t// The pie transport defaults to Polly when no provider is named.\n\tconst provider = (config.provider || \"polly\").toLowerCase();\n\treturn SSML_CAPABLE_PIE_PROVIDERS.has(provider);\n};\n\nconst trimTrailingSlash = (value: string): string => value.replace(/\\/+$/, \"\");\n\nconst resolveVoicesValidationUrl = (\n\tconfig: ServerTTSProviderConfig,\n): string => {\n\tconst base = trimTrailingSlash(config.apiEndpoint);\n\tconst provider = (config.provider || \"\").toLowerCase();\n\tif (provider === \"polly\" || provider === \"google\") {\n\t\treturn `${base}/${provider}/voices`;\n\t}\n\treturn `${base}/voices`;\n};\n\nconst resolveTransportMode = (\n\tconfig: ServerTTSProviderConfig,\n): TransportAdapter[\"id\"] => {\n\tif (config.transportMode === \"custom\") return \"custom\";\n\tif (config.transportMode === \"pie\") return \"pie\";\n\treturn config.provider === \"custom\" ? \"custom\" : \"pie\";\n};\n\nconst resolveEndpointMode = (\n\tconfig: ServerTTSProviderConfig,\n\tmode: TransportAdapter[\"id\"],\n): NonNullable<ServerTTSProviderConfig[\"endpointMode\"]> => {\n\tif (config.endpointMode) return config.endpointMode;\n\treturn mode === \"custom\" ? \"rootPost\" : \"synthesizePath\";\n};\n\nconst resolveValidationMode = (\n\tconfig: ServerTTSProviderConfig,\n\tmode: TransportAdapter[\"id\"],\n): NonNullable<ServerTTSProviderConfig[\"endpointValidationMode\"]> => {\n\tif (config.endpointValidationMode) return config.endpointValidationMode;\n\treturn mode === \"custom\" ? \"none\" : \"voices\";\n};\n\nconst resolveSpeedRate = (config: ServerTTSProviderConfig): string => {\n\tconst providerOptions = (config.providerOptions || {}) as Record<string, unknown>;\n\tif (typeof providerOptions.speedRate === \"string\") {\n\t\treturn providerOptions.speedRate;\n\t}\n\tconst rate = Number(config.rate ?? 1);\n\tif (!Number.isFinite(rate) || rate <= 0.95) return \"slow\";\n\tif (rate >= 1.5) return \"fast\";\n\treturn \"medium\";\n};\n\nconst parseJSONLSpeechMarks = (raw: string): NormalizedSynthesisResult[\"speechMarks\"] => {\n\tconst marks: NormalizedSynthesisResult[\"speechMarks\"] = [];\n\tlet fallbackIndex = 0;\n\tconst lines = raw\n\t\t.split(\"\\n\")\n\t\t.map((line) => line.trim())\n\t\t.filter(Boolean);\n\tfor (const line of lines) {\n\t\ttry {\n\t\t\tconst parsed = JSON.parse(line) as Record<string, unknown>;\n\t\t\tconst type = typeof parsed.type === \"string\" ? parsed.type : \"word\";\n\t\t\tconst time =\n\t\t\t\ttypeof parsed.time === \"number\" && Number.isFinite(parsed.time)\n\t\t\t\t\t? parsed.time\n\t\t\t\t\t: 0;\n\t\t\tconst value = typeof parsed.value === \"string\" ? parsed.value : \"\";\n\t\t\tconst explicitStart =\n\t\t\t\ttypeof parsed.start === \"number\" && Number.isFinite(parsed.start)\n\t\t\t\t\t? parsed.start\n\t\t\t\t\t: null;\n\t\t\tconst explicitEnd =\n\t\t\t\ttypeof parsed.end === \"number\" && Number.isFinite(parsed.end)\n\t\t\t\t\t? parsed.end\n\t\t\t\t\t: null;\n\t\t\tconst start = explicitStart ?? fallbackIndex;\n\t\t\tconst computedEnd =\n\t\t\t\texplicitEnd ??\n\t\t\t\t(start + Math.max(1, value.length || String(parsed.value || \"\").length));\n\t\t\tfallbackIndex = Math.max(computedEnd + 1, fallbackIndex);\n\t\t\tmarks.push({\n\t\t\t\ttime,\n\t\t\t\ttype,\n\t\t\t\tstart,\n\t\t\t\tend: computedEnd,\n\t\t\t\tvalue,\n\t\t\t});\n\t\t} catch {\n\t\t\t// Ignore malformed lines and keep parsing valid marks.\n\t\t}\n\t}\n\treturn marks;\n};\n\nconst parseInlineSpeechMarks = (\n\tinput: CustomTransportResponse[\"speechMarks\"],\n): NormalizedSynthesisResult[\"speechMarks\"] => {\n\tif (!Array.isArray(input)) return [];\n\tconst marks: NormalizedSynthesisResult[\"speechMarks\"] = [];\n\tlet fallbackIndex = 0;\n\tfor (const entry of input) {\n\t\tif (!entry || typeof entry !== \"object\") continue;\n\t\tconst type = typeof entry.type === \"string\" ? entry.type : \"word\";\n\t\tconst time =\n\t\t\ttypeof entry.time === \"number\" && Number.isFinite(entry.time)\n\t\t\t\t? entry.time\n\t\t\t\t: 0;\n\t\tconst value = typeof entry.value === \"string\" ? entry.value : \"\";\n\t\tconst explicitStart =\n\t\t\ttypeof entry.start === \"number\" && Number.isFinite(entry.start)\n\t\t\t\t? entry.start\n\t\t\t\t: null;\n\t\tconst explicitEnd =\n\t\t\ttypeof entry.end === \"number\" && Number.isFinite(entry.end)\n\t\t\t\t? entry.end\n\t\t\t\t: null;\n\t\tconst start = explicitStart ?? fallbackIndex;\n\t\tconst end =\n\t\t\texplicitEnd ?? (start + Math.max(1, value.length || 1));\n\t\tfallbackIndex = Math.max(fallbackIndex, end + 1);\n\t\tmarks.push({ time, type, start, end, value });\n\t}\n\treturn marks.sort((left, right) => {\n\t\tif (left.time !== right.time) return left.time - right.time;\n\t\tif (left.start !== right.start) return left.start - right.start;\n\t\treturn left.end - right.end;\n\t});\n};\n\nconst pieAdapter: TransportAdapter = {\n\tid: \"pie\",\n\tresolveSynthesisUrl: (config) => {\n\t\tconst endpointMode = resolveEndpointMode(config, \"pie\");\n\t\tconst base = trimTrailingSlash(config.apiEndpoint);\n\t\treturn endpointMode === \"rootPost\" ? base : `${base}/synthesize`;\n\t},\n\tbuildRequestBody: (text, config) => {\n\t\tconst providerOptions = (config.providerOptions ||\n\t\t\t{}) as Record<string, unknown>;\n\t\tconst engine =\n\t\t\ttypeof config.engine === \"string\"\n\t\t\t\t? config.engine\n\t\t\t\t: typeof providerOptions.engine === \"string\"\n\t\t\t\t\t? providerOptions.engine\n\t\t\t\t\t: undefined;\n\t\tconst sampleRate =\n\t\t\ttypeof providerOptions.sampleRate === \"number\" &&\n\t\t\tNumber.isFinite(providerOptions.sampleRate)\n\t\t\t\t? providerOptions.sampleRate\n\t\t\t\t: undefined;\n\t\tconst format =\n\t\t\tproviderOptions.format === \"mp3\" ||\n\t\t\tproviderOptions.format === \"ogg\" ||\n\t\t\tproviderOptions.format === \"pcm\"\n\t\t\t\t? providerOptions.format\n\t\t\t\t: undefined;\n\t\tconst speechMarkTypes = Array.isArray(providerOptions.speechMarkTypes)\n\t\t\t? providerOptions.speechMarkTypes.filter(\n\t\t\t\t\t(entry): entry is \"word\" | \"sentence\" | \"ssml\" =>\n\t\t\t\t\t\tentry === \"word\" || entry === \"sentence\" || entry === \"ssml\",\n\t\t\t\t)\n\t\t\t: undefined;\n\t\treturn {\n\t\t\ttext,\n\t\t\tprovider: config.provider || \"polly\",\n\t\t\tvoice: config.voice,\n\t\t\tlanguage: config.language,\n\t\t\trate: config.rate,\n\t\t\tengine,\n\t\t\tsampleRate,\n\t\t\tformat,\n\t\t\tspeechMarkTypes,\n\t\t\tincludeSpeechMarks: true,\n\t\t};\n\t},\n\tparseResponse: async (response) => {\n\t\tconst data: SynthesizeAPIResponse = await response.json();\n\t\treturn {\n\t\t\taudio: {\n\t\t\t\tkind: \"base64\",\n\t\t\t\tdata: data.audio,\n\t\t\t\tcontentType: data.contentType,\n\t\t\t},\n\t\t\tspeechMarks: Array.isArray(data.speechMarks) ? data.speechMarks : [],\n\t\t};\n\t},\n};\n\nconst customAdapter: TransportAdapter = {\n\tid: \"custom\",\n\tresolveSynthesisUrl: (config) => {\n\t\tconst endpointMode = resolveEndpointMode(config, \"custom\");\n\t\tconst base = trimTrailingSlash(config.apiEndpoint);\n\t\treturn endpointMode === \"synthesizePath\" ? `${base}/synthesize` : base;\n\t},\n\tbuildRequestBody: (text, config) => {\n\t\tconst providerOptions = (config.providerOptions || {}) as Record<string, unknown>;\n\t\tconst langId =\n\t\t\ttypeof providerOptions.lang_id === \"string\"\n\t\t\t\t? providerOptions.lang_id\n\t\t\t\t: config.language || \"en-US\";\n\t\tconst cache =\n\t\t\ttypeof providerOptions.cache === \"boolean\" ? providerOptions.cache : true;\n\t\treturn {\n\t\t\ttext,\n\t\t\tspeedRate: resolveSpeedRate(config),\n\t\t\tlang_id: langId,\n\t\t\tcache,\n\t\t};\n\t},\n\tparseResponse: async (response, config, headers, signal) => {\n\t\tconst data: CustomTransportResponse = await response.json();\n\t\tconst marksHeaders: Record<string, string> = {};\n\t\tif (config.includeAuthOnAssetFetch) {\n\t\t\tfor (const [key, value] of Object.entries(headers)) {\n\t\t\t\tif (key.toLowerCase() === \"authorization\") {\n\t\t\t\t\tmarksHeaders[key] = value;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tlet speechMarks: NormalizedSynthesisResult[\"speechMarks\"] = [];\n\t\tconst inlineSpeechMarks = parseInlineSpeechMarks(data.speechMarks);\n\t\tif (inlineSpeechMarks.length > 0) {\n\t\t\tspeechMarks = inlineSpeechMarks;\n\t\t} else if (typeof data.word === \"string\" && data.word.length > 0) {\n\t\t\tconst marksUrl = parseAssetUrl(data.word, config);\n\t\t\tif (marksUrl !== null) {\n\t\t\t\tconst effectiveMarksHeaders = isOriginTrustedForAuth(marksUrl, config)\n\t\t\t\t\t? marksHeaders\n\t\t\t\t\t: scrubAuthHeaders(marksHeaders);\n\t\t\t\tconst marksResponse = await fetch(marksUrl.toString(), {\n\t\t\t\t\theaders: effectiveMarksHeaders,\n\t\t\t\t\tsignal,\n\t\t\t\t\t// Fail loud on redirects; the origin allow-list decision\n\t\t\t\t\t// is made pre-redirect and a silent hop could leak auth\n\t\t\t\t\t// to an unlisted origin.\n\t\t\t\t\tredirect: \"error\",\n\t\t\t\t});\n\t\t\t\tif (marksResponse.ok) {\n\t\t\t\t\tconst marksRaw = await marksResponse.text();\n\t\t\t\t\tspeechMarks = parseJSONLSpeechMarks(marksRaw);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tconst parsedAudioUrl = parseAssetUrl(data.audioContent, config);\n\t\tif (parsedAudioUrl === null) {\n\t\t\tthrow new Error(\"TTS server returned an invalid audio URL\");\n\t\t}\n\t\treturn {\n\t\t\taudio: {\n\t\t\t\tkind: \"url\",\n\t\t\t\turl: parsedAudioUrl.toString(),\n\t\t\t},\n\t\t\tspeechMarks,\n\t\t};\n\t},\n};\n\nconst ADAPTERS: Record<TransportAdapter[\"id\"], TransportAdapter> = {\n\tpie: pieAdapter,\n\tcustom: customAdapter,\n};\n\n/**\n * Provider implementation that handles audio playback\n */\nclass ServerTTSProviderImpl implements ITTSProviderImplementation {\n\tprivate config: ServerTTSProviderConfig;\n\tprivate adapter: TransportAdapter;\n\tprivate currentAudio: HTMLAudioElement | null = null;\n\tprivate pausedState = false;\n\tprivate wordTimings: WordTiming[] = [];\n\tprivate highlightInterval: number | null = null;\n\tprivate intentionallyStopped = false;\n\tprivate activeSynthesisController: AbortController | null = null;\n\tprivate synthesisRunId = 0;\n\tprivate readonly telemetryReporter: TelemetryReporter | undefined;\n\n\tpublic onWordBoundary?: (\n\t\tword: string,\n\t\tposition: number,\n\t\tlength?: number,\n\t) => void;\n\n\tconstructor(config: ServerTTSProviderConfig, adapter: TransportAdapter) {\n\t\tthis.config = config;\n\t\tthis.adapter = adapter;\n\t\tthis.telemetryReporter = getTelemetryReporter(config);\n\t}\n\n\tprivate async emitTelemetry(\n\t\teventName: string,\n\t\tpayload?: Record<string, unknown>,\n\t): Promise<void> {\n\t\ttry {\n\t\t\tawait this.telemetryReporter?.(eventName, payload);\n\t\t} catch (error) {\n\t\t\tconsole.warn(\"[ServerTTSProvider] telemetry callback failed:\", error);\n\t\t}\n\t}\n\n\tasync speak(text: string): Promise<void> {\n\t\t// Stop any current playback\n\t\tthis.stop();\n\n\t\t// Reset intentionally stopped flag for new playback\n\t\tthis.intentionallyStopped = false;\n\t\tconst runId = ++this.synthesisRunId;\n\t\tconst synthesisController = new AbortController();\n\t\tthis.activeSynthesisController = synthesisController;\n\n\t\t// Call server API to synthesize speech\n\t\tconst { audioUrl, wordTimings } = await this.synthesizeSpeech(\n\t\t\ttext,\n\t\t\tsynthesisController.signal,\n\t\t\trunId,\n\t\t);\n\t\tif (runId !== this.synthesisRunId) {\n\t\t\tURL.revokeObjectURL(audioUrl);\n\t\t\treturn;\n\t\t}\n\n\t\tthis.wordTimings = wordTimings;\n\n\t\treturn new Promise((resolve, reject) => {\n\t\t\t// Create audio element\n\t\t\tconst audio = new Audio(audioUrl);\n\t\t\tthis.currentAudio = audio;\n\n\t\t\t// Apply volume from config\n\t\t\tif (this.config.volume !== undefined) {\n\t\t\t\taudio.volume = Math.max(0, Math.min(1, this.config.volume));\n\t\t\t}\n\n\t\t\t// Setup event handlers\n\t\t\taudio.onplay = () => {\n\t\t\t\tthis.pausedState = false;\n\n\t\t\t\t// Start word highlighting\n\t\t\t\tif (this.onWordBoundary && this.wordTimings.length > 0) {\n\t\t\t\t\tthis.startWordHighlighting();\n\t\t\t\t}\n\t\t\t};\n\n\t\t\taudio.onended = () => {\n\t\t\t\tthis.stopWordHighlighting();\n\t\t\t\tURL.revokeObjectURL(audioUrl);\n\t\t\t\tthis.currentAudio = null;\n\t\t\t\tthis.wordTimings = [];\n\t\t\t\tresolve();\n\t\t\t};\n\n\t\t\taudio.onerror = (event) => {\n\t\t\t\tthis.stopWordHighlighting();\n\t\t\t\tURL.revokeObjectURL(audioUrl);\n\t\t\t\tthis.currentAudio = null;\n\t\t\t\tthis.wordTimings = [];\n\t\t\t\tvoid event;\n\t\t\t\t// Only reject if this wasn't an intentional stop\n\t\t\t\tif (!this.intentionallyStopped) {\n\t\t\t\t\treject(new Error(\"Failed to play audio from server\"));\n\t\t\t\t} else {\n\t\t\t\t\t// Intentional stop, resolve normally\n\t\t\t\t\tresolve();\n\t\t\t\t}\n\t\t\t};\n\n\t\t\taudio.onpause = () => {\n\t\t\t\tthis.stopWordHighlighting();\n\t\t\t\tthis.pausedState = true;\n\t\t\t};\n\n\t\t\t// Start playback\n\t\t\taudio.play().catch(reject);\n\t\t});\n\t}\n\n\t/**\n\t * Call server API to synthesize speech\n\t */\n\tprivate async synthesizeSpeech(\n\t\ttext: string,\n\t\tsignal: AbortSignal,\n\t\trunId: number,\n\t): Promise<{ audioUrl: string; wordTimings: WordTiming[] }> {\n\t\tconst synthStartedAt = Date.now();\n\t\tawait this.emitTelemetry(\"pie-tool-backend-call-start\", {\n\t\t\ttoolId: \"tts\",\n\t\t\tbackend: this.config.provider || \"server\",\n\t\t\toperation: \"synthesize-speech\",\n\t\t});\n\t\tconst headers: Record<string, string> = {\n\t\t\t\"Content-Type\": \"application/json\",\n\t\t\t...this.config.headers,\n\t\t};\n\n\t\t// Add authentication if provided\n\t\tif (this.config.authToken) {\n\t\t\theaders[\"Authorization\"] = `Bearer ${this.config.authToken}`;\n\t\t}\n\n\t\tconst synthUrl = this.adapter.resolveSynthesisUrl(this.config);\n\t\tconst requestBody = this.adapter.buildRequestBody(text, this.config);\n\t\tconst response = await (async () => {\n\t\t\ttry {\n\t\t\t\treturn await fetch(synthUrl, {\n\t\t\t\t\tmethod: \"POST\",\n\t\t\t\t\theaders,\n\t\t\t\t\tbody: JSON.stringify(requestBody),\n\t\t\t\t\tsignal,\n\t\t\t\t});\n\t\t\t} catch (error) {\n\t\t\t\tawait this.emitTelemetry(\"pie-tool-backend-call-error\", {\n\t\t\t\t\ttoolId: \"tts\",\n\t\t\t\t\tbackend: this.config.provider || \"server\",\n\t\t\t\t\toperation: \"synthesize-speech\",\n\t\t\t\t\tduration: Date.now() - synthStartedAt,\n\t\t\t\t\terrorType: \"TTSBackendNetworkError\",\n\t\t\t\t\tmessage: error instanceof Error ? error.message : String(error),\n\t\t\t\t});\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t})();\n\n\t\tif (!response.ok) {\n\t\t\tconst rawBody = await response.text().catch(() => \"\");\n\t\t\tlet errorData: Record<string, unknown> = {};\n\t\t\tif (rawBody) {\n\t\t\t\ttry {\n\t\t\t\t\terrorData = JSON.parse(rawBody) as Record<string, unknown>;\n\t\t\t\t} catch (parseError) {\n\t\t\t\t\tconsole.warn(\n\t\t\t\t\t\t\"[ServerTTSProvider] non-OK response body was not JSON\",\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tstatus: response.status,\n\t\t\t\t\t\t\turl: synthUrl,\n\t\t\t\t\t\t\trawBodyPreview: rawBody.slice(0, 500),\n\t\t\t\t\t\t\tparseError:\n\t\t\t\t\t\t\t\tparseError instanceof Error\n\t\t\t\t\t\t\t\t\t? parseError.message\n\t\t\t\t\t\t\t\t\t: String(parseError),\n\t\t\t\t\t\t},\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t\tconst errorMessage =\n\t\t\t\t(typeof errorData.message === \"string\" && errorData.message) ||\n\t\t\t\t(typeof (errorData.error as { message?: string })?.message === \"string\"\n\t\t\t\t\t? (errorData.error as { message: string }).message\n\t\t\t\t\t: undefined) ||\n\t\t\t\t`Server returned ${response.status}`;\n\t\t\tawait this.emitTelemetry(\"pie-tool-backend-call-error\", {\n\t\t\t\ttoolId: \"tts\",\n\t\t\t\tbackend: this.config.provider || \"server\",\n\t\t\t\toperation: \"synthesize-speech\",\n\t\t\t\tduration: Date.now() - synthStartedAt,\n\t\t\t\tstatusCode: response.status,\n\t\t\t\terrorType: \"TTSBackendRequestError\",\n\t\t\t\tmessage: errorMessage,\n\t\t\t});\n\t\t\tconst textPreview = text.replace(/\\s+/g, \" \").trim().slice(0, 120);\n\t\t\tconsole.error(\"[ServerTTSProvider] synthesize request failed\", {\n\t\t\t\tstatus: response.status,\n\t\t\t\turl: synthUrl,\n\t\t\t\tbackend: this.config.provider || \"server\",\n\t\t\t\tmessage: errorMessage,\n\t\t\t\tresponseBody: errorData,\n\t\t\t\ttextLength: text.length,\n\t\t\t\ttextPreview:\n\t\t\t\t\ttextPreview.length < text.length ? `${textPreview}…` : textPreview,\n\t\t\t});\n\t\t\tthrow new Error(errorMessage);\n\t\t}\n\n\t\tconst normalized = await this.adapter.parseResponse(\n\t\t\tresponse,\n\t\t\tthis.config,\n\t\t\theaders,\n\t\t\tsignal,\n\t\t);\n\t\tif (runId !== this.synthesisRunId || signal.aborted) {\n\t\t\tthrow new Error(\"Synthesis superseded by a newer request\");\n\t\t}\n\n\t\tlet audioBlob: Blob;\n\t\tif (normalized.audio.kind === \"base64\") {\n\t\t\taudioBlob = this.base64ToBlob(\n\t\t\t\tnormalized.audio.data,\n\t\t\t\tnormalized.audio.contentType,\n\t\t\t);\n\t\t} else {\n\t\t\tconst audioAssetUrl = normalized.audio.url;\n\t\t\tconst assetFetchStartedAt = Date.now();\n\t\t\tawait this.emitTelemetry(\"pie-tool-backend-call-start\", {\n\t\t\t\ttoolId: \"tts\",\n\t\t\t\tbackend: this.config.provider || \"server\",\n\t\t\t\toperation: \"fetch-synthesized-audio-asset\",\n\t\t\t});\n\t\t\tconst assetHeaders: Record<string, string> = {};\n\t\t\tif (this.config.includeAuthOnAssetFetch) {\n\t\t\t\tif (this.config.authToken) {\n\t\t\t\t\tassetHeaders[\"Authorization\"] = `Bearer ${this.config.authToken}`;\n\t\t\t\t}\n\t\t\t}\n\t\t\tconst parsedAssetUrl = parseAssetUrl(audioAssetUrl, this.config);\n\t\t\tif (parsedAssetUrl === null) {\n\t\t\t\tawait this.emitTelemetry(\"pie-tool-backend-call-error\", {\n\t\t\t\t\ttoolId: \"tts\",\n\t\t\t\t\tbackend: this.config.provider || \"server\",\n\t\t\t\t\toperation: \"fetch-synthesized-audio-asset\",\n\t\t\t\t\tduration: Date.now() - assetFetchStartedAt,\n\t\t\t\t\terrorType: \"TTSAssetInvalidUrl\",\n\t\t\t\t\tmessage: \"TTS asset URL rejected (non-http(s) or malformed)\",\n\t\t\t\t});\n\t\t\t\tthrow new Error(\"TTS asset URL rejected (non-http(s) or malformed)\");\n\t\t\t}\n\t\t\tconst effectiveAssetHeaders = isOriginTrustedForAuth(\n\t\t\t\tparsedAssetUrl,\n\t\t\t\tthis.config,\n\t\t\t)\n\t\t\t\t? assetHeaders\n\t\t\t\t: scrubAuthHeaders(assetHeaders);\n\t\t\tconst audioResponse = await (async () => {\n\t\t\t\ttry {\n\t\t\t\t\treturn await fetch(parsedAssetUrl.toString(), {\n\t\t\t\t\t\theaders: effectiveAssetHeaders,\n\t\t\t\t\t\tsignal,\n\t\t\t\t\t\tredirect: \"error\",\n\t\t\t\t\t});\n\t\t\t\t} catch (error) {\n\t\t\t\t\tawait this.emitTelemetry(\"pie-tool-backend-call-error\", {\n\t\t\t\t\t\ttoolId: \"tts\",\n\t\t\t\t\t\tbackend: this.config.provider || \"server\",\n\t\t\t\t\t\toperation: \"fetch-synthesized-audio-asset\",\n\t\t\t\t\t\tduration: Date.now() - assetFetchStartedAt,\n\t\t\t\t\t\terrorType: \"TTSAssetNetworkError\",\n\t\t\t\t\t\tmessage: error instanceof Error ? error.message : String(error),\n\t\t\t\t\t});\n\t\t\t\t\tthrow error;\n\t\t\t\t}\n\t\t\t})();\n\t\t\tif (!audioResponse.ok) {\n\t\t\t\tawait this.emitTelemetry(\"pie-tool-backend-call-error\", {\n\t\t\t\t\ttoolId: \"tts\",\n\t\t\t\t\tbackend: this.config.provider || \"server\",\n\t\t\t\t\toperation: \"fetch-synthesized-audio-asset\",\n\t\t\t\t\tduration: Date.now() - assetFetchStartedAt,\n\t\t\t\t\tstatusCode: audioResponse.status,\n\t\t\t\t\terrorType: \"TTSAssetFetchError\",\n\t\t\t\t\tmessage: `Failed to download synthesized audio (${audioResponse.status})`,\n\t\t\t\t});\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`Failed to download synthesized audio (${audioResponse.status})`,\n\t\t\t\t);\n\t\t\t}\n\t\t\taudioBlob = await audioResponse.blob();\n\t\t\tawait this.emitTelemetry(\"pie-tool-backend-call-success\", {\n\t\t\t\ttoolId: \"tts\",\n\t\t\t\tbackend: this.config.provider || \"server\",\n\t\t\t\toperation: \"fetch-synthesized-audio-asset\",\n\t\t\t\tduration: Date.now() - assetFetchStartedAt,\n\t\t\t});\n\t\t}\n\t\tconst audioUrl = URL.createObjectURL(audioBlob);\n\n\t\t// Convert speech marks to word timings\n\t\tconst wordTimings = this.parseSpeechMarks(normalized.speechMarks);\n\t\tawait this.emitTelemetry(\"pie-tool-backend-call-success\", {\n\t\t\ttoolId: \"tts\",\n\t\t\tbackend: this.config.provider || \"server\",\n\t\t\toperation: \"synthesize-speech\",\n\t\t\tduration: Date.now() - synthStartedAt,\n\t\t});\n\n\t\treturn { audioUrl, wordTimings };\n\t}\n\n\t/**\n\t * Convert base64 to Blob\n\t */\n\tprivate base64ToBlob(base64: string, contentType: string): Blob {\n\t\tconst byteCharacters = atob(base64);\n\t\tconst byteNumbers = new Array(byteCharacters.length);\n\n\t\tfor (let i = 0; i < byteCharacters.length; i++) {\n\t\t\tbyteNumbers[i] = byteCharacters.charCodeAt(i);\n\t\t}\n\n\t\tconst byteArray = new Uint8Array(byteNumbers);\n\t\treturn new Blob([byteArray], { type: contentType });\n\t}\n\n\t/**\n\t * Parse speech marks into word timings\n\t */\n\tprivate parseSpeechMarks(\n\t\tmarks: SynthesizeAPIResponse[\"speechMarks\"],\n\t): WordTiming[] {\n\t\treturn marks\n\t\t\t.filter((mark) => mark.type === \"word\")\n\t\t\t.map((mark, index) => ({\n\t\t\t\ttime: mark.time,\n\t\t\t\twordIndex: index,\n\t\t\t\tcharIndex: mark.start,\n\t\t\t\tlength: mark.end - mark.start,\n\t\t\t\tword: mark.value,\n\t\t\t}));\n\t}\n\n\t/**\n\t * Start word highlighting synchronized with audio playback\n\t */\n\tprivate startWordHighlighting(): void {\n\t\tthis.stopWordHighlighting();\n\n\t\tif (\n\t\t\t!this.currentAudio ||\n\t\t\t!this.onWordBoundary ||\n\t\t\tthis.wordTimings.length === 0\n\t\t) {\n\t\t\tconsole.log(\"[ServerTTSProvider] Cannot start highlighting:\", {\n\t\t\t\thasAudio: !!this.currentAudio,\n\t\t\t\thasCallback: !!this.onWordBoundary,\n\t\t\t\twordTimingsCount: this.wordTimings.length,\n\t\t\t});\n\t\t\treturn;\n\t\t}\n\n\t\tconsole.log(\n\t\t\t\"[ServerTTSProvider] Starting word highlighting with\",\n\t\t\tthis.wordTimings.length,\n\t\t\t\"word timings\",\n\t\t);\n\t\tconsole.log(\n\t\t\t\"[ServerTTSProvider] Playback rate:\",\n\t\t\tthis.currentAudio.playbackRate,\n\t\t);\n\t\tconsole.log(\n\t\t\t\"[ServerTTSProvider] First 3 timings:\",\n\t\t\tthis.wordTimings.slice(0, 3),\n\t\t);\n\n\t\tlet lastWordIndex = -1;\n\n\t\t// Poll every 50ms to check current playback time\n\t\tthis.highlightInterval = window.setInterval(() => {\n\t\t\tif (!this.currentAudio) {\n\t\t\t\tthis.stopWordHighlighting();\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Get current playback time in milliseconds\n\t\t\tconst currentTime = this.currentAudio.currentTime * 1000;\n\n\t\t\t// Find words that should be highlighted at current time\n\t\t\tfor (let i = 0; i < this.wordTimings.length; i++) {\n\t\t\t\tconst timing = this.wordTimings[i];\n\n\t\t\t\tif (currentTime >= timing.time && i > lastWordIndex) {\n\t\t\t\t\t// Fire word boundary callback\n\t\t\t\t\tif (this.onWordBoundary) {\n\t\t\t\t\t\tconsole.log(\n\t\t\t\t\t\t\t\"[ServerTTSProvider] Highlighting word at charIndex:\",\n\t\t\t\t\t\t\ttiming.charIndex,\n\t\t\t\t\t\t\t\"length:\",\n\t\t\t\t\t\t\ttiming.length,\n\t\t\t\t\t\t\t\"time:\",\n\t\t\t\t\t\t\ttiming.time,\n\t\t\t\t\t\t\t\"currentTime:\",\n\t\t\t\t\t\t\tcurrentTime,\n\t\t\t\t\t\t);\n\t\t\t\t\t\tthis.onWordBoundary(timing.word, timing.charIndex, timing.length);\n\t\t\t\t\t}\n\t\t\t\t\tlastWordIndex = i;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}, 50); // 50ms polling = 20 times per second\n\t}\n\n\t/**\n\t * Stop word highlighting\n\t */\n\tprivate stopWordHighlighting(): void {\n\t\tif (this.highlightInterval !== null) {\n\t\t\tclearInterval(this.highlightInterval);\n\t\t\tthis.highlightInterval = null;\n\t\t}\n\t}\n\n\tpause(): void {\n\t\tif (this.currentAudio && !this.pausedState) {\n\t\t\tthis.currentAudio.pause();\n\t\t\tthis.stopWordHighlighting();\n\t\t\tthis.pausedState = true;\n\t\t}\n\t}\n\n\tresume(): void {\n\t\tif (this.currentAudio && this.pausedState) {\n\t\t\tthis.currentAudio.play();\n\t\t\tthis.pausedState = false;\n\n\t\t\t// Resume word highlighting\n\t\t\tif (this.onWordBoundary && this.wordTimings.length > 0) {\n\t\t\t\tthis.startWordHighlighting();\n\t\t\t}\n\t\t}\n\t}\n\n\tstop(): void {\n\t\tthis.synthesisRunId += 1;\n\t\tif (this.activeSynthesisController) {\n\t\t\tthis.activeSynthesisController.abort();\n\t\t\tthis.activeSynthesisController = null;\n\t\t}\n\t\tthis.stopWordHighlighting();\n\n\t\tif (this.currentAudio) {\n\t\t\t// Mark as intentionally stopped to prevent error handler from rejecting\n\t\t\tthis.intentionallyStopped = true;\n\t\t\tthis.currentAudio.pause();\n\t\t\tif (this.currentAudio.src) {\n\t\t\t\tURL.revokeObjectURL(this.currentAudio.src);\n\t\t\t}\n\t\t\tthis.currentAudio.src = \"\";\n\t\t\tthis.currentAudio = null;\n\t\t}\n\n\t\tthis.pausedState = false;\n\t\tthis.wordTimings = [];\n\t}\n\n\tisPlaying(): boolean {\n\t\treturn this.currentAudio !== null && !this.pausedState;\n\t}\n\n\tisPaused(): boolean {\n\t\treturn this.pausedState;\n\t}\n\n\t/**\n\t * Update settings dynamically (rate, pitch, voice)\n\t * Note: Voice changes require resynthesis, so voice updates are stored but\n\t * take effect on the next speak() call. Rate can be applied to current playback.\n\t */\n\tupdateSettings(settings: Partial<ServerTTSProviderConfig>): void {\n\t\t// Update config\n\t\tif (settings.rate !== undefined) {\n\t\t\tthis.config.rate = settings.rate;\n\t\t}\n\t\tif (settings.pitch !== undefined) {\n\t\t\t// Server-side pitch is baked into audio, so this only affects next speak()\n\t\t\tthis.config.pitch = settings.pitch;\n\t\t}\n\t\tif (settings.voice !== undefined) {\n\t\t\t// Voice change requires resynthesis, affects next speak()\n\t\t\tthis.config.voice = settings.voice;\n\t\t}\n\t}\n}\n\n/**\n * Server TTS Provider\n *\n * Client-side provider that calls a server API for TTS synthesis.\n * The server handles provider selection (Polly, Google, etc.) and credential management.\n */\nexport class ServerTTSProvider implements ITTSProvider {\n\treadonly providerId = \"server-tts\";\n\treadonly providerName = \"Server TTS\";\n\treadonly version = \"1.0.0\";\n\n\tprivate config: ServerTTSProviderConfig | null = null;\n\tprivate adapter: TransportAdapter | null = null;\n\tprivate telemetryReporter: TelemetryReporter | undefined;\n\n\tprivate async emitTelemetry(\n\t\teventName: string,\n\t\tpayload?: Record<string, unknown>,\n\t): Promise<void> {\n\t\ttry {\n\t\t\tawait this.telemetryReporter?.(eventName, payload);\n\t\t} catch (error) {\n\t\t\tconsole.warn(\"[ServerTTSProvider] telemetry callback failed:\", error);\n\t\t}\n\t}\n\n\t/**\n\t * Initialize the server TTS provider.\n\t *\n\t * This is designed to be fast by default (no API calls).\n\t * Set validateEndpoint: true in config to test API availability during initialization.\n\t *\n\t * @performance Default: <10ms, With validation: 100-500ms\n\t */\n\tasync initialize(config: TTSConfig): Promise<ITTSProviderImplementation> {\n\t\tconst serverConfig = config as ServerTTSProviderConfig;\n\n\t\tif (!serverConfig.apiEndpoint) {\n\t\t\tthrow new Error(\"apiEndpoint is required for ServerTTSProvider\");\n\t\t}\n\n\t\tthis.config = serverConfig;\n\t\tthis.telemetryReporter = getTelemetryReporter(serverConfig);\n\t\tconst transportMode = resolveTransportMode(serverConfig);\n\t\tthis.adapter = ADAPTERS[transportMode];\n\n\t\t// Only test API availability if explicitly requested (slower but safer)\n\t\tif (serverConfig.validateEndpoint) {\n\t\t\tconst validationStartedAt = Date.now();\n\t\t\tawait this.emitTelemetry(\"pie-tool-backend-call-start\", {\n\t\t\t\ttoolId: \"tts\",\n\t\t\t\tbackend: serverConfig.provider || \"server\",\n\t\t\t\toperation: \"validate-endpoint\",\n\t\t\t});\n\t\t\tconst available = await this.testAPIAvailability();\n\t\t\tif (!available) {\n\t\t\t\tawait this.emitTelemetry(\"pie-tool-backend-call-error\", {\n\t\t\t\t\ttoolId: \"tts\",\n\t\t\t\t\tbackend: serverConfig.provider || \"server\",\n\t\t\t\t\toperation: \"validate-endpoint\",\n\t\t\t\t\tduration: Date.now() - validationStartedAt,\n\t\t\t\t\terrorType: \"TTSEndpointValidationError\",\n\t\t\t\t\tmessage: `Server TTS API not available at ${serverConfig.apiEndpoint}`,\n\t\t\t\t});\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`Server TTS API not available at ${serverConfig.apiEndpoint}`,\n\t\t\t\t);\n\t\t\t}\n\t\t\tawait this.emitTelemetry(\"pie-tool-backend-call-success\", {\n\t\t\t\ttoolId: \"tts\",\n\t\t\t\tbackend: serverConfig.provider || \"server\",\n\t\t\t\toperation: \"validate-endpoint\",\n\t\t\t\tduration: Date.now() - validationStartedAt,\n\t\t\t});\n\t\t}\n\n\t\treturn new ServerTTSProviderImpl(serverConfig, this.adapter);\n\t}\n\n\t/**\n\t * Test if API endpoint is available (with timeout).\n\t *\n\t * @performance 100-500ms depending on network\n\t */\n\tprivate async testAPIAvailability(): Promise<boolean> {\n\t\tif (!this.config || !this.adapter) return false;\n\n\t\ttry {\n\t\t\tconst headers: Record<string, string> = { ...this.config.headers };\n\n\t\t\tif (this.config.authToken) {\n\t\t\t\theaders[\"Authorization\"] = `Bearer ${this.config.authToken}`;\n\t\t\t}\n\n\t\t\t// Create abort controller for timeout\n\t\t\tconst controller = new AbortController();\n\t\t\tconst timeoutId = setTimeout(() => controller.abort(), 5000); // 5 second timeout\n\n\t\t\tconst mode = resolveValidationMode(this.config, this.adapter.id);\n\t\t\tif (mode === \"none\") {\n\t\t\t\tclearTimeout(timeoutId);\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tconst validationUrl =\n\t\t\t\tmode === \"voices\"\n\t\t\t\t\t? resolveVoicesValidationUrl(this.config)\n\t\t\t\t\t: this.adapter.resolveSynthesisUrl(this.config);\n\t\t\tconst method = mode === \"voices\" ? \"GET\" : \"OPTIONS\";\n\t\t\ttry {\n\t\t\t\tconst response = await fetch(validationUrl, {\n\t\t\t\t\tmethod,\n\t\t\t\t\theaders,\n\t\t\t\t\tsignal: controller.signal,\n\t\t\t\t});\n\t\t\t\tclearTimeout(timeoutId);\n\t\t\t\t// Some endpoints may not accept OPTIONS; treat 405 as reachable.\n\t\t\t\treturn response.ok || response.status === 405;\n\t\t\t} catch {\n\t\t\t\tclearTimeout(timeoutId);\n\t\t\t\treturn false;\n\t\t\t}\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tsupportsFeature(feature: TTSFeature): boolean {\n\t\tswitch (feature) {\n\t\t\tcase \"pause\":\n\t\t\tcase \"resume\":\n\t\t\tcase \"wordBoundary\":\n\t\t\tcase \"voiceSelection\":\n\t\t\tcase \"rateControl\":\n\t\t\t\treturn true;\n\t\t\tcase \"pitchControl\":\n\t\t\t\t// Depends on server provider, assume no for safety\n\t\t\t\treturn false;\n\t\t\tdefault:\n\t\t\t\treturn false;\n\t\t}\n\t}\n\n\tgetCapabilities(): TTSProviderCapabilities {\n\t\tconst mode = this.config ? resolveTransportMode(this.config) : \"pie\";\n\t\treturn {\n\t\t\tsupportsPause: true,\n\t\t\tsupportsResume: true,\n\t\t\tsupportsWordBoundary: true, // ✅ Via speech marks from server\n\t\t\tsupportsVoiceSelection: true,\n\t\t\tsupportsRateControl: true,\n\t\t\tsupportsPitchControl: false, // Depends on server provider\n\t\t\tsupportsSSML: resolveSupportsSSML(this.config),\n\t\t\tmaxTextLength: MAX_TEXT_LENGTH_BY_MODE[mode],\n\t\t};\n\t}\n\n\tdestroy(): void {\n\t\tthis.config = null;\n\t\tthis.adapter = null;\n\t\tthis.telemetryReporter = undefined;\n\t}\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pie-players/tts-client-server",
3
- "version": "0.3.44",
3
+ "version": "0.3.45",
4
4
  "description": "Client-side TTS provider that calls server API for synthesis",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -38,7 +38,7 @@
38
38
  "access": "public"
39
39
  },
40
40
  "dependencies": {
41
- "@pie-players/pie-tts": "0.3.44"
41
+ "@pie-players/pie-tts": "0.3.45"
42
42
  },
43
43
  "devDependencies": {
44
44
  "typescript": "^5.9.3",