@absolutejs/voice 0.0.22-beta.59 → 0.0.22-beta.60

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.
@@ -882,8 +882,133 @@ var createVoiceProviderStatusStore = (path = "/api/provider-status", options = {
882
882
  };
883
883
  };
884
884
 
885
+ // src/client/providerStatusWidget.ts
886
+ var DEFAULT_TITLE2 = "Voice Providers";
887
+ var DEFAULT_DESCRIPTION2 = "Live provider health, fallback counts, latency, and suppression state from your self-hosted trace store.";
888
+ var escapeHtml2 = (value) => value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&#39;");
889
+ var formatProvider = (provider) => provider.split(/[-_\s]+/).filter(Boolean).map((part) => `${part[0]?.toUpperCase() ?? ""}${part.slice(1)}`).join(" ") || provider;
890
+ var formatStatus = (status) => status.split("-").map((part) => `${part[0]?.toUpperCase() ?? ""}${part.slice(1)}`).join(" ");
891
+ var formatLatency = (value) => typeof value === "number" ? `${value}ms` : "No samples";
892
+ var formatSuppression = (value) => typeof value === "number" ? `${Math.ceil(value / 1000)}s` : "None";
893
+ var getProviderDetail = (provider) => {
894
+ if (provider.status === "suppressed") {
895
+ return provider.lastError ? `Suppressed for ${formatSuppression(provider.suppressionRemainingMs)} after ${provider.lastError}.` : `Suppressed for ${formatSuppression(provider.suppressionRemainingMs)}.`;
896
+ }
897
+ if (provider.status === "recoverable") {
898
+ return "Cooldown expired; ready for recovery traffic.";
899
+ }
900
+ if (provider.status === "rate-limited") {
901
+ return "Rate limit detected; router should avoid this provider.";
902
+ }
903
+ if (provider.status === "degraded") {
904
+ return provider.lastError ?? "Recent provider errors detected.";
905
+ }
906
+ if (provider.status === "healthy") {
907
+ return provider.recommended ? "Healthy and currently recommended." : "Healthy and available for routing.";
908
+ }
909
+ return "No provider traffic observed yet.";
910
+ };
911
+ var isWarningStatus = (status) => status === "degraded" || status === "rate-limited" || status === "recoverable" || status === "suppressed";
912
+ var createVoiceProviderStatusViewModel = (snapshot, options = {}) => {
913
+ const providers = snapshot.providers.map((provider) => ({
914
+ ...provider,
915
+ detail: getProviderDetail(provider),
916
+ label: `${formatProvider(provider.provider)}${provider.recommended ? " recommended" : ""}`,
917
+ rows: [
918
+ { label: "Runs", value: String(provider.runCount) },
919
+ { label: "Avg latency", value: formatLatency(provider.averageElapsedMs) },
920
+ { label: "Errors", value: String(provider.errorCount) },
921
+ { label: "Timeouts", value: String(provider.timeoutCount) },
922
+ { label: "Fallbacks", value: String(provider.fallbackCount) },
923
+ {
924
+ label: "Suppression",
925
+ value: formatSuppression(provider.suppressionRemainingMs)
926
+ }
927
+ ]
928
+ }));
929
+ const warningCount = providers.filter((provider) => isWarningStatus(provider.status)).length;
930
+ const healthyCount = providers.filter((provider) => provider.status === "healthy").length;
931
+ return {
932
+ description: options.description ?? DEFAULT_DESCRIPTION2,
933
+ error: snapshot.error,
934
+ isLoading: snapshot.isLoading,
935
+ label: snapshot.error ? "Unavailable" : providers.length ? warningCount > 0 ? `${warningCount} needs attention` : `${healthyCount} healthy` : snapshot.isLoading ? "Checking" : "No provider traffic",
936
+ providers,
937
+ status: snapshot.error ? "error" : providers.length ? warningCount > 0 ? "warning" : "ready" : snapshot.isLoading ? "loading" : "empty",
938
+ title: options.title ?? DEFAULT_TITLE2,
939
+ updatedAt: snapshot.updatedAt
940
+ };
941
+ };
942
+ var renderVoiceProviderStatusHTML = (snapshot, options = {}) => {
943
+ const model = createVoiceProviderStatusViewModel(snapshot, options);
944
+ const providers = model.providers.length ? `<div class="absolute-voice-provider-status__providers">${model.providers.map((provider) => `<article class="absolute-voice-provider-status__provider absolute-voice-provider-status__provider--${escapeHtml2(provider.status)}">
945
+ <header>
946
+ <strong>${escapeHtml2(provider.label)}</strong>
947
+ <span>${escapeHtml2(formatStatus(provider.status))}</span>
948
+ </header>
949
+ <p>${escapeHtml2(provider.detail)}</p>
950
+ <dl>${provider.rows.map((row) => `<div>
951
+ <dt>${escapeHtml2(row.label)}</dt>
952
+ <dd>${escapeHtml2(row.value)}</dd>
953
+ </div>`).join("")}</dl>
954
+ </article>`).join("")}</div>` : '<p class="absolute-voice-provider-status__empty">Run voice traffic to see provider health.</p>';
955
+ return `<section class="absolute-voice-provider-status absolute-voice-provider-status--${escapeHtml2(model.status)}">
956
+ <header class="absolute-voice-provider-status__header">
957
+ <span class="absolute-voice-provider-status__eyebrow">${escapeHtml2(model.title)}</span>
958
+ <strong class="absolute-voice-provider-status__label">${escapeHtml2(model.label)}</strong>
959
+ </header>
960
+ <p class="absolute-voice-provider-status__description">${escapeHtml2(model.description)}</p>
961
+ ${providers}
962
+ ${model.error ? `<p class="absolute-voice-provider-status__error">${escapeHtml2(model.error)}</p>` : ""}
963
+ </section>`;
964
+ };
965
+ var getVoiceProviderStatusCSS = () => `.absolute-voice-provider-status{border:1px solid #d8d2c4;border-radius:20px;background:#fffaf0;color:#16130d;padding:18px;box-shadow:0 18px 40px rgba(47,37,18,.12);font-family:inherit}.absolute-voice-provider-status--error,.absolute-voice-provider-status--warning{border-color:#f2a7a7;background:#fff5f3}.absolute-voice-provider-status__header,.absolute-voice-provider-status__provider header{align-items:start;display:flex;gap:12px;justify-content:space-between}.absolute-voice-provider-status__eyebrow{color:#73664f;font-size:12px;font-weight:800;letter-spacing:.08em;text-transform:uppercase}.absolute-voice-provider-status__label{font-size:24px;line-height:1}.absolute-voice-provider-status__description,.absolute-voice-provider-status__provider p,.absolute-voice-provider-status__provider dt,.absolute-voice-provider-status__empty{color:#514733}.absolute-voice-provider-status__providers{display:grid;gap:12px;margin-top:14px}.absolute-voice-provider-status__provider{background:#fff;border:1px solid #eee4d2;border-radius:16px;padding:14px}.absolute-voice-provider-status__provider--degraded,.absolute-voice-provider-status__provider--rate-limited,.absolute-voice-provider-status__provider--suppressed{border-color:#f2a7a7}.absolute-voice-provider-status__provider--recoverable{border-color:#fbbf24}.absolute-voice-provider-status__provider p{margin:10px 0}.absolute-voice-provider-status__provider dl{display:grid;gap:8px;grid-template-columns:repeat(2,minmax(0,1fr));margin:0}.absolute-voice-provider-status__provider div{background:#fffaf0;border:1px solid #eee4d2;border-radius:12px;padding:8px}.absolute-voice-provider-status__provider dt{font-size:12px}.absolute-voice-provider-status__provider dd{font-weight:800;margin:4px 0 0}.absolute-voice-provider-status__empty{margin:14px 0 0}.absolute-voice-provider-status__error{color:#9f1239;font-weight:700}`;
966
+ var mountVoiceProviderStatus = (element, path = "/api/provider-status", options = {}) => {
967
+ const store = createVoiceProviderStatusStore(path, options);
968
+ const render = () => {
969
+ element.innerHTML = renderVoiceProviderStatusHTML(store.getSnapshot(), options);
970
+ };
971
+ const unsubscribe = store.subscribe(render);
972
+ render();
973
+ store.refresh().catch(() => {});
974
+ return {
975
+ close: () => {
976
+ unsubscribe();
977
+ store.close();
978
+ },
979
+ refresh: store.refresh
980
+ };
981
+ };
982
+ var defineVoiceProviderStatusElement = (tagName = "absolute-voice-provider-status") => {
983
+ if (typeof window === "undefined" || typeof customElements === "undefined" || customElements.get(tagName)) {
984
+ return;
985
+ }
986
+ customElements.define(tagName, class AbsoluteVoiceProviderStatusElement extends HTMLElement {
987
+ mounted;
988
+ connectedCallback() {
989
+ const intervalMs = Number(this.getAttribute("interval-ms") ?? 5000);
990
+ this.mounted = mountVoiceProviderStatus(this, this.getAttribute("path") ?? "/api/provider-status", {
991
+ description: this.getAttribute("description") ?? undefined,
992
+ intervalMs: Number.isFinite(intervalMs) ? intervalMs : 5000,
993
+ title: this.getAttribute("title") ?? undefined
994
+ });
995
+ }
996
+ disconnectedCallback() {
997
+ this.mounted?.close();
998
+ this.mounted = undefined;
999
+ }
1000
+ });
1001
+ };
1002
+
885
1003
  // src/svelte/createVoiceProviderStatus.ts
