@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.
- package/dist/client/index.d.ts +2 -0
- package/dist/client/index.js +122 -0
- package/dist/client/providerStatusWidget.d.ts +32 -0
- package/dist/react/VoiceProviderStatus.d.ts +6 -0
- package/dist/react/index.d.ts +1 -0
- package/dist/react/index.js +334 -135
- package/dist/svelte/createVoiceProviderStatus.d.ts +4 -2
- package/dist/svelte/index.js +138 -13
- package/dist/vue/VoiceProviderStatus.d.ts +51 -0
- package/dist/vue/index.d.ts +1 -0
- package/dist/vue/index.js +358 -166
- package/package.json +1 -1
package/dist/svelte/index.js
CHANGED
|
@@ -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("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
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 = {}) =>
|
|
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
|
|
969
|
-
var
|
|
970
|
-
var
|
|
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("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
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 ??
|
|
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 ??
|
|
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>${
|
|
1008
|
-
<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--${
|
|
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">${
|
|
1013
|
-
<strong class="absolute-voice-routing-status__label">${
|
|
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">${
|
|
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">${
|
|
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>;
|
package/dist/vue/index.d.ts
CHANGED
|
@@ -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';
|