@pie-players/pie-tool-graph 0.3.42 → 0.3.44

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.
@@ -0,0 +1,33 @@
1
+ function o(e) {
2
+ "@babel/helpers - typeof";
3
+ return o = typeof Symbol == "function" && typeof Symbol.iterator == "symbol" ? function(t) {
4
+ return typeof t;
5
+ } : function(t) {
6
+ return t && typeof Symbol == "function" && t.constructor === Symbol && t !== Symbol.prototype ? "symbol" : typeof t;
7
+ }, o(e);
8
+ }
9
+ function i(e, t) {
10
+ if (o(e) != "object" || !e) return e;
11
+ var r = e[Symbol.toPrimitive];
12
+ if (r !== void 0) {
13
+ var n = r.call(e, t || "default");
14
+ if (o(n) != "object") return n;
15
+ throw new TypeError("@@toPrimitive must return a primitive value.");
16
+ }
17
+ return (t === "string" ? String : Number)(e);
18
+ }
19
+ function u(e) {
20
+ var t = i(e, "string");
21
+ return o(t) == "symbol" ? t : t + "";
22
+ }
23
+ function f(e, t, r) {
24
+ return (t = u(t)) in e ? Object.defineProperty(e, t, {
25
+ value: r,
26
+ enumerable: !0,
27
+ configurable: !0,
28
+ writable: !0
29
+ }) : e[t] = r, e;
30
+ }
31
+ export {
32
+ f as t
33
+ };
@@ -0,0 +1,466 @@
1
+ import { t as l } from "./defineProperty-CyepwRr5.js";
2
+ function w(e, t) {
3
+ if (typeof e != "string" || e.length === 0) return null;
4
+ try {
5
+ const r = typeof t.apiEndpoint == "string" && /^https?:\/\//i.test(t.apiEndpoint) ? t.apiEndpoint : void 0, i = r ? new URL(e, r) : new URL(e);
6
+ return i.protocol !== "http:" && i.protocol !== "https:" ? null : i;
7
+ } catch {
8
+ return null;
9
+ }
10
+ }
11
+ function k(e, t) {
12
+ const r = Array.isArray(t.assetOrigins) ? t.assetOrigins.filter((i) => typeof i == "string" && i.length > 0).map((i) => {
13
+ try {
14
+ return new URL(i).origin;
15
+ } catch {
16
+ return i;
17
+ }
18
+ }) : [];
19
+ if (r.length === 0) {
20
+ if (typeof t.apiEndpoint == "string" && /^https?:\/\//i.test(t.apiEndpoint)) try {
21
+ return new URL(t.apiEndpoint).origin === e.origin;
22
+ } catch {
23
+ return !1;
24
+ }
25
+ return typeof window < "u" && typeof window.location?.origin == "string" && window.location.origin === e.origin;
26
+ }
27
+ return r.includes(e.origin);
28
+ }
29
+ function A(e) {
30
+ const t = {};
31
+ for (const [r, i] of Object.entries(e)) {
32
+ const o = r.toLowerCase();
33
+ o === "authorization" || o === "proxy-authorization" || o === "cookie" || o.startsWith("x-auth-") || (t[r] = i);
34
+ }
35
+ return t;
36
+ }
37
+ var R = (e) => {
38
+ const t = (e.providerOptions && typeof e.providerOptions == "object" ? e.providerOptions : {}).__pieTelemetry;
39
+ return typeof t == "function" ? t : void 0;
40
+ }, M = {
41
+ pie: 3e3,
42
+ custom: 3e3
43
+ }, T = (e) => e.replace(/\/+$/, ""), E = (e) => {
44
+ const t = T(e.apiEndpoint), r = (e.provider || "").toLowerCase();
45
+ return r === "polly" || r === "google" ? `${t}/${r}/voices` : `${t}/voices`;
46
+ }, b = (e) => e.transportMode === "custom" ? "custom" : e.transportMode === "pie" ? "pie" : e.provider === "custom" ? "custom" : "pie", S = (e, t) => e.endpointMode ? e.endpointMode : t === "custom" ? "rootPost" : "synthesizePath", U = (e, t) => e.endpointValidationMode ? e.endpointValidationMode : t === "custom" ? "none" : "voices", O = (e) => {
47
+ const t = e.providerOptions || {};
48
+ if (typeof t.speedRate == "string") return t.speedRate;
49
+ const r = Number(e.rate ?? 1);
50
+ return !Number.isFinite(r) || r <= 0.95 ? "slow" : r >= 1.5 ? "fast" : "medium";
51
+ }, P = (e) => {
52
+ const t = [];
53
+ let r = 0;
54
+ const i = e.split(`
55
+ `).map((o) => o.trim()).filter(Boolean);
56
+ for (const o of i) try {
57
+ const s = JSON.parse(o), a = typeof s.type == "string" ? s.type : "word", d = typeof s.time == "number" && Number.isFinite(s.time) ? s.time : 0, n = typeof s.value == "string" ? s.value : "", h = typeof s.start == "number" && Number.isFinite(s.start) ? s.start : null, u = typeof s.end == "number" && Number.isFinite(s.end) ? s.end : null, p = h ?? r, c = u ?? p + Math.max(1, n.length || String(s.value || "").length);
58
+ r = Math.max(c + 1, r), t.push({
59
+ time: d,
60
+ type: a,
61
+ start: p,
62
+ end: c,
63
+ value: n
64
+ });
65
+ } catch {
66
+ }
67
+ return t;
68
+ }, C = (e) => {
69
+ if (!Array.isArray(e)) return [];
70
+ const t = [];
71
+ let r = 0;
72
+ for (const i of e) {
73
+ if (!i || typeof i != "object") continue;
74
+ const o = typeof i.type == "string" ? i.type : "word", s = typeof i.time == "number" && Number.isFinite(i.time) ? i.time : 0, a = typeof i.value == "string" ? i.value : "", d = typeof i.start == "number" && Number.isFinite(i.start) ? i.start : null, n = typeof i.end == "number" && Number.isFinite(i.end) ? i.end : null, h = d ?? r, u = n ?? h + Math.max(1, a.length || 1);
75
+ r = Math.max(r, u + 1), t.push({
76
+ time: s,
77
+ type: o,
78
+ start: h,
79
+ end: u,
80
+ value: a
81
+ });
82
+ }
83
+ return t.sort((i, o) => i.time !== o.time ? i.time - o.time : i.start !== o.start ? i.start - o.start : i.end - o.end);
84
+ }, x = {
85
+ pie: {
86
+ id: "pie",
87
+ resolveSynthesisUrl: (e) => {
88
+ const t = S(e, "pie"), r = T(e.apiEndpoint);
89
+ return t === "rootPost" ? r : `${r}/synthesize`;
90
+ },
91
+ buildRequestBody: (e, t) => {
92
+ const r = t.providerOptions || {}, i = typeof t.engine == "string" ? t.engine : typeof r.engine == "string" ? r.engine : void 0, o = typeof r.sampleRate == "number" && Number.isFinite(r.sampleRate) ? r.sampleRate : void 0, s = r.format === "mp3" || r.format === "ogg" || r.format === "pcm" ? r.format : void 0, a = Array.isArray(r.speechMarkTypes) ? r.speechMarkTypes.filter((d) => d === "word" || d === "sentence" || d === "ssml") : void 0;
93
+ return {
94
+ text: e,
95
+ provider: t.provider || "polly",
96
+ voice: t.voice,
97
+ language: t.language,
98
+ rate: t.rate,
99
+ engine: i,
100
+ sampleRate: o,
101
+ format: s,
102
+ speechMarkTypes: a,
103
+ includeSpeechMarks: !0
104
+ };
105
+ },
106
+ parseResponse: async (e) => {
107
+ const t = await e.json();
108
+ return {
109
+ audio: {
110
+ kind: "base64",
111
+ data: t.audio,
112
+ contentType: t.contentType
113
+ },
114
+ speechMarks: Array.isArray(t.speechMarks) ? t.speechMarks : []
115
+ };
116
+ }
117
+ },
118
+ custom: {
119
+ id: "custom",
120
+ resolveSynthesisUrl: (e) => {
121
+ const t = S(e, "custom"), r = T(e.apiEndpoint);
122
+ return t === "synthesizePath" ? `${r}/synthesize` : r;
123
+ },
124
+ buildRequestBody: (e, t) => {
125
+ const r = t.providerOptions || {}, i = typeof r.lang_id == "string" ? r.lang_id : t.language || "en-US", o = typeof r.cache == "boolean" ? r.cache : !0;
126
+ return {
127
+ text: e,
128
+ speedRate: O(t),
129
+ lang_id: i,
130
+ cache: o
131
+ };
132
+ },
133
+ parseResponse: async (e, t, r, i) => {
134
+ const o = await e.json(), s = {};
135
+ if (t.includeAuthOnAssetFetch)
136
+ for (const [h, u] of Object.entries(r)) h.toLowerCase() === "authorization" && (s[h] = u);
137
+ let a = [];
138
+ const d = C(o.speechMarks);
139
+ if (d.length > 0) a = d;
140
+ else if (typeof o.word == "string" && o.word.length > 0) {
141
+ const h = w(o.word, t);
142
+ if (h !== null) {
143
+ const u = k(h, t) ? s : A(s), p = await fetch(h.toString(), {
144
+ headers: u,
145
+ signal: i,
146
+ redirect: "error"
147
+ });
148
+ p.ok && (a = P(await p.text()));
149
+ }
150
+ }
151
+ const n = w(o.audioContent, t);
152
+ if (n === null) throw new Error("TTS server returned an invalid audio URL");
153
+ return {
154
+ audio: {
155
+ kind: "url",
156
+ url: n.toString()
157
+ },
158
+ speechMarks: a
159
+ };
160
+ }
161
+ }
162
+ }, z = class {
163
+ constructor(e, t) {
164
+ l(this, "config", void 0), l(this, "adapter", void 0), l(this, "currentAudio", null), l(this, "pausedState", !1), l(this, "wordTimings", []), l(this, "highlightInterval", null), l(this, "intentionallyStopped", !1), l(this, "activeSynthesisController", null), l(this, "synthesisRunId", 0), l(this, "telemetryReporter", void 0), l(this, "onWordBoundary", void 0), this.config = e, this.adapter = t, this.telemetryReporter = R(e);
165
+ }
166
+ async emitTelemetry(e, t) {
167
+ try {
168
+ await this.telemetryReporter?.(e, t);
169
+ } catch (r) {
170
+ console.warn("[ServerTTSProvider] telemetry callback failed:", r);
171
+ }
172
+ }
173
+ async speak(e) {
174
+ this.stop(), this.intentionallyStopped = !1;
175
+ const t = ++this.synthesisRunId, r = new AbortController();
176
+ this.activeSynthesisController = r;
177
+ const { audioUrl: i, wordTimings: o } = await this.synthesizeSpeech(e, r.signal, t);
178
+ if (t !== this.synthesisRunId) {
179
+ URL.revokeObjectURL(i);
180
+ return;
181
+ }
182
+ const s = this.config.rate || 1;
183
+ return this.wordTimings = o.map((a) => ({
184
+ ...a,
185
+ time: a.time / s
186
+ })), new Promise((a, d) => {
187
+ const n = new Audio(i);
188
+ this.currentAudio = n, this.config.rate && (n.playbackRate = Math.max(0.25, Math.min(4, this.config.rate))), this.config.volume !== void 0 && (n.volume = Math.max(0, Math.min(1, this.config.volume))), n.onplay = () => {
189
+ this.pausedState = !1, this.onWordBoundary && this.wordTimings.length > 0 && this.startWordHighlighting();
190
+ }, n.onended = () => {
191
+ this.stopWordHighlighting(), URL.revokeObjectURL(i), this.currentAudio = null, this.wordTimings = [], a();
192
+ }, n.onerror = (h) => {
193
+ this.stopWordHighlighting(), URL.revokeObjectURL(i), this.currentAudio = null, this.wordTimings = [], this.intentionallyStopped ? a() : d(/* @__PURE__ */ new Error("Failed to play audio from server"));
194
+ }, n.onpause = () => {
195
+ this.stopWordHighlighting(), this.pausedState = !0;
196
+ }, n.play().catch(d);
197
+ });
198
+ }
199
+ async synthesizeSpeech(e, t, r) {
200
+ const i = Date.now();
201
+ await this.emitTelemetry("pie-tool-backend-call-start", {
202
+ toolId: "tts",
203
+ backend: this.config.provider || "server",
204
+ operation: "synthesize-speech"
205
+ });
206
+ const o = {
207
+ "Content-Type": "application/json",
208
+ ...this.config.headers
209
+ };
210
+ this.config.authToken && (o.Authorization = `Bearer ${this.config.authToken}`);
211
+ const s = this.adapter.resolveSynthesisUrl(this.config), a = this.adapter.buildRequestBody(e, this.config), d = await (async () => {
212
+ try {
213
+ return await fetch(s, {
214
+ method: "POST",
215
+ headers: o,
216
+ body: JSON.stringify(a),
217
+ signal: t
218
+ });
219
+ } catch (c) {
220
+ throw await this.emitTelemetry("pie-tool-backend-call-error", {
221
+ toolId: "tts",
222
+ backend: this.config.provider || "server",
223
+ operation: "synthesize-speech",
224
+ duration: Date.now() - i,
225
+ errorType: "TTSBackendNetworkError",
226
+ message: c instanceof Error ? c.message : String(c)
227
+ }), c;
228
+ }
229
+ })();
230
+ if (!d.ok) {
231
+ const c = await d.json().catch(() => ({})), y = c.message || c.error?.message || `Server returned ${d.status}`;
232
+ throw await this.emitTelemetry("pie-tool-backend-call-error", {
233
+ toolId: "tts",
234
+ backend: this.config.provider || "server",
235
+ operation: "synthesize-speech",
236
+ duration: Date.now() - i,
237
+ statusCode: d.status,
238
+ errorType: "TTSBackendRequestError",
239
+ message: y
240
+ }), new Error(y);
241
+ }
242
+ const n = await this.adapter.parseResponse(d, this.config, o, t);
243
+ if (r !== this.synthesisRunId || t.aborted) throw new Error("Synthesis superseded by a newer request");
244
+ let h;
245
+ if (n.audio.kind === "base64") h = this.base64ToBlob(n.audio.data, n.audio.contentType);
246
+ else {
247
+ const c = n.audio.url, y = Date.now();
248
+ await this.emitTelemetry("pie-tool-backend-call-start", {
249
+ toolId: "tts",
250
+ backend: this.config.provider || "server",
251
+ operation: "fetch-synthesized-audio-asset"
252
+ });
253
+ const f = {};
254
+ this.config.includeAuthOnAssetFetch && this.config.authToken && (f.Authorization = `Bearer ${this.config.authToken}`);
255
+ const v = w(c, this.config);
256
+ if (v === null)
257
+ throw await this.emitTelemetry("pie-tool-backend-call-error", {
258
+ toolId: "tts",
259
+ backend: this.config.provider || "server",
260
+ operation: "fetch-synthesized-audio-asset",
261
+ duration: Date.now() - y,
262
+ errorType: "TTSAssetInvalidUrl",
263
+ message: "TTS asset URL rejected (non-http(s) or malformed)"
264
+ }), new Error("TTS asset URL rejected (non-http(s) or malformed)");
265
+ const I = k(v, this.config) ? f : A(f), m = await (async () => {
266
+ try {
267
+ return await fetch(v.toString(), {
268
+ headers: I,
269
+ signal: t,
270
+ redirect: "error"
271
+ });
272
+ } catch (g) {
273
+ throw await this.emitTelemetry("pie-tool-backend-call-error", {
274
+ toolId: "tts",
275
+ backend: this.config.provider || "server",
276
+ operation: "fetch-synthesized-audio-asset",
277
+ duration: Date.now() - y,
278
+ errorType: "TTSAssetNetworkError",
279
+ message: g instanceof Error ? g.message : String(g)
280
+ }), g;
281
+ }
282
+ })();
283
+ if (!m.ok)
284
+ throw await this.emitTelemetry("pie-tool-backend-call-error", {
285
+ toolId: "tts",
286
+ backend: this.config.provider || "server",
287
+ operation: "fetch-synthesized-audio-asset",
288
+ duration: Date.now() - y,
289
+ statusCode: m.status,
290
+ errorType: "TTSAssetFetchError",
291
+ message: `Failed to download synthesized audio (${m.status})`
292
+ }), new Error(`Failed to download synthesized audio (${m.status})`);
293
+ h = await m.blob(), await this.emitTelemetry("pie-tool-backend-call-success", {
294
+ toolId: "tts",
295
+ backend: this.config.provider || "server",
296
+ operation: "fetch-synthesized-audio-asset",
297
+ duration: Date.now() - y
298
+ });
299
+ }
300
+ const u = URL.createObjectURL(h), p = this.parseSpeechMarks(n.speechMarks);
301
+ return await this.emitTelemetry("pie-tool-backend-call-success", {
302
+ toolId: "tts",
303
+ backend: this.config.provider || "server",
304
+ operation: "synthesize-speech",
305
+ duration: Date.now() - i
306
+ }), {
307
+ audioUrl: u,
308
+ wordTimings: p
309
+ };
310
+ }
311
+ base64ToBlob(e, t) {
312
+ const r = atob(e), i = new Array(r.length);
313
+ for (let s = 0; s < r.length; s++) i[s] = r.charCodeAt(s);
314
+ const o = new Uint8Array(i);
315
+ return new Blob([o], { type: t });
316
+ }
317
+ parseSpeechMarks(e) {
318
+ return e.filter((t) => t.type === "word").map((t, r) => ({
319
+ time: t.time,
320
+ wordIndex: r,
321
+ charIndex: t.start,
322
+ length: t.end - t.start
323
+ }));
324
+ }
325
+ startWordHighlighting() {
326
+ if (this.stopWordHighlighting(), !this.currentAudio || !this.onWordBoundary || this.wordTimings.length === 0) {
327
+ console.log("[ServerTTSProvider] Cannot start highlighting:", {
328
+ hasAudio: !!this.currentAudio,
329
+ hasCallback: !!this.onWordBoundary,
330
+ wordTimingsCount: this.wordTimings.length
331
+ });
332
+ return;
333
+ }
334
+ console.log("[ServerTTSProvider] Starting word highlighting with", this.wordTimings.length, "word timings"), console.log("[ServerTTSProvider] Playback rate:", this.currentAudio.playbackRate), console.log("[ServerTTSProvider] First 3 timings:", this.wordTimings.slice(0, 3));
335
+ let e = -1;
336
+ this.highlightInterval = window.setInterval(() => {
337
+ if (!this.currentAudio) {
338
+ this.stopWordHighlighting();
339
+ return;
340
+ }
341
+ const t = this.currentAudio.currentTime * 1e3;
342
+ for (let r = 0; r < this.wordTimings.length; r++) {
343
+ const i = this.wordTimings[r];
344
+ if (t >= i.time && r > e) {
345
+ this.onWordBoundary && (console.log("[ServerTTSProvider] Highlighting word at charIndex:", i.charIndex, "length:", i.length, "time:", i.time, "currentTime:", t), this.onWordBoundary("", i.charIndex, i.length)), e = r;
346
+ break;
347
+ }
348
+ }
349
+ }, 50);
350
+ }
351
+ stopWordHighlighting() {
352
+ this.highlightInterval !== null && (clearInterval(this.highlightInterval), this.highlightInterval = null);
353
+ }
354
+ pause() {
355
+ this.currentAudio && !this.pausedState && (this.currentAudio.pause(), this.stopWordHighlighting(), this.pausedState = !0);
356
+ }
357
+ resume() {
358
+ this.currentAudio && this.pausedState && (this.currentAudio.play(), this.pausedState = !1, this.onWordBoundary && this.wordTimings.length > 0 && this.startWordHighlighting());
359
+ }
360
+ stop() {
361
+ this.synthesisRunId += 1, this.activeSynthesisController && (this.activeSynthesisController.abort(), this.activeSynthesisController = null), this.stopWordHighlighting(), this.currentAudio && (this.intentionallyStopped = !0, this.currentAudio.pause(), this.currentAudio.src && URL.revokeObjectURL(this.currentAudio.src), this.currentAudio.src = "", this.currentAudio = null), this.pausedState = !1, this.wordTimings = [];
362
+ }
363
+ isPlaying() {
364
+ return this.currentAudio !== null && !this.pausedState;
365
+ }
366
+ isPaused() {
367
+ return this.pausedState;
368
+ }
369
+ updateSettings(e) {
370
+ e.rate !== void 0 && (this.config.rate = e.rate, this.currentAudio && (this.currentAudio.playbackRate = Math.max(0.25, Math.min(4, e.rate)))), e.pitch !== void 0 && (this.config.pitch = e.pitch), e.voice !== void 0 && (this.config.voice = e.voice);
371
+ }
372
+ }, L = class {
373
+ constructor() {
374
+ l(this, "providerId", "server-tts"), l(this, "providerName", "Server TTS"), l(this, "version", "1.0.0"), l(this, "config", null), l(this, "adapter", null), l(this, "telemetryReporter", void 0);
375
+ }
376
+ async emitTelemetry(e, t) {
377
+ try {
378
+ await this.telemetryReporter?.(e, t);
379
+ } catch (r) {
380
+ console.warn("[ServerTTSProvider] telemetry callback failed:", r);
381
+ }
382
+ }
383
+ async initialize(e) {
384
+ const t = e;
385
+ if (!t.apiEndpoint) throw new Error("apiEndpoint is required for ServerTTSProvider");
386
+ this.config = t, this.telemetryReporter = R(t);
387
+ const r = b(t);
388
+ if (this.adapter = x[r], t.validateEndpoint) {
389
+ const i = Date.now();
390
+ if (await this.emitTelemetry("pie-tool-backend-call-start", {
391
+ toolId: "tts",
392
+ backend: t.provider || "server",
393
+ operation: "validate-endpoint"
394
+ }), !await this.testAPIAvailability())
395
+ throw await this.emitTelemetry("pie-tool-backend-call-error", {
396
+ toolId: "tts",
397
+ backend: t.provider || "server",
398
+ operation: "validate-endpoint",
399
+ duration: Date.now() - i,
400
+ errorType: "TTSEndpointValidationError",
401
+ message: `Server TTS API not available at ${t.apiEndpoint}`
402
+ }), new Error(`Server TTS API not available at ${t.apiEndpoint}`);
403
+ await this.emitTelemetry("pie-tool-backend-call-success", {
404
+ toolId: "tts",
405
+ backend: t.provider || "server",
406
+ operation: "validate-endpoint",
407
+ duration: Date.now() - i
408
+ });
409
+ }
410
+ return new z(t, this.adapter);
411
+ }
412
+ async testAPIAvailability() {
413
+ if (!this.config || !this.adapter) return !1;
414
+ try {
415
+ const e = { ...this.config.headers };
416
+ this.config.authToken && (e.Authorization = `Bearer ${this.config.authToken}`);
417
+ const t = new AbortController(), r = setTimeout(() => t.abort(), 5e3), i = U(this.config, this.adapter.id);
418
+ if (i === "none")
419
+ return clearTimeout(r), !0;
420
+ const o = i === "voices" ? E(this.config) : this.adapter.resolveSynthesisUrl(this.config), s = i === "voices" ? "GET" : "OPTIONS";
421
+ try {
422
+ const a = await fetch(o, {
423
+ method: s,
424
+ headers: e,
425
+ signal: t.signal
426
+ });
427
+ return clearTimeout(r), a.ok || a.status === 405;
428
+ } catch {
429
+ return clearTimeout(r), !1;
430
+ }
431
+ } catch {
432
+ return !1;
433
+ }
434
+ }
435
+ supportsFeature(e) {
436
+ switch (e) {
437
+ case "pause":
438
+ case "resume":
439
+ case "wordBoundary":
440
+ case "voiceSelection":
441
+ case "rateControl":
442
+ return !0;
443
+ case "pitchControl":
444
+ return !1;
445
+ default:
446
+ return !1;
447
+ }
448
+ }
449
+ getCapabilities() {
450
+ return {
451
+ supportsPause: !0,
452
+ supportsResume: !0,
453
+ supportsWordBoundary: !0,
454
+ supportsVoiceSelection: !0,
455
+ supportsRateControl: !0,
456
+ supportsPitchControl: !1,
457
+ maxTextLength: M[this.config ? b(this.config) : "pie"]
458
+ };
459
+ }
460
+ destroy() {
461
+ this.config = null, this.adapter = null, this.telemetryReporter = void 0;
462
+ }
463
+ };
464
+ export {
465
+ L as ServerTTSProvider
466
+ };