@creativeorange/azure-text-to-speech 2.2.3 → 3.0.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.
@@ -4,6 +4,193 @@ var __publicField = (obj, key, value) => {
4
4
  __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
5
5
  return value;
6
6
  };
7
+ const DEFAULT_TOKEN_LIFETIME_MS = 8 * 60 * 1e3;
8
+ function createSpeechAuthorizationProvider(options) {
9
+ var _a;
10
+ const hasTokenEndpoint = typeof options.tokenEndpoint === "string" && options.tokenEndpoint.trim() !== "";
11
+ const hasProvider = typeof options.getAuthorizationToken === "function";
12
+ if (hasTokenEndpoint && hasProvider) {
13
+ throw new Error(
14
+ "Provide either tokenEndpoint or getAuthorizationToken, not both."
15
+ );
16
+ }
17
+ if (!hasTokenEndpoint && !hasProvider) {
18
+ throw new Error(
19
+ "A tokenEndpoint or getAuthorizationToken provider is required."
20
+ );
21
+ }
22
+ if (hasProvider) {
23
+ return options.getAuthorizationToken;
24
+ }
25
+ const tokenEndpoint = options.tokenEndpoint.trim();
26
+ const requestOptions = (_a = options.tokenRequestOptions) != null ? _a : {};
27
+ return async () => {
28
+ var _a2, _b, _c;
29
+ let response;
30
+ try {
31
+ response = await fetch(tokenEndpoint, {
32
+ method: "GET",
33
+ cache: (_a2 = requestOptions.cache) != null ? _a2 : "no-store",
34
+ credentials: (_b = requestOptions.credentials) != null ? _b : "same-origin",
35
+ headers: {
36
+ Accept: "application/json",
37
+ ...(_c = requestOptions.headers) != null ? _c : {}
38
+ }
39
+ });
40
+ } catch {
41
+ throw createSafeError(
42
+ "Could not reach the speech token endpoint.",
43
+ "TOKEN_ENDPOINT_UNREACHABLE"
44
+ );
45
+ }
46
+ if (!response.ok) {
47
+ throw createSafeError(
48
+ `Speech token endpoint returned HTTP ${response.status}.`,
49
+ "TOKEN_ENDPOINT_HTTP_ERROR"
50
+ );
51
+ }
52
+ let payload;
53
+ try {
54
+ payload = await response.json();
55
+ } catch {
56
+ throw createSafeError(
57
+ "Speech token endpoint returned invalid JSON.",
58
+ "TOKEN_ENDPOINT_INVALID_JSON"
59
+ );
60
+ }
61
+ return validateSpeechAuthorization(payload);
62
+ };
63
+ }
64
+ function validateSpeechAuthorization(value) {
65
+ if (!value || typeof value !== "object") {
66
+ throw createSafeError(
67
+ "Speech authorization response must be an object.",
68
+ "TOKEN_INVALID_RESPONSE"
69
+ );
70
+ }
71
+ const candidate = value;
72
+ const token = typeof candidate.token === "string" ? candidate.token.trim() : "";
73
+ const region = typeof candidate.region === "string" ? candidate.region.trim() : "";
74
+ if (token === "") {
75
+ throw createSafeError(
76
+ "Speech authorization token is missing or empty.",
77
+ "TOKEN_EMPTY"
78
+ );
79
+ }
80
+ if (region === "") {
81
+ throw createSafeError(
82
+ "Speech authorization region is missing or empty.",
83
+ "TOKEN_REGION_MISSING"
84
+ );
85
+ }
86
+ return { token, region };
87
+ }
88
+ function createSafeError(message, code) {
89
+ const error = new Error(sanitizeErrorText(message));
90
+ error.message = sanitizeErrorText(message);
91
+ if (code) {
92
+ error.code = code;
93
+ }
94
+ return error;
95
+ }
96
+ function toSafeErrorDetail(error) {
97
+ if (typeof error === "string") {
98
+ const message = error.trim() !== "" ? sanitizeErrorText(error) : "An unexpected speech error occurred.";
99
+ return { message };
100
+ }
101
+ if (error && typeof error === "object") {
102
+ const candidate = error;
103
+ const rawMessage = [
104
+ typeof candidate.message === "string" ? candidate.message : "",
105
+ typeof candidate.errorDetails === "string" ? candidate.errorDetails : "",
106
+ typeof candidate.reason === "string" ? candidate.reason : ""
107
+ ].filter((part) => part.trim() !== "").join(" ");
108
+ const message = rawMessage !== "" ? sanitizeErrorText(rawMessage) : "An unexpected speech error occurred.";
109
+ const detail = { message };
110
+ if (typeof candidate.code === "string" && candidate.code.trim() !== "") {
111
+ detail.code = candidate.code;
112
+ } else if (typeof candidate.code === "number") {
113
+ detail.code = String(candidate.code);
114
+ }
115
+ return detail;
116
+ }
117
+ return {
118
+ message: "An unexpected speech error occurred."
119
+ };
120
+ }
121
+ function sanitizeErrorText(message) {
122
+ return message.replace(/Authorization\s*:\s*Bearer\s+\S+/gi, "Authorization: Bearer [redacted]").replace(/Bearer\s+\S+/gi, "Bearer [redacted]").replace(
123
+ /authorization[_-]?token["']?\s*[:=]\s*["']?[^"',\s}]+/gi,
124
+ "authorizationToken=[redacted]"
125
+ ).replace(
126
+ /access[_-]?token["']?\s*[:=]\s*["']?[^"',\s}]+/gi,
127
+ "accessToken=[redacted]"
128
+ ).replace(
129
+ /["']token["']\s*:\s*["'][^"']+["']/gi,
130
+ '"token":"[redacted]"'
131
+ ).replace(
132
+ /token["']?\s*[:=]\s*["']?[^"',\s}]+/gi,
133
+ "token=[redacted]"
134
+ );
135
+ }
136
+ function isLikelyAuthorizationError(error) {
137
+ var _a;
138
+ const detail = toSafeErrorDetail(error);
139
+ const haystack = `${detail.message} ${(_a = detail.code) != null ? _a : ""}`.toLowerCase();
140
+ return haystack.includes("unauthorized") || haystack.includes("authentication") || haystack.includes("authorization") || haystack.includes("invalid token") || haystack.includes("token expired") || haystack.includes("expired token") || haystack.includes("403") || haystack.includes("401") || detail.code === "TOKEN_REFRESH_ERROR";
141
+ }
142
+ class SpeechAuthorizationManager {
143
+ constructor(options) {
144
+ __publicField(this, "authorization");
145
+ __publicField(this, "authorizationExpiresAt", 0);
146
+ __publicField(this, "authorizationRequest");
147
+ __publicField(this, "provider");
148
+ __publicField(this, "tokenLifetimeMs");
149
+ this.provider = createSpeechAuthorizationProvider(options);
150
+ this.tokenLifetimeMs = options.tokenLifetimeMs && options.tokenLifetimeMs > 0 ? options.tokenLifetimeMs : DEFAULT_TOKEN_LIFETIME_MS;
151
+ }
152
+ getTokenLifetimeMs() {
153
+ return this.tokenLifetimeMs;
154
+ }
155
+ async getAuthorization() {
156
+ if (this.authorization && Date.now() < this.authorizationExpiresAt) {
157
+ return this.authorization;
158
+ }
159
+ if (this.authorizationRequest) {
160
+ return this.authorizationRequest;
161
+ }
162
+ this.authorizationRequest = this.fetchAuthorization().then((authorization) => {
163
+ this.authorization = authorization;
164
+ this.authorizationExpiresAt = Date.now() + this.tokenLifetimeMs;
165
+ return authorization;
166
+ }).finally(() => {
167
+ this.authorizationRequest = void 0;
168
+ });
169
+ return this.authorizationRequest;
170
+ }
171
+ async refreshAuthorization() {
172
+ this.authorization = void 0;
173
+ this.authorizationExpiresAt = 0;
174
+ if (this.authorizationRequest) {
175
+ return this.authorizationRequest;
176
+ }
177
+ return this.getAuthorization();
178
+ }
179
+ clearAuthorization() {
180
+ this.authorization = void 0;
181
+ this.authorizationExpiresAt = 0;
182
+ }
183
+ async fetchAuthorization() {
184
+ try {
185
+ const authorization = await this.provider();
186
+ return validateSpeechAuthorization(authorization);
187
+ } catch (error) {
188
+ this.clearAuthorization();
189
+ const detail = toSafeErrorDetail(error);
190
+ throw createSafeError(detail.message, detail.code);
191
+ }
192
+ }
193
+ }
7
194
  var ws = {};