886
- var createVoiceProviderStatus = (path = "/api/provider-status", options = {}) => createVoiceProviderStatusStore(path, options);
1004
+ var createVoiceProviderStatus = (path = "/api/provider-status", options = {}) => {
1005
+ const store = createVoiceProviderStatusStore(path, options);
1006
+ return {
1007
+ ...store,
1008
+ getHTML: () => renderVoiceProviderStatusHTML(store.getSnapshot(), options),
1009
+ getViewModel: () => createVoiceProviderStatusViewModel(store.getSnapshot(), options)
1010
+ };
1011
+ };
887
1012
  // src/client/routingStatus.ts
888
1013
  var fetchVoiceRoutingStatus = async (path = "/api/routing/latest", options = {}) => {
889
1014
  const fetchImpl = options.fetch ?? globalThis.fetch;
@@ -965,9 +1090,9 @@ var createVoiceRoutingStatusStore = (path = "/api/routing/latest", options = {})
965
1090
  };
966
1091
 
967
1092
  // src/client/routingStatusWidget.ts
968
- var DEFAULT_TITLE2 = "Voice Routing";
969
- var DEFAULT_DESCRIPTION2 = "Latest provider routing decision from the self-hosted trace store.";
970
- var escapeHtml2 = (value) => value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&#39;");
1093
+ var DEFAULT_TITLE3 = "Voice Routing";
1094
+ var DEFAULT_DESCRIPTION3 = "Latest provider routing decision from the self-hosted trace store.";
1095
+ var escapeHtml3 = (value) => value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&#39;");
971
1096
  var formatValue = (value, fallback = "None") => typeof value === "string" && value.trim() ? value : typeof value === "number" && Number.isFinite(value) ? String(value) : fallback;
