@absolutejs/voice 0.0.22-beta.324 → 0.0.22-beta.326
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/angular/index.js +103 -0
- package/dist/client/index.js +103 -0
- package/dist/index.js +190 -10
- package/dist/productionReadiness.d.ts +3 -0
- package/dist/react/index.js +103 -0
- package/dist/svelte/index.js +103 -0
- package/dist/telephonyMediaRoutes.d.ts +3 -1
- package/dist/testing/index.js +103 -0
- package/dist/vue/index.js +103 -0
- package/dist/vue/useVoiceReadinessFailures.d.ts +18 -0
- package/package.json +2 -2
package/dist/angular/index.js
CHANGED
|
@@ -1471,6 +1471,29 @@ var telephonyDirection = (track) => {
|
|
|
1471
1471
|
return "unknown";
|
|
1472
1472
|
};
|
|
1473
1473
|
var telephonyFrameKind = (direction) => direction === "outbound" ? "assistant-audio" : "input-audio";
|
|
1474
|
+
var telephonyEventKind = (envelope) => {
|
|
1475
|
+
const raw = firstString([envelope], ["event", "type", "eventType"]) ?? firstString([unknownRecord(envelope.message)], ["event", "type"]);
|
|
1476
|
+
const normalized = raw?.toLowerCase().replace(/[_\s-]+/g, "-");
|
|
1477
|
+
if (!normalized) {
|
|
1478
|
+
return "unknown";
|
|
1479
|
+
}
|
|
1480
|
+
if (normalized.includes("connected")) {
|
|
1481
|
+
return "connected";
|
|
1482
|
+
}
|
|
1483
|
+
if (normalized.includes("start")) {
|
|
1484
|
+
return "start";
|
|
1485
|
+
}
|
|
1486
|
+
if (normalized.includes("media")) {
|
|
1487
|
+
return "media";
|
|
1488
|
+
}
|
|
1489
|
+
if (normalized.includes("stop") || normalized.includes("closed")) {
|
|
1490
|
+
return "stop";
|
|
1491
|
+
}
|
|
1492
|
+
if (normalized.includes("error") || normalized.includes("failed")) {
|
|
1493
|
+
return "error";
|
|
1494
|
+
}
|
|
1495
|
+
return "unknown";
|
|
1496
|
+
};
|
|
1474
1497
|
var normalizeWebRTCStat = (stat) => {
|
|
1475
1498
|
const sample = {};
|
|
1476
1499
|
for (const [key, value] of Object.entries(stat)) {
|
|
@@ -1587,6 +1610,86 @@ var createTelephonyMediaSerializer = (input) => {
|
|
|
1587
1610
|
})
|
|
1588
1611
|
};
|
|
1589
1612
|
};
|
|
1613
|
+
var parseTelephonyStreamEvent = (input) => {
|
|
1614
|
+
const envelope = input.envelope;
|
|
1615
|
+
const media = unknownRecord(envelope.media);
|
|
1616
|
+
const start = unknownRecord(envelope.start);
|
|
1617
|
+
const stop = unknownRecord(envelope.stop);
|
|
1618
|
+
const errorRecord = unknownRecord(envelope.error);
|
|
1619
|
+
const kind = telephonyEventKind(envelope);
|
|
1620
|
+
const carrier = input.carrier ?? firstString([envelope], ["provider", "carrier"]) ?? "telephony";
|
|
1621
|
+
const frame = kind === "media" ? parseTelephonyMediaFrame({
|
|
1622
|
+
carrier,
|
|
1623
|
+
envelope,
|
|
1624
|
+
format: input.format,
|
|
1625
|
+
sessionId: input.sessionId
|
|
1626
|
+
}) : undefined;
|
|
1627
|
+
const streamId = firstString([media, start, stop, envelope], ["streamSid", "stream_id", "streamId", "callSid", "call_id"]) ?? input.sessionId;
|
|
1628
|
+
const sequenceNumber = firstString([media, envelope], ["sequenceNumber", "sequence_number", "chunk"]);
|
|
1629
|
+
const track = firstString([media, envelope], ["track", "direction"]);
|
|
1630
|
+
return {
|
|
1631
|
+
audioBytes: frame?.audio ? frame.audio instanceof ArrayBuffer ? frame.audio.byteLength : frame.audio.byteLength : 0,
|
|
1632
|
+
at: frame?.at ?? firstNumber([media, start, stop, envelope], ["timestamp", "time", "startedAt"]),
|
|
1633
|
+
carrier,
|
|
1634
|
+
direction: telephonyDirection(track),
|
|
1635
|
+
error: firstString([errorRecord, envelope], ["message", "error", "reason"]),
|
|
1636
|
+
kind,
|
|
1637
|
+
sequenceNumber,
|
|
1638
|
+
streamId
|
|
1639
|
+
};
|
|
1640
|
+
};
|
|
1641
|
+
var buildMediaTelephonyStreamLifecycleReport = (input = {}) => {
|
|
1642
|
+
const envelopes = input.envelopes ?? [];
|
|
1643
|
+
const events = envelopes.map((envelope) => parseTelephonyStreamEvent({
|
|
1644
|
+
carrier: input.carrier,
|
|
1645
|
+
envelope
|
|
1646
|
+
}));
|
|
1647
|
+
const issues = [];
|
|
1648
|
+
const startedIndex = events.findIndex((event) => event.kind === "start");
|
|
1649
|
+
const firstMediaIndex = events.findIndex((event) => event.kind === "media");
|
|
1650
|
+
const stoppedIndex = events.findIndex((event) => event.kind === "stop");
|
|
1651
|
+
const started = startedIndex >= 0;
|
|
1652
|
+
const stopped = stoppedIndex >= 0;
|
|
1653
|
+
const mediaEvents = events.filter((event) => event.kind === "media");
|
|
1654
|
+
const audioBytes = events.reduce((total, event) => total + event.audioBytes, 0);
|
|
1655
|
+
const minAudioBytes = input.minAudioBytes ?? 1;
|
|
1656
|
+
const streamIds = Array.from(new Set(events.map((event) => event.streamId).filter(Boolean)));
|
|
1657
|
+
if ((input.requireStart ?? true) && !started) {
|
|
1658
|
+
pushIssue(issues, "error", "media.telephony_missing_start", "Telephony media stream did not include a start event.");
|
|
1659
|
+
}
|
|
1660
|
+
if ((input.requireMedia ?? true) && mediaEvents.length === 0) {
|
|
1661
|
+
pushIssue(issues, "error", "media.telephony_missing_media", "Telephony media stream did not include media payload events.");
|
|
1662
|
+
}
|
|
1663
|
+
if ((input.requireStop ?? true) && !stopped) {
|
|
1664
|
+
pushIssue(issues, input.maxMissingStop === false ? "warning" : "error", "media.telephony_missing_stop", "Telephony media stream did not include a stop event.");
|
|
1665
|
+
}
|
|
1666
|
+
if (started && firstMediaIndex >= 0 && firstMediaIndex < startedIndex) {
|
|
1667
|
+
pushIssue(issues, "error", "media.telephony_media_before_start", "Telephony media payload arrived before the stream start event.");
|
|
1668
|
+
}
|
|
1669
|
+
if (stopped && firstMediaIndex >= 0 && stoppedIndex < firstMediaIndex) {
|
|
1670
|
+
pushIssue(issues, "error", "media.telephony_stop_before_media", "Telephony media stream stopped before any media payload arrived.");
|
|
1671
|
+
}
|
|
1672
|
+
if (mediaEvents.length > 0 && audioBytes < minAudioBytes) {
|
|
1673
|
+
pushIssue(issues, "error", "media.telephony_no_audio_bytes", `Telephony media stream parsed ${String(audioBytes)} audio byte(s), below required ${String(minAudioBytes)}.`);
|
|
1674
|
+
}
|
|
1675
|
+
for (const event of events) {
|
|
1676
|
+
if (event.kind === "error") {
|
|
1677
|
+
pushIssue(issues, "error", "media.telephony_stream_error", event.error ?? "Telephony media stream emitted an error event.");
|
|
1678
|
+
}
|
|
1679
|
+
}
|
|
1680
|
+
return {
|
|
1681
|
+
audioBytes,
|
|
1682
|
+
carrier: input.carrier,
|
|
1683
|
+
checkedAt: Date.now(),
|
|
1684
|
+
events,
|
|
1685
|
+
issues,
|
|
1686
|
+
mediaEvents: mediaEvents.length,
|
|
1687
|
+
started,
|
|
1688
|
+
status: issues.some((issue) => issue.severity === "error") ? "fail" : issues.length > 0 ? "warn" : "pass",
|
|
1689
|
+
stopped,
|
|
1690
|
+
streamIds
|
|
1691
|
+
};
|
|
1692
|
+
};
|
|
1590
1693
|
var buildMediaResamplingPlan = (input) => {
|
|
1591
1694
|
const required = !formatMatches(input.inputFormat, input.outputFormat);
|
|
1592
1695
|
return {
|
package/dist/client/index.js
CHANGED
|
@@ -864,6 +864,29 @@ var telephonyDirection = (track) => {
|
|
|
864
864
|
return "unknown";
|
|
865
865
|
};
|
|
866
866
|
var telephonyFrameKind = (direction) => direction === "outbound" ? "assistant-audio" : "input-audio";
|
|
867
|
+
var telephonyEventKind = (envelope) => {
|
|
868
|
+
const raw = firstString([envelope], ["event", "type", "eventType"]) ?? firstString([unknownRecord(envelope.message)], ["event", "type"]);
|
|
869
|
+
const normalized = raw?.toLowerCase().replace(/[_\s-]+/g, "-");
|
|
870
|
+
if (!normalized) {
|
|
871
|
+
return "unknown";
|
|
872
|
+
}
|
|
873
|
+
if (normalized.includes("connected")) {
|
|
874
|
+
return "connected";
|
|
875
|
+
}
|
|
876
|
+
if (normalized.includes("start")) {
|
|
877
|
+
return "start";
|
|
878
|
+
}
|
|
879
|
+
if (normalized.includes("media")) {
|
|
880
|
+
return "media";
|
|
881
|
+
}
|
|
882
|
+
if (normalized.includes("stop") || normalized.includes("closed")) {
|
|
883
|
+
return "stop";
|
|
884
|
+
}
|
|
885
|
+
if (normalized.includes("error") || normalized.includes("failed")) {
|
|
886
|
+
return "error";
|
|
887
|
+
}
|
|
888
|
+
return "unknown";
|
|
889
|
+
};
|
|
867
890
|
var normalizeWebRTCStat = (stat) => {
|
|
868
891
|
const sample = {};
|
|
869
892
|
for (const [key, value] of Object.entries(stat)) {
|
|
@@ -980,6 +1003,86 @@ var createTelephonyMediaSerializer = (input) => {
|
|
|
980
1003
|
})
|
|
981
1004
|
};
|
|
982
1005
|
};
|
|
1006
|
+
var parseTelephonyStreamEvent = (input) => {
|
|
1007
|
+
const envelope = input.envelope;
|
|
1008
|
+
const media = unknownRecord(envelope.media);
|
|
1009
|
+
const start = unknownRecord(envelope.start);
|
|
1010
|
+
const stop = unknownRecord(envelope.stop);
|
|
1011
|
+
const errorRecord = unknownRecord(envelope.error);
|
|
1012
|
+
const kind = telephonyEventKind(envelope);
|
|
1013
|
+
const carrier = input.carrier ?? firstString([envelope], ["provider", "carrier"]) ?? "telephony";
|
|
1014
|
+
const frame = kind === "media" ? parseTelephonyMediaFrame({
|
|
1015
|
+
carrier,
|
|
1016
|
+
envelope,
|
|
1017
|
+
format: input.format,
|
|
1018
|
+
sessionId: input.sessionId
|
|
1019
|
+
}) : undefined;
|
|
1020
|
+
const streamId = firstString([media, start, stop, envelope], ["streamSid", "stream_id", "streamId", "callSid", "call_id"]) ?? input.sessionId;
|
|
1021
|
+
const sequenceNumber = firstString([media, envelope], ["sequenceNumber", "sequence_number", "chunk"]);
|
|
1022
|
+
const track = firstString([media, envelope], ["track", "direction"]);
|
|
1023
|
+
return {
|
|
1024
|
+
audioBytes: frame?.audio ? frame.audio instanceof ArrayBuffer ? frame.audio.byteLength : frame.audio.byteLength : 0,
|
|
1025
|
+
at: frame?.at ?? firstNumber([media, start, stop, envelope], ["timestamp", "time", "startedAt"]),
|
|
1026
|
+
carrier,
|
|
1027
|
+
direction: telephonyDirection(track),
|
|
1028
|
+
error: firstString([errorRecord, envelope], ["message", "error", "reason"]),
|
|
1029
|
+
kind,
|
|
1030
|
+
sequenceNumber,
|
|
1031
|
+
streamId
|
|
1032
|
+
};
|
|
1033
|
+
};
|
|
1034
|
+
var buildMediaTelephonyStreamLifecycleReport = (input = {}) => {
|
|
1035
|
+
const envelopes = input.envelopes ?? [];
|
|
1036
|
+
const events = envelopes.map((envelope) => parseTelephonyStreamEvent({
|
|
1037
|
+
carrier: input.carrier,
|
|
1038
|
+
envelope
|
|
1039
|
+
}));
|
|
1040
|
+
const issues = [];
|
|
1041
|
+
const startedIndex = events.findIndex((event) => event.kind === "start");
|
|
1042
|
+
const firstMediaIndex = events.findIndex((event) => event.kind === "media");
|
|
1043
|
+
const stoppedIndex = events.findIndex((event) => event.kind === "stop");
|
|
1044
|
+
const started = startedIndex >= 0;
|
|
1045
|
+
const stopped = stoppedIndex >= 0;
|
|
1046
|
+
const mediaEvents = events.filter((event) => event.kind === "media");
|
|
1047
|
+
const audioBytes = events.reduce((total, event) => total + event.audioBytes, 0);
|
|
1048
|
+
const minAudioBytes = input.minAudioBytes ?? 1;
|
|
1049
|
+
const streamIds = Array.from(new Set(events.map((event) => event.streamId).filter(Boolean)));
|
|
1050
|
+
if ((input.requireStart ?? true) && !started) {
|
|
1051
|
+
pushIssue(issues, "error", "media.telephony_missing_start", "Telephony media stream did not include a start event.");
|
|
1052
|
+
}
|
|
1053
|
+
if ((input.requireMedia ?? true) && mediaEvents.length === 0) {
|
|
1054
|
+
pushIssue(issues, "error", "media.telephony_missing_media", "Telephony media stream did not include media payload events.");
|
|
1055
|
+
}
|
|
1056
|
+
if ((input.requireStop ?? true) && !stopped) {
|
|
1057
|
+
pushIssue(issues, input.maxMissingStop === false ? "warning" : "error", "media.telephony_missing_stop", "Telephony media stream did not include a stop event.");
|
|
1058
|
+
}
|
|
1059
|
+
if (started && firstMediaIndex >= 0 && firstMediaIndex < startedIndex) {
|
|
1060
|
+
pushIssue(issues, "error", "media.telephony_media_before_start", "Telephony media payload arrived before the stream start event.");
|
|
1061
|
+
}
|
|
1062
|
+
if (stopped && firstMediaIndex >= 0 && stoppedIndex < firstMediaIndex) {
|
|
1063
|
+
pushIssue(issues, "error", "media.telephony_stop_before_media", "Telephony media stream stopped before any media payload arrived.");
|
|
1064
|
+
}
|
|
1065
|
+
if (mediaEvents.length > 0 && audioBytes < minAudioBytes) {
|
|
1066
|
+
pushIssue(issues, "error", "media.telephony_no_audio_bytes", `Telephony media stream parsed ${String(audioBytes)} audio byte(s), below required ${String(minAudioBytes)}.`);
|
|
1067
|
+
}
|
|
1068
|
+
for (const event of events) {
|
|
1069
|
+
if (event.kind === "error") {
|
|
1070
|
+
pushIssue(issues, "error", "media.telephony_stream_error", event.error ?? "Telephony media stream emitted an error event.");
|
|
1071
|
+
}
|
|
1072
|
+
}
|
|
1073
|
+
return {
|
|
1074
|
+
audioBytes,
|
|
1075
|
+
carrier: input.carrier,
|
|
1076
|
+
checkedAt: Date.now(),
|
|
1077
|
+
events,
|
|
1078
|
+
issues,
|
|
1079
|
+
mediaEvents: mediaEvents.length,
|
|
1080
|
+
started,
|
|
1081
|
+
status: issues.some((issue) => issue.severity === "error") ? "fail" : issues.length > 0 ? "warn" : "pass",
|
|
1082
|
+
stopped,
|
|
1083
|
+
streamIds
|
|
1084
|
+
};
|
|
1085
|
+
};
|
|
983
1086
|
var buildMediaResamplingPlan = (input) => {
|
|
984
1087
|
const required = !formatMatches(input.inputFormat, input.outputFormat);
|
|
985
1088
|
return {
|
package/dist/index.js
CHANGED
|
@@ -11696,6 +11696,29 @@ var telephonyDirection = (track) => {
|
|
|
11696
11696
|
return "unknown";
|
|
11697
11697
|
};
|
|
11698
11698
|
var telephonyFrameKind = (direction) => direction === "outbound" ? "assistant-audio" : "input-audio";
|
|
11699
|
+
var telephonyEventKind = (envelope) => {
|
|
11700
|
+
const raw = firstString2([envelope], ["event", "type", "eventType"]) ?? firstString2([unknownRecord(envelope.message)], ["event", "type"]);
|
|
11701
|
+
const normalized = raw?.toLowerCase().replace(/[_\s-]+/g, "-");
|
|
11702
|
+
if (!normalized) {
|
|
11703
|
+
return "unknown";
|
|
11704
|
+
}
|
|
11705
|
+
if (normalized.includes("connected")) {
|
|
11706
|
+
return "connected";
|
|
11707
|
+
}
|
|
11708
|
+
if (normalized.includes("start")) {
|
|
11709
|
+
return "start";
|
|
11710
|
+
}
|
|
11711
|
+
if (normalized.includes("media")) {
|
|
11712
|
+
return "media";
|
|
11713
|
+
}
|
|
11714
|
+
if (normalized.includes("stop") || normalized.includes("closed")) {
|
|
11715
|
+
return "stop";
|
|
11716
|
+
}
|
|
11717
|
+
if (normalized.includes("error") || normalized.includes("failed")) {
|
|
11718
|
+
return "error";
|
|
11719
|
+
}
|
|
11720
|
+
return "unknown";
|
|
11721
|
+
};
|
|
11699
11722
|
var normalizeWebRTCStat = (stat) => {
|
|
11700
11723
|
const sample = {};
|
|
11701
11724
|
for (const [key, value] of Object.entries(stat)) {
|
|
@@ -11812,6 +11835,86 @@ var createTelephonyMediaSerializer = (input) => {
|
|
|
11812
11835
|
})
|
|
11813
11836
|
};
|
|
11814
11837
|
};
|
|
11838
|
+
var parseTelephonyStreamEvent = (input) => {
|
|
11839
|
+
const envelope = input.envelope;
|
|
11840
|
+
const media = unknownRecord(envelope.media);
|
|
11841
|
+
const start = unknownRecord(envelope.start);
|
|
11842
|
+
const stop = unknownRecord(envelope.stop);
|
|
11843
|
+
const errorRecord = unknownRecord(envelope.error);
|
|
11844
|
+
const kind = telephonyEventKind(envelope);
|
|
11845
|
+
const carrier = input.carrier ?? firstString2([envelope], ["provider", "carrier"]) ?? "telephony";
|
|
11846
|
+
const frame = kind === "media" ? parseTelephonyMediaFrame({
|
|
11847
|
+
carrier,
|
|
11848
|
+
envelope,
|
|
11849
|
+
format: input.format,
|
|
11850
|
+
sessionId: input.sessionId
|
|
11851
|
+
}) : undefined;
|
|
11852
|
+
const streamId = firstString2([media, start, stop, envelope], ["streamSid", "stream_id", "streamId", "callSid", "call_id"]) ?? input.sessionId;
|
|
11853
|
+
const sequenceNumber = firstString2([media, envelope], ["sequenceNumber", "sequence_number", "chunk"]);
|
|
11854
|
+
const track = firstString2([media, envelope], ["track", "direction"]);
|
|
11855
|
+
return {
|
|
11856
|
+
audioBytes: frame?.audio ? frame.audio instanceof ArrayBuffer ? frame.audio.byteLength : frame.audio.byteLength : 0,
|
|
11857
|
+
at: frame?.at ?? firstNumber([media, start, stop, envelope], ["timestamp", "time", "startedAt"]),
|
|
11858
|
+
carrier,
|
|
11859
|
+
direction: telephonyDirection(track),
|
|
11860
|
+
error: firstString2([errorRecord, envelope], ["message", "error", "reason"]),
|
|
11861
|
+
kind,
|
|
11862
|
+
sequenceNumber,
|
|
11863
|
+
streamId
|
|
11864
|
+
};
|
|
11865
|
+
};
|
|
11866
|
+
var buildMediaTelephonyStreamLifecycleReport = (input = {}) => {
|
|
11867
|
+
const envelopes = input.envelopes ?? [];
|
|
11868
|
+
const events = envelopes.map((envelope) => parseTelephonyStreamEvent({
|
|
11869
|
+
carrier: input.carrier,
|
|
11870
|
+
envelope
|
|
11871
|
+
}));
|
|
11872
|
+
const issues = [];
|
|
11873
|
+
const startedIndex = events.findIndex((event) => event.kind === "start");
|
|
11874
|
+
const firstMediaIndex = events.findIndex((event) => event.kind === "media");
|
|
11875
|
+
const stoppedIndex = events.findIndex((event) => event.kind === "stop");
|
|
11876
|
+
const started = startedIndex >= 0;
|
|
11877
|
+
const stopped = stoppedIndex >= 0;
|
|
11878
|
+
const mediaEvents = events.filter((event) => event.kind === "media");
|
|
11879
|
+
const audioBytes = events.reduce((total, event) => total + event.audioBytes, 0);
|
|
11880
|
+
const minAudioBytes = input.minAudioBytes ?? 1;
|
|
11881
|
+
const streamIds = Array.from(new Set(events.map((event) => event.streamId).filter(Boolean)));
|
|
11882
|
+
if ((input.requireStart ?? true) && !started) {
|
|
11883
|
+
pushIssue(issues, "error", "media.telephony_missing_start", "Telephony media stream did not include a start event.");
|
|
11884
|
+
}
|
|
11885
|
+
if ((input.requireMedia ?? true) && mediaEvents.length === 0) {
|
|
11886
|
+
pushIssue(issues, "error", "media.telephony_missing_media", "Telephony media stream did not include media payload events.");
|
|
11887
|
+
}
|
|
11888
|
+
if ((input.requireStop ?? true) && !stopped) {
|
|
11889
|
+
pushIssue(issues, input.maxMissingStop === false ? "warning" : "error", "media.telephony_missing_stop", "Telephony media stream did not include a stop event.");
|
|
11890
|
+
}
|
|
11891
|
+
if (started && firstMediaIndex >= 0 && firstMediaIndex < startedIndex) {
|
|
11892
|
+
pushIssue(issues, "error", "media.telephony_media_before_start", "Telephony media payload arrived before the stream start event.");
|
|
11893
|
+
}
|
|
11894
|
+
if (stopped && firstMediaIndex >= 0 && stoppedIndex < firstMediaIndex) {
|
|
11895
|
+
pushIssue(issues, "error", "media.telephony_stop_before_media", "Telephony media stream stopped before any media payload arrived.");
|
|
11896
|
+
}
|
|
11897
|
+
if (mediaEvents.length > 0 && audioBytes < minAudioBytes) {
|
|
11898
|
+
pushIssue(issues, "error", "media.telephony_no_audio_bytes", `Telephony media stream parsed ${String(audioBytes)} audio byte(s), below required ${String(minAudioBytes)}.`);
|
|
11899
|
+
}
|
|
11900
|
+
for (const event of events) {
|
|
11901
|
+
if (event.kind === "error") {
|
|
11902
|
+
pushIssue(issues, "error", "media.telephony_stream_error", event.error ?? "Telephony media stream emitted an error event.");
|
|
11903
|
+
}
|
|
11904
|
+
}
|
|
11905
|
+
return {
|
|
11906
|
+
audioBytes,
|
|
11907
|
+
carrier: input.carrier,
|
|
11908
|
+
checkedAt: Date.now(),
|
|
11909
|
+
events,
|
|
11910
|
+
issues,
|
|
11911
|
+
mediaEvents: mediaEvents.length,
|
|
11912
|
+
started,
|
|
11913
|
+
status: issues.some((issue) => issue.severity === "error") ? "fail" : issues.length > 0 ? "warn" : "pass",
|
|
11914
|
+
stopped,
|
|
11915
|
+
streamIds
|
|
11916
|
+
};
|
|
11917
|
+
};
|
|
11815
11918
|
var buildMediaResamplingPlan = (input) => {
|
|
11816
11919
|
const required = !formatMatches2(input.inputFormat, input.outputFormat);
|
|
11817
11920
|
return {
|
|
@@ -12453,6 +12556,49 @@ var demoEnvelope = (carrier) => {
|
|
|
12453
12556
|
streamId: "proof-plivo-media"
|
|
12454
12557
|
};
|
|
12455
12558
|
};
|
|
12559
|
+
var demoLifecycleEnvelopes = (carrier) => {
|
|
12560
|
+
if (carrier === "twilio") {
|
|
12561
|
+
return [
|
|
12562
|
+
{
|
|
12563
|
+
event: "start",
|
|
12564
|
+
start: {
|
|
12565
|
+
streamSid: "proof-twilio-media"
|
|
12566
|
+
}
|
|
12567
|
+
},
|
|
12568
|
+
demoEnvelope(carrier),
|
|
12569
|
+
{
|
|
12570
|
+
event: "stop",
|
|
12571
|
+
stop: {
|
|
12572
|
+
streamSid: "proof-twilio-media"
|
|
12573
|
+
}
|
|
12574
|
+
}
|
|
12575
|
+
];
|
|
12576
|
+
}
|
|
12577
|
+
if (carrier === "telnyx") {
|
|
12578
|
+
return [
|
|
12579
|
+
{
|
|
12580
|
+
event: "start",
|
|
12581
|
+
stream_id: "proof-telnyx-media"
|
|
12582
|
+
},
|
|
12583
|
+
demoEnvelope(carrier),
|
|
12584
|
+
{
|
|
12585
|
+
event: "stop",
|
|
12586
|
+
stream_id: "proof-telnyx-media"
|
|
12587
|
+
}
|
|
12588
|
+
];
|
|
12589
|
+
}
|
|
12590
|
+
return [
|
|
12591
|
+
{
|
|
12592
|
+
event: "start",
|
|
12593
|
+
streamId: "proof-plivo-media"
|
|
12594
|
+
},
|
|
12595
|
+
demoEnvelope(carrier),
|
|
12596
|
+
{
|
|
12597
|
+
event: "stop",
|
|
12598
|
+
streamId: "proof-plivo-media"
|
|
12599
|
+
}
|
|
12600
|
+
];
|
|
12601
|
+
};
|
|
12456
12602
|
var byteLength = (audio) => {
|
|
12457
12603
|
if (!audio) {
|
|
12458
12604
|
return 0;
|
|
@@ -12478,6 +12624,10 @@ var buildVoiceTelephonyMediaReport = (input = {}) => {
|
|
|
12478
12624
|
carrier: entry.carrier,
|
|
12479
12625
|
frame
|
|
12480
12626
|
}) : undefined;
|
|
12627
|
+
const lifecycle = buildMediaTelephonyStreamLifecycleReport({
|
|
12628
|
+
carrier: entry.carrier,
|
|
12629
|
+
envelopes: entry.lifecycleEnvelopes ?? demoLifecycleEnvelopes(entry.carrier)
|
|
12630
|
+
});
|
|
12481
12631
|
const audioBytes = byteLength(frame?.audio);
|
|
12482
12632
|
const issues2 = [];
|
|
12483
12633
|
if (!frame) {
|
|
@@ -12495,11 +12645,15 @@ var buildVoiceTelephonyMediaReport = (input = {}) => {
|
|
|
12495
12645
|
if (!serialized || typeof serialized !== "object") {
|
|
12496
12646
|
issues2.push("MediaFrame did not serialize back into a carrier envelope.");
|
|
12497
12647
|
}
|
|
12648
|
+
for (const issue of lifecycle.issues) {
|
|
12649
|
+
issues2.push(issue.message);
|
|
12650
|
+
}
|
|
12498
12651
|
return {
|
|
12499
12652
|
audioBytes,
|
|
12500
12653
|
carrier: entry.carrier,
|
|
12501
12654
|
frame,
|
|
12502
12655
|
issues: issues2,
|
|
12656
|
+
lifecycle,
|
|
12503
12657
|
serialized,
|
|
12504
12658
|
status: issues2.length === 0 ? "pass" : "fail"
|
|
12505
12659
|
};
|
|
@@ -12514,8 +12668,8 @@ var buildVoiceTelephonyMediaReport = (input = {}) => {
|
|
|
12514
12668
|
};
|
|
12515
12669
|
var renderVoiceTelephonyMediaHTML = (report, options = {}) => {
|
|
12516
12670
|
const title = options.title ?? "Voice Telephony Media Proof";
|
|
12517
|
-
const rows = report.carriers.map((carrier) => `<tr><td>${escapeHtml16(carrier.carrier)}</td><td>${escapeHtml16(carrier.status)}</td><td>${String(carrier.audioBytes)}</td><td>${escapeHtml16(carrier.frame?.kind ?? "missing")}</td><td>${escapeHtml16(carrier.frame?.format?.encoding ?? "missing")}</td><td>${escapeHtml16(carrier.issues.join(" ") || "none")}</td></tr>`).join("");
|
|
12518
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml16(title)}</title><style>body{background:#111827;color:#e5e7eb;font-family:ui-sans-serif,system-ui,sans-serif;margin:0}main{margin:auto;max-width:980px;padding:32px}.hero,table{background:#0f172a;border:1px solid #334155;border-radius:20px;margin-bottom:16px}.hero{padding:22px}.eyebrow{color:#67e8f9;font-weight:900;letter-spacing:.12em;text-transform:uppercase}h1{font-size:clamp(2.2rem,6vw,4.5rem);line-height:.92;margin:.2rem 0 1rem}.status{border:1px solid #64748b;border-radius:999px;display:inline-flex;font-weight:900;padding:8px 12px}.pass{color:#86efac}.fail{color:#fecaca}code{color:#bfdbfe}table{border-collapse:collapse;overflow:hidden;width:100%}td,th{border-bottom:1px solid #334155;padding:10px;text-align:left}</style></head><body><main><section class="hero"><p class="eyebrow">Carrier media serializer proof</p><h1>${escapeHtml16(title)}</h1><p class="status ${escapeHtml16(report.status)}">Status: ${escapeHtml16(report.status)}</p><p>Twilio, Telnyx, and Plivo media payload envelopes are parsed into generic <code>MediaFrame</code> objects
|
|
12671
|
+
const rows = report.carriers.map((carrier) => `<tr><td>${escapeHtml16(carrier.carrier)}</td><td>${escapeHtml16(carrier.status)}</td><td>${String(carrier.audioBytes)}</td><td>${String(carrier.lifecycle.mediaEvents)}</td><td>${escapeHtml16(carrier.lifecycle.started ? "yes" : "no")}</td><td>${escapeHtml16(carrier.lifecycle.stopped ? "yes" : "no")}</td><td>${escapeHtml16(carrier.frame?.kind ?? "missing")}</td><td>${escapeHtml16(carrier.frame?.format?.encoding ?? "missing")}</td><td>${escapeHtml16(carrier.issues.join(" ") || "none")}</td></tr>`).join("");
|
|
12672
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml16(title)}</title><style>body{background:#111827;color:#e5e7eb;font-family:ui-sans-serif,system-ui,sans-serif;margin:0}main{margin:auto;max-width:980px;padding:32px}.hero,table{background:#0f172a;border:1px solid #334155;border-radius:20px;margin-bottom:16px}.hero{padding:22px}.eyebrow{color:#67e8f9;font-weight:900;letter-spacing:.12em;text-transform:uppercase}h1{font-size:clamp(2.2rem,6vw,4.5rem);line-height:.92;margin:.2rem 0 1rem}.status{border:1px solid #64748b;border-radius:999px;display:inline-flex;font-weight:900;padding:8px 12px}.pass{color:#86efac}.fail{color:#fecaca}code{color:#bfdbfe}table{border-collapse:collapse;overflow:hidden;width:100%}td,th{border-bottom:1px solid #334155;padding:10px;text-align:left}</style></head><body><main><section class="hero"><p class="eyebrow">Carrier media serializer proof</p><h1>${escapeHtml16(title)}</h1><p class="status ${escapeHtml16(report.status)}">Status: ${escapeHtml16(report.status)}</p><p>Twilio, Telnyx, and Plivo media payload envelopes are parsed into generic <code>MediaFrame</code> objects, serialized back into carrier envelopes, and checked for start/media/stop lifecycle sequencing by <code>@absolutejs/media</code>.</p></section><table><thead><tr><th>Carrier</th><th>Status</th><th>Audio bytes</th><th>Media events</th><th>Started</th><th>Stopped</th><th>Frame kind</th><th>Encoding</th><th>Issues</th></tr></thead><tbody>${rows}</tbody></table></main></body></html>`;
|
|
12519
12673
|
};
|
|
12520
12674
|
var createVoiceTelephonyMediaRoutes = (options = {}) => {
|
|
12521
12675
|
const path = options.path ?? "/api/voice/telephony/media";
|
|
@@ -30124,6 +30278,21 @@ var buildOperationsRecordLinks = (input) => {
|
|
|
30124
30278
|
sessionId,
|
|
30125
30279
|
status: "fail"
|
|
30126
30280
|
})) : [];
|
|
30281
|
+
const telephonyMedia = input.telephonyMedia && input.telephonyMedia.status !== "pass" ? input.telephonyMedia.carriers.filter((carrier) => carrier.status !== "pass").flatMap((carrier) => {
|
|
30282
|
+
const sessionIds = [
|
|
30283
|
+
...carrier.lifecycle.streamIds,
|
|
30284
|
+
carrier.frame?.sessionId
|
|
30285
|
+
].filter((value, index, values) => typeof value === "string" && value.length > 0 && values.indexOf(value) === index);
|
|
30286
|
+
const fallbackSessionId = `${carrier.carrier}-telephony-media`;
|
|
30287
|
+
const ids = sessionIds.length > 0 ? sessionIds : [fallbackSessionId];
|
|
30288
|
+
return ids.map((sessionId) => ({
|
|
30289
|
+
detail: carrier.issues[0] ?? `${carrier.lifecycle.issues.length} telephony media lifecycle issue(s)`,
|
|
30290
|
+
href: voiceOperationsRecordHref(input.base, sessionId),
|
|
30291
|
+
label: "Open telephony media operations record",
|
|
30292
|
+
sessionId,
|
|
30293
|
+
status: "fail"
|
|
30294
|
+
}));
|
|
30295
|
+
}) : [];
|
|
30127
30296
|
return {
|
|
30128
30297
|
failedSessions: input.failedSessionIds.map((sessionId) => ({
|
|
30129
30298
|
href: voiceOperationsRecordHref(input.base, sessionId),
|
|
@@ -30133,7 +30302,8 @@ var buildOperationsRecordLinks = (input) => {
|
|
|
30133
30302
|
})),
|
|
30134
30303
|
failingLatency,
|
|
30135
30304
|
mediaQuality,
|
|
30136
|
-
providerErrors
|
|
30305
|
+
providerErrors,
|
|
30306
|
+
telephonyMedia
|
|
30137
30307
|
};
|
|
30138
30308
|
};
|
|
30139
30309
|
var firstOperationsRecordHref = (links) => links[0]?.href;
|
|
@@ -30234,7 +30404,8 @@ var buildVoiceProductionReadinessReport = async (options, input = {}) => {
|
|
|
30234
30404
|
liveLatencyFailAfterMs: options.liveLatencyFailAfterMs ?? 3200,
|
|
30235
30405
|
liveLatencyMaxAgeMs: options.liveLatencyMaxAgeMs,
|
|
30236
30406
|
liveLatencyWarnAfterMs: options.liveLatencyWarnAfterMs ?? 1800,
|
|
30237
|
-
mediaPipeline
|
|
30407
|
+
mediaPipeline,
|
|
30408
|
+
telephonyMedia
|
|
30238
30409
|
});
|
|
30239
30410
|
const checks = [
|
|
30240
30411
|
{
|
|
@@ -30416,6 +30587,8 @@ var buildVoiceProductionReadinessReport = async (options, input = {}) => {
|
|
|
30416
30587
|
carriers: telephonyMedia.carriers.length,
|
|
30417
30588
|
failed: telephonyMedia.carriers.filter((carrier) => carrier.status !== "pass").length,
|
|
30418
30589
|
issues: telephonyMedia.issues.length,
|
|
30590
|
+
lifecycleFailures: telephonyMedia.carriers.filter((carrier) => carrier.lifecycle.status !== "pass").length,
|
|
30591
|
+
mediaEvents: telephonyMedia.carriers.reduce((total, carrier) => total + carrier.lifecycle.mediaEvents, 0),
|
|
30419
30592
|
passed: telephonyMedia.carriers.filter((carrier) => carrier.status === "pass").length,
|
|
30420
30593
|
status: telephonyMedia.status === "pass" ? "pass" : "fail"
|
|
30421
30594
|
} : undefined;
|
|
@@ -30514,22 +30687,29 @@ var buildVoiceProductionReadinessReport = async (options, input = {}) => {
|
|
|
30514
30687
|
if (telephonyMedia && telephonyMediaSummary) {
|
|
30515
30688
|
const firstIssue = telephonyMedia.issues[0];
|
|
30516
30689
|
checks.push({
|
|
30517
|
-
detail: telephonyMediaSummary.status === "pass" ? `Telephony media serializers are passing for ${telephonyMediaSummary.passed}/${telephonyMediaSummary.carriers} carrier(s) with ${telephonyMediaSummary.audioBytes} audio byte(s)
|
|
30518
|
-
href: options.links?.telephonyMedia ?? "/voice/telephony-media",
|
|
30690
|
+
detail: telephonyMediaSummary.status === "pass" ? `Telephony media serializers are passing for ${telephonyMediaSummary.passed}/${telephonyMediaSummary.carriers} carrier(s) with ${telephonyMediaSummary.audioBytes} audio byte(s), ${telephonyMediaSummary.mediaEvents} media event(s), and valid start/media/stop lifecycle sequencing.` : firstIssue ?? `${telephonyMediaSummary.issues} telephony media serializer issue(s) need review.`,
|
|
30691
|
+
href: firstOperationsRecordHref(operationsRecords.telephonyMedia) ?? options.links?.telephonyMedia ?? "/voice/telephony-media",
|
|
30519
30692
|
label: "Telephony media serializers",
|
|
30520
30693
|
proofSource: proofSource("telephonyMedia", "carrierMediaSerializers"),
|
|
30521
30694
|
gateExplanation: telephonyMediaSummary.status === "pass" ? undefined : {
|
|
30522
|
-
evidenceHref: options.links?.telephonyMedia ?? "/voice/telephony-media",
|
|
30695
|
+
evidenceHref: firstOperationsRecordHref(operationsRecords.telephonyMedia) ?? options.links?.telephonyMedia ?? "/voice/telephony-media",
|
|
30523
30696
|
observed: firstIssue ?? `${telephonyMediaSummary.issues} issue(s)`,
|
|
30524
|
-
remediation: "Inspect carrier media
|
|
30525
|
-
thresholdLabel: "Telephony media serializer status",
|
|
30697
|
+
remediation: "Inspect carrier media proof, fix start/media/stop sequencing, payload parsing, byte flow, or outbound envelope serialization, then rerun readiness proof.",
|
|
30698
|
+
thresholdLabel: "Telephony media serializer and lifecycle status",
|
|
30526
30699
|
unit: "status"
|
|
30527
30700
|
},
|
|
30528
30701
|
status: telephonyMediaSummary.status,
|
|
30529
30702
|
value: telephonyMediaSummary.status === "pass" ? `${telephonyMediaSummary.passed}/${telephonyMediaSummary.carriers}` : `${telephonyMediaSummary.failed}/${telephonyMediaSummary.carriers} failing`,
|
|
30530
30703
|
actions: telephonyMediaSummary.status === "pass" ? [] : [
|
|
30704
|
+
...firstOperationsRecordHref(operationsRecords.telephonyMedia) ? [
|
|
30705
|
+
{
|
|
30706
|
+
description: "Open the exact call/session operations record for the first telephony media lifecycle issue.",
|
|
30707
|
+
href: firstOperationsRecordHref(operationsRecords.telephonyMedia),
|
|
30708
|
+
label: "Open telephony media operations record"
|
|
30709
|
+
}
|
|
30710
|
+
] : [],
|
|
30531
30711
|
{
|
|
30532
|
-
description: "Open telephony media proof and inspect carrier media payload parsing, MediaFrame shape, and outbound envelope serialization.",
|
|
30712
|
+
description: "Open telephony media proof and inspect carrier media lifecycle sequencing, payload parsing, MediaFrame shape, byte flow, and outbound envelope serialization.",
|
|
30533
30713
|
href: options.links?.telephonyMedia ?? "/voice/telephony-media",
|
|
30534
30714
|
label: "Open telephony media proof"
|
|
30535
30715
|
}
|
|
@@ -301,6 +301,8 @@ export type VoiceProductionReadinessReport = {
|
|
|
301
301
|
carriers: number;
|
|
302
302
|
failed: number;
|
|
303
303
|
issues: number;
|
|
304
|
+
lifecycleFailures: number;
|
|
305
|
+
mediaEvents: number;
|
|
304
306
|
passed: number;
|
|
305
307
|
status: VoiceProductionReadinessStatus;
|
|
306
308
|
};
|
|
@@ -349,6 +351,7 @@ export type VoiceProductionReadinessOperationsRecordLinks = {
|
|
|
349
351
|
failingLatency: VoiceProductionReadinessOperationsRecordLink[];
|
|
350
352
|
mediaQuality: VoiceProductionReadinessOperationsRecordLink[];
|
|
351
353
|
providerErrors: VoiceProductionReadinessOperationsRecordLink[];
|
|
354
|
+
telephonyMedia: VoiceProductionReadinessOperationsRecordLink[];
|
|
352
355
|
};
|
|
353
356
|
export type VoiceProductionReadinessAuditRequirement = {
|
|
354
357
|
label?: string;
|
package/dist/react/index.js
CHANGED
|
@@ -5107,6 +5107,29 @@ var telephonyDirection = (track) => {
|
|
|
5107
5107
|
return "unknown";
|
|
5108
5108
|
};
|
|
5109
5109
|
var telephonyFrameKind = (direction) => direction === "outbound" ? "assistant-audio" : "input-audio";
|
|
5110
|
+
var telephonyEventKind = (envelope) => {
|
|
5111
|
+
const raw = firstString([envelope], ["event", "type", "eventType"]) ?? firstString([unknownRecord(envelope.message)], ["event", "type"]);
|
|
5112
|
+
const normalized = raw?.toLowerCase().replace(/[_\s-]+/g, "-");
|
|
5113
|
+
if (!normalized) {
|
|
5114
|
+
return "unknown";
|
|
5115
|
+
}
|
|
5116
|
+
if (normalized.includes("connected")) {
|
|
5117
|
+
return "connected";
|
|
5118
|
+
}
|
|
5119
|
+
if (normalized.includes("start")) {
|
|
5120
|
+
return "start";
|
|
5121
|
+
}
|
|
5122
|
+
if (normalized.includes("media")) {
|
|
5123
|
+
return "media";
|
|
5124
|
+
}
|
|
5125
|
+
if (normalized.includes("stop") || normalized.includes("closed")) {
|
|
5126
|
+
return "stop";
|
|
5127
|
+
}
|
|
5128
|
+
if (normalized.includes("error") || normalized.includes("failed")) {
|
|
5129
|
+
return "error";
|
|
5130
|
+
}
|
|
5131
|
+
return "unknown";
|
|
5132
|
+
};
|
|
5110
5133
|
var normalizeWebRTCStat = (stat) => {
|
|
5111
5134
|
const sample = {};
|
|
5112
5135
|
for (const [key, value] of Object.entries(stat)) {
|
|
@@ -5223,6 +5246,86 @@ var createTelephonyMediaSerializer = (input) => {
|
|
|
5223
5246
|
})
|
|
5224
5247
|
};
|
|
5225
5248
|
};
|
|
5249
|
+
var parseTelephonyStreamEvent = (input) => {
|
|
5250
|
+
const envelope = input.envelope;
|
|
5251
|
+
const media = unknownRecord(envelope.media);
|
|
5252
|
+
const start = unknownRecord(envelope.start);
|
|
5253
|
+
const stop = unknownRecord(envelope.stop);
|
|
5254
|
+
const errorRecord = unknownRecord(envelope.error);
|
|
5255
|
+
const kind = telephonyEventKind(envelope);
|
|
5256
|
+
const carrier = input.carrier ?? firstString([envelope], ["provider", "carrier"]) ?? "telephony";
|
|
5257
|
+
const frame = kind === "media" ? parseTelephonyMediaFrame({
|
|
5258
|
+
carrier,
|
|
5259
|
+
envelope,
|
|
5260
|
+
format: input.format,
|
|
5261
|
+
sessionId: input.sessionId
|
|
5262
|
+
}) : undefined;
|
|
5263
|
+
const streamId = firstString([media, start, stop, envelope], ["streamSid", "stream_id", "streamId", "callSid", "call_id"]) ?? input.sessionId;
|
|
5264
|
+
const sequenceNumber = firstString([media, envelope], ["sequenceNumber", "sequence_number", "chunk"]);
|
|
5265
|
+
const track = firstString([media, envelope], ["track", "direction"]);
|
|
5266
|
+
return {
|
|
5267
|
+
audioBytes: frame?.audio ? frame.audio instanceof ArrayBuffer ? frame.audio.byteLength : frame.audio.byteLength : 0,
|
|
5268
|
+
at: frame?.at ?? firstNumber([media, start, stop, envelope], ["timestamp", "time", "startedAt"]),
|
|
5269
|
+
carrier,
|
|
5270
|
+
direction: telephonyDirection(track),
|
|
5271
|
+
error: firstString([errorRecord, envelope], ["message", "error", "reason"]),
|
|
5272
|
+
kind,
|
|
5273
|
+
sequenceNumber,
|
|
5274
|
+
streamId
|
|
5275
|
+
};
|
|
5276
|
+
};
|
|
5277
|
+
var buildMediaTelephonyStreamLifecycleReport = (input = {}) => {
|
|
5278
|
+
const envelopes = input.envelopes ?? [];
|
|
5279
|
+
const events = envelopes.map((envelope) => parseTelephonyStreamEvent({
|
|
5280
|
+
carrier: input.carrier,
|
|
5281
|
+
envelope
|
|
5282
|
+
}));
|
|
5283
|
+
const issues = [];
|
|
5284
|
+
const startedIndex = events.findIndex((event) => event.kind === "start");
|
|
5285
|
+
const firstMediaIndex = events.findIndex((event) => event.kind === "media");
|
|
5286
|
+
const stoppedIndex = events.findIndex((event) => event.kind === "stop");
|
|
5287
|
+
const started = startedIndex >= 0;
|
|
5288
|
+
const stopped = stoppedIndex >= 0;
|
|
5289
|
+
const mediaEvents = events.filter((event) => event.kind === "media");
|
|
5290
|
+
const audioBytes = events.reduce((total, event) => total + event.audioBytes, 0);
|
|
5291
|
+
const minAudioBytes = input.minAudioBytes ?? 1;
|
|
5292
|
+
const streamIds = Array.from(new Set(events.map((event) => event.streamId).filter(Boolean)));
|
|
5293
|
+
if ((input.requireStart ?? true) && !started) {
|
|
5294
|
+
pushIssue(issues, "error", "media.telephony_missing_start", "Telephony media stream did not include a start event.");
|
|
5295
|
+
}
|
|
5296
|
+
if ((input.requireMedia ?? true) && mediaEvents.length === 0) {
|
|
5297
|
+
pushIssue(issues, "error", "media.telephony_missing_media", "Telephony media stream did not include media payload events.");
|
|
5298
|
+
}
|
|
5299
|
+
if ((input.requireStop ?? true) && !stopped) {
|
|
5300
|
+
pushIssue(issues, input.maxMissingStop === false ? "warning" : "error", "media.telephony_missing_stop", "Telephony media stream did not include a stop event.");
|
|
5301
|
+
}
|
|
5302
|
+
if (started && firstMediaIndex >= 0 && firstMediaIndex < startedIndex) {
|
|
5303
|
+
pushIssue(issues, "error", "media.telephony_media_before_start", "Telephony media payload arrived before the stream start event.");
|
|
5304
|
+
}
|
|
5305
|
+
if (stopped && firstMediaIndex >= 0 && stoppedIndex < firstMediaIndex) {
|
|
5306
|
+
pushIssue(issues, "error", "media.telephony_stop_before_media", "Telephony media stream stopped before any media payload arrived.");
|
|
5307
|
+
}
|
|
5308
|
+
if (mediaEvents.length > 0 && audioBytes < minAudioBytes) {
|
|
5309
|
+
pushIssue(issues, "error", "media.telephony_no_audio_bytes", `Telephony media stream parsed ${String(audioBytes)} audio byte(s), below required ${String(minAudioBytes)}.`);
|
|
5310
|
+
}
|
|
5311
|
+
for (const event of events) {
|
|
5312
|
+
if (event.kind === "error") {
|
|
5313
|
+
pushIssue(issues, "error", "media.telephony_stream_error", event.error ?? "Telephony media stream emitted an error event.");
|
|
5314
|
+
}
|
|
5315
|
+
}
|
|
5316
|
+
return {
|
|
5317
|
+
audioBytes,
|
|
5318
|
+
carrier: input.carrier,
|
|
5319
|
+
checkedAt: Date.now(),
|
|
5320
|
+
events,
|
|
5321
|
+
issues,
|
|
5322
|
+
mediaEvents: mediaEvents.length,
|
|
5323
|
+
started,
|
|
5324
|
+
status: issues.some((issue) => issue.severity === "error") ? "fail" : issues.length > 0 ? "warn" : "pass",
|
|
5325
|
+
stopped,
|
|
5326
|
+
streamIds
|
|
5327
|
+
};
|
|
5328
|
+
};
|
|
5226
5329
|
var buildMediaResamplingPlan = (input) => {
|
|
5227
5330
|
const required = !formatMatches(input.inputFormat, input.outputFormat);
|
|
5228
5331
|
return {
|
package/dist/svelte/index.js
CHANGED
|
@@ -3441,6 +3441,29 @@ var telephonyDirection = (track) => {
|
|
|
3441
3441
|
return "unknown";
|
|
3442
3442
|
};
|
|
3443
3443
|
var telephonyFrameKind = (direction) => direction === "outbound" ? "assistant-audio" : "input-audio";
|
|
3444
|
+
var telephonyEventKind = (envelope) => {
|
|
3445
|
+
const raw = firstString([envelope], ["event", "type", "eventType"]) ?? firstString([unknownRecord(envelope.message)], ["event", "type"]);
|
|
3446
|
+
const normalized = raw?.toLowerCase().replace(/[_\s-]+/g, "-");
|
|
3447
|
+
if (!normalized) {
|
|
3448
|
+
return "unknown";
|
|
3449
|
+
}
|
|
3450
|
+
if (normalized.includes("connected")) {
|
|
3451
|
+
return "connected";
|
|
3452
|
+
}
|
|
3453
|
+
if (normalized.includes("start")) {
|
|
3454
|
+
return "start";
|
|
3455
|
+
}
|
|
3456
|
+
if (normalized.includes("media")) {
|
|
3457
|
+
return "media";
|
|
3458
|
+
}
|
|
3459
|
+
if (normalized.includes("stop") || normalized.includes("closed")) {
|
|
3460
|
+
return "stop";
|
|
3461
|
+
}
|
|
3462
|
+
if (normalized.includes("error") || normalized.includes("failed")) {
|
|
3463
|
+
return "error";
|
|
3464
|
+
}
|
|
3465
|
+
return "unknown";
|
|
3466
|
+
};
|
|
3444
3467
|
var normalizeWebRTCStat = (stat) => {
|
|
3445
3468
|
const sample = {};
|
|
3446
3469
|
for (const [key, value] of Object.entries(stat)) {
|
|
@@ -3557,6 +3580,86 @@ var createTelephonyMediaSerializer = (input) => {
|
|
|
3557
3580
|
})
|
|
3558
3581
|
};
|
|
3559
3582
|
};
|
|
3583
|
+
var parseTelephonyStreamEvent = (input) => {
|
|
3584
|
+
const envelope = input.envelope;
|
|
3585
|
+
const media = unknownRecord(envelope.media);
|
|
3586
|
+
const start = unknownRecord(envelope.start);
|
|
3587
|
+
const stop = unknownRecord(envelope.stop);
|
|
3588
|
+
const errorRecord = unknownRecord(envelope.error);
|
|
3589
|
+
const kind = telephonyEventKind(envelope);
|
|
3590
|
+
const carrier = input.carrier ?? firstString([envelope], ["provider", "carrier"]) ?? "telephony";
|
|
3591
|
+
const frame = kind === "media" ? parseTelephonyMediaFrame({
|
|
3592
|
+
carrier,
|
|
3593
|
+
envelope,
|
|
3594
|
+
format: input.format,
|
|
3595
|
+
sessionId: input.sessionId
|
|
3596
|
+
}) : undefined;
|
|
3597
|
+
const streamId = firstString([media, start, stop, envelope], ["streamSid", "stream_id", "streamId", "callSid", "call_id"]) ?? input.sessionId;
|
|
3598
|
+
const sequenceNumber = firstString([media, envelope], ["sequenceNumber", "sequence_number", "chunk"]);
|
|
3599
|
+
const track = firstString([media, envelope], ["track", "direction"]);
|
|
3600
|
+
return {
|
|
3601
|
+
audioBytes: frame?.audio ? frame.audio instanceof ArrayBuffer ? frame.audio.byteLength : frame.audio.byteLength : 0,
|
|
3602
|
+
at: frame?.at ?? firstNumber([media, start, stop, envelope], ["timestamp", "time", "startedAt"]),
|
|
3603
|
+
carrier,
|
|
3604
|
+
direction: telephonyDirection(track),
|
|
3605
|
+
error: firstString([errorRecord, envelope], ["message", "error", "reason"]),
|
|
3606
|
+
kind,
|
|
3607
|
+
sequenceNumber,
|
|
3608
|
+
streamId
|
|
3609
|
+
};
|
|
3610
|
+
};
|
|
3611
|
+
var buildMediaTelephonyStreamLifecycleReport = (input = {}) => {
|
|
3612
|
+
const envelopes = input.envelopes ?? [];
|
|
3613
|
+
const events = envelopes.map((envelope) => parseTelephonyStreamEvent({
|
|
3614
|
+
carrier: input.carrier,
|
|
3615
|
+
envelope
|
|
3616
|
+
}));
|
|
3617
|
+
const issues = [];
|
|
3618
|
+
const startedIndex = events.findIndex((event) => event.kind === "start");
|
|
3619
|
+
const firstMediaIndex = events.findIndex((event) => event.kind === "media");
|
|
3620
|
+
const stoppedIndex = events.findIndex((event) => event.kind === "stop");
|
|
3621
|
+
const started = startedIndex >= 0;
|
|
3622
|
+
const stopped = stoppedIndex >= 0;
|
|
3623
|
+
const mediaEvents = events.filter((event) => event.kind === "media");
|
|
3624
|
+
const audioBytes = events.reduce((total, event) => total + event.audioBytes, 0);
|
|
3625
|
+
const minAudioBytes = input.minAudioBytes ?? 1;
|
|
3626
|
+
const streamIds = Array.from(new Set(events.map((event) => event.streamId).filter(Boolean)));
|
|
3627
|
+
if ((input.requireStart ?? true) && !started) {
|
|
3628
|
+
pushIssue(issues, "error", "media.telephony_missing_start", "Telephony media stream did not include a start event.");
|
|
3629
|
+
}
|
|
3630
|
+
if ((input.requireMedia ?? true) && mediaEvents.length === 0) {
|
|
3631
|
+
pushIssue(issues, "error", "media.telephony_missing_media", "Telephony media stream did not include media payload events.");
|
|
3632
|
+
}
|
|
3633
|
+
if ((input.requireStop ?? true) && !stopped) {
|
|
3634
|
+
pushIssue(issues, input.maxMissingStop === false ? "warning" : "error", "media.telephony_missing_stop", "Telephony media stream did not include a stop event.");
|
|
3635
|
+
}
|
|
3636
|
+
if (started && firstMediaIndex >= 0 && firstMediaIndex < startedIndex) {
|
|
3637
|
+
pushIssue(issues, "error", "media.telephony_media_before_start", "Telephony media payload arrived before the stream start event.");
|
|
3638
|
+
}
|
|
3639
|
+
if (stopped && firstMediaIndex >= 0 && stoppedIndex < firstMediaIndex) {
|
|
3640
|
+
pushIssue(issues, "error", "media.telephony_stop_before_media", "Telephony media stream stopped before any media payload arrived.");
|
|
3641
|
+
}
|
|
3642
|
+
if (mediaEvents.length > 0 && audioBytes < minAudioBytes) {
|
|
3643
|
+
pushIssue(issues, "error", "media.telephony_no_audio_bytes", `Telephony media stream parsed ${String(audioBytes)} audio byte(s), below required ${String(minAudioBytes)}.`);
|
|
3644
|
+
}
|
|
3645
|
+
for (const event of events) {
|
|
3646
|
+
if (event.kind === "error") {
|
|
3647
|
+
pushIssue(issues, "error", "media.telephony_stream_error", event.error ?? "Telephony media stream emitted an error event.");
|
|
3648
|
+
}
|
|
3649
|
+
}
|
|
3650
|
+
return {
|
|
3651
|
+
audioBytes,
|
|
3652
|
+
carrier: input.carrier,
|
|
3653
|
+
checkedAt: Date.now(),
|
|
3654
|
+
events,
|
|
3655
|
+
issues,
|
|
3656
|
+
mediaEvents: mediaEvents.length,
|
|
3657
|
+
started,
|
|
3658
|
+
status: issues.some((issue) => issue.severity === "error") ? "fail" : issues.length > 0 ? "warn" : "pass",
|
|
3659
|
+
stopped,
|
|
3660
|
+
streamIds
|
|
3661
|
+
};
|
|
3662
|
+
};
|
|
3560
3663
|
var buildMediaResamplingPlan = (input) => {
|
|
3561
3664
|
const required = !formatMatches(input.inputFormat, input.outputFormat);
|
|
3562
3665
|
return {
|
|
@@ -1,15 +1,17 @@
|
|
|
1
1
|
import { Elysia } from 'elysia';
|
|
2
|
-
import type { MediaFrame, MediaTelephonyCarrier, MediaTelephonyEnvelope } from '@absolutejs/media';
|
|
2
|
+
import type { MediaFrame, MediaTelephonyCarrier, MediaTelephonyEnvelope, MediaTelephonyStreamLifecycleReport } from '@absolutejs/media';
|
|
3
3
|
export type VoiceTelephonyMediaStatus = 'fail' | 'pass';
|
|
4
4
|
export type VoiceTelephonyMediaCarrierInput = {
|
|
5
5
|
carrier: MediaTelephonyCarrier;
|
|
6
6
|
envelope?: MediaTelephonyEnvelope;
|
|
7
|
+
lifecycleEnvelopes?: readonly MediaTelephonyEnvelope[];
|
|
7
8
|
};
|
|
8
9
|
export type VoiceTelephonyMediaCarrierReport = {
|
|
9
10
|
audioBytes: number;
|
|
10
11
|
carrier: MediaTelephonyCarrier;
|
|
11
12
|
frame?: MediaFrame;
|
|
12
13
|
issues: string[];
|
|
14
|
+
lifecycle: MediaTelephonyStreamLifecycleReport;
|
|
13
15
|
serialized?: MediaTelephonyEnvelope;
|
|
14
16
|
status: VoiceTelephonyMediaStatus;
|
|
15
17
|
};
|
package/dist/testing/index.js
CHANGED
|
@@ -2243,6 +2243,29 @@ var telephonyDirection = (track) => {
|
|
|
2243
2243
|
return "unknown";
|
|
2244
2244
|
};
|
|
2245
2245
|
var telephonyFrameKind = (direction) => direction === "outbound" ? "assistant-audio" : "input-audio";
|
|
2246
|
+
var telephonyEventKind = (envelope) => {
|
|
2247
|
+
const raw = firstString([envelope], ["event", "type", "eventType"]) ?? firstString([unknownRecord(envelope.message)], ["event", "type"]);
|
|
2248
|
+
const normalized = raw?.toLowerCase().replace(/[_\s-]+/g, "-");
|
|
2249
|
+
if (!normalized) {
|
|
2250
|
+
return "unknown";
|
|
2251
|
+
}
|
|
2252
|
+
if (normalized.includes("connected")) {
|
|
2253
|
+
return "connected";
|
|
2254
|
+
}
|
|
2255
|
+
if (normalized.includes("start")) {
|
|
2256
|
+
return "start";
|
|
2257
|
+
}
|
|
2258
|
+
if (normalized.includes("media")) {
|
|
2259
|
+
return "media";
|
|
2260
|
+
}
|
|
2261
|
+
if (normalized.includes("stop") || normalized.includes("closed")) {
|
|
2262
|
+
return "stop";
|
|
2263
|
+
}
|
|
2264
|
+
if (normalized.includes("error") || normalized.includes("failed")) {
|
|
2265
|
+
return "error";
|
|
2266
|
+
}
|
|
2267
|
+
return "unknown";
|
|
2268
|
+
};
|
|
2246
2269
|
var normalizeWebRTCStat = (stat) => {
|
|
2247
2270
|
const sample = {};
|
|
2248
2271
|
for (const [key, value] of Object.entries(stat)) {
|
|
@@ -2359,6 +2382,86 @@ var createTelephonyMediaSerializer = (input) => {
|
|
|
2359
2382
|
})
|
|
2360
2383
|
};
|
|
2361
2384
|
};
|
|
2385
|
+
var parseTelephonyStreamEvent = (input) => {
|
|
2386
|
+
const envelope = input.envelope;
|
|
2387
|
+
const media = unknownRecord(envelope.media);
|
|
2388
|
+
const start = unknownRecord(envelope.start);
|
|
2389
|
+
const stop = unknownRecord(envelope.stop);
|
|
2390
|
+
const errorRecord = unknownRecord(envelope.error);
|
|
2391
|
+
const kind = telephonyEventKind(envelope);
|
|
2392
|
+
const carrier = input.carrier ?? firstString([envelope], ["provider", "carrier"]) ?? "telephony";
|
|
2393
|
+
const frame = kind === "media" ? parseTelephonyMediaFrame({
|
|
2394
|
+
carrier,
|
|
2395
|
+
envelope,
|
|
2396
|
+
format: input.format,
|
|
2397
|
+
sessionId: input.sessionId
|
|
2398
|
+
}) : undefined;
|
|
2399
|
+
const streamId = firstString([media, start, stop, envelope], ["streamSid", "stream_id", "streamId", "callSid", "call_id"]) ?? input.sessionId;
|
|
2400
|
+
const sequenceNumber = firstString([media, envelope], ["sequenceNumber", "sequence_number", "chunk"]);
|
|
2401
|
+
const track = firstString([media, envelope], ["track", "direction"]);
|
|
2402
|
+
return {
|
|
2403
|
+
audioBytes: frame?.audio ? frame.audio instanceof ArrayBuffer ? frame.audio.byteLength : frame.audio.byteLength : 0,
|
|
2404
|
+
at: frame?.at ?? firstNumber([media, start, stop, envelope], ["timestamp", "time", "startedAt"]),
|
|
2405
|
+
carrier,
|
|
2406
|
+
direction: telephonyDirection(track),
|
|
2407
|
+
error: firstString([errorRecord, envelope], ["message", "error", "reason"]),
|
|
2408
|
+
kind,
|
|
2409
|
+
sequenceNumber,
|
|
2410
|
+
streamId
|
|
2411
|
+
};
|
|
2412
|
+
};
|
|
2413
|
+
var buildMediaTelephonyStreamLifecycleReport = (input = {}) => {
|
|
2414
|
+
const envelopes = input.envelopes ?? [];
|
|
2415
|
+
const events = envelopes.map((envelope) => parseTelephonyStreamEvent({
|
|
2416
|
+
carrier: input.carrier,
|
|
2417
|
+
envelope
|
|
2418
|
+
}));
|
|
2419
|
+
const issues = [];
|
|
2420
|
+
const startedIndex = events.findIndex((event) => event.kind === "start");
|
|
2421
|
+
const firstMediaIndex = events.findIndex((event) => event.kind === "media");
|
|
2422
|
+
const stoppedIndex = events.findIndex((event) => event.kind === "stop");
|
|
2423
|
+
const started = startedIndex >= 0;
|
|
2424
|
+
const stopped = stoppedIndex >= 0;
|
|
2425
|
+
const mediaEvents = events.filter((event) => event.kind === "media");
|
|
2426
|
+
const audioBytes = events.reduce((total, event) => total + event.audioBytes, 0);
|
|
2427
|
+
const minAudioBytes = input.minAudioBytes ?? 1;
|
|
2428
|
+
const streamIds = Array.from(new Set(events.map((event) => event.streamId).filter(Boolean)));
|
|
2429
|
+
if ((input.requireStart ?? true) && !started) {
|
|
2430
|
+
pushIssue(issues, "error", "media.telephony_missing_start", "Telephony media stream did not include a start event.");
|
|
2431
|
+
}
|
|
2432
|
+
if ((input.requireMedia ?? true) && mediaEvents.length === 0) {
|
|
2433
|
+
pushIssue(issues, "error", "media.telephony_missing_media", "Telephony media stream did not include media payload events.");
|
|
2434
|
+
}
|
|
2435
|
+
if ((input.requireStop ?? true) && !stopped) {
|
|
2436
|
+
pushIssue(issues, input.maxMissingStop === false ? "warning" : "error", "media.telephony_missing_stop", "Telephony media stream did not include a stop event.");
|
|
2437
|
+
}
|
|
2438
|
+
if (started && firstMediaIndex >= 0 && firstMediaIndex < startedIndex) {
|
|
2439
|
+
pushIssue(issues, "error", "media.telephony_media_before_start", "Telephony media payload arrived before the stream start event.");
|
|
2440
|
+
}
|
|
2441
|
+
if (stopped && firstMediaIndex >= 0 && stoppedIndex < firstMediaIndex) {
|
|
2442
|
+
pushIssue(issues, "error", "media.telephony_stop_before_media", "Telephony media stream stopped before any media payload arrived.");
|
|
2443
|
+
}
|
|
2444
|
+
if (mediaEvents.length > 0 && audioBytes < minAudioBytes) {
|
|
2445
|
+
pushIssue(issues, "error", "media.telephony_no_audio_bytes", `Telephony media stream parsed ${String(audioBytes)} audio byte(s), below required ${String(minAudioBytes)}.`);
|
|
2446
|
+
}
|
|
2447
|
+
for (const event of events) {
|
|
2448
|
+
if (event.kind === "error") {
|
|
2449
|
+
pushIssue(issues, "error", "media.telephony_stream_error", event.error ?? "Telephony media stream emitted an error event.");
|
|
2450
|
+
}
|
|
2451
|
+
}
|
|
2452
|
+
return {
|
|
2453
|
+
audioBytes,
|
|
2454
|
+
carrier: input.carrier,
|
|
2455
|
+
checkedAt: Date.now(),
|
|
2456
|
+
events,
|
|
2457
|
+
issues,
|
|
2458
|
+
mediaEvents: mediaEvents.length,
|
|
2459
|
+
started,
|
|
2460
|
+
status: issues.some((issue) => issue.severity === "error") ? "fail" : issues.length > 0 ? "warn" : "pass",
|
|
2461
|
+
stopped,
|
|
2462
|
+
streamIds
|
|
2463
|
+
};
|
|
2464
|
+
};
|
|
2362
2465
|
var buildMediaResamplingPlan = (input) => {
|
|
2363
2466
|
const required = !formatMatches(input.inputFormat, input.outputFormat);
|
|
2364
2467
|
return {
|
package/dist/vue/index.js
CHANGED
|
@@ -4825,6 +4825,29 @@ var telephonyDirection = (track) => {
|
|
|
4825
4825
|
return "unknown";
|
|
4826
4826
|
};
|
|
4827
4827
|
var telephonyFrameKind = (direction) => direction === "outbound" ? "assistant-audio" : "input-audio";
|
|
4828
|
+
var telephonyEventKind = (envelope) => {
|
|
4829
|
+
const raw = firstString([envelope], ["event", "type", "eventType"]) ?? firstString([unknownRecord(envelope.message)], ["event", "type"]);
|
|
4830
|
+
const normalized = raw?.toLowerCase().replace(/[_\s-]+/g, "-");
|
|
4831
|
+
if (!normalized) {
|
|
4832
|
+
return "unknown";
|
|
4833
|
+
}
|
|
4834
|
+
if (normalized.includes("connected")) {
|
|
4835
|
+
return "connected";
|
|
4836
|
+
}
|
|
4837
|
+
if (normalized.includes("start")) {
|
|
4838
|
+
return "start";
|
|
4839
|
+
}
|
|
4840
|
+
if (normalized.includes("media")) {
|
|
4841
|
+
return "media";
|
|
4842
|
+
}
|
|
4843
|
+
if (normalized.includes("stop") || normalized.includes("closed")) {
|
|
4844
|
+
return "stop";
|
|
4845
|
+
}
|
|
4846
|
+
if (normalized.includes("error") || normalized.includes("failed")) {
|
|
4847
|
+
return "error";
|
|
4848
|
+
}
|
|
4849
|
+
return "unknown";
|
|
4850
|
+
};
|
|
4828
4851
|
var normalizeWebRTCStat = (stat) => {
|
|
4829
4852
|
const sample = {};
|
|
4830
4853
|
for (const [key, value] of Object.entries(stat)) {
|
|
@@ -4941,6 +4964,86 @@ var createTelephonyMediaSerializer = (input) => {
|
|
|
4941
4964
|
})
|
|
4942
4965
|
};
|
|
4943
4966
|
};
|
|
4967
|
+
var parseTelephonyStreamEvent = (input) => {
|
|
4968
|
+
const envelope = input.envelope;
|
|
4969
|
+
const media = unknownRecord(envelope.media);
|
|
4970
|
+
const start = unknownRecord(envelope.start);
|
|
4971
|
+
const stop = unknownRecord(envelope.stop);
|
|
4972
|
+
const errorRecord = unknownRecord(envelope.error);
|
|
4973
|
+
const kind = telephonyEventKind(envelope);
|
|
4974
|
+
const carrier = input.carrier ?? firstString([envelope], ["provider", "carrier"]) ?? "telephony";
|
|
4975
|
+
const frame = kind === "media" ? parseTelephonyMediaFrame({
|
|
4976
|
+
carrier,
|
|
4977
|
+
envelope,
|
|
4978
|
+
format: input.format,
|
|
4979
|
+
sessionId: input.sessionId
|
|
4980
|
+
}) : undefined;
|
|
4981
|
+
const streamId = firstString([media, start, stop, envelope], ["streamSid", "stream_id", "streamId", "callSid", "call_id"]) ?? input.sessionId;
|
|
4982
|
+
const sequenceNumber = firstString([media, envelope], ["sequenceNumber", "sequence_number", "chunk"]);
|
|
4983
|
+
const track = firstString([media, envelope], ["track", "direction"]);
|
|
4984
|
+
return {
|
|
4985
|
+
audioBytes: frame?.audio ? frame.audio instanceof ArrayBuffer ? frame.audio.byteLength : frame.audio.byteLength : 0,
|
|
4986
|
+
at: frame?.at ?? firstNumber([media, start, stop, envelope], ["timestamp", "time", "startedAt"]),
|
|
4987
|
+
carrier,
|
|
4988
|
+
direction: telephonyDirection(track),
|
|
4989
|
+
error: firstString([errorRecord, envelope], ["message", "error", "reason"]),
|
|
4990
|
+
kind,
|
|
4991
|
+
sequenceNumber,
|
|
4992
|
+
streamId
|
|
4993
|
+
};
|
|
4994
|
+
};
|
|
4995
|
+
var buildMediaTelephonyStreamLifecycleReport = (input = {}) => {
|
|
4996
|
+
const envelopes = input.envelopes ?? [];
|
|
4997
|
+
const events = envelopes.map((envelope) => parseTelephonyStreamEvent({
|
|
4998
|
+
carrier: input.carrier,
|
|
4999
|
+
envelope
|
|
5000
|
+
}));
|
|
5001
|
+
const issues = [];
|
|
5002
|
+
const startedIndex = events.findIndex((event) => event.kind === "start");
|
|
5003
|
+
const firstMediaIndex = events.findIndex((event) => event.kind === "media");
|
|
5004
|
+
const stoppedIndex = events.findIndex((event) => event.kind === "stop");
|
|
5005
|
+
const started = startedIndex >= 0;
|
|
5006
|
+
const stopped = stoppedIndex >= 0;
|
|
5007
|
+
const mediaEvents = events.filter((event) => event.kind === "media");
|
|
5008
|
+
const audioBytes = events.reduce((total, event) => total + event.audioBytes, 0);
|
|
5009
|
+
const minAudioBytes = input.minAudioBytes ?? 1;
|
|
5010
|
+
const streamIds = Array.from(new Set(events.map((event) => event.streamId).filter(Boolean)));
|
|
5011
|
+
if ((input.requireStart ?? true) && !started) {
|
|
5012
|
+
pushIssue(issues, "error", "media.telephony_missing_start", "Telephony media stream did not include a start event.");
|
|
5013
|
+
}
|
|
5014
|
+
if ((input.requireMedia ?? true) && mediaEvents.length === 0) {
|
|
5015
|
+
pushIssue(issues, "error", "media.telephony_missing_media", "Telephony media stream did not include media payload events.");
|
|
5016
|
+
}
|
|
5017
|
+
if ((input.requireStop ?? true) && !stopped) {
|
|
5018
|
+
pushIssue(issues, input.maxMissingStop === false ? "warning" : "error", "media.telephony_missing_stop", "Telephony media stream did not include a stop event.");
|
|
5019
|
+
}
|
|
5020
|
+
if (started && firstMediaIndex >= 0 && firstMediaIndex < startedIndex) {
|
|
5021
|
+
pushIssue(issues, "error", "media.telephony_media_before_start", "Telephony media payload arrived before the stream start event.");
|
|
5022
|
+
}
|
|
5023
|
+
if (stopped && firstMediaIndex >= 0 && stoppedIndex < firstMediaIndex) {
|
|
5024
|
+
pushIssue(issues, "error", "media.telephony_stop_before_media", "Telephony media stream stopped before any media payload arrived.");
|
|
5025
|
+
}
|
|
5026
|
+
if (mediaEvents.length > 0 && audioBytes < minAudioBytes) {
|
|
5027
|
+
pushIssue(issues, "error", "media.telephony_no_audio_bytes", `Telephony media stream parsed ${String(audioBytes)} audio byte(s), below required ${String(minAudioBytes)}.`);
|
|
5028
|
+
}
|
|
5029
|
+
for (const event of events) {
|
|
5030
|
+
if (event.kind === "error") {
|
|
5031
|
+
pushIssue(issues, "error", "media.telephony_stream_error", event.error ?? "Telephony media stream emitted an error event.");
|
|
5032
|
+
}
|
|
5033
|
+
}
|
|
5034
|
+
return {
|
|
5035
|
+
audioBytes,
|
|
5036
|
+
carrier: input.carrier,
|
|
5037
|
+
checkedAt: Date.now(),
|
|
5038
|
+
events,
|
|
5039
|
+
issues,
|
|
5040
|
+
mediaEvents: mediaEvents.length,
|
|
5041
|
+
started,
|
|
5042
|
+
status: issues.some((issue) => issue.severity === "error") ? "fail" : issues.length > 0 ? "warn" : "pass",
|
|
5043
|
+
stopped,
|
|
5044
|
+
streamIds
|
|
5045
|
+
};
|
|
5046
|
+
};
|
|
4944
5047
|
var buildMediaResamplingPlan = (input) => {
|
|
4945
5048
|
const required = !formatMatches(input.inputFormat, input.outputFormat);
|
|
4946
5049
|
return {
|
|
@@ -118,6 +118,13 @@ export declare const useVoiceReadinessFailures: (path?: string, options?: VoiceR
|
|
|
118
118
|
readonly sessionId: string;
|
|
119
119
|
readonly status: import("..").VoiceProductionReadinessStatus;
|
|
120
120
|
}[];
|
|
121
|
+
readonly telephonyMedia: readonly {
|
|
122
|
+
readonly detail?: string | undefined;
|
|
123
|
+
readonly href: string;
|
|
124
|
+
readonly label: string;
|
|
125
|
+
readonly sessionId: string;
|
|
126
|
+
readonly status: import("..").VoiceProductionReadinessStatus;
|
|
127
|
+
}[];
|
|
121
128
|
} | undefined;
|
|
122
129
|
readonly status: import("..").VoiceProductionReadinessStatus;
|
|
123
130
|
readonly summary: {
|
|
@@ -379,6 +386,8 @@ export declare const useVoiceReadinessFailures: (path?: string, options?: VoiceR
|
|
|
379
386
|
readonly carriers: number;
|
|
380
387
|
readonly failed: number;
|
|
381
388
|
readonly issues: number;
|
|
389
|
+
readonly lifecycleFailures: number;
|
|
390
|
+
readonly mediaEvents: number;
|
|
382
391
|
readonly passed: number;
|
|
383
392
|
readonly status: import("..").VoiceProductionReadinessStatus;
|
|
384
393
|
} | undefined;
|
|
@@ -541,6 +550,13 @@ export declare const useVoiceReadinessFailures: (path?: string, options?: VoiceR
|
|
|
541
550
|
readonly sessionId: string;
|
|
542
551
|
readonly status: import("..").VoiceProductionReadinessStatus;
|
|
543
552
|
}[];
|
|
553
|
+
readonly telephonyMedia: readonly {
|
|
554
|
+
readonly detail?: string | undefined;
|
|
555
|
+
readonly href: string;
|
|
556
|
+
readonly label: string;
|
|
557
|
+
readonly sessionId: string;
|
|
558
|
+
readonly status: import("..").VoiceProductionReadinessStatus;
|
|
559
|
+
}[];
|
|
544
560
|
} | undefined;
|
|
545
561
|
readonly status: import("..").VoiceProductionReadinessStatus;
|
|
546
562
|
readonly summary: {
|
|
@@ -802,6 +818,8 @@ export declare const useVoiceReadinessFailures: (path?: string, options?: VoiceR
|
|
|
802
818
|
readonly carriers: number;
|
|
803
819
|
readonly failed: number;
|
|
804
820
|
readonly issues: number;
|
|
821
|
+
readonly lifecycleFailures: number;
|
|
822
|
+
readonly mediaEvents: number;
|
|
805
823
|
readonly passed: number;
|
|
806
824
|
readonly status: import("..").VoiceProductionReadinessStatus;
|
|
807
825
|
} | undefined;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@absolutejs/voice",
|
|
3
|
-
"version": "0.0.22-beta.
|
|
3
|
+
"version": "0.0.22-beta.326",
|
|
4
4
|
"description": "Voice primitives and Elysia plugin for AbsoluteJS",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -246,7 +246,7 @@
|
|
|
246
246
|
}
|
|
247
247
|
},
|
|
248
248
|
"dependencies": {
|
|
249
|
-
"@absolutejs/media": "0.0.1-beta.
|
|
249
|
+
"@absolutejs/media": "0.0.1-beta.7"
|
|
250
250
|
},
|
|
251
251
|
"devDependencies": {
|
|
252
252
|
"@absolutejs/absolute": "0.19.0-beta.646",
|