8
195
  var getRandomValues;
9
196
  var rnds8 = new Uint8Array(16);
@@ -1079,7 +1266,7 @@ class Timeout {
1079
1266
  throw new Error(message);
1080
1267
  }
1081
1268
  });
1082
- const clearTimeout = (timerId) => {
1269
+ const clearTimeout2 = (timerId) => {
1083
1270
  const id = Math.random();
1084
1271
  unhandledRequests.set(id, timerId);
1085
1272
  scheduledTimeoutFunctions.set(timerId, id);
@@ -1104,7 +1291,7 @@ class Timeout {
1104
1291
  return timerId;
1105
1292
  };
1106
1293
  return {
1107
- clearTimeout,
1294
+ clearTimeout: clearTimeout2,
1108
1295
  setTimeout: setTimeout2
1109
1296
  };
1110
1297
  }
@@ -8351,16 +8538,23 @@ class RestMessageAdapter {
8351
8538
  }
8352
8539
  }
8353
8540
  class SpeechToText {
8354
- constructor(key, region, sourceLanguage, targetLanguage = null) {
8355
- __publicField(this, "key");
8356
- __publicField(this, "region");
8541
+ constructor(options) {
8542
+ __publicField(this, "region", "");
8357
8543
  __publicField(this, "sourceLanguage");
8358
8544
  __publicField(this, "targetLanguage");
8359
8545
  __publicField(this, "recognizer");
8360
- this.key = key;
8361
- this.region = region;
8362
- this.sourceLanguage = sourceLanguage;
8363
- this.targetLanguage = targetLanguage !== null ? targetLanguage : sourceLanguage;
8546
+ __publicField(this, "authorizationManager");
8547
+ __publicField(this, "authorizationRefreshIntervalMs");
8548
+ __publicField(this, "authorizationRefreshTimer");
8549
+ __publicField(this, "stopPromise");
8550
+ var _a;
8551
+ if (!options || typeof options.sourceLanguage !== "string" || options.sourceLanguage.trim() === "") {
8552
+ throw new Error("A sourceLanguage is required.");
8553
+ }
8554
+ this.authorizationManager = new SpeechAuthorizationManager(options);
8555
+ this.authorizationRefreshIntervalMs = options.tokenLifetimeMs && options.tokenLifetimeMs > 0 ? options.tokenLifetimeMs : DEFAULT_TOKEN_LIFETIME_MS;
8556
+ this.sourceLanguage = options.sourceLanguage;
8557
+ this.targetLanguage = (_a = options.targetLanguage) != null ? _a : options.sourceLanguage;
8364
8558
  }
8365
8559
  async start() {
8366
8560
  await this.registerBindings(document);
@@ -8374,9 +8568,15 @@ class SpeechToText {
8374
8568
  const currentNode = nodes[i];
8375
8569
  if (currentNode.attributes) {
8376
8570
  if (currentNode.attributes.getNamedItem("co-stt.start")) {
8377
- await this.handleStartModifier(currentNode, currentNode.attributes.getNamedItem("co-stt.start"));
8571
+ await this.handleStartModifier(
8572
+ currentNode,
8573
+ currentNode.attributes.getNamedItem("co-stt.start")
8574
+ );
8378
8575
  } else if (currentNode.attributes.getNamedItem("co-stt.stop")) {
8379
- await this.handleStopModifier(currentNode, currentNode.attributes.getNamedItem("co-stt.stop"));
8576
+ await this.handleStopModifier(
8577
+ currentNode,
8578
+ currentNode.attributes.getNamedItem("co-stt.stop")
8579
+ );
8380
8580
  }
8381
8581
  }
8382
8582
  if (currentNode.childNodes.length > 0) {
@@ -8384,59 +8584,163 @@ class SpeechToText {
8384
8584
  }
8385
8585
  }
8386
8586
  }
8587
+ async createSpeechTranslationConfig() {
8588
+ const authorization = await this.authorizationManager.getAuthorization();
8589
+ const speechConfig = SpeechTranslationConfig.fromAuthorizationToken(
8590
+ authorization.token,
8591
+ authorization.region
8592
+ );
8593
+ speechConfig.speechRecognitionLanguage = this.sourceLanguage;
8594
+ speechConfig.addTargetLanguage(
8595
+ this.targetLanguage
8596
+ );
8597
+ this.region = authorization.region;
8598
+ return speechConfig;
8599
+ }
8387
8600
  async handleStartModifier(node, attr) {
8388
8601
  node.addEventListener("click", async (_) => {
8389
- const speechConfig = SpeechTranslationConfig.fromSubscription(this.key, this.region);
8390
- speechConfig.speechRecognitionLanguage = this.sourceLanguage;
8391
- speechConfig.addTargetLanguage(this.targetLanguage);
8392
- const audioConfig = AudioConfig.fromDefaultMicrophoneInput();
8393
- this.recognizer = new TranslationRecognizer(speechConfig, audioConfig);
8394
- document.dispatchEvent(new CustomEvent("COAzureSTTStartedRecording", {}));
8395
- const prevResults = [];
8396
- this.recognizer.recognizing = (sender, event) => {
8397
- const result = event.result;
8398
- if (result && result.reason === ResultReason.TranslatingSpeech) {
8399
- const translation = result.translations.get(this.targetLanguage);
8400
- prevResults["result_" + result.privOffset.toString()] = translation;
8401
- const totalResult = Object.values(prevResults).join(". ");
8402
- const inputElement = document.getElementById(attr.value);
8403
- if (inputElement !== null) {
8404
- if (inputElement instanceof HTMLInputElement) {
8405
- inputElement.value = `${totalResult} `;
8406
- } else {
8407
- inputElement.innerHTML = `${totalResult} `;
8602
+ try {
8603
+ await this.stop();
8604
+ const speechConfig = await this.createSpeechTranslationConfig();
8605
+ const audioConfig = AudioConfig.fromDefaultMicrophoneInput();
8606
+ this.recognizer = new TranslationRecognizer(speechConfig, audioConfig);
8607
+ document.dispatchEvent(new CustomEvent("COAzureSTTStartedRecording", {}));
8608
+ const prevResults = {};
8609
+ this.recognizer.recognizing = (_sender, event) => {
8610
+ const result = event.result;
8611
+ if (result && result.reason === ResultReason.TranslatingSpeech) {
8612
+ const translation = result.translations.get(this.targetLanguage);
8613
+ prevResults["result_" + result.privOffset.toString()] = translation;
8614
+ const totalResult = Object.values(prevResults).join(". ");
8615
+ const inputElement = document.getElementById(attr.value);
8616
+ if (inputElement !== null) {
8617
+ if (inputElement instanceof HTMLInputElement) {
8618
+ inputElement.value = `${totalResult} `;
8619
+ } else {
8620
+ inputElement.innerHTML = `${totalResult} `;
8621
+ }
8408
8622
  }
8409
8623
  }
8624
+ };
8625
+ this.recognizer.startContinuousRecognitionAsync(
8626
+ () => {
8627
+ this.scheduleAuthorizationRefresh();
8628
+ },
8629
+ (err) => {
8630
+ if (isLikelyAuthorizationError(err)) {
8631
+ this.authorizationManager.clearAuthorization();
8632
+ }
8633
+ this.dispatchError(err, "RECOGNITION_ERROR");
8634
+ void this.stop();
8635
+ }
8636
+ );
8637
+ } catch (error) {
8638
+ this.clearAuthorizationRefreshTimer();
8639
+ if (isLikelyAuthorizationError(error)) {
8640
+ this.authorizationManager.clearAuthorization();
8410
8641
  }
8411
- };
8412
- this.recognizer.startContinuousRecognitionAsync(
8413
- (result) => {
8414
- },
8415
- (err) => {
8416
- console.log(err);
8417
- this.stop();
8418
- }
8419
- );
8642
+ this.dispatchError(error);
8643
+ await this.stop();
8644
+ }
8420
8645
  });
8421
8646
  }
8422
- async handleStopModifier(node, attr) {
8647
+ async handleStopModifier(node, _attr) {
8423
8648
  node.addEventListener("click", async (_) => {
8424
8649
  await this.stop();
8425
8650
  });
8426
8651
  }
8427
8652
  async stop() {
8428
- if (this.recognizer !== void 0) {
8429
- this.recognizer.stopContinuousRecognitionAsync();
8430
- this.recognizer.close();
8431
- this.recognizer = void 0;
8653
+ if (this.stopPromise) {
8654
+ return this.stopPromise;
8655
+ }
8656
+ this.clearAuthorizationRefreshTimer();
8657
+ const recognizer = this.recognizer;
8658
+ if (!recognizer) {
8659
+ return;
8432
8660
  }
8433
- document.dispatchEvent(new CustomEvent("COAzureSTTStoppedRecording", {}));
8661
+ this.recognizer = void 0;
8662
+ this.stopPromise = this.stopRecognizer(recognizer).finally(() => {
8663
+ this.stopPromise = void 0;
8664
+ document.dispatchEvent(
8665
+ new CustomEvent("COAzureSTTStoppedRecording", {})
8666
+ );
8667
+ });
8668
+ return this.stopPromise;
8669
+ }
8670
+ scheduleAuthorizationRefresh() {
8671
+ this.clearAuthorizationRefreshTimer();
8672
+ this.authorizationRefreshTimer = setTimeout(async () => {
8673
+ if (!this.recognizer) {
8674
+ return;
8675
+ }
8676
+ try {
8677
+ const authorization = await this.authorizationManager.refreshAuthorization();
8678
+ if (!this.recognizer) {
8679
+ return;
8680
+ }
8681
+ this.recognizer.authorizationToken = authorization.token;
8682
+ this.region = authorization.region;
8683
+ this.scheduleAuthorizationRefresh();
8684
+ } catch (error) {
8685
+ this.dispatchError(error, "TOKEN_REFRESH_ERROR");
8686
+ await this.stop();
8687
+ }
8688
+ }, this.authorizationRefreshIntervalMs);
8689
+ }
8690
+ clearAuthorizationRefreshTimer() {
8691
+ if (!this.authorizationRefreshTimer) {
8692
+ return;
8693
+ }
8694
+ clearTimeout(this.authorizationRefreshTimer);
8695
+ this.authorizationRefreshTimer = void 0;
8696
+ }
8697
+ stopRecognizer(recognizer) {
8698
+ return new Promise((resolve) => {
8699
+ let closed = false;
8700
+ const closeOnce = () => {
8701
+ if (closed) {
8702
+ resolve();
8703
+ return;
8704
+ }
8705
+ closed = true;
8706
+ try {
8707
+ recognizer.close();
8708
+ } catch {
8709
+ } finally {
8710
+ resolve();
8711
+ }
8712
+ };
8713
+ try {
8714
+ recognizer.stopContinuousRecognitionAsync(
8715
+ () => {
8716
+ closeOnce();
8717
+ },
8718
+ () => {
8719
+ closeOnce();
8720
+ }
8721
+ );
8722
+ } catch {
8723
+ closeOnce();
8724
+ }
8725
+ });
8726
+ }
8727
+ dispatchError(error, code) {
8728
+ const detail = toSafeErrorDetail(error);
8729
+ if (code && !detail.code) {
8730
+ detail.code = code;
8731
+ }
8732
+ document.dispatchEvent(
8733
+ new CustomEvent("COAzureSTTError", {
8734
+ detail: {
8735
+ error: detail
8736
+ }
8737
+ })
8738
+ );
8434
8739
  }
8435
8740
  }
8436
8741
  class TextToSpeech {
8437
- constructor(key, region, voice, rate = 0, pitch = 0, url = "") {
8438
- __publicField(this, "key");
8439
- __publicField(this, "region");
8742
+ constructor(options) {
8743
+ __publicField(this, "region", "");
8440
8744
  __publicField(this, "voice");
8441
8745
  __publicField(this, "rate");
8442
8746
  __publicField(this, "pitch");
@@ -8462,12 +8766,17 @@ class TextToSpeech {
8462
8766
  __publicField(this, "prefetchPromises", /* @__PURE__ */ new Map());
8463
8767
  __publicField(this, "activePrefetchedAudioUrl", "");
8464
8768
  __publicField(this, "playbackSegments", []);
8465
- this.key = key;
8466
- this.region = region;
8467
- this.voice = voice;
8468
- this.rate = rate;
8469
- this.pitch = pitch;
8470
- this.url = url;
8769
+ __publicField(this, "authorizationManager");
8770
+ __publicField(this, "stopPromise");
8771
+ var _a, _b, _c;
8772
+ if (!options || typeof options.voice !== "string" || options.voice.trim() === "") {
8773
+ throw new Error("A voice is required.");
8774
+ }
8775
+ this.authorizationManager = new SpeechAuthorizationManager(options);
8776
+ this.voice = options.voice;
8777
+ this.rate = (_a = options.rate) != null ? _a : 0;
8778
+ this.pitch = (_b = options.pitch) != null ? _b : 0;
8779
+ this.url = (_c = options.url) != null ? _c : "";
8471
8780
  }
8472
8781
  async start() {
8473
8782
  await this.registerBindings(document);
@@ -8496,17 +8805,35 @@ class TextToSpeech {
8496
8805
  const currentNode = nodes[i];
8497
8806
  if (currentNode.attributes) {
8498
8807
  if (currentNode.attributes.getNamedItem("co-tts.id")) {
8499
- await this.handleIdModifier(currentNode, currentNode.attributes.getNamedItem("co-tts.id"));
8808
+ await this.handleIdModifier(
8809
+ currentNode,
8810
+ currentNode.attributes.getNamedItem("co-tts.id")
8811
+ );
8500
8812
  } else if (currentNode.attributes.getNamedItem("co-tts.ajax")) {
8501
- await this.handleAjaxModifier(currentNode, currentNode.attributes.getNamedItem("co-tts.ajax"));
8813
+ await this.handleAjaxModifier(
8814
+ currentNode,
8815
+ currentNode.attributes.getNamedItem("co-tts.ajax")
8816
+ );
8502
8817
  } else if (currentNode.attributes.getNamedItem("co-tts")) {
8503
- await this.handleDefault(currentNode, currentNode.attributes.getNamedItem("co-tts"));
8818
+ await this.handleDefault(
8819
+ currentNode,
8820
+ currentNode.attributes.getNamedItem("co-tts")
8821
+ );
8504
8822
  } else if (currentNode.attributes.getNamedItem("co-tts.stop")) {
8505
- await this.handleStopModifier(currentNode, currentNode.attributes.getNamedItem("co-tts.stop"));
8823
+ await this.handleStopModifier(
8824
+ currentNode,
8825
+ currentNode.attributes.getNamedItem("co-tts.stop")
8826
+ );
8506
8827
  } else if (currentNode.attributes.getNamedItem("co-tts.resume")) {
8507
- await this.handleResumeModifier(currentNode, currentNode.attributes.getNamedItem("co-tts.resume"));
8828
+ await this.handleResumeModifier(
8829
+ currentNode,
8830
+ currentNode.attributes.getNamedItem("co-tts.resume")
8831
+ );
8508
8832
  } else if (currentNode.attributes.getNamedItem("co-tts.pause")) {
8509
- await this.handlePauseModifier(currentNode, currentNode.attributes.getNamedItem("co-tts.pause"));
8833
+ await this.handlePauseModifier(
8834
+ currentNode,
8835
+ currentNode.attributes.getNamedItem("co-tts.pause")
8836
+ );
8510
8837
  }
8511
8838
  }
8512
8839
  if (currentNode.childNodes.length > 0) {
@@ -8516,8 +8843,8 @@ class TextToSpeech {
8516
8843
  }
8517
8844
  async handleIdModifier(node, attr) {
8518
8845
  node.addEventListener("click", async (_) => {
8519
- var _a, _b;
8520
- this.stopPlayer();
8846
+ var _a, _b, _c, _d, _e, _f;
8847
+ await this.stopPlayer();
8521
8848
  await this.createInterval();
8522
8849
  const referenceDiv = document.getElementById(attr.value);
8523
8850
  this.clickedNode = referenceDiv;
@@ -8527,65 +8854,69 @@ class TextToSpeech {
8527
8854
  if (referenceDiv.hasAttribute("co-tts.text") && referenceDiv.getAttribute("co-tts.text") !== "") {
8528
8855
  this.textToRead = (_a = referenceDiv.getAttribute("co-tts.text")) != null ? _a : "";
8529
8856
  } else {
8530
- this.textToRead = referenceDiv.innerText;
8857
+ this.textToRead = (_c = (_b = referenceDiv.innerText) != null ? _b : referenceDiv.textContent) != null ? _c : "";
8531
8858
  }
8532
8859
  if (referenceDiv.hasAttribute("co-tts.highlight")) {
8533
- if (((_b = referenceDiv.attributes.getNamedItem("co-tts.highlight")) == null ? void 0 : _b.value) !== "") {
8534
- const newReferenceDiv = document.getElementById(referenceDiv.attributes.getNamedItem("co-tts.highlight").value);
8860
+ const highlightTarget = (_e = (_d = referenceDiv.attributes.getNamedItem("co-tts.highlight")) == null ? void 0 : _d.value) != null ? _e : "";
8861
+ if (highlightTarget !== "") {
8862
+ const newReferenceDiv = document.getElementById(highlightTarget);
8535
8863
  this.highlightDiv = newReferenceDiv;
8536
- this.originalHighlightDivInnerHTML = newReferenceDiv.innerHTML;
8864
+ this.originalHighlightDivInnerHTML = (_f = newReferenceDiv == null ? void 0 : newReferenceDiv.innerHTML) != null ? _f : "";
8537
8865
  } else {
8538
8866
  this.highlightDiv = referenceDiv;
8539
8867
  this.originalHighlightDivInnerHTML = referenceDiv.innerHTML;
8540
8868
  }
8541
8869
  }
8542
- this.startSynthesizer(node, attr);
8870
+ await this.startSynthesizer(node, attr);
8543
8871
  });
8544
8872
  }
8545
8873
  async handleAjaxModifier(node, attr) {
8546
8874
  node.addEventListener("click", async (_) => {
8547
- this.stopPlayer();
8875
+ await this.stopPlayer();
8548
8876
  await this.createInterval();
8549
8877
  this.clickedNode = node;
8550
8878
  const response = await fetch(attr.value, {
8551
8879
  method: `GET`
8552
8880
  });
8553
8881
  this.textToRead = await response.text();
8554
- this.startSynthesizer(node, attr);
8882
+ await this.startSynthesizer(node, attr);
8555
8883
  });
8556
8884
  }
8557
8885
  async handleDefault(node, attr) {
8558
8886
  node.addEventListener("click", async (_) => {
8559
- var _a;
8560
- this.stopPlayer();
8887
+ var _a, _b, _c, _d, _e;
8888
+ await this.stopPlayer();
8561
8889
  await this.createInterval();
8562
8890
  this.clickedNode = node;
8563
8891
  if (node.hasAttribute("co-tts.highlight")) {
8564
- if (((_a = node.attributes.getNamedItem("co-tts.highlight")) == null ? void 0 : _a.value) !== "") {
8565
- const newReferenceDiv = document.getElementById(node.attributes.getNamedItem("co-tts.highlight").value);
8892
+ const highlightTarget = (_b = (_a = node.attributes.getNamedItem("co-tts.highlight")) == null ? void 0 : _a.value) != null ? _b : "";
8893
+ if (highlightTarget !== "") {
8894
+ const newReferenceDiv = document.getElementById(highlightTarget);
8566
8895
  this.highlightDiv = newReferenceDiv;
8567
- this.originalHighlightDivInnerHTML = newReferenceDiv.innerHTML;
8896
+ this.originalHighlightDivInnerHTML = (_c = newReferenceDiv == null ? void 0 : newReferenceDiv.innerHTML) != null ? _c : "";
8568
8897
  } else {
8569
8898
  this.highlightDiv = node;
8570
8899
  this.originalHighlightDivInnerHTML = node.innerHTML;
8571
8900
  }
8572
8901
  }
8573
8902
  if (attr.value === "") {
8574
- this.textToRead = node.innerText;
8903
+ this.textToRead = (_e = (_d = node.innerText) != null ? _d : node.textContent) != null ? _e : "";
8575
8904
  } else {
8576
8905
  this.textToRead = attr.value;
8577
8906
  }
8578
- this.startSynthesizer(node, attr);
8907
+ await this.startSynthesizer(node, attr);
8579
8908
  });
8580
8909
  }
8581
8910
  async handleWithoutClick(node, attr) {
8582
- var _a;
8583
- this.stopPlayer();
8911
+ var _a, _b, _c;
8912
+ await this.stopPlayer();
8584
8913
  await this.createInterval();
8585
8914
  this.clickedNode = node;
8586
8915
  if (node.hasAttribute("co-tts.highlight")) {
8587
8916
  if (((_a = node.attributes.getNamedItem("co-tts.highlight")) == null ? void 0 : _a.value) !== "") {
8588
- const newReferenceDiv = document.getElementById(node.attributes.getNamedItem("co-tts.highlight").value);
8917
+ const newReferenceDiv = document.getElementById(
8918
+ node.attributes.getNamedItem("co-tts.highlight").value
8919
+ );
8589
8920
  this.highlightDiv = newReferenceDiv;
8590
8921
  if (newReferenceDiv !== null) {
8591
8922
  this.originalHighlightDivInnerHTML = newReferenceDiv.innerHTML;
@@ -8596,111 +8927,172 @@ class TextToSpeech {
8596
8927
  }
8597
8928
  }
8598
8929
  if (attr.value === "") {
8599
- this.textToRead = node.innerText;
8930
+ this.textToRead = (_c = (_b = node.innerText) != null ? _b : node.textContent) != null ? _c : "";
8600
8931
  } else {
8601
8932
  this.textToRead = attr.value;
8602
8933
  }
8603
- this.startSynthesizer(node, attr);
8934
+ await this.startSynthesizer(node, attr);
8604
8935
  }
8605
- async handleStopModifier(node, attr) {
8936
+ async handleStopModifier(node, _attr) {
8606
8937
  node.addEventListener("click", async (_) => {
8607
8938
  await this.stopPlayer();
8608
8939
  document.dispatchEvent(new CustomEvent("COAzureTTSStoppedPlaying", {}));
8609
8940
  });
8610
8941
  }
8611
- async handlePauseModifier(node, attr) {
8942
+ async handlePauseModifier(node, _attr) {
8612
8943
  node.addEventListener("click", async (_) => {
8944
+ if (!this.player || typeof this.player.pause !== "function") {
8945
+ return;
8946
+ }
8613
8947
  await this.clearInterval();
8614
8948
  await this.player.pause();
8615
8949
  document.dispatchEvent(new CustomEvent("COAzureTTSPausedPlaying", {}));
8616
8950
  });
8617
8951
  }
8618
- async handleResumeModifier(node, attr) {
8952
+ async handleResumeModifier(node, _attr) {
8619
8953
  node.addEventListener("click", async (_) => {
8954
+ if (!this.player) {
8955
+ return;
8956
+ }
8620
8957
  await this.createInterval();
8621
- await this.player.resume();
8958
+ if (typeof this.player.resume === "function") {
8959
+ await this.player.resume();
8960
+ } else if (typeof this.player.play === "function") {
8961
+ await this.player.play();
8962
+ } else {
8963
+ return;
8964
+ }
8622
8965
  document.dispatchEvent(new CustomEvent("COAzureTTSResumedPlaying", {}));
8623
8966
  });
8624
8967
  }
8625
8968
  async stopPlayer() {
8969
+ if (this.stopPromise) {
8970
+ return this.stopPromise;
8971
+ }
8972
+ this.stopPromise = this.performStopPlayer().finally(() => {
8973
+ this.stopPromise = void 0;
8974
+ });
8975
+ return this.stopPromise;
8976
+ }
8977
+ async performStopPlayer() {
8626
8978
  await this.clearInterval();
8627
8979
  if (this.highlightDiv !== void 0) {
8628
8980
  this.highlightDiv.innerHTML = this.originalHighlightDivInnerHTML;
8629
8981
  }
8982
+ this.resetPlaybackSegments();
8983
+ const player = this.player;
8984
+ const activePrefetchedAudioUrl = this.activePrefetchedAudioUrl;
8985
+ const synthesizer = this.synthesizer;
8630
8986
  this.textToRead = "";
8631
8987
  this.currentWord = "";
8632
8988
  this.originalHighlightDivInnerHTML = "";
8633
8989
  this.wordBoundryList = [];
8634
8990
  this.wordEncounters = [];
8635
- this.resetPlaybackSegments();
8636
8991
  this.playbackSegments = [];
8637
- if (this.player !== void 0) {
8638
- this.player.pause();
8639
- }
8640
- if (this.activePrefetchedAudioUrl !== "") {
8641
- URL.revokeObjectURL(this.activePrefetchedAudioUrl);
8642
- this.activePrefetchedAudioUrl = "";
8643
- }
8644
- this.player = void 0;
8645
8992
  this.highlightDiv = void 0;
8646
8993
  this.prevTextOffset = 0;
8647
8994
  this.playbackTextOffsetBase = void 0;
8648
- }
8649
- async startSynthesizer(node, attr) {
8650
- this.speechConfig = SpeechConfig.fromSubscription(this.key, this.region);
8651
- this.speechConfig.speechSynthesisVoiceName = `Microsoft Server Speech Text to Speech Voice (${this.voice})`;
8652
- this.speechConfig.speechSynthesisOutputFormat = SpeechSynthesisOutputFormat.Audio24Khz160KBitRateMonoMp3;
8653
- this.player = new SpeakerAudioDestination();
8654
- this.audioConfig = AudioConfig.fromSpeakerOutput(this.player);
8655
- this.synthesizer = new SpeechSynthesizer(this.speechConfig, this.audioConfig);
8656
- this.synthesizer.wordBoundary = (s, e) => {
8657
- this.wordBoundryList.push(e);
8658
- };
8659
- const playbackChain = this.collectPlaybackChain(this.clickedNode);
8660
- const isChainedPlayback = playbackChain.length > 1;
8661
- if (isChainedPlayback) {
8662
- this.preparePlaybackChain(playbackChain);
8663
- } else {
8664
- this.playbackSegments = [];
8995
+ this.audioConfig = void 0;
8996
+ this.speechConfig = void 0;
8997
+ if (this.player === player) {
8998
+ this.player = void 0;
8665
8999
  }
8666
- this.player.onAudioEnd = async () => {
8667
- const wasChainedPlayback = this.playbackSegments.length > 0;
8668
- this.stopPlayer();
8669
- if (wasChainedPlayback) {
8670
- document.dispatchEvent(new CustomEvent("COAzureTTSFinishedPlaying", {}));
8671
- return;
9000
+ if (this.activePrefetchedAudioUrl === activePrefetchedAudioUrl) {
9001
+ this.activePrefetchedAudioUrl = "";
9002
+ }
9003
+ if (this.synthesizer === synthesizer) {
9004
+ this.synthesizer = void 0;
9005
+ }
9006
+ if (player !== void 0 && typeof player.pause === "function") {
9007
+ try {
9008
+ player.pause();
9009
+ } catch {
8672
9010
  }
8673
- if (this.clickedNode.hasAttribute("co-tts.next")) {
8674
- const nextNode = document.getElementById(this.clickedNode.getAttribute("co-tts.next"));
8675
- if (nextNode && await this.playPrefetchedNode(nextNode)) {
9011
+ }
9012
+ if (activePrefetchedAudioUrl !== "") {
9013
+ URL.revokeObjectURL(activePrefetchedAudioUrl);
9014
+ }
9015
+ this.closeResource(synthesizer);
9016
+ }
9017
+ async createSpeechConfig() {
9018
+ const authorization = await this.authorizationManager.getAuthorization();
9019
+ const speechConfig = SpeechConfig.fromAuthorizationToken(
9020
+ authorization.token,
9021
+ authorization.region
9022
+ );
9023
+ speechConfig.speechSynthesisVoiceName = `Microsoft Server Speech Text to Speech Voice (${this.voice})`;
9024
+ speechConfig.speechSynthesisOutputFormat = SpeechSynthesisOutputFormat.Audio24Khz160KBitRateMonoMp3;
9025
+ this.region = authorization.region;
9026
+ return speechConfig;
9027
+ }
9028
+ async startSynthesizer(_node, _attr) {
9029
+ try {
9030
+ this.speechConfig = await this.createSpeechConfig();
9031
+ this.player = new SpeakerAudioDestination();
9032
+ this.audioConfig = AudioConfig.fromSpeakerOutput(this.player);
9033
+ this.synthesizer = new SpeechSynthesizer(this.speechConfig, this.audioConfig);
9034
+ this.synthesizer.wordBoundary = (_s, e) => {
9035
+ this.wordBoundryList.push(e);
9036
+ };
9037
+ const playbackChain = this.collectPlaybackChain(this.clickedNode);
9038
+ const isChainedPlayback = playbackChain.length > 1;
9039
+ if (isChainedPlayback) {
9040
+ this.preparePlaybackChain(playbackChain);
9041
+ } else {
9042
+ this.playbackSegments = [];
9043
+ }
9044
+ this.player.onAudioEnd = async () => {
9045
+ var _a;
9046
+ const clickedNode = this.clickedNode;
9047
+ const wasChainedPlayback = this.playbackSegments.length > 0;
9048
+ const nextNodeId = ((_a = clickedNode == null ? void 0 : clickedNode.hasAttribute) == null ? void 0 : _a.call(clickedNode, "co-tts.next")) ? clickedNode.getAttribute("co-tts.next") : null;
9049
+ await this.stopPlayer();
9050
+ if (wasChainedPlayback) {
9051
+ document.dispatchEvent(new CustomEvent("COAzureTTSFinishedPlaying", {}));
8676
9052
  return;
8677
9053
  }
8678
- if (nextNode && nextNode.attributes.getNamedItem("co-tts.text")) {
8679
- this.handleWithoutClick(nextNode, nextNode.attributes.getNamedItem("co-tts.text"));
8680
- } else if (nextNode) {
8681
- nextNode.dispatchEvent(new Event("click"));
9054
+ if (nextNodeId) {
9055
+ const nextNode = document.getElementById(nextNodeId);
9056
+ if (nextNode && await this.playPrefetchedNode(nextNode)) {
9057
+ return;
9058
+ }
9059
+ const nextTextAttr = nextNode == null ? void 0 : nextNode.attributes.getNamedItem("co-tts.text");
9060
+ if (nextNode && nextTextAttr) {
9061
+ await this.handleWithoutClick(nextNode, nextTextAttr);
9062
+ } else if (nextNode) {
9063
+ nextNode.dispatchEvent(new Event("click"));
9064
+ }
9065
+ } else {
9066
+ document.dispatchEvent(new CustomEvent("COAzureTTSFinishedPlaying", {}));
8682
9067
  }
8683
- } else {
8684
- document.dispatchEvent(new CustomEvent("COAzureTTSFinishedPlaying", {}));
9068
+ };
9069
+ this.player.onAudioStart = async () => {
9070
+ document.dispatchEvent(new CustomEvent("COAzureTTSStartedPlaying", {}));
9071
+ };
9072
+ if (!isChainedPlayback) {
9073
+ void this.prefetchNextNode(this.clickedNode);
8685
9074
  }
8686
- };
8687
- this.player.onAudioStart = async () => {
8688
- document.dispatchEvent(new CustomEvent("COAzureTTSStartedPlaying", {}));
8689
- };
8690
- if (!isChainedPlayback) {
8691
- this.prefetchNextNode(this.clickedNode);
8692
- }
8693
- this.synthesizer.speakSsmlAsync(
8694
- this.buildSSML(this.textToRead),
8695
- () => {
8696
- this.synthesizer.close();
8697
- this.synthesizer = void 0;
8698
- },
8699
- () => {
8700
- this.synthesizer.close();
8701
- this.synthesizer = void 0;
9075
+ this.synthesizer.speakSsmlAsync(
9076
+ this.buildSSML(this.textToRead),
9077
+ () => {
9078
+ this.closeSynthesizer();
9079
+ },
9080
+ (error) => {
9081
+ if (isLikelyAuthorizationError(error)) {
9082
+ this.authorizationManager.clearAuthorization();
9083
+ }
9084
+ this.dispatchError(error, "SYNTHESIS_ERROR");
9085
+ this.closeSynthesizer();
9086
+ void this.stopPlayer();
9087
+ }
9088
+ );
9089
+ } catch (error) {
9090
+ if (isLikelyAuthorizationError(error)) {
9091
+ this.authorizationManager.clearAuthorization();
8702
9092
  }
8703
- );
9093
+ this.dispatchError(error);
9094
+ await this.stopPlayer();
9095
+ }
8704
9096
  }
8705
9097
  collectPlaybackChain(node) {
8706
9098
  var _a, _b;
@@ -8751,7 +9143,9 @@ class TextToSpeech {
8751
9143
  return void 0;
8752
9144
  }
8753
9145
  if (((_a = node.attributes.getNamedItem("co-tts.highlight")) == null ? void 0 : _a.value) !== "") {
8754
- return document.getElementById(node.attributes.getNamedItem("co-tts.highlight").value);
9146
+ return document.getElementById(
9147
+ node.attributes.getNamedItem("co-tts.highlight").value
9148
+ );
8755
9149
  }
8756
9150
  return node;
8757
9151
  }
@@ -8763,7 +9157,7 @@ class TextToSpeech {
8763
9157
  });
8764
9158
  }
8765
9159
  updateChainedHighlight(wordBoundary) {
8766
- var _a;
9160
+ var _a, _b;
8767
9161
  if (~[".", ",", "!", "?", "*", "(", ")", "&", "\\", "/", "^", "[", "]", "<", ">", ":"].indexOf(wordBoundary.text)) {
8768
9162
  wordBoundary = (_a = this.previousWordBoundary) != null ? _a : void 0;
8769
9163
  }
@@ -8774,7 +9168,10 @@ class TextToSpeech {
8774
9168
  if (this.playbackTextOffsetBase === void 0) {
8775
9169
  this.playbackTextOffsetBase = wordBoundary.textOffset;
8776
9170
  }
8777
- const normalizedTextOffset = Math.max(0, wordBoundary.textOffset - this.playbackTextOffsetBase);
9171
+ const normalizedTextOffset = Math.max(
9172
+ 0,
9173
+ wordBoundary.textOffset - ((_b = this.playbackTextOffsetBase) != null ? _b : 0)
9174
+ );
8778
9175
  const segment = this.playbackSegments.find((candidate) => normalizedTextOffset >= candidate.start && normalizedTextOffset < candidate.end);
8779
9176
  this.resetPlaybackSegments();
8780
9177
  if (!(segment == null ? void 0 : segment.highlightDiv)) {
@@ -8782,7 +9179,11 @@ class TextToSpeech {
8782
9179
  return;
8783
9180
  }
8784
9181
  const relativeTextOffset = normalizedTextOffset - segment.start;
8785
- const currentOffset = this.getPosition(segment.originalHighlightDivInnerHTML, wordBoundary.text, relativeTextOffset);
9182
+ const currentOffset = this.getPosition(
9183
+ segment.originalHighlightDivInnerHTML,
9184
+ wordBoundary.text,
9185
+ relativeTextOffset
9186
+ );
8786
9187
  if (currentOffset === Number.MAX_SAFE_INTEGER) {
8787
9188
  this.previousWordBoundary = wordBoundary;
8788
9189
  return;
@@ -8796,14 +9197,14 @@ class TextToSpeech {
8796
9197
  this.previousWordBoundary = wordBoundary;
8797
9198
  }
8798
9199
  getNodeText(node, attr) {
8799
- var _a;
9200
+ var _a, _b, _c;
8800
9201
  if (attr && attr.value !== "") {
8801
9202
  return attr.value;
8802
9203
  }
8803
9204
  if (node.hasAttribute("co-tts.text") && node.getAttribute("co-tts.text") !== "") {
8804
9205
  return (_a = node.getAttribute("co-tts.text")) != null ? _a : "";
8805
9206
  }
8806
- return node.innerText;
9207
+ return (_c = (_b = node.innerText) != null ? _b : node.textContent) != null ? _c : "";
8807
9208
  }
8808
9209
  getPrefetchKey(node, text) {
8809
9210
  return [
@@ -8842,41 +9243,58 @@ class TextToSpeech {
8842
9243
  if (this.prefetchedAudio.has(key) || this.prefetchPromises.has(key)) {
8843
9244
  return;
8844
9245
  }
8845
- const speechConfig = SpeechConfig.fromSubscription(this.key, this.region);
8846
- speechConfig.speechSynthesisVoiceName = `Microsoft Server Speech Text to Speech Voice (${this.voice})`;
8847
- speechConfig.speechSynthesisOutputFormat = SpeechSynthesisOutputFormat.Audio24Khz160KBitRateMonoMp3;
8848
- const synthesizer = new SpeechSynthesizer(speechConfig, null);
8849
- const wordBoundryList = [];
8850
- synthesizer.wordBoundary = (s, e) => {
8851
- wordBoundryList.push(e);
8852
- };
8853
- const prefetchPromise = new Promise((resolve) => {
8854
- synthesizer.speakSsmlAsync(
8855
- this.buildSSML(text),
8856
- (result) => {
8857
- synthesizer.close();
8858
- if (!(result == null ? void 0 : result.audioData)) {
8859
- resolve(null);
8860
- return;
8861
- }
8862
- const blob = new Blob([result.audioData], { type: "audio/mpeg" });
8863
- const url = URL.createObjectURL(blob);
8864
- const prefetch = {
8865
- key,
8866
- nodeId: nextNode.id,
8867
- text,
8868
- url,
8869
- wordBoundryList
8870
- };
8871
- this.prefetchedAudio.set(key, prefetch);
8872
- resolve(prefetch);
8873
- },
8874
- () => {
8875
- synthesizer.close();
8876
- resolve(null);
9246
+ let synthesizer;
9247
+ const prefetchPromise = (async () => {
9248
+ try {
9249
+ const speechConfig = await this.createSpeechConfig();
9250
+ synthesizer = new SpeechSynthesizer(speechConfig, void 0);
9251
+ const wordBoundryList = [];
9252
+ synthesizer.wordBoundary = (_s, e) => {
9253
+ wordBoundryList.push(e);
9254
+ };
9255
+ return await new Promise((resolve) => {
9256
+ synthesizer == null ? void 0 : synthesizer.speakSsmlAsync(
9257
+ this.buildSSML(text),
9258
+ (result) => {
9259
+ this.closeResource(synthesizer);
9260
+ synthesizer = void 0;
9261
+ if (!(result == null ? void 0 : result.audioData)) {
9262
+ resolve(null);
9263
+ return;
9264
+ }
9265
+ const blob = new Blob([result.audioData], { type: "audio/mpeg" });
9266
+ const url = URL.createObjectURL(blob);
9267
+ const prefetch = {
9268
+ key,
9269
+ nodeId: nextNode.id,
9270
+ text,
9271
+ url,
9272
+ wordBoundryList
9273
+ };
9274
+ this.prefetchedAudio.set(key, prefetch);
9275
+ resolve(prefetch);
9276
+ },
9277
+ (error) => {
9278
+ this.closeResource(synthesizer);
9279
+ synthesizer = void 0;
9280
+ if (isLikelyAuthorizationError(error)) {
9281
+ this.authorizationManager.clearAuthorization();
9282
+ }
9283
+ this.dispatchError(error, "PREFETCH_ERROR");
9284
+ resolve(null);
9285
+ }
9286
+ );
9287
+ });
9288
+ } catch (error) {
9289
+ this.closeResource(synthesizer);
9290
+ synthesizer = void 0;
9291
+ if (isLikelyAuthorizationError(error)) {
9292
+ this.authorizationManager.clearAuthorization();
8877
9293
  }
8878
- );
8879
- }).finally(() => {
9294
+ this.dispatchError(error);
9295
+ return null;
9296
+ }
9297
+ })().finally(() => {
8880
9298
  this.prefetchPromises.delete(key);
8881
9299
  });
8882
9300
  this.prefetchPromises.set(key, prefetchPromise);
@@ -8902,7 +9320,9 @@ class TextToSpeech {
8902
9320
  this.wordBoundaryOffset = 0;
8903
9321
  if (node.hasAttribute("co-tts.highlight")) {
8904
9322
  if (((_c = node.attributes.getNamedItem("co-tts.highlight")) == null ? void 0 : _c.value) !== "") {
8905
- const newReferenceDiv = document.getElementById(node.attributes.getNamedItem("co-tts.highlight").value);
9323
+ const newReferenceDiv = document.getElementById(
9324
+ node.attributes.getNamedItem("co-tts.highlight").value
9325
+ );
8906
9326
  this.highlightDiv = newReferenceDiv;
8907
9327
  if (newReferenceDiv !== null) {
8908
9328
  this.originalHighlightDivInnerHTML = newReferenceDiv.innerHTML;
@@ -8913,31 +9333,48 @@ class TextToSpeech {
8913
9333
  }
8914
9334
  }
8915
9335
  await this.createInterval();
8916
- const audio = new Audio(prefetch.url);
8917
- this.activePrefetchedAudioUrl = prefetch.url;
8918
- this.player = audio;
8919
- audio.addEventListener("play", () => {
8920
- document.dispatchEvent(new CustomEvent("COAzureTTSStartedPlaying", {}));
8921
- }, { once: true });
8922
- audio.addEventListener("ended", async () => {
8923
- this.stopPlayer();
8924
- if (this.clickedNode.hasAttribute("co-tts.next")) {
8925
- const nextNode = document.getElementById(this.clickedNode.getAttribute("co-tts.next"));
8926
- if (nextNode && await this.playPrefetchedNode(nextNode)) {
8927
- return;
8928
- }
8929
- if (nextNode && nextNode.attributes.getNamedItem("co-tts.text")) {
8930
- this.handleWithoutClick(nextNode, nextNode.attributes.getNamedItem("co-tts.text"));
8931
- } else if (nextNode) {
8932
- nextNode.dispatchEvent(new Event("click"));
9336
+ try {
9337
+ const audio = new Audio(prefetch.url);
9338
+ this.activePrefetchedAudioUrl = prefetch.url;
9339
+ this.player = audio;
9340
+ audio.addEventListener("play", () => {
9341
+ document.dispatchEvent(new CustomEvent("COAzureTTSStartedPlaying", {}));
9342
+ }, { once: true });
9343
+ audio.addEventListener("ended", async () => {
9344
+ var _a2;
9345
+ const clickedNode = this.clickedNode;
9346
+ const nextNodeId = ((_a2 = clickedNode == null ? void 0 : clickedNode.hasAttribute) == null ? void 0 : _a2.call(clickedNode, "co-tts.next")) ? clickedNode.getAttribute("co-tts.next") : null;
9347
+ await this.stopPlayer();
9348
+ if (nextNodeId) {
9349
+ const nextNode = document.getElementById(nextNodeId);
9350
+ if (nextNode && await this.playPrefetchedNode(nextNode)) {
9351
+ return;
9352
+ }
9353
+ const nextTextAttr = nextNode == null ? void 0 : nextNode.attributes.getNamedItem("co-tts.text");
9354
+ if (nextNode && nextTextAttr) {
9355
+ await this.handleWithoutClick(nextNode, nextTextAttr);
9356
+ } else if (nextNode) {
9357
+ nextNode.dispatchEvent(new Event("click"));
9358
+ }
9359
+ } else {
9360
+ document.dispatchEvent(new CustomEvent("COAzureTTSFinishedPlaying", {}));
8933
9361
  }
8934
- } else {
8935
- document.dispatchEvent(new CustomEvent("COAzureTTSFinishedPlaying", {}));
8936
- }
8937
- }, { once: true });
8938
- this.prefetchNextNode(node);
8939
- await audio.play();
8940
- return true;
9362
+ }, { once: true });
9363
+ audio.addEventListener("error", () => {
9364
+ this.dispatchError(
9365
+ new Error("Prefetched audio resource closed unexpectedly."),
9366
+ "AUDIO_RESOURCE_ERROR"
9367
+ );
9368
+ void this.stopPlayer();
9369
+ }, { once: true });
9370
+ void this.prefetchNextNode(node);
9371
+ await audio.play();
9372
+ return true;
9373
+ } catch (error) {
9374
+ this.dispatchError(error, "AUDIO_RESOURCE_ERROR");
9375
+ await this.stopPlayer();
9376
+ return false;
9377
+ }
8941
9378
  }
8942
9379
  async clearInterval() {
8943
9380
  clearInterval(this.interval);
@@ -9040,5 +9477,31 @@ class TextToSpeech {
9040
9477
  p.textContent = input;
9041
9478
  return p.innerHTML;
9042
9479
  }
9480
+ closeSynthesizer() {
9481
+ this.closeResource(this.synthesizer);
9482
+ this.synthesizer = void 0;
9483
+ }
9484
+ closeResource(resource) {
9485
+ if (!resource || typeof resource.close !== "function") {
9486
+ return;
9487
+ }
9488
+ try {
9489
+ resource.close();
9490
+ } catch {
9491
+ }
9492
+ }
9493
+ dispatchError(error, code) {
9494
+ const detail = toSafeErrorDetail(error);
9495
+ if (code && !detail.code) {
9496
+ detail.code = code;
9497
+ }
9498
+ document.dispatchEvent(
9499
+ new CustomEvent("COAzureTTSError", {
9500
+ detail: {
9501
+ error: detail
9502
+ }
9503
+ })
9504
+ );
9505
+ }
9043
9506
  }
9044
- export { SpeechToText, TextToSpeech };
9507
+ export { DEFAULT_TOKEN_LIFETIME_MS, SpeechAuthorizationManager, SpeechToText, TextToSpeech, createSpeechAuthorizationProvider, isLikelyAuthorizationError, sanitizeErrorText, toSafeErrorDetail, validateSpeechAuthorization };