972
1097
  var createVoiceRoutingStatusViewModel = (snapshot, options = {}) => {
973
1098
  const decision = snapshot.decision;
@@ -991,30 +1116,30 @@ var createVoiceRoutingStatusViewModel = (snapshot, options = {}) => {
991
1116
  ] : [];
992
1117
  return {
993
1118
  decision,
994
- description: options.description ?? DEFAULT_DESCRIPTION2,
1119
+ description: options.description ?? DEFAULT_DESCRIPTION3,
995
1120
  error: snapshot.error,
996
1121
  isLoading: snapshot.isLoading,
997
1122
  label: snapshot.error ? "Unavailable" : decision ? `${formatValue(decision.kind).toUpperCase()} ${formatValue(decision.status, "unknown")}` : snapshot.isLoading ? "Checking" : "No routing yet",
998
1123
  rows,
999
1124
  status: snapshot.error ? "error" : decision ? "ready" : snapshot.isLoading ? "loading" : "empty",
1000
- title: options.title ?? DEFAULT_TITLE2,
1125
+ title: options.title ?? DEFAULT_TITLE3,
1001
1126
  updatedAt: snapshot.updatedAt
1002
1127
  };
1003
1128
  };
1004
1129
  var renderVoiceRoutingStatusHTML = (snapshot, options = {}) => {
1005
1130
  const model = createVoiceRoutingStatusViewModel(snapshot, options);
1006
1131
  const rows = model.rows.length ? `<div class="absolute-voice-routing-status__grid">${model.rows.map((row) => `<div>
1007
- <span>${escapeHtml2(row.label)}</span>
1008
- <strong>${escapeHtml2(row.value)}</strong>
1132
+ <span>${escapeHtml3(row.label)}</span>
1133
+ <strong>${escapeHtml3(row.value)}</strong>
1009
1134
  </div>`).join("")}</div>` : '<p class="absolute-voice-routing-status__empty">Start a voice session to see the selected provider.</p>';
1010
- return `<section class="absolute-voice-routing-status absolute-voice-routing-status--${escapeHtml2(model.status)}">
1135
+ return `<section class="absolute-voice-routing-status absolute-voice-routing-status--${escapeHtml3(model.status)}">
1011
1136
  <header class="absolute-voice-routing-status__header">
1012
- <span class="absolute-voice-routing-status__eyebrow">${escapeHtml2(model.title)}</span>
1013
- <strong class="absolute-voice-routing-status__label">${escapeHtml2(model.label)}</strong>
1137
+ <span class="absolute-voice-routing-status__eyebrow">${escapeHtml3(model.title)}</span>
1138
+ <strong class="absolute-voice-routing-status__label">${escapeHtml3(model.label)}</strong>
1014
1139
  </header>
1015
- <p class="absolute-voice-routing-status__description">${escapeHtml2(model.description)}</p>
1140
+ <p class="absolute-voice-routing-status__description">${escapeHtml3(model.description)}</p>
1016
1141
  ${rows}
1017
- ${model.error ? `<p class="absolute-voice-routing-status__error">${escapeHtml2(model.error)}</p>` : ""}
1142
+ ${model.error ? `<p class="absolute-voice-routing-status__error">${escapeHtml3(model.error)}</p>` : ""}
1018
1143
  </section>`;
1019
1144
  };
1020
1145
  var getVoiceRoutingStatusCSS = () => `.absolute-voice-routing-status{border:1px solid #d8d2c4;border-radius:20px;background:#fffaf0;color:#16130d;padding:18px;box-shadow:0 18px 40px rgba(47,37,18,.12);font-family:inherit}.absolute-voice-routing-status--error{border-color:#f2a7a7;background:#fff5f3}.absolute-voice-routing-status__header{align-items:start;display:flex;gap:12px;justify-content:space-between}.absolute-voice-routing-status__eyebrow{color:#73664f;font-size:12px;font-weight:800;letter-spacing:.08em;text-transform:uppercase}.absolute-voice-routing-status__label{font-size:24px;line-height:1}.absolute-voice-routing-status__description{color:#514733;margin:12px 0 0}.absolute-voice-routing-status__grid{display:grid;gap:8px;grid-template-columns:repeat(2,minmax(0,1fr));margin-top:14px}.absolute-voice-routing-status__grid div{background:#fff;border:1px solid #eee4d2;border-radius:14px;padding:10px 12px}.absolute-voice-routing-status__grid span{color:#655944;display:block;font-size:12px;margin-bottom:4px}.absolute-voice-routing-status__grid strong{overflow-wrap:anywhere}.absolute-voice-routing-status__empty{color:#655944;margin:14px 0 0}.absolute-voice-routing-status__error{color:#9f1239;font-weight:700}`;
@@ -0,0 +1,51 @@
1
+ export declare const VoiceProviderStatus: import("vue").DefineComponent<import("vue").ExtractPropTypes<{
2
+ class: {
3
+ default: string;
4
+ type: StringConstructor;
5
+ };
6
+ description: {
7
+ default: undefined;
8
+ type: StringConstructor;
9
+ };
10
+ intervalMs: {
11
+ default: number;
12
+ type: NumberConstructor;
13
+ };
14
+ path: {
15
+ default: string;
16
+ type: StringConstructor;
17
+ };
18
+ title: {
19
+ default: undefined;
20
+ type: StringConstructor;
21
+ };
22
+ }>, () => import("vue").VNode<import("vue").RendererNode, import("vue").RendererElement, {
23
+ [key: string]: any;
24
+ }>, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{
25
+ class: {
26
+ default: string;
27
+ type: StringConstructor;
28
+ };
29
+ description: {
30
+ default: undefined;
31
+ type: StringConstructor;
32
+ };
33
+ intervalMs: {
34
+ default: number;
35
+ type: NumberConstructor;
36
+ };
37
+ path: {
38
+ default: string;
39
+ type: StringConstructor;
40
+ };
41
+ title: {
42
+ default: undefined;
43
+ type: StringConstructor;
44
+ };
45
+ }>> & Readonly<{}>, {
46
+ description: string;
47
+ title: string;
48
+ path: string;
49
+ intervalMs: number;
50
+ class: string;
51
+ }, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
@@ -1,4 +1,5 @@
1
1
  export { VoiceOpsStatus } from './VoiceOpsStatus';
2
+ export { VoiceProviderStatus } from './VoiceProviderStatus';
2
3
  export { VoiceRoutingStatus } from './VoiceRoutingStatus';
3
4
  export { useVoiceAppKitStatus } from './useVoiceAppKitStatus';
4
5
  export { useVoiceStream } from './useVoiceStream';