@microsoft/omnichannel-chat-sdk
Version:
Microsoft Omnichannel Chat SDK
106 lines • 6.76 kB
JavaScript
;
var __assign = (this && this.__assign) || function () {
__assign = Object.assign || function(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
t[p] = s[p];
}
return t;
};
return __assign.apply(this, arguments);
};
Object.defineProperty(exports, "__esModule", { value: true });
var TelemetryEvent_1 = require("../telemetry/TelemetryEvent");
var MessageSource_1 = require("../telemetry/MessageSource");
var createOmnichannelMessage_1 = require("./createOmnichannelMessage");
var MAX_TRACKED_STREAMS = 1000;
var createOmnichannelStreamingMessage = function (event, params) {
var _a, _b, _c, _d, _e, _f;
// Drop duplicate finals — log and bail before doing any other work.
var incomingType = (_a = event.streamingMetadata) === null || _a === void 0 ? void 0 : _a.streamingMessageType;
if (incomingType === 'final' && params.finalizedMessageIds.has(event.id)) {
(_b = params.logger) === null || _b === void 0 ? void 0 : _b.recordIndividualEvent(TelemetryEvent_1.default.StreamingDuplicateFinal, MessageSource_1.MessageSource.WebSocketStreaming, { messageId: event.id });
return undefined;
}
// ACS chunks are typed as ChatMessageEditedEvent; defensively log if the
// content field is missing (should be impossible per protocol but the
// type system lets it slip through). createOmnichannelMessage handles
// the absent-content case by falling back to empty string.
if (event.message === undefined || event.message === null) {
(_c = params.logger) === null || _c === void 0 ? void 0 : _c.recordIndividualEvent(TelemetryEvent_1.default.StreamingChunkNoContent, MessageSource_1.MessageSource.WebSocketStreaming, { messageId: event.id, eventName: params.eventName });
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
var baseMessage = (0, createOmnichannelMessage_1.default)(event, {
liveChatVersion: params.liveChatVersion,
});
var streamingMetadata = normalizeStreamingMetadata(event, params);
if (streamingMetadata.streamingMessageType === 'final') {
// LRU-bounded: evict oldest finalized ID when at capacity.
if (params.finalizedMessageIds.size >= MAX_TRACKED_STREAMS && !params.finalizedMessageIds.has(event.id)) {
var oldestId = params.finalizedMessageIds.values().next().value;
if (oldestId !== undefined) {
params.finalizedMessageIds.delete(oldestId);
(_d = params.logger) === null || _d === void 0 ? void 0 : _d.recordIndividualEvent(TelemetryEvent_1.default.StreamingFinalizedIdEvicted, MessageSource_1.MessageSource.WebSocketStreaming, { evictedMessageId: oldestId });
}
}
params.finalizedMessageIds.add(event.id);
params.sequenceCounters.delete(event.id);
}
else if (params.finalizedMessageIds.has(event.id)) {
// Late chunk after final — log but still emit so consumers can
// decide UX behavior.
(_e = params.logger) === null || _e === void 0 ? void 0 : _e.recordIndividualEvent(TelemetryEvent_1.default.StreamingChunkAfterFinal, MessageSource_1.MessageSource.WebSocketStreaming, { messageId: event.id });
}
// Surface policyViolation (only present on chunk-typed events).
var policyViolation = event.policyViolation;
if (policyViolation) {
(_f = params.logger) === null || _f === void 0 ? void 0 : _f.recordIndividualEvent(TelemetryEvent_1.default.StreamingPolicyViolation, MessageSource_1.MessageSource.WebSocketStreaming, { messageId: event.id, result: policyViolation.result });
}
return __assign(__assign(__assign({}, baseMessage), { streamingMetadata: streamingMetadata }), (policyViolation ? { policyViolation: policyViolation } : {}));
};
function normalizeStreamingMetadata(event, params) {
var _a, _b, _c, _d, _e, _f;
var fallbackType = params.eventName === 'streamingChatMessageStarted' ? 'start' : 'streaming';
if (((_a = event.streamingMetadata) === null || _a === void 0 ? void 0 : _a.streamingMessageType) === undefined) {
(_b = params.logger) === null || _b === void 0 ? void 0 : _b.recordIndividualEvent(TelemetryEvent_1.default.StreamingMetadataMissingType, MessageSource_1.MessageSource.WebSocketStreaming, { messageId: event.id, eventName: params.eventName });
}
// For start events, the event name takes precedence over what ACS sends.
// ACS currently sends streamingMessageType: "streaming" even for the start event
// (event 250 / streamingChatMessageStarted), so we override to "start".
var inferredType = params.eventName === 'streamingChatMessageStarted'
? 'start'
: ((_d = (_c = event.streamingMetadata) === null || _c === void 0 ? void 0 : _c.streamingMessageType) !== null && _d !== void 0 ? _d : fallbackType);
var inferredSequence = recordAndGetSequence(event, params);
var endReason = (_e = event.streamingMetadata) === null || _e === void 0 ? void 0 : _e.streamEndReason;
if (inferredType === 'final' && endReason === undefined) {
(_f = params.logger) === null || _f === void 0 ? void 0 : _f.recordIndividualEvent(TelemetryEvent_1.default.StreamingFinalMissingReason, MessageSource_1.MessageSource.WebSocketStreaming, { messageId: event.id });
endReason = 'completed';
}
return {
streamingMessageType: inferredType,
streamingSequenceNumber: inferredSequence,
streamEndReason: endReason,
};
}
function recordAndGetSequence(event, params) {
var _a, _b, _c;
var fromAcs = (_a = event.streamingMetadata) === null || _a === void 0 ? void 0 : _a.streamingSequenceNumber;
if (fromAcs !== undefined) {
return fromAcs;
}
// SDK fallback: monotonic counter per messageId, LRU-bounded at MAX_TRACKED_STREAMS.
if (params.sequenceCounters.size >= MAX_TRACKED_STREAMS &&
!params.sequenceCounters.has(event.id)) {
var oldestKey = params.sequenceCounters.keys().next().value;
if (oldestKey !== undefined) {
params.sequenceCounters.delete(oldestKey);
(_b = params.logger) === null || _b === void 0 ? void 0 : _b.recordIndividualEvent(TelemetryEvent_1.default.StreamingCounterEvicted, MessageSource_1.MessageSource.WebSocketStreaming, { evictedMessageId: oldestKey });
}
}
var next = ((_c = params.sequenceCounters.get(event.id)) !== null && _c !== void 0 ? _c : 0) + 1;
params.sequenceCounters.set(event.id, next);
return next;
}
exports.default = createOmnichannelStreamingMessage;
//# sourceMappingURL=createOmnichannelStreamingMessage.js.map