@memberjunction/server 5.43.0 → 5.45.0
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/agentSessions/ReturningVisitorRecap.d.ts +39 -0
- package/dist/agentSessions/ReturningVisitorRecap.d.ts.map +1 -0
- package/dist/agentSessions/ReturningVisitorRecap.js +198 -0
- package/dist/agentSessions/ReturningVisitorRecap.js.map +1 -0
- package/dist/agentSessions/SessionJanitor.d.ts +13 -0
- package/dist/agentSessions/SessionJanitor.d.ts.map +1 -1
- package/dist/agentSessions/SessionJanitor.js +65 -7
- package/dist/agentSessions/SessionJanitor.js.map +1 -1
- package/dist/agentSessions/SessionManager.d.ts.map +1 -1
- package/dist/agentSessions/SessionManager.js +40 -0
- package/dist/agentSessions/SessionManager.js.map +1 -1
- package/dist/agentSessions/index.d.ts.map +1 -1
- package/dist/agentSessions/index.js +3 -1
- package/dist/agentSessions/index.js.map +1 -1
- package/dist/auth/magicLink/MagicLinkRouter.d.ts +10 -0
- package/dist/auth/magicLink/MagicLinkRouter.d.ts.map +1 -1
- package/dist/auth/magicLink/MagicLinkRouter.js +16 -0
- package/dist/auth/magicLink/MagicLinkRouter.js.map +1 -1
- package/dist/auth/magicLink/index.d.ts +1 -1
- package/dist/auth/magicLink/index.d.ts.map +1 -1
- package/dist/auth/magicLink/index.js +1 -1
- package/dist/auth/magicLink/index.js.map +1 -1
- package/dist/auth/magicLink/types.d.ts +26 -0
- package/dist/auth/magicLink/types.d.ts.map +1 -1
- package/dist/config.d.ts +1883 -144
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +160 -36
- package/dist/config.js.map +1 -1
- package/dist/context.d.ts.map +1 -1
- package/dist/context.js +45 -2
- package/dist/context.js.map +1 -1
- package/dist/generated/generated.d.ts +1684 -54
- package/dist/generated/generated.d.ts.map +1 -1
- package/dist/generated/generated.js +57105 -48029
- package/dist/generated/generated.js.map +1 -1
- package/dist/generic/ResolverBase.d.ts +13 -0
- package/dist/generic/ResolverBase.d.ts.map +1 -1
- package/dist/generic/ResolverBase.js +22 -0
- package/dist/generic/ResolverBase.js.map +1 -1
- package/dist/generic/RunViewResolver.d.ts.map +1 -1
- package/dist/generic/RunViewResolver.js +4 -0
- package/dist/generic/RunViewResolver.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +110 -3
- package/dist/index.js.map +1 -1
- package/dist/integration/CustomColumnPromoter.d.ts.map +1 -1
- package/dist/integration/CustomColumnPromoter.js +6 -0
- package/dist/integration/CustomColumnPromoter.js.map +1 -1
- package/dist/realtimeWidget/WidgetRouter.d.ts +26 -0
- package/dist/realtimeWidget/WidgetRouter.d.ts.map +1 -0
- package/dist/realtimeWidget/WidgetRouter.js +182 -0
- package/dist/realtimeWidget/WidgetRouter.js.map +1 -0
- package/dist/realtimeWidget/WidgetSessionService.d.ts +265 -0
- package/dist/realtimeWidget/WidgetSessionService.d.ts.map +1 -0
- package/dist/realtimeWidget/WidgetSessionService.js +477 -0
- package/dist/realtimeWidget/WidgetSessionService.js.map +1 -0
- package/dist/realtimeWidget/host-identity.d.ts +40 -0
- package/dist/realtimeWidget/host-identity.d.ts.map +1 -0
- package/dist/realtimeWidget/host-identity.js +58 -0
- package/dist/realtimeWidget/host-identity.js.map +1 -0
- package/dist/realtimeWidget/index.d.ts +10 -0
- package/dist/realtimeWidget/index.d.ts.map +1 -0
- package/dist/realtimeWidget/index.js +9 -0
- package/dist/realtimeWidget/index.js.map +1 -0
- package/dist/realtimeWidget/visitorIdentity.d.ts +76 -0
- package/dist/realtimeWidget/visitorIdentity.d.ts.map +1 -0
- package/dist/realtimeWidget/visitorIdentity.js +202 -0
- package/dist/realtimeWidget/visitorIdentity.js.map +1 -0
- package/dist/realtimeWidget/widgetCore.d.ts +106 -0
- package/dist/realtimeWidget/widgetCore.d.ts.map +1 -0
- package/dist/realtimeWidget/widgetCore.js +173 -0
- package/dist/realtimeWidget/widgetCore.js.map +1 -0
- package/dist/realtimeWidget/widgetGuestElevation.d.ts +51 -0
- package/dist/realtimeWidget/widgetGuestElevation.d.ts.map +1 -0
- package/dist/realtimeWidget/widgetGuestElevation.js +79 -0
- package/dist/realtimeWidget/widgetGuestElevation.js.map +1 -0
- package/dist/resolvers/ComponentRegistryResolver.d.ts +7 -0
- package/dist/resolvers/ComponentRegistryResolver.d.ts.map +1 -1
- package/dist/resolvers/ComponentRegistryResolver.js +31 -8
- package/dist/resolvers/ComponentRegistryResolver.js.map +1 -1
- package/dist/resolvers/EntityPermissionResolver.d.ts.map +1 -1
- package/dist/resolvers/EntityPermissionResolver.js +1 -2
- package/dist/resolvers/EntityPermissionResolver.js.map +1 -1
- package/dist/resolvers/FileResolver.d.ts +74 -0
- package/dist/resolvers/FileResolver.d.ts.map +1 -1
- package/dist/resolvers/FileResolver.js +211 -2
- package/dist/resolvers/FileResolver.js.map +1 -1
- package/dist/resolvers/QueryResolver.d.ts +6 -4
- package/dist/resolvers/QueryResolver.d.ts.map +1 -1
- package/dist/resolvers/QueryResolver.js +29 -14
- package/dist/resolvers/QueryResolver.js.map +1 -1
- package/dist/resolvers/QuerySystemUserResolver.d.ts +9 -6
- package/dist/resolvers/QuerySystemUserResolver.d.ts.map +1 -1
- package/dist/resolvers/QuerySystemUserResolver.js +15 -13
- package/dist/resolvers/QuerySystemUserResolver.js.map +1 -1
- package/dist/resolvers/RealtimeBridgeResolver.d.ts +8 -1
- package/dist/resolvers/RealtimeBridgeResolver.d.ts.map +1 -1
- package/dist/resolvers/RealtimeBridgeResolver.js +28 -4
- package/dist/resolvers/RealtimeBridgeResolver.js.map +1 -1
- package/dist/resolvers/RealtimeClientSessionResolver.d.ts +91 -2
- package/dist/resolvers/RealtimeClientSessionResolver.d.ts.map +1 -1
- package/dist/resolvers/RealtimeClientSessionResolver.js +337 -17
- package/dist/resolvers/RealtimeClientSessionResolver.js.map +1 -1
- package/dist/resolvers/RemoteBrowserActionResolver.d.ts.map +1 -1
- package/dist/resolvers/RemoteBrowserActionResolver.js +17 -2
- package/dist/resolvers/RemoteBrowserActionResolver.js.map +1 -1
- package/dist/resolvers/RingCentralTelephonyResolver.d.ts +27 -0
- package/dist/resolvers/RingCentralTelephonyResolver.d.ts.map +1 -0
- package/dist/resolvers/RingCentralTelephonyResolver.js +87 -0
- package/dist/resolvers/RingCentralTelephonyResolver.js.map +1 -0
- package/dist/resolvers/RunAIAgentResolver.d.ts +12 -3
- package/dist/resolvers/RunAIAgentResolver.d.ts.map +1 -1
- package/dist/resolvers/RunAIAgentResolver.js +50 -17
- package/dist/resolvers/RunAIAgentResolver.js.map +1 -1
- package/dist/resolvers/SearchKnowledgeResolver.d.ts.map +1 -1
- package/dist/resolvers/SearchKnowledgeResolver.js +2 -0
- package/dist/resolvers/SearchKnowledgeResolver.js.map +1 -1
- package/dist/resolvers/TeamsMeetingsResolver.d.ts +28 -0
- package/dist/resolvers/TeamsMeetingsResolver.d.ts.map +1 -0
- package/dist/resolvers/TeamsMeetingsResolver.js +91 -0
- package/dist/resolvers/TeamsMeetingsResolver.js.map +1 -0
- package/dist/resolvers/TelephonyResolver.d.ts +26 -0
- package/dist/resolvers/TelephonyResolver.d.ts.map +1 -0
- package/dist/resolvers/TelephonyResolver.js +86 -0
- package/dist/resolvers/TelephonyResolver.js.map +1 -0
- package/dist/resolvers/TestQuerySQLResolver.d.ts +1 -1
- package/dist/resolvers/TestQuerySQLResolver.d.ts.map +1 -1
- package/dist/resolvers/TestQuerySQLResolver.js +2 -3
- package/dist/resolvers/TestQuerySQLResolver.js.map +1 -1
- package/dist/resolvers/VonageTelephonyResolver.d.ts +26 -0
- package/dist/resolvers/VonageTelephonyResolver.d.ts.map +1 -0
- package/dist/resolvers/VonageTelephonyResolver.js +86 -0
- package/dist/resolvers/VonageTelephonyResolver.js.map +1 -0
- package/dist/resolvers/meetingRecordingRegistration.d.ts +124 -0
- package/dist/resolvers/meetingRecordingRegistration.d.ts.map +1 -0
- package/dist/resolvers/meetingRecordingRegistration.js +311 -0
- package/dist/resolvers/meetingRecordingRegistration.js.map +1 -0
- package/dist/resolvers/peaksSidecar.d.ts +30 -0
- package/dist/resolvers/peaksSidecar.d.ts.map +1 -0
- package/dist/resolvers/peaksSidecar.js +51 -0
- package/dist/resolvers/peaksSidecar.js.map +1 -0
- package/dist/rest/MediaAccessKeys.d.ts +68 -0
- package/dist/rest/MediaAccessKeys.d.ts.map +1 -0
- package/dist/rest/MediaAccessKeys.js +96 -0
- package/dist/rest/MediaAccessKeys.js.map +1 -0
- package/dist/rest/MediaStreamHandler.d.ts +26 -0
- package/dist/rest/MediaStreamHandler.d.ts.map +1 -0
- package/dist/rest/MediaStreamHandler.js +195 -0
- package/dist/rest/MediaStreamHandler.js.map +1 -0
- package/dist/rest/mediaRange.d.ts +41 -0
- package/dist/rest/mediaRange.d.ts.map +1 -0
- package/dist/rest/mediaRange.js +60 -0
- package/dist/rest/mediaRange.js.map +1 -0
- package/dist/telephony/RingCentralTelephonyService.d.ts +115 -0
- package/dist/telephony/RingCentralTelephonyService.d.ts.map +1 -0
- package/dist/telephony/RingCentralTelephonyService.js +262 -0
- package/dist/telephony/RingCentralTelephonyService.js.map +1 -0
- package/dist/telephony/TeamsMeetingsRouter.d.ts +40 -0
- package/dist/telephony/TeamsMeetingsRouter.d.ts.map +1 -0
- package/dist/telephony/TeamsMeetingsRouter.js +96 -0
- package/dist/telephony/TeamsMeetingsRouter.js.map +1 -0
- package/dist/telephony/TeamsMeetingsService.d.ts +113 -0
- package/dist/telephony/TeamsMeetingsService.d.ts.map +1 -0
- package/dist/telephony/TeamsMeetingsService.js +196 -0
- package/dist/telephony/TeamsMeetingsService.js.map +1 -0
- package/dist/telephony/TwilioTelephonyRouter.d.ts +36 -0
- package/dist/telephony/TwilioTelephonyRouter.d.ts.map +1 -0
- package/dist/telephony/TwilioTelephonyRouter.js +143 -0
- package/dist/telephony/TwilioTelephonyRouter.js.map +1 -0
- package/dist/telephony/TwilioTelephonyService.d.ts +95 -0
- package/dist/telephony/TwilioTelephonyService.d.ts.map +1 -0
- package/dist/telephony/TwilioTelephonyService.js +193 -0
- package/dist/telephony/TwilioTelephonyService.js.map +1 -0
- package/dist/telephony/VonageTelephonyRouter.d.ts +41 -0
- package/dist/telephony/VonageTelephonyRouter.d.ts.map +1 -0
- package/dist/telephony/VonageTelephonyRouter.js +201 -0
- package/dist/telephony/VonageTelephonyRouter.js.map +1 -0
- package/dist/telephony/VonageTelephonyService.d.ts +90 -0
- package/dist/telephony/VonageTelephonyService.d.ts.map +1 -0
- package/dist/telephony/VonageTelephonyService.js +191 -0
- package/dist/telephony/VonageTelephonyService.js.map +1 -0
- package/dist/telephony/calendar-scheduler.d.ts +67 -0
- package/dist/telephony/calendar-scheduler.d.ts.map +1 -0
- package/dist/telephony/calendar-scheduler.js +133 -0
- package/dist/telephony/calendar-scheduler.js.map +1 -0
- package/dist/telephony/index.d.ts +29 -0
- package/dist/telephony/index.d.ts.map +1 -0
- package/dist/telephony/index.js +38 -0
- package/dist/telephony/index.js.map +1 -0
- package/dist/telephony/media-upgrade-router.d.ts +33 -0
- package/dist/telephony/media-upgrade-router.d.ts.map +1 -0
- package/dist/telephony/media-upgrade-router.js +56 -0
- package/dist/telephony/media-upgrade-router.js.map +1 -0
- package/dist/telephony/ringcentral-runtime.d.ts +14 -0
- package/dist/telephony/ringcentral-runtime.d.ts.map +1 -0
- package/dist/telephony/ringcentral-runtime.js +18 -0
- package/dist/telephony/ringcentral-runtime.js.map +1 -0
- package/dist/telephony/teams-meetings-runtime.d.ts +16 -0
- package/dist/telephony/teams-meetings-runtime.d.ts.map +1 -0
- package/dist/telephony/teams-meetings-runtime.js +20 -0
- package/dist/telephony/teams-meetings-runtime.js.map +1 -0
- package/dist/telephony/teamsAcsMediaRegistry.d.ts +69 -0
- package/dist/telephony/teamsAcsMediaRegistry.d.ts.map +1 -0
- package/dist/telephony/teamsAcsMediaRegistry.js +118 -0
- package/dist/telephony/teamsAcsMediaRegistry.js.map +1 -0
- package/dist/telephony/telephony-runtime.d.ts +14 -0
- package/dist/telephony/telephony-runtime.d.ts.map +1 -0
- package/dist/telephony/telephony-runtime.js +18 -0
- package/dist/telephony/telephony-runtime.js.map +1 -0
- package/dist/telephony/twilioMediaRegistry.d.ts +60 -0
- package/dist/telephony/twilioMediaRegistry.d.ts.map +1 -0
- package/dist/telephony/twilioMediaRegistry.js +106 -0
- package/dist/telephony/twilioMediaRegistry.js.map +1 -0
- package/dist/telephony/vonage-runtime.d.ts +14 -0
- package/dist/telephony/vonage-runtime.d.ts.map +1 -0
- package/dist/telephony/vonage-runtime.js +18 -0
- package/dist/telephony/vonage-runtime.js.map +1 -0
- package/dist/telephony/vonageMediaRegistry.d.ts +78 -0
- package/dist/telephony/vonageMediaRegistry.d.ts.map +1 -0
- package/dist/telephony/vonageMediaRegistry.js +158 -0
- package/dist/telephony/vonageMediaRegistry.js.map +1 -0
- package/package.json +88 -82
- package/src/__tests__/FileResolverPeaks.test.ts +64 -0
- package/src/__tests__/MediaAccessKeys.test.ts +68 -0
- package/src/__tests__/MediaStreamHandler.test.ts +91 -0
- package/src/__tests__/RealtimeBridgeResolver.test.ts +10 -1
- package/src/__tests__/RealtimeClientSessionResolver.test.ts +69 -0
- package/src/__tests__/RealtimeCoAgentAppAwareness.integration.test.ts +191 -0
- package/src/__tests__/RemoteBrowserSnapshot.test.ts +123 -0
- package/src/__tests__/SessionManager.test.ts +2 -0
- package/src/__tests__/meetingRecordingRegistration.test.ts +248 -0
- package/src/__tests__/search-knowledge-tags.test.ts +9 -9
- package/src/__tests__/teams-meetings-service.test.ts +65 -0
- package/src/__tests__/teamsAcsMediaRegistry.test.ts +82 -0
- package/src/__tests__/twilio-telephony-service.test.ts +23 -0
- package/src/__tests__/twilioMediaRegistry.test.ts +103 -0
- package/src/__tests__/vonageMediaRegistry.test.ts +180 -0
- package/src/__tests__/widget.test.ts +204 -0
- package/src/__tests__/widgetGuestElevation.test.ts +57 -0
- package/src/__tests__/widgetHostIdentity.test.ts +117 -0
- package/src/agentSessions/ReturningVisitorRecap.ts +242 -0
- package/src/agentSessions/SessionJanitor.ts +66 -6
- package/src/agentSessions/SessionManager.ts +41 -0
- package/src/agentSessions/index.ts +3 -1
- package/src/auth/magicLink/MagicLinkRouter.ts +17 -0
- package/src/auth/magicLink/index.ts +1 -0
- package/src/auth/magicLink/types.ts +26 -0
- package/src/config.ts +172 -38
- package/src/context.ts +50 -3
- package/src/generated/generated.ts +26726 -20464
- package/src/generic/ResolverBase.ts +28 -0
- package/src/generic/RunViewResolver.ts +4 -0
- package/src/index.ts +119 -3
- package/src/integration/CustomColumnPromoter.ts +6 -0
- package/src/realtimeWidget/WidgetRouter.ts +204 -0
- package/src/realtimeWidget/WidgetSessionService.ts +714 -0
- package/src/realtimeWidget/host-identity.ts +84 -0
- package/src/realtimeWidget/index.ts +15 -0
- package/src/realtimeWidget/visitorIdentity.ts +258 -0
- package/src/realtimeWidget/widgetCore.ts +231 -0
- package/src/realtimeWidget/widgetGuestElevation.ts +115 -0
- package/src/resolvers/ComponentRegistryResolver.ts +36 -10
- package/src/resolvers/EntityPermissionResolver.ts +1 -2
- package/src/resolvers/FileResolver.ts +199 -1
- package/src/resolvers/QueryResolver.ts +33 -19
- package/src/resolvers/QuerySystemUserResolver.ts +14 -10
- package/src/resolvers/RealtimeBridgeResolver.ts +36 -4
- package/src/resolvers/RealtimeClientSessionResolver.ts +383 -9
- package/src/resolvers/RemoteBrowserActionResolver.ts +18 -2
- package/src/resolvers/RingCentralTelephonyResolver.ts +64 -0
- package/src/resolvers/RunAIAgentResolver.ts +60 -15
- package/src/resolvers/SearchKnowledgeResolver.ts +2 -0
- package/src/resolvers/TeamsMeetingsResolver.ts +68 -0
- package/src/resolvers/TelephonyResolver.ts +63 -0
- package/src/resolvers/TestQuerySQLResolver.ts +2 -3
- package/src/resolvers/VonageTelephonyResolver.ts +63 -0
- package/src/resolvers/meetingRecordingRegistration.ts +432 -0
- package/src/resolvers/peaksSidecar.ts +52 -0
- package/src/rest/MediaAccessKeys.ts +121 -0
- package/src/rest/MediaStreamHandler.ts +223 -0
- package/src/rest/mediaRange.ts +76 -0
- package/src/telephony/RingCentralTelephonyService.ts +342 -0
- package/src/telephony/TeamsMeetingsRouter.ts +124 -0
- package/src/telephony/TeamsMeetingsService.ts +268 -0
- package/src/telephony/TwilioTelephonyRouter.ts +184 -0
- package/src/telephony/TwilioTelephonyService.ts +261 -0
- package/src/telephony/VonageTelephonyRouter.ts +239 -0
- package/src/telephony/VonageTelephonyService.ts +259 -0
- package/src/telephony/calendar-scheduler.ts +176 -0
- package/src/telephony/index.ts +43 -0
- package/src/telephony/media-upgrade-router.ts +62 -0
- package/src/telephony/ringcentral-runtime.ts +22 -0
- package/src/telephony/teams-meetings-runtime.ts +24 -0
- package/src/telephony/teamsAcsMediaRegistry.ts +152 -0
- package/src/telephony/telephony-runtime.ts +22 -0
- package/src/telephony/twilioMediaRegistry.ts +137 -0
- package/src/telephony/vonage-runtime.ts +22 -0
- package/src/telephony/vonageMediaRegistry.ts +200 -0
- package/dist/agents/skip-agent.d.ts +0 -111
- package/dist/agents/skip-agent.d.ts.map +0 -1
- package/dist/agents/skip-agent.js +0 -1487
- package/dist/agents/skip-agent.js.map +0 -1
- package/dist/agents/skip-sdk.d.ts +0 -261
- package/dist/agents/skip-sdk.d.ts.map +0 -1
- package/dist/agents/skip-sdk.js +0 -909
- package/dist/agents/skip-sdk.js.map +0 -1
- package/src/agents/skip-agent.ts +0 -1594
- package/src/agents/skip-sdk.ts +0 -1210
|
@@ -127,7 +127,7 @@ describe('SearchKnowledgeResolver', () => {
|
|
|
127
127
|
const resolver = createResolver();
|
|
128
128
|
mockSearch.mockResolvedValueOnce(createMockSearchResult());
|
|
129
129
|
|
|
130
|
-
const result = await resolver.SearchKnowledge('test query', 20, undefined, undefined, fakeContext as never);
|
|
130
|
+
const result = await resolver.SearchKnowledge('test query', 20, undefined, undefined, undefined, undefined, undefined, fakeContext as never);
|
|
131
131
|
|
|
132
132
|
expect(mockSearch).toHaveBeenCalledOnce();
|
|
133
133
|
expect(result.Success).toBe(true);
|
|
@@ -139,7 +139,7 @@ describe('SearchKnowledgeResolver', () => {
|
|
|
139
139
|
const resolver = createResolver();
|
|
140
140
|
mockSearch.mockResolvedValueOnce(createMockSearchResult());
|
|
141
141
|
|
|
142
|
-
await resolver.SearchKnowledge('cheese', 50, undefined, undefined, fakeContext as never);
|
|
142
|
+
await resolver.SearchKnowledge('cheese', 50, undefined, undefined, undefined, undefined, undefined, fakeContext as never);
|
|
143
143
|
|
|
144
144
|
const searchParams = mockSearch.mock.calls[0][0];
|
|
145
145
|
expect(searchParams.Query).toBe('cheese');
|
|
@@ -151,7 +151,7 @@ describe('SearchKnowledgeResolver', () => {
|
|
|
151
151
|
mockSearch.mockResolvedValueOnce(createMockSearchResult());
|
|
152
152
|
|
|
153
153
|
const filters = { EntityNames: ['Contacts'], SourceTypes: undefined, Tags: ['VIP'] };
|
|
154
|
-
await resolver.SearchKnowledge('test', 20, filters as never, undefined, fakeContext as never);
|
|
154
|
+
await resolver.SearchKnowledge('test', 20, filters as never, undefined, undefined, undefined, undefined, fakeContext as never);
|
|
155
155
|
|
|
156
156
|
const searchParams = mockSearch.mock.calls[0][0];
|
|
157
157
|
expect(searchParams.Filters).toEqual({
|
|
@@ -165,7 +165,7 @@ describe('SearchKnowledgeResolver', () => {
|
|
|
165
165
|
const resolver = createResolver();
|
|
166
166
|
mockSearch.mockResolvedValueOnce(createMockSearchResult());
|
|
167
167
|
|
|
168
|
-
await resolver.SearchKnowledge('test', 20, undefined, 0.5, fakeContext as never);
|
|
168
|
+
await resolver.SearchKnowledge('test', 20, undefined, 0.5, undefined, undefined, undefined, fakeContext as never);
|
|
169
169
|
|
|
170
170
|
const searchParams = mockSearch.mock.calls[0][0];
|
|
171
171
|
expect(searchParams.MinScore).toBe(0.5);
|
|
@@ -175,7 +175,7 @@ describe('SearchKnowledgeResolver', () => {
|
|
|
175
175
|
const resolver = createResolver();
|
|
176
176
|
mockSearch.mockResolvedValueOnce(createMockSearchResult());
|
|
177
177
|
|
|
178
|
-
const result = await resolver.SearchKnowledge('test', 20, undefined, undefined, fakeContext as never);
|
|
178
|
+
const result = await resolver.SearchKnowledge('test', 20, undefined, undefined, undefined, undefined, undefined, fakeContext as never);
|
|
179
179
|
|
|
180
180
|
const item = result.Results[0];
|
|
181
181
|
expect(item.ID).toBe('result-1');
|
|
@@ -193,7 +193,7 @@ describe('SearchKnowledgeResolver', () => {
|
|
|
193
193
|
SourceCounts: { Vector: 5, FullText: 3, Entity: 10, Storage: 0 }
|
|
194
194
|
}));
|
|
195
195
|
|
|
196
|
-
const result = await resolver.SearchKnowledge('test', 20, undefined, undefined, fakeContext as never);
|
|
196
|
+
const result = await resolver.SearchKnowledge('test', 20, undefined, undefined, undefined, undefined, undefined, fakeContext as never);
|
|
197
197
|
|
|
198
198
|
expect(result.SourceCounts.Vector).toBe(5);
|
|
199
199
|
expect(result.SourceCounts.FullText).toBe(3);
|
|
@@ -204,7 +204,7 @@ describe('SearchKnowledgeResolver', () => {
|
|
|
204
204
|
const resolver = createResolver();
|
|
205
205
|
mockSearch.mockResolvedValueOnce(createMockSearchResult({ Success: false, ErrorMessage: 'Query cannot be empty' }));
|
|
206
206
|
|
|
207
|
-
const result = await resolver.SearchKnowledge(' ', 20, undefined, undefined, fakeContext as never);
|
|
207
|
+
const result = await resolver.SearchKnowledge(' ', 20, undefined, undefined, undefined, undefined, undefined, fakeContext as never);
|
|
208
208
|
|
|
209
209
|
expect(mockSearch).toHaveBeenCalled();
|
|
210
210
|
expect(result.Success).toBe(false);
|
|
@@ -214,7 +214,7 @@ describe('SearchKnowledgeResolver', () => {
|
|
|
214
214
|
const resolver = new SearchKnowledgeResolver();
|
|
215
215
|
(resolver as Record<string, unknown>)['GetUserFromPayload'] = vi.fn().mockReturnValue(null);
|
|
216
216
|
|
|
217
|
-
const result = await resolver.SearchKnowledge('test', 20, undefined, undefined, fakeContext as never);
|
|
217
|
+
const result = await resolver.SearchKnowledge('test', 20, undefined, undefined, undefined, undefined, undefined, fakeContext as never);
|
|
218
218
|
|
|
219
219
|
expect(result.Success).toBe(false);
|
|
220
220
|
expect(result.ErrorMessage).toContain('current user');
|
|
@@ -224,7 +224,7 @@ describe('SearchKnowledgeResolver', () => {
|
|
|
224
224
|
const resolver = createResolver();
|
|
225
225
|
mockSearch.mockRejectedValueOnce(new Error('Connection failed'));
|
|
226
226
|
|
|
227
|
-
const result = await resolver.SearchKnowledge('test', 20, undefined, undefined, fakeContext as never);
|
|
227
|
+
const result = await resolver.SearchKnowledge('test', 20, undefined, undefined, undefined, undefined, undefined, fakeContext as never);
|
|
228
228
|
|
|
229
229
|
expect(result.Success).toBe(false);
|
|
230
230
|
expect(result.ErrorMessage).toContain('Connection failed');
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { describe, it, expect, vi } from 'vitest';
|
|
2
|
+
import { TeamsMeetingsService } from '../telephony/TeamsMeetingsService.js';
|
|
3
|
+
import { TeamsAcsMediaRegistry } from '../telephony/teamsAcsMediaRegistry.js';
|
|
4
|
+
import { RealTeamsBindings } from '@memberjunction/ai-bridge-teams';
|
|
5
|
+
import type { BaseRealtimeBridge } from '@memberjunction/ai-bridge-base';
|
|
6
|
+
import type { TeamsMeetingsConfig } from '../config.js';
|
|
7
|
+
|
|
8
|
+
const CONFIG: TeamsMeetingsConfig = {
|
|
9
|
+
enabled: true,
|
|
10
|
+
tenantId: 'tenant-1',
|
|
11
|
+
botAccessToken: 'tok',
|
|
12
|
+
notificationClientState: 'secret',
|
|
13
|
+
acsSampleRate: 16000,
|
|
14
|
+
modelSampleRate: 16000,
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
describe('TeamsMeetingsService.buildBindSdk', () => {
|
|
18
|
+
it('binds a RealTeamsBindings factory onto the Teams driver (the live Graph + ACS wiring seam)', () => {
|
|
19
|
+
const service = new TeamsMeetingsService(CONFIG, new TeamsAcsMediaRegistry());
|
|
20
|
+
const setSdkFactory = vi.fn();
|
|
21
|
+
const fakeDriver = { SetSdkFactory: setSdkFactory } as unknown as BaseRealtimeBridge;
|
|
22
|
+
// A minimal fake Graph client (only used as a constructor arg here).
|
|
23
|
+
const graphClient = { CreateCall: vi.fn() } as unknown as Parameters<typeof service.buildBindSdk>[0];
|
|
24
|
+
|
|
25
|
+
service.buildBindSdk(graphClient)(fakeDriver);
|
|
26
|
+
|
|
27
|
+
expect(setSdkFactory).toHaveBeenCalledTimes(1);
|
|
28
|
+
const factory = setSdkFactory.mock.calls[0][0] as () => unknown;
|
|
29
|
+
expect(typeof factory).toBe('function');
|
|
30
|
+
// The factory yields a real (bound) RealTeamsBindings — proving the offline seam meets live plumbing.
|
|
31
|
+
expect(factory()).toBeInstanceOf(RealTeamsBindings);
|
|
32
|
+
});
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
describe('TeamsMeetingsService.JoinMeetingByUrl', () => {
|
|
36
|
+
it('returns accepted=false (never throws) when the agent identity is missing', async () => {
|
|
37
|
+
const provider = {
|
|
38
|
+
GetEntityObject: vi.fn().mockResolvedValue({ Load: vi.fn().mockResolvedValue(false), IsActive: false }),
|
|
39
|
+
};
|
|
40
|
+
const service = new TeamsMeetingsService(CONFIG, new TeamsAcsMediaRegistry());
|
|
41
|
+
const result = await service.JoinMeetingByUrl(
|
|
42
|
+
'identity-1',
|
|
43
|
+
'https://teams.microsoft.com/l/meetup-join/x',
|
|
44
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- minimal UserInfo stub for a pure-branch test
|
|
45
|
+
{ ID: 'u1' } as never,
|
|
46
|
+
provider as never,
|
|
47
|
+
);
|
|
48
|
+
expect(result.accepted).toBe(false);
|
|
49
|
+
expect(result.reason).toMatch(/not found or inactive/i);
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
describe('TeamsMeetingsService notification drive helpers', () => {
|
|
54
|
+
it('tears down the call + registry on call-ended (no throw when no client registered)', () => {
|
|
55
|
+
const registry = new TeamsAcsMediaRegistry();
|
|
56
|
+
registry.RegisterCall('call-1');
|
|
57
|
+
expect(registry.HasCall('call-1')).toBe(true);
|
|
58
|
+
const service = new TeamsMeetingsService(CONFIG, registry);
|
|
59
|
+
|
|
60
|
+
service.DriveCallEnded('call-1');
|
|
61
|
+
expect(registry.HasCall('call-1')).toBe(false);
|
|
62
|
+
// An unknown roster drive is a silent no-op.
|
|
63
|
+
expect(() => service.DriveParticipantsUpdated('unknown', [{ id: 'p1' }])).not.toThrow();
|
|
64
|
+
});
|
|
65
|
+
});
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { TeamsAcsMediaRegistry, type IAcsMediaTransport } from '../telephony/teamsAcsMediaRegistry.js';
|
|
3
|
+
import type { AcsInboundAudioFrame } from '@memberjunction/ai-bridge-teams';
|
|
4
|
+
|
|
5
|
+
class FakeTransport implements IAcsMediaTransport {
|
|
6
|
+
public readonly Sent: ArrayBuffer[] = [];
|
|
7
|
+
public Closed = false;
|
|
8
|
+
public sendAudioFrame(pcm: ArrayBuffer): void {
|
|
9
|
+
this.Sent.push(pcm);
|
|
10
|
+
}
|
|
11
|
+
public close(): void {
|
|
12
|
+
this.Closed = true;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const frame = (bytes: number): ArrayBuffer => new ArrayBuffer(bytes);
|
|
17
|
+
|
|
18
|
+
describe('TeamsAcsMediaRegistry', () => {
|
|
19
|
+
it('exposes the configured ACS sample rate', () => {
|
|
20
|
+
expect(new TeamsAcsMediaRegistry().SampleRate).toBe(16000);
|
|
21
|
+
expect(new TeamsAcsMediaRegistry(8000).SampleRate).toBe(8000);
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it('buffers outbound PCM before the transport attaches, then flushes on attach', () => {
|
|
25
|
+
const registry = new TeamsAcsMediaRegistry();
|
|
26
|
+
const a = frame(4);
|
|
27
|
+
const b = frame(8);
|
|
28
|
+
registry.Send('call-1', a);
|
|
29
|
+
registry.Send('call-1', b);
|
|
30
|
+
|
|
31
|
+
const transport = new FakeTransport();
|
|
32
|
+
registry.AttachTransport('call-1', transport);
|
|
33
|
+
|
|
34
|
+
expect(transport.Sent).toEqual([a, b]);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it('sends immediately once the transport is attached', () => {
|
|
38
|
+
const registry = new TeamsAcsMediaRegistry();
|
|
39
|
+
const transport = new FakeTransport();
|
|
40
|
+
registry.AttachTransport('call-1', transport);
|
|
41
|
+
const a = frame(4);
|
|
42
|
+
registry.Send('call-1', a);
|
|
43
|
+
expect(transport.Sent).toEqual([a]);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it('dispatches inbound frames to all registered handlers for the call', () => {
|
|
47
|
+
const registry = new TeamsAcsMediaRegistry();
|
|
48
|
+
const seen: AcsInboundAudioFrame[] = [];
|
|
49
|
+
registry.OnFrame('call-1', (f) => seen.push(f));
|
|
50
|
+
registry.OnFrame('call-1', (f) => seen.push(f));
|
|
51
|
+
const f: AcsInboundAudioFrame = { Pcm: frame(2), ParticipantId: 'p1' };
|
|
52
|
+
registry.DispatchInbound('call-1', f);
|
|
53
|
+
expect(seen).toEqual([f, f]);
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
it('dispatches hand-raise signals to registered hand-raise handlers', () => {
|
|
57
|
+
const registry = new TeamsAcsMediaRegistry();
|
|
58
|
+
let raised: boolean | undefined;
|
|
59
|
+
registry.OnHandRaise('call-1', (_p, r) => (raised = r));
|
|
60
|
+
registry.DispatchHandRaise('call-1', 'p1', true);
|
|
61
|
+
expect(raised).toBe(true);
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it('ignores inbound dispatch + hand-raise for unknown calls (no throw)', () => {
|
|
65
|
+
const registry = new TeamsAcsMediaRegistry();
|
|
66
|
+
expect(() => registry.DispatchInbound('nope', { Pcm: frame(2), ParticipantId: 'p1' })).not.toThrow();
|
|
67
|
+
expect(() => registry.DispatchHandRaise('nope', 'p1', true)).not.toThrow();
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it('closes the transport and forgets the call on EndCall', () => {
|
|
71
|
+
const registry = new TeamsAcsMediaRegistry();
|
|
72
|
+
const transport = new FakeTransport();
|
|
73
|
+
registry.AttachTransport('call-1', transport);
|
|
74
|
+
expect(registry.HasCall('call-1')).toBe(true);
|
|
75
|
+
|
|
76
|
+
registry.EndCall('call-1');
|
|
77
|
+
expect(transport.Closed).toBe(true);
|
|
78
|
+
expect(registry.HasCall('call-1')).toBe(false);
|
|
79
|
+
// EndCall on an unknown call is a silent no-op.
|
|
80
|
+
expect(() => registry.EndCall('unknown')).not.toThrow();
|
|
81
|
+
});
|
|
82
|
+
});
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { describe, it, expect, vi } from 'vitest';
|
|
2
|
+
import { TwilioTelephonyService } from '../telephony/TwilioTelephonyService.js';
|
|
3
|
+
import { TwilioCallMediaRegistry } from '../telephony/twilioMediaRegistry.js';
|
|
4
|
+
import { TwilioCallSdk } from '@memberjunction/ai-bridge-twilio';
|
|
5
|
+
import type { BaseTelephonyBridge } from '@memberjunction/ai-bridge-base';
|
|
6
|
+
|
|
7
|
+
const CONFIG = { accountSid: 'AC1', authToken: 'tok', streamPublicUrl: 'wss://api.test/telephony/twilio/media' };
|
|
8
|
+
|
|
9
|
+
describe('TwilioTelephonyService.buildBindSdk', () => {
|
|
10
|
+
it('binds a TwilioCallSdk factory onto the telephony driver (the live REST + media wiring seam)', () => {
|
|
11
|
+
const service = new TwilioTelephonyService(CONFIG, new TwilioCallMediaRegistry());
|
|
12
|
+
const setSdkFactory = vi.fn();
|
|
13
|
+
const fakeDriver = { SetSdkFactory: setSdkFactory } as unknown as BaseTelephonyBridge;
|
|
14
|
+
|
|
15
|
+
service.buildBindSdk()(fakeDriver);
|
|
16
|
+
|
|
17
|
+
expect(setSdkFactory).toHaveBeenCalledTimes(1);
|
|
18
|
+
const factory = setSdkFactory.mock.calls[0][0] as () => unknown;
|
|
19
|
+
expect(typeof factory).toBe('function');
|
|
20
|
+
// The factory yields a real (bound) TwilioCallSdk — proving the offline seam meets live plumbing.
|
|
21
|
+
expect(factory()).toBeInstanceOf(TwilioCallSdk);
|
|
22
|
+
});
|
|
23
|
+
});
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { describe, it, expect, vi } from 'vitest';
|
|
2
|
+
import { TwilioCallMediaRegistry, type ITelephonyMediaSocket } from '../telephony/twilioMediaRegistry.js';
|
|
3
|
+
import type { TwilioMediaFrame } from '@memberjunction/ai-bridge-twilio';
|
|
4
|
+
|
|
5
|
+
/** A fake Media-Streams socket capturing sends + tracking close. */
|
|
6
|
+
function fakeSocket(): ITelephonyMediaSocket & { sent: string[]; closed: boolean } {
|
|
7
|
+
const sent: string[] = [];
|
|
8
|
+
return {
|
|
9
|
+
sent,
|
|
10
|
+
closed: false,
|
|
11
|
+
send(data: string) {
|
|
12
|
+
sent.push(data);
|
|
13
|
+
},
|
|
14
|
+
close() {
|
|
15
|
+
(this as { closed: boolean }).closed = true;
|
|
16
|
+
},
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const mediaFrame = (payload: string): TwilioMediaFrame => ({ event: 'media', streamSid: 'MZ1', media: { payload } });
|
|
21
|
+
|
|
22
|
+
describe('TwilioCallMediaRegistry', () => {
|
|
23
|
+
describe('outbound buffering', () => {
|
|
24
|
+
it('buffers outbound frames before the socket connects, then flushes on AttachSocket in order', () => {
|
|
25
|
+
const reg = new TwilioCallMediaRegistry();
|
|
26
|
+
reg.RegisterCall('CA1');
|
|
27
|
+
reg.Send('CA1', mediaFrame('a'));
|
|
28
|
+
reg.Send('CA1', mediaFrame('b'));
|
|
29
|
+
|
|
30
|
+
const sock = fakeSocket();
|
|
31
|
+
reg.AttachSocket('CA1', sock, 'MZ1');
|
|
32
|
+
|
|
33
|
+
expect(sock.sent).toHaveLength(2);
|
|
34
|
+
expect(JSON.parse(sock.sent[0]).media.payload).toBe('a');
|
|
35
|
+
expect(JSON.parse(sock.sent[1]).media.payload).toBe('b');
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it('sends immediately once the socket is attached', () => {
|
|
39
|
+
const reg = new TwilioCallMediaRegistry();
|
|
40
|
+
const sock = fakeSocket();
|
|
41
|
+
reg.AttachSocket('CA1', sock, 'MZ1');
|
|
42
|
+
reg.Send('CA1', mediaFrame('c'));
|
|
43
|
+
expect(sock.sent).toHaveLength(1);
|
|
44
|
+
expect(JSON.parse(sock.sent[0]).media.payload).toBe('c');
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
describe('inbound dispatch', () => {
|
|
49
|
+
it('delivers inbound frames to handlers registered before the socket existed', () => {
|
|
50
|
+
const reg = new TwilioCallMediaRegistry();
|
|
51
|
+
const received: TwilioMediaFrame[] = [];
|
|
52
|
+
reg.OnFrame('CA1', (f) => received.push(f));
|
|
53
|
+
|
|
54
|
+
reg.DispatchInbound('CA1', mediaFrame('in'));
|
|
55
|
+
|
|
56
|
+
expect(received).toHaveLength(1);
|
|
57
|
+
expect(received[0].media?.payload).toBe('in');
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
it('captures the stream SID from a start frame and exposes it via GetStreamSid', () => {
|
|
61
|
+
const reg = new TwilioCallMediaRegistry();
|
|
62
|
+
reg.RegisterCall('CA1');
|
|
63
|
+
expect(reg.GetStreamSid('CA1')).toBe('');
|
|
64
|
+
reg.DispatchInbound('CA1', { event: 'start', streamSid: 'MZ-XYZ' });
|
|
65
|
+
expect(reg.GetStreamSid('CA1')).toBe('MZ-XYZ');
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it('drops inbound frames for an unknown call (no channel) without throwing', () => {
|
|
69
|
+
const reg = new TwilioCallMediaRegistry();
|
|
70
|
+
expect(() => reg.DispatchInbound('UNKNOWN', mediaFrame('x'))).not.toThrow();
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
it('fans out to multiple handlers (audio + dtmf + status all registered on one call)', () => {
|
|
74
|
+
const reg = new TwilioCallMediaRegistry();
|
|
75
|
+
const h1 = vi.fn();
|
|
76
|
+
const h2 = vi.fn();
|
|
77
|
+
reg.OnFrame('CA1', h1);
|
|
78
|
+
reg.OnFrame('CA1', h2);
|
|
79
|
+
reg.DispatchInbound('CA1', mediaFrame('y'));
|
|
80
|
+
expect(h1).toHaveBeenCalledTimes(1);
|
|
81
|
+
expect(h2).toHaveBeenCalledTimes(1);
|
|
82
|
+
});
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
describe('lifecycle', () => {
|
|
86
|
+
it('EndCall closes the socket and forgets the channel', () => {
|
|
87
|
+
const reg = new TwilioCallMediaRegistry();
|
|
88
|
+
const sock = fakeSocket();
|
|
89
|
+
reg.AttachSocket('CA1', sock, 'MZ1');
|
|
90
|
+
expect(reg.HasCall('CA1')).toBe(true);
|
|
91
|
+
|
|
92
|
+
reg.EndCall('CA1');
|
|
93
|
+
|
|
94
|
+
expect(sock.closed).toBe(true);
|
|
95
|
+
expect(reg.HasCall('CA1')).toBe(false);
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
it('EndCall on an unknown call is a no-op', () => {
|
|
99
|
+
const reg = new TwilioCallMediaRegistry();
|
|
100
|
+
expect(() => reg.EndCall('NOPE')).not.toThrow();
|
|
101
|
+
});
|
|
102
|
+
});
|
|
103
|
+
});
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
import { describe, it, expect, vi } from 'vitest';
|
|
2
|
+
import { VonageCallMediaRegistry, type ITelephonyMediaSocket } from '../telephony/vonageMediaRegistry.js';
|
|
3
|
+
import type { VonageControlEvent } from '@memberjunction/ai-bridge-vonage';
|
|
4
|
+
|
|
5
|
+
/** A fake media socket capturing outbound BINARY audio + TEXT control frames + tracking close. */
|
|
6
|
+
function fakeSocket(): ITelephonyMediaSocket & { sent: Uint8Array[]; text: string[]; closed: boolean } {
|
|
7
|
+
const sent: Uint8Array[] = [];
|
|
8
|
+
const text: string[] = [];
|
|
9
|
+
return {
|
|
10
|
+
sent,
|
|
11
|
+
text,
|
|
12
|
+
closed: false,
|
|
13
|
+
sendBinary(data: Uint8Array) {
|
|
14
|
+
sent.push(data);
|
|
15
|
+
},
|
|
16
|
+
sendText(data: string) {
|
|
17
|
+
text.push(data);
|
|
18
|
+
},
|
|
19
|
+
close() {
|
|
20
|
+
(this as { closed: boolean }).closed = true;
|
|
21
|
+
},
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/** A small PCM16 ArrayBuffer carrying one recognizable little-endian sample, for ordering/round-trip checks. */
|
|
26
|
+
const pcm = (sample: number): ArrayBuffer => {
|
|
27
|
+
const buf = new ArrayBuffer(2);
|
|
28
|
+
new DataView(buf).setInt16(0, sample, true);
|
|
29
|
+
return buf;
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
/** Vonage's outbound frame size: 20 ms @ 8 kHz / 16-bit = 320 bytes. */
|
|
33
|
+
const FRAME_BYTES = 320;
|
|
34
|
+
|
|
35
|
+
/** Builds an N-byte PCM ArrayBuffer whose first byte is `tag`, so frames are distinguishable. */
|
|
36
|
+
const audio = (bytes: number, tag = 0): ArrayBuffer => {
|
|
37
|
+
const buf = new Uint8Array(bytes);
|
|
38
|
+
if (bytes > 0) buf[0] = tag;
|
|
39
|
+
return buf.buffer;
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
describe('VonageCallMediaRegistry', () => {
|
|
43
|
+
describe('outbound audio framing (20 ms / 320-byte slices)', () => {
|
|
44
|
+
it('buffers whole 320-byte frames before the socket connects, then flushes them on AttachSocket in order', () => {
|
|
45
|
+
const reg = new VonageCallMediaRegistry();
|
|
46
|
+
reg.RegisterCall('CALL-1');
|
|
47
|
+
reg.SendAudio('CALL-1', audio(FRAME_BYTES, 0xa1));
|
|
48
|
+
reg.SendAudio('CALL-1', audio(FRAME_BYTES, 0xb2));
|
|
49
|
+
|
|
50
|
+
const sock = fakeSocket();
|
|
51
|
+
reg.AttachSocket('CALL-1', sock);
|
|
52
|
+
|
|
53
|
+
expect(sock.sent).toHaveLength(2);
|
|
54
|
+
expect(sock.sent[0]).toHaveLength(FRAME_BYTES);
|
|
55
|
+
expect(sock.sent[0][0]).toBe(0xa1);
|
|
56
|
+
expect(sock.sent[1][0]).toBe(0xb2);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it('slices an oversized delta into multiple 320-byte frames and carries the sub-frame remainder', () => {
|
|
60
|
+
const reg = new VonageCallMediaRegistry();
|
|
61
|
+
const sock = fakeSocket();
|
|
62
|
+
reg.AttachSocket('CALL-1', sock);
|
|
63
|
+
|
|
64
|
+
// 800 bytes = two whole frames + 160 left over (held, not sent).
|
|
65
|
+
reg.SendAudio('CALL-1', audio(800));
|
|
66
|
+
expect(sock.sent).toHaveLength(2);
|
|
67
|
+
expect(sock.sent.every((f) => f.length === FRAME_BYTES)).toBe(true);
|
|
68
|
+
|
|
69
|
+
// Another 160 bytes completes the carried remainder into a third whole frame.
|
|
70
|
+
reg.SendAudio('CALL-1', audio(160));
|
|
71
|
+
expect(sock.sent).toHaveLength(3);
|
|
72
|
+
expect(sock.sent[2]).toHaveLength(FRAME_BYTES);
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
it('holds a sub-frame delta until enough bytes accumulate (no silence-padded partial frame)', () => {
|
|
76
|
+
const reg = new VonageCallMediaRegistry();
|
|
77
|
+
const sock = fakeSocket();
|
|
78
|
+
reg.AttachSocket('CALL-1', sock);
|
|
79
|
+
reg.SendAudio('CALL-1', audio(100));
|
|
80
|
+
expect(sock.sent).toHaveLength(0); // < one frame — nothing sent yet
|
|
81
|
+
});
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
describe('inbound audio dispatch', () => {
|
|
85
|
+
it('delivers inbound audio to handlers registered before the socket existed', () => {
|
|
86
|
+
const reg = new VonageCallMediaRegistry();
|
|
87
|
+
const received: number[] = [];
|
|
88
|
+
reg.OnAudio('CALL-1', (p) => received.push(new DataView(p).getInt16(0, true)));
|
|
89
|
+
|
|
90
|
+
reg.DispatchInboundAudio('CALL-1', pcm(42));
|
|
91
|
+
|
|
92
|
+
expect(received).toEqual([42]);
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
it('drops inbound audio for an unknown call (no channel) without throwing', () => {
|
|
96
|
+
const reg = new VonageCallMediaRegistry();
|
|
97
|
+
expect(() => reg.DispatchInboundAudio('UNKNOWN', pcm(1))).not.toThrow();
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
it('fans out audio to multiple handlers registered on one call', () => {
|
|
101
|
+
const reg = new VonageCallMediaRegistry();
|
|
102
|
+
const h1 = vi.fn();
|
|
103
|
+
const h2 = vi.fn();
|
|
104
|
+
reg.OnAudio('CALL-1', h1);
|
|
105
|
+
reg.OnAudio('CALL-1', h2);
|
|
106
|
+
reg.DispatchInboundAudio('CALL-1', pcm(7));
|
|
107
|
+
expect(h1).toHaveBeenCalledTimes(1);
|
|
108
|
+
expect(h2).toHaveBeenCalledTimes(1);
|
|
109
|
+
});
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
describe('inbound control-event dispatch', () => {
|
|
113
|
+
it('delivers parsed control events (DTMF / close) to event handlers, separate from audio', () => {
|
|
114
|
+
const reg = new VonageCallMediaRegistry();
|
|
115
|
+
const events: VonageControlEvent[] = [];
|
|
116
|
+
const audio = vi.fn();
|
|
117
|
+
reg.OnEvent('CALL-1', (e) => events.push(e));
|
|
118
|
+
reg.OnAudio('CALL-1', audio);
|
|
119
|
+
|
|
120
|
+
reg.DispatchInboundEvent('CALL-1', { event: 'websocket:dtmf', digit: '5', duration: 260 });
|
|
121
|
+
reg.DispatchInboundEvent('CALL-1', { event: 'close' });
|
|
122
|
+
|
|
123
|
+
expect(audio).not.toHaveBeenCalled();
|
|
124
|
+
expect(events).toHaveLength(2);
|
|
125
|
+
expect(events[0].digit).toBe('5');
|
|
126
|
+
expect(events[1].event).toBe('close');
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
it('drops inbound events for an unknown call without throwing', () => {
|
|
130
|
+
const reg = new VonageCallMediaRegistry();
|
|
131
|
+
expect(() => reg.DispatchInboundEvent('UNKNOWN', { event: 'close' })).not.toThrow();
|
|
132
|
+
});
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
describe('barge-in flush (Clear)', () => {
|
|
136
|
+
it('sends Vonage {"action":"clear"} on the live socket and drops locally-buffered audio', () => {
|
|
137
|
+
const reg = new VonageCallMediaRegistry();
|
|
138
|
+
const sock = fakeSocket();
|
|
139
|
+
reg.AttachSocket('CALL-1', sock);
|
|
140
|
+
reg.Clear('CALL-1');
|
|
141
|
+
expect(sock.text).toEqual([JSON.stringify({ action: 'clear' })]);
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
it('drops outbound frames buffered before the socket connected, sending nothing on attach', () => {
|
|
145
|
+
const reg = new VonageCallMediaRegistry();
|
|
146
|
+
reg.RegisterCall('CALL-1');
|
|
147
|
+
reg.SendAudio('CALL-1', audio(FRAME_BYTES)); // a whole frame, buffered (no socket yet)
|
|
148
|
+
reg.SendAudio('CALL-1', audio(100)); // sub-frame remainder, held in partial
|
|
149
|
+
reg.Clear('CALL-1'); // barge-in before the media socket connected
|
|
150
|
+
|
|
151
|
+
const sock = fakeSocket();
|
|
152
|
+
reg.AttachSocket('CALL-1', sock);
|
|
153
|
+
expect(sock.sent).toHaveLength(0); // both the buffered frame and the partial were cleared
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
it('Clear on an unknown call is a no-op', () => {
|
|
157
|
+
const reg = new VonageCallMediaRegistry();
|
|
158
|
+
expect(() => reg.Clear('NOPE')).not.toThrow();
|
|
159
|
+
});
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
describe('lifecycle', () => {
|
|
163
|
+
it('EndCall closes the socket and forgets the channel', () => {
|
|
164
|
+
const reg = new VonageCallMediaRegistry();
|
|
165
|
+
const sock = fakeSocket();
|
|
166
|
+
reg.AttachSocket('CALL-1', sock);
|
|
167
|
+
expect(reg.HasCall('CALL-1')).toBe(true);
|
|
168
|
+
|
|
169
|
+
reg.EndCall('CALL-1');
|
|
170
|
+
|
|
171
|
+
expect(sock.closed).toBe(true);
|
|
172
|
+
expect(reg.HasCall('CALL-1')).toBe(false);
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
it('EndCall on an unknown call is a no-op', () => {
|
|
176
|
+
const reg = new VonageCallMediaRegistry();
|
|
177
|
+
expect(() => reg.EndCall('NOPE')).not.toThrow();
|
|
178
|
+
});
|
|
179
|
+
});
|
|
180
|
+
});
|