@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.
Files changed (309) hide show
  1. package/dist/agentSessions/ReturningVisitorRecap.d.ts +39 -0
  2. package/dist/agentSessions/ReturningVisitorRecap.d.ts.map +1 -0
  3. package/dist/agentSessions/ReturningVisitorRecap.js +198 -0
  4. package/dist/agentSessions/ReturningVisitorRecap.js.map +1 -0
  5. package/dist/agentSessions/SessionJanitor.d.ts +13 -0
  6. package/dist/agentSessions/SessionJanitor.d.ts.map +1 -1
  7. package/dist/agentSessions/SessionJanitor.js +65 -7
  8. package/dist/agentSessions/SessionJanitor.js.map +1 -1
  9. package/dist/agentSessions/SessionManager.d.ts.map +1 -1
  10. package/dist/agentSessions/SessionManager.js +40 -0
  11. package/dist/agentSessions/SessionManager.js.map +1 -1
  12. package/dist/agentSessions/index.d.ts.map +1 -1
  13. package/dist/agentSessions/index.js +3 -1
  14. package/dist/agentSessions/index.js.map +1 -1
  15. package/dist/auth/magicLink/MagicLinkRouter.d.ts +10 -0
  16. package/dist/auth/magicLink/MagicLinkRouter.d.ts.map +1 -1
  17. package/dist/auth/magicLink/MagicLinkRouter.js +16 -0
  18. package/dist/auth/magicLink/MagicLinkRouter.js.map +1 -1
  19. package/dist/auth/magicLink/index.d.ts +1 -1
  20. package/dist/auth/magicLink/index.d.ts.map +1 -1
  21. package/dist/auth/magicLink/index.js +1 -1
  22. package/dist/auth/magicLink/index.js.map +1 -1
  23. package/dist/auth/magicLink/types.d.ts +26 -0
  24. package/dist/auth/magicLink/types.d.ts.map +1 -1
  25. package/dist/config.d.ts +1883 -144
  26. package/dist/config.d.ts.map +1 -1
  27. package/dist/config.js +160 -36
  28. package/dist/config.js.map +1 -1
  29. package/dist/context.d.ts.map +1 -1
  30. package/dist/context.js +45 -2
  31. package/dist/context.js.map +1 -1
  32. package/dist/generated/generated.d.ts +1684 -54
  33. package/dist/generated/generated.d.ts.map +1 -1
  34. package/dist/generated/generated.js +57105 -48029
  35. package/dist/generated/generated.js.map +1 -1
  36. package/dist/generic/ResolverBase.d.ts +13 -0
  37. package/dist/generic/ResolverBase.d.ts.map +1 -1
  38. package/dist/generic/ResolverBase.js +22 -0
  39. package/dist/generic/ResolverBase.js.map +1 -1
  40. package/dist/generic/RunViewResolver.d.ts.map +1 -1
  41. package/dist/generic/RunViewResolver.js +4 -0
  42. package/dist/generic/RunViewResolver.js.map +1 -1
  43. package/dist/index.d.ts +2 -2
  44. package/dist/index.d.ts.map +1 -1
  45. package/dist/index.js +110 -3
  46. package/dist/index.js.map +1 -1
  47. package/dist/integration/CustomColumnPromoter.d.ts.map +1 -1
  48. package/dist/integration/CustomColumnPromoter.js +6 -0
  49. package/dist/integration/CustomColumnPromoter.js.map +1 -1
  50. package/dist/realtimeWidget/WidgetRouter.d.ts +26 -0
  51. package/dist/realtimeWidget/WidgetRouter.d.ts.map +1 -0
  52. package/dist/realtimeWidget/WidgetRouter.js +182 -0
  53. package/dist/realtimeWidget/WidgetRouter.js.map +1 -0
  54. package/dist/realtimeWidget/WidgetSessionService.d.ts +265 -0
  55. package/dist/realtimeWidget/WidgetSessionService.d.ts.map +1 -0
  56. package/dist/realtimeWidget/WidgetSessionService.js +477 -0
  57. package/dist/realtimeWidget/WidgetSessionService.js.map +1 -0
  58. package/dist/realtimeWidget/host-identity.d.ts +40 -0
  59. package/dist/realtimeWidget/host-identity.d.ts.map +1 -0
  60. package/dist/realtimeWidget/host-identity.js +58 -0
  61. package/dist/realtimeWidget/host-identity.js.map +1 -0
  62. package/dist/realtimeWidget/index.d.ts +10 -0
  63. package/dist/realtimeWidget/index.d.ts.map +1 -0
  64. package/dist/realtimeWidget/index.js +9 -0
  65. package/dist/realtimeWidget/index.js.map +1 -0
  66. package/dist/realtimeWidget/visitorIdentity.d.ts +76 -0
  67. package/dist/realtimeWidget/visitorIdentity.d.ts.map +1 -0
  68. package/dist/realtimeWidget/visitorIdentity.js +202 -0
  69. package/dist/realtimeWidget/visitorIdentity.js.map +1 -0
  70. package/dist/realtimeWidget/widgetCore.d.ts +106 -0
  71. package/dist/realtimeWidget/widgetCore.d.ts.map +1 -0
  72. package/dist/realtimeWidget/widgetCore.js +173 -0
  73. package/dist/realtimeWidget/widgetCore.js.map +1 -0
  74. package/dist/realtimeWidget/widgetGuestElevation.d.ts +51 -0
  75. package/dist/realtimeWidget/widgetGuestElevation.d.ts.map +1 -0
  76. package/dist/realtimeWidget/widgetGuestElevation.js +79 -0
  77. package/dist/realtimeWidget/widgetGuestElevation.js.map +1 -0
  78. package/dist/resolvers/ComponentRegistryResolver.d.ts +7 -0
  79. package/dist/resolvers/ComponentRegistryResolver.d.ts.map +1 -1
  80. package/dist/resolvers/ComponentRegistryResolver.js +31 -8
  81. package/dist/resolvers/ComponentRegistryResolver.js.map +1 -1
  82. package/dist/resolvers/EntityPermissionResolver.d.ts.map +1 -1
  83. package/dist/resolvers/EntityPermissionResolver.js +1 -2
  84. package/dist/resolvers/EntityPermissionResolver.js.map +1 -1
  85. package/dist/resolvers/FileResolver.d.ts +74 -0
  86. package/dist/resolvers/FileResolver.d.ts.map +1 -1
  87. package/dist/resolvers/FileResolver.js +211 -2
  88. package/dist/resolvers/FileResolver.js.map +1 -1
  89. package/dist/resolvers/QueryResolver.d.ts +6 -4
  90. package/dist/resolvers/QueryResolver.d.ts.map +1 -1
  91. package/dist/resolvers/QueryResolver.js +29 -14
  92. package/dist/resolvers/QueryResolver.js.map +1 -1
  93. package/dist/resolvers/QuerySystemUserResolver.d.ts +9 -6
  94. package/dist/resolvers/QuerySystemUserResolver.d.ts.map +1 -1
  95. package/dist/resolvers/QuerySystemUserResolver.js +15 -13
  96. package/dist/resolvers/QuerySystemUserResolver.js.map +1 -1
  97. package/dist/resolvers/RealtimeBridgeResolver.d.ts +8 -1
  98. package/dist/resolvers/RealtimeBridgeResolver.d.ts.map +1 -1
  99. package/dist/resolvers/RealtimeBridgeResolver.js +28 -4
  100. package/dist/resolvers/RealtimeBridgeResolver.js.map +1 -1
  101. package/dist/resolvers/RealtimeClientSessionResolver.d.ts +91 -2
  102. package/dist/resolvers/RealtimeClientSessionResolver.d.ts.map +1 -1
  103. package/dist/resolvers/RealtimeClientSessionResolver.js +337 -17
  104. package/dist/resolvers/RealtimeClientSessionResolver.js.map +1 -1
  105. package/dist/resolvers/RemoteBrowserActionResolver.d.ts.map +1 -1
  106. package/dist/resolvers/RemoteBrowserActionResolver.js +17 -2
  107. package/dist/resolvers/RemoteBrowserActionResolver.js.map +1 -1
  108. package/dist/resolvers/RingCentralTelephonyResolver.d.ts +27 -0
  109. package/dist/resolvers/RingCentralTelephonyResolver.d.ts.map +1 -0
  110. package/dist/resolvers/RingCentralTelephonyResolver.js +87 -0
  111. package/dist/resolvers/RingCentralTelephonyResolver.js.map +1 -0
  112. package/dist/resolvers/RunAIAgentResolver.d.ts +12 -3
  113. package/dist/resolvers/RunAIAgentResolver.d.ts.map +1 -1
  114. package/dist/resolvers/RunAIAgentResolver.js +50 -17
  115. package/dist/resolvers/RunAIAgentResolver.js.map +1 -1
  116. package/dist/resolvers/SearchKnowledgeResolver.d.ts.map +1 -1
  117. package/dist/resolvers/SearchKnowledgeResolver.js +2 -0
  118. package/dist/resolvers/SearchKnowledgeResolver.js.map +1 -1
  119. package/dist/resolvers/TeamsMeetingsResolver.d.ts +28 -0
  120. package/dist/resolvers/TeamsMeetingsResolver.d.ts.map +1 -0
  121. package/dist/resolvers/TeamsMeetingsResolver.js +91 -0
  122. package/dist/resolvers/TeamsMeetingsResolver.js.map +1 -0
  123. package/dist/resolvers/TelephonyResolver.d.ts +26 -0
  124. package/dist/resolvers/TelephonyResolver.d.ts.map +1 -0
  125. package/dist/resolvers/TelephonyResolver.js +86 -0
  126. package/dist/resolvers/TelephonyResolver.js.map +1 -0
  127. package/dist/resolvers/TestQuerySQLResolver.d.ts +1 -1
  128. package/dist/resolvers/TestQuerySQLResolver.d.ts.map +1 -1
  129. package/dist/resolvers/TestQuerySQLResolver.js +2 -3
  130. package/dist/resolvers/TestQuerySQLResolver.js.map +1 -1
  131. package/dist/resolvers/VonageTelephonyResolver.d.ts +26 -0
  132. package/dist/resolvers/VonageTelephonyResolver.d.ts.map +1 -0
  133. package/dist/resolvers/VonageTelephonyResolver.js +86 -0
  134. package/dist/resolvers/VonageTelephonyResolver.js.map +1 -0
  135. package/dist/resolvers/meetingRecordingRegistration.d.ts +124 -0
  136. package/dist/resolvers/meetingRecordingRegistration.d.ts.map +1 -0
  137. package/dist/resolvers/meetingRecordingRegistration.js +311 -0
  138. package/dist/resolvers/meetingRecordingRegistration.js.map +1 -0
  139. package/dist/resolvers/peaksSidecar.d.ts +30 -0
  140. package/dist/resolvers/peaksSidecar.d.ts.map +1 -0
  141. package/dist/resolvers/peaksSidecar.js +51 -0
  142. package/dist/resolvers/peaksSidecar.js.map +1 -0
  143. package/dist/rest/MediaAccessKeys.d.ts +68 -0
  144. package/dist/rest/MediaAccessKeys.d.ts.map +1 -0
  145. package/dist/rest/MediaAccessKeys.js +96 -0
  146. package/dist/rest/MediaAccessKeys.js.map +1 -0
  147. package/dist/rest/MediaStreamHandler.d.ts +26 -0
  148. package/dist/rest/MediaStreamHandler.d.ts.map +1 -0
  149. package/dist/rest/MediaStreamHandler.js +195 -0
  150. package/dist/rest/MediaStreamHandler.js.map +1 -0
  151. package/dist/rest/mediaRange.d.ts +41 -0
  152. package/dist/rest/mediaRange.d.ts.map +1 -0
  153. package/dist/rest/mediaRange.js +60 -0
  154. package/dist/rest/mediaRange.js.map +1 -0
  155. package/dist/telephony/RingCentralTelephonyService.d.ts +115 -0
  156. package/dist/telephony/RingCentralTelephonyService.d.ts.map +1 -0
  157. package/dist/telephony/RingCentralTelephonyService.js +262 -0
  158. package/dist/telephony/RingCentralTelephonyService.js.map +1 -0
  159. package/dist/telephony/TeamsMeetingsRouter.d.ts +40 -0
  160. package/dist/telephony/TeamsMeetingsRouter.d.ts.map +1 -0
  161. package/dist/telephony/TeamsMeetingsRouter.js +96 -0
  162. package/dist/telephony/TeamsMeetingsRouter.js.map +1 -0
  163. package/dist/telephony/TeamsMeetingsService.d.ts +113 -0
  164. package/dist/telephony/TeamsMeetingsService.d.ts.map +1 -0
  165. package/dist/telephony/TeamsMeetingsService.js +196 -0
  166. package/dist/telephony/TeamsMeetingsService.js.map +1 -0
  167. package/dist/telephony/TwilioTelephonyRouter.d.ts +36 -0
  168. package/dist/telephony/TwilioTelephonyRouter.d.ts.map +1 -0
  169. package/dist/telephony/TwilioTelephonyRouter.js +143 -0
  170. package/dist/telephony/TwilioTelephonyRouter.js.map +1 -0
  171. package/dist/telephony/TwilioTelephonyService.d.ts +95 -0
  172. package/dist/telephony/TwilioTelephonyService.d.ts.map +1 -0
  173. package/dist/telephony/TwilioTelephonyService.js +193 -0
  174. package/dist/telephony/TwilioTelephonyService.js.map +1 -0
  175. package/dist/telephony/VonageTelephonyRouter.d.ts +41 -0
  176. package/dist/telephony/VonageTelephonyRouter.d.ts.map +1 -0
  177. package/dist/telephony/VonageTelephonyRouter.js +201 -0
  178. package/dist/telephony/VonageTelephonyRouter.js.map +1 -0
  179. package/dist/telephony/VonageTelephonyService.d.ts +90 -0
  180. package/dist/telephony/VonageTelephonyService.d.ts.map +1 -0
  181. package/dist/telephony/VonageTelephonyService.js +191 -0
  182. package/dist/telephony/VonageTelephonyService.js.map +1 -0
  183. package/dist/telephony/calendar-scheduler.d.ts +67 -0
  184. package/dist/telephony/calendar-scheduler.d.ts.map +1 -0
  185. package/dist/telephony/calendar-scheduler.js +133 -0
  186. package/dist/telephony/calendar-scheduler.js.map +1 -0
  187. package/dist/telephony/index.d.ts +29 -0
  188. package/dist/telephony/index.d.ts.map +1 -0
  189. package/dist/telephony/index.js +38 -0
  190. package/dist/telephony/index.js.map +1 -0
  191. package/dist/telephony/media-upgrade-router.d.ts +33 -0
  192. package/dist/telephony/media-upgrade-router.d.ts.map +1 -0
  193. package/dist/telephony/media-upgrade-router.js +56 -0
  194. package/dist/telephony/media-upgrade-router.js.map +1 -0
  195. package/dist/telephony/ringcentral-runtime.d.ts +14 -0
  196. package/dist/telephony/ringcentral-runtime.d.ts.map +1 -0
  197. package/dist/telephony/ringcentral-runtime.js +18 -0
  198. package/dist/telephony/ringcentral-runtime.js.map +1 -0
  199. package/dist/telephony/teams-meetings-runtime.d.ts +16 -0
  200. package/dist/telephony/teams-meetings-runtime.d.ts.map +1 -0
  201. package/dist/telephony/teams-meetings-runtime.js +20 -0
  202. package/dist/telephony/teams-meetings-runtime.js.map +1 -0
  203. package/dist/telephony/teamsAcsMediaRegistry.d.ts +69 -0
  204. package/dist/telephony/teamsAcsMediaRegistry.d.ts.map +1 -0
  205. package/dist/telephony/teamsAcsMediaRegistry.js +118 -0
  206. package/dist/telephony/teamsAcsMediaRegistry.js.map +1 -0
  207. package/dist/telephony/telephony-runtime.d.ts +14 -0
  208. package/dist/telephony/telephony-runtime.d.ts.map +1 -0
  209. package/dist/telephony/telephony-runtime.js +18 -0
  210. package/dist/telephony/telephony-runtime.js.map +1 -0
  211. package/dist/telephony/twilioMediaRegistry.d.ts +60 -0
  212. package/dist/telephony/twilioMediaRegistry.d.ts.map +1 -0
  213. package/dist/telephony/twilioMediaRegistry.js +106 -0
  214. package/dist/telephony/twilioMediaRegistry.js.map +1 -0
  215. package/dist/telephony/vonage-runtime.d.ts +14 -0
  216. package/dist/telephony/vonage-runtime.d.ts.map +1 -0
  217. package/dist/telephony/vonage-runtime.js +18 -0
  218. package/dist/telephony/vonage-runtime.js.map +1 -0
  219. package/dist/telephony/vonageMediaRegistry.d.ts +78 -0
  220. package/dist/telephony/vonageMediaRegistry.d.ts.map +1 -0
  221. package/dist/telephony/vonageMediaRegistry.js +158 -0
  222. package/dist/telephony/vonageMediaRegistry.js.map +1 -0
  223. package/package.json +88 -82
  224. package/src/__tests__/FileResolverPeaks.test.ts +64 -0
  225. package/src/__tests__/MediaAccessKeys.test.ts +68 -0
  226. package/src/__tests__/MediaStreamHandler.test.ts +91 -0
  227. package/src/__tests__/RealtimeBridgeResolver.test.ts +10 -1
  228. package/src/__tests__/RealtimeClientSessionResolver.test.ts +69 -0
  229. package/src/__tests__/RealtimeCoAgentAppAwareness.integration.test.ts +191 -0
  230. package/src/__tests__/RemoteBrowserSnapshot.test.ts +123 -0
  231. package/src/__tests__/SessionManager.test.ts +2 -0
  232. package/src/__tests__/meetingRecordingRegistration.test.ts +248 -0
  233. package/src/__tests__/search-knowledge-tags.test.ts +9 -9
  234. package/src/__tests__/teams-meetings-service.test.ts +65 -0
  235. package/src/__tests__/teamsAcsMediaRegistry.test.ts +82 -0
  236. package/src/__tests__/twilio-telephony-service.test.ts +23 -0
  237. package/src/__tests__/twilioMediaRegistry.test.ts +103 -0
  238. package/src/__tests__/vonageMediaRegistry.test.ts +180 -0
  239. package/src/__tests__/widget.test.ts +204 -0
  240. package/src/__tests__/widgetGuestElevation.test.ts +57 -0
  241. package/src/__tests__/widgetHostIdentity.test.ts +117 -0
  242. package/src/agentSessions/ReturningVisitorRecap.ts +242 -0
  243. package/src/agentSessions/SessionJanitor.ts +66 -6
  244. package/src/agentSessions/SessionManager.ts +41 -0
  245. package/src/agentSessions/index.ts +3 -1
  246. package/src/auth/magicLink/MagicLinkRouter.ts +17 -0
  247. package/src/auth/magicLink/index.ts +1 -0
  248. package/src/auth/magicLink/types.ts +26 -0
  249. package/src/config.ts +172 -38
  250. package/src/context.ts +50 -3
  251. package/src/generated/generated.ts +26726 -20464
  252. package/src/generic/ResolverBase.ts +28 -0
  253. package/src/generic/RunViewResolver.ts +4 -0
  254. package/src/index.ts +119 -3
  255. package/src/integration/CustomColumnPromoter.ts +6 -0
  256. package/src/realtimeWidget/WidgetRouter.ts +204 -0
  257. package/src/realtimeWidget/WidgetSessionService.ts +714 -0
  258. package/src/realtimeWidget/host-identity.ts +84 -0
  259. package/src/realtimeWidget/index.ts +15 -0
  260. package/src/realtimeWidget/visitorIdentity.ts +258 -0
  261. package/src/realtimeWidget/widgetCore.ts +231 -0
  262. package/src/realtimeWidget/widgetGuestElevation.ts +115 -0
  263. package/src/resolvers/ComponentRegistryResolver.ts +36 -10
  264. package/src/resolvers/EntityPermissionResolver.ts +1 -2
  265. package/src/resolvers/FileResolver.ts +199 -1
  266. package/src/resolvers/QueryResolver.ts +33 -19
  267. package/src/resolvers/QuerySystemUserResolver.ts +14 -10
  268. package/src/resolvers/RealtimeBridgeResolver.ts +36 -4
  269. package/src/resolvers/RealtimeClientSessionResolver.ts +383 -9
  270. package/src/resolvers/RemoteBrowserActionResolver.ts +18 -2
  271. package/src/resolvers/RingCentralTelephonyResolver.ts +64 -0
  272. package/src/resolvers/RunAIAgentResolver.ts +60 -15
  273. package/src/resolvers/SearchKnowledgeResolver.ts +2 -0
  274. package/src/resolvers/TeamsMeetingsResolver.ts +68 -0
  275. package/src/resolvers/TelephonyResolver.ts +63 -0
  276. package/src/resolvers/TestQuerySQLResolver.ts +2 -3
  277. package/src/resolvers/VonageTelephonyResolver.ts +63 -0
  278. package/src/resolvers/meetingRecordingRegistration.ts +432 -0
  279. package/src/resolvers/peaksSidecar.ts +52 -0
  280. package/src/rest/MediaAccessKeys.ts +121 -0
  281. package/src/rest/MediaStreamHandler.ts +223 -0
  282. package/src/rest/mediaRange.ts +76 -0
  283. package/src/telephony/RingCentralTelephonyService.ts +342 -0
  284. package/src/telephony/TeamsMeetingsRouter.ts +124 -0
  285. package/src/telephony/TeamsMeetingsService.ts +268 -0
  286. package/src/telephony/TwilioTelephonyRouter.ts +184 -0
  287. package/src/telephony/TwilioTelephonyService.ts +261 -0
  288. package/src/telephony/VonageTelephonyRouter.ts +239 -0
  289. package/src/telephony/VonageTelephonyService.ts +259 -0
  290. package/src/telephony/calendar-scheduler.ts +176 -0
  291. package/src/telephony/index.ts +43 -0
  292. package/src/telephony/media-upgrade-router.ts +62 -0
  293. package/src/telephony/ringcentral-runtime.ts +22 -0
  294. package/src/telephony/teams-meetings-runtime.ts +24 -0
  295. package/src/telephony/teamsAcsMediaRegistry.ts +152 -0
  296. package/src/telephony/telephony-runtime.ts +22 -0
  297. package/src/telephony/twilioMediaRegistry.ts +137 -0
  298. package/src/telephony/vonage-runtime.ts +22 -0
  299. package/src/telephony/vonageMediaRegistry.ts +200 -0
  300. package/dist/agents/skip-agent.d.ts +0 -111
  301. package/dist/agents/skip-agent.d.ts.map +0 -1
  302. package/dist/agents/skip-agent.js +0 -1487
  303. package/dist/agents/skip-agent.js.map +0 -1
  304. package/dist/agents/skip-sdk.d.ts +0 -261
  305. package/dist/agents/skip-sdk.d.ts.map +0 -1
  306. package/dist/agents/skip-sdk.js +0 -909
  307. package/dist/agents/skip-sdk.js.map +0 -1
  308. package/src/agents/skip-agent.ts +0 -1594
  309. package/src/agents/skip-sdk.ts +0 -1210
@@ -0,0 +1,158 @@
1
+ /**
2
+ * @fileoverview Per-call WebSocket-media pump registry for the Vonage telephony ingress.
3
+ *
4
+ * This is the server half of the Vonage media plane: it implements the provider package's
5
+ * `IVonageMediaPump` seam (which `RealVonageBindings` drives) on top of the live bidirectional media
6
+ * websockets MJAPI accepts at `WSS /telephony/vonage/media`. It exists because of the same lifecycle
7
+ * gap the Twilio ingress has: when an INBOUND call's answer webhook fires, the bridge session starts and
8
+ * `RealVonageBindings` registers its inbound-audio handler — but Vonage has not yet opened the media
9
+ * websocket (it does so only AFTER the answer-webhook's `connect`-websocket NCCO response). The registry
10
+ * bridges that gap by buffering handler registrations + outbound frames per call UUID and binding them to
11
+ * the socket once it connects (matched on the call UUID Vonage forwards on the websocket handshake).
12
+ *
13
+ * Pure + unit-testable: the websocket is injected as the minimal {@link ITelephonyMediaSocket} surface,
14
+ * so the registry's buffering / dispatch logic is exercised with a fake socket and no `ws` install or
15
+ * network.
16
+ *
17
+ * @module @memberjunction/server/telephony
18
+ */
19
+ /**
20
+ * The exact byte size of one outbound audio frame Vonage expects on the `audio/l16;rate=8000` leg: a
21
+ * 20 ms slice = 8000 Hz × 0.020 s = 160 samples × 2 bytes = **320 bytes**. The realtime model emits audio
22
+ * in arbitrary-size deltas; the bridge resamples each whole delta to 8 kHz, so the registry MUST re-slice
23
+ * into these fixed frames before sending. Handing Vonage one oversized blob per delta garbles playback and
24
+ * (worse) defeats barge-in: `{"action":"clear"}` can drop QUEUED frames but not a giant in-flight one.
25
+ */
26
+ const OUTBOUND_FRAME_BYTES = 320;
27
+ /**
28
+ * Coordinates Vonage media websockets with the bridge bindings, keyed by call UUID. One instance per
29
+ * server, shared between the inbound-webhook service (which binds `RealVonageBindings` over this as the
30
+ * `IVonageMediaPump`) and the WSS server (which attaches sockets + dispatches inbound audio/events).
31
+ *
32
+ * Models Vonage's wire split: outbound audio → binary frames; inbound binary frames → audio handlers;
33
+ * inbound JSON text frames → parsed {@link VonageControlEvent}s → event handlers. There is no base64
34
+ * envelope or μ-law transcode — that's the Twilio path, not Vonage's.
35
+ */
36
+ export class VonageCallMediaRegistry {
37
+ constructor() {
38
+ this.channels = new Map();
39
+ }
40
+ /** Ensures a channel exists for a call (called when a bridge session for the call starts). */
41
+ RegisterCall(callUuid) {
42
+ this.ensureChannel(callUuid);
43
+ }
44
+ // ── IVonageMediaPump ─────────────────────────────────────────────────────────
45
+ /**
46
+ * @inheritdoc — re-slices the (already-8 kHz) delta into fixed 320-byte / 20 ms frames before sending,
47
+ * carrying any sub-frame remainder into the next delta so the stream stays continuous (no silence gaps).
48
+ * Each whole frame is sent immediately if the socket is live, else buffered for {@link AttachSocket}.
49
+ */
50
+ SendAudio(callUuid, pcm) {
51
+ const channel = this.ensureChannel(callUuid);
52
+ const incoming = new Uint8Array(pcm);
53
+ const data = channel.partial.length > 0 ? concatBytes(channel.partial, incoming) : incoming;
54
+ let offset = 0;
55
+ while (data.length - offset >= OUTBOUND_FRAME_BYTES) {
56
+ // `.slice` returns a fresh copy, so a buffered frame never retains the larger backing buffer.
57
+ const frame = data.slice(offset, offset + OUTBOUND_FRAME_BYTES);
58
+ offset += OUTBOUND_FRAME_BYTES;
59
+ if (channel.socket) {
60
+ channel.socket.sendBinary(frame);
61
+ }
62
+ else {
63
+ channel.outboundBuffer.push(frame);
64
+ }
65
+ }
66
+ channel.partial = offset < data.length ? data.slice(offset) : new Uint8Array(0);
67
+ }
68
+ /** @inheritdoc — stores the audio handler so frames delivered before AND after socket connect reach it. */
69
+ OnAudio(callUuid, handler) {
70
+ this.ensureChannel(callUuid).audioHandlers.push(handler);
71
+ }
72
+ /** @inheritdoc — stores the control-event handler (DTMF / connected / close). */
73
+ OnEvent(callUuid, handler) {
74
+ this.ensureChannel(callUuid).eventHandlers.push(handler);
75
+ }
76
+ /**
77
+ * @inheritdoc — barge-in flush: drop any outbound audio buffered locally (socket not yet connected) AND,
78
+ * if the socket is live, send Vonage's `{"action":"clear"}` text command to discard its playback queue.
79
+ * Safe to call for an unknown/uninitialized call (no-op).
80
+ */
81
+ Clear(callUuid) {
82
+ const channel = this.channels.get(callUuid);
83
+ if (!channel) {
84
+ return;
85
+ }
86
+ channel.outboundBuffer.length = 0;
87
+ channel.partial = new Uint8Array(0); // drop the half-built frame too, or it would prepend to the next turn
88
+ channel.socket?.sendText(JSON.stringify({ action: 'clear' }));
89
+ }
90
+ // ── WSS-server side ─────────────────────────────────────────────────────────
91
+ /**
92
+ * Binds a connected media socket to a call, flushing any outbound audio produced while the socket
93
+ * was still connecting. Called by the WSS server once it resolves the call UUID for the socket (from
94
+ * the `call_uuid` query param the answer NCCO embedded on the media URL).
95
+ */
96
+ AttachSocket(callUuid, socket) {
97
+ const channel = this.ensureChannel(callUuid);
98
+ channel.socket = socket;
99
+ const buffered = channel.outboundBuffer.splice(0, channel.outboundBuffer.length);
100
+ for (const frame of buffered) {
101
+ socket.sendBinary(frame);
102
+ }
103
+ }
104
+ /** Dispatches one inbound binary audio frame (raw L16 PCM) to all audio handlers for the call. */
105
+ DispatchInboundAudio(callUuid, pcm) {
106
+ const channel = this.channels.get(callUuid);
107
+ if (!channel) {
108
+ return;
109
+ }
110
+ for (const handler of channel.audioHandlers) {
111
+ handler(pcm);
112
+ }
113
+ }
114
+ /** Dispatches one inbound control event (parsed from a JSON text frame) to all event handlers for the call. */
115
+ DispatchInboundEvent(callUuid, event) {
116
+ const channel = this.channels.get(callUuid);
117
+ if (!channel) {
118
+ return;
119
+ }
120
+ for (const handler of channel.eventHandlers) {
121
+ handler(event);
122
+ }
123
+ }
124
+ /** Tears down a call's channel and closes its socket (called on socket close / call end). */
125
+ EndCall(callUuid) {
126
+ const channel = this.channels.get(callUuid);
127
+ if (!channel) {
128
+ return;
129
+ }
130
+ try {
131
+ channel.socket?.close();
132
+ }
133
+ catch {
134
+ /* best-effort */
135
+ }
136
+ this.channels.delete(callUuid);
137
+ }
138
+ /** Whether a channel is currently tracked for the call (test/observability helper). */
139
+ HasCall(callUuid) {
140
+ return this.channels.has(callUuid);
141
+ }
142
+ ensureChannel(callUuid) {
143
+ let channel = this.channels.get(callUuid);
144
+ if (!channel) {
145
+ channel = { audioHandlers: [], eventHandlers: [], outboundBuffer: [], partial: new Uint8Array(0) };
146
+ this.channels.set(callUuid, channel);
147
+ }
148
+ return channel;
149
+ }
150
+ }
151
+ /** Concatenates two byte arrays into a fresh `Uint8Array` (the carry-over remainder + the next delta). */
152
+ function concatBytes(a, b) {
153
+ const out = new Uint8Array(a.length + b.length);
154
+ out.set(a, 0);
155
+ out.set(b, a.length);
156
+ return out;
157
+ }
158
+ //# sourceMappingURL=vonageMediaRegistry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vonageMediaRegistry.js","sourceRoot":"","sources":["../../src/telephony/vonageMediaRegistry.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@memberjunction/server",
3
- "version": "5.43.0",
3
+ "version": "5.45.0",
4
4
  "description": "MemberJunction: This project provides API access via GraphQL to the common data store.",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./src/index.ts",
@@ -27,87 +27,93 @@
27
27
  "@as-integrations/express5": "^1.0.0",
28
28
  "@graphql-tools/schema": "latest",
29
29
  "@graphql-tools/utils": "^11.0.0",
30
- "@memberjunction/actions": "5.43.0",
31
- "@memberjunction/actions-apollo": "5.43.0",
32
- "@memberjunction/actions-base": "5.43.0",
33
- "@memberjunction/actions-bizapps-accounting": "5.43.0",
34
- "@memberjunction/actions-bizapps-crm": "5.43.0",
35
- "@memberjunction/actions-bizapps-formbuilders": "5.43.0",
36
- "@memberjunction/actions-bizapps-lms": "5.43.0",
37
- "@memberjunction/actions-bizapps-social": "5.43.0",
38
- "@memberjunction/ai": "5.43.0",
39
- "@memberjunction/ai-agent-manager": "5.43.0",
40
- "@memberjunction/ai-agent-manager-actions": "5.43.0",
41
- "@memberjunction/ai-agents": "5.43.0",
42
- "@memberjunction/ai-bridge-server": "5.43.0",
43
- "@memberjunction/ai-core-plus": "5.43.0",
44
- "@memberjunction/ai-engine-base": "5.43.0",
45
- "@memberjunction/ai-mcp-client": "5.43.0",
46
- "@memberjunction/ai-prompts": "5.43.0",
47
- "@memberjunction/ai-provider-bundle": "5.43.0",
48
- "@memberjunction/ai-vector-sync": "5.43.0",
49
- "@memberjunction/ai-vectordb": "5.43.0",
50
- "@memberjunction/ai-vectors-pinecone": "5.43.0",
51
- "@memberjunction/aiengine": "5.43.0",
52
- "@memberjunction/api-keys": "5.43.0",
53
- "@memberjunction/auth-providers": "5.43.0",
54
- "@memberjunction/clustering-engine": "5.43.0",
55
- "@memberjunction/codegen-lib": "5.43.0",
56
- "@memberjunction/communication-engine": "5.43.0",
57
- "@memberjunction/communication-ms-graph": "5.43.0",
58
- "@memberjunction/notifications": "5.43.0",
59
- "@memberjunction/communication-sendgrid": "5.43.0",
60
- "@memberjunction/communication-types": "5.43.0",
61
- "@memberjunction/component-registry-client-sdk": "5.43.0",
62
- "@memberjunction/computer-use": "5.43.0",
63
- "@memberjunction/computer-use-engine": "5.43.0",
64
- "@memberjunction/config": "5.43.0",
65
- "@memberjunction/core": "5.43.0",
66
- "@memberjunction/core-actions": "5.43.0",
67
- "@memberjunction/core-entities": "5.43.0",
68
- "@memberjunction/core-entities-server": "5.43.0",
69
- "@memberjunction/data-context": "5.43.0",
70
- "@memberjunction/data-context-server": "5.43.0",
71
- "@memberjunction/doc-utils": "5.43.0",
72
- "@memberjunction/encryption": "5.43.0",
73
- "@memberjunction/entity-communications-base": "5.43.0",
74
- "@memberjunction/entity-communications-server": "5.43.0",
75
- "@memberjunction/esignature": "5.43.0",
76
- "@memberjunction/external-change-detection": "5.43.0",
77
- "@memberjunction/generic-database-provider": "5.43.0",
78
- "@memberjunction/global": "5.43.0",
79
- "@memberjunction/graphql-dataprovider": "5.43.0",
80
- "@memberjunction/integration-engine": "5.43.0",
81
- "@memberjunction/integration-progress-artifacts": "5.43.0",
82
- "@memberjunction/integration-schema-builder": "5.43.0",
83
- "@memberjunction/interactive-component-types": "5.43.0",
84
- "@memberjunction/lists": "5.43.0",
85
- "@memberjunction/lists-base": "5.43.0",
86
- "@memberjunction/livekit-room-server": "5.43.0",
87
- "@memberjunction/postgresql-dataprovider": "5.43.0",
88
- "@memberjunction/queue": "5.43.0",
89
- "@memberjunction/redis-provider": "5.43.0",
90
- "@memberjunction/remote-browser-base": "5.43.0",
91
- "@memberjunction/remote-browser-cdp": "5.43.0",
92
- "@memberjunction/remote-browser-selfhost": "5.43.0",
93
- "@memberjunction/remote-browser-server": "5.43.0",
94
- "@memberjunction/scheduling-actions": "5.43.0",
95
- "@memberjunction/scheduling-base-types": "5.43.0",
96
- "@memberjunction/scheduling-engine": "5.43.0",
97
- "@memberjunction/scheduling-engine-base": "5.43.0",
98
- "@memberjunction/schema-engine": "5.43.0",
99
- "@memberjunction/search-engine": "5.43.0",
100
- "@memberjunction/server-extensions-core": "5.43.0",
101
- "@memberjunction/skip-types": "5.43.0",
102
- "@memberjunction/sql-dialect": "5.43.0",
103
- "@memberjunction/sqlserver-dataprovider": "5.43.0",
104
- "@memberjunction/storage": "5.43.0",
105
- "@memberjunction/tag-engine": "5.43.0",
106
- "@memberjunction/tag-engine-base": "5.43.0",
107
- "@memberjunction/templates": "5.43.0",
108
- "@memberjunction/testing-engine": "5.43.0",
109
- "@memberjunction/testing-engine-base": "5.43.0",
110
- "@memberjunction/version-history": "5.43.0",
30
+ "@memberjunction/actions": "5.45.0",
31
+ "@memberjunction/actions-apollo": "5.45.0",
32
+ "@memberjunction/actions-base": "5.45.0",
33
+ "@memberjunction/actions-bizapps-accounting": "5.45.0",
34
+ "@memberjunction/actions-bizapps-crm": "5.45.0",
35
+ "@memberjunction/actions-bizapps-formbuilders": "5.45.0",
36
+ "@memberjunction/actions-bizapps-lms": "5.45.0",
37
+ "@memberjunction/actions-bizapps-social": "5.45.0",
38
+ "@memberjunction/ai": "5.45.0",
39
+ "@memberjunction/ai-agent-manager": "5.45.0",
40
+ "@memberjunction/ai-agent-manager-actions": "5.45.0",
41
+ "@memberjunction/ai-agents": "5.45.0",
42
+ "@memberjunction/ai-bridge-base": "5.45.0",
43
+ "@memberjunction/ai-bridge-ringcentral": "5.45.0",
44
+ "@memberjunction/ai-bridge-server": "5.45.0",
45
+ "@memberjunction/ai-bridge-teams": "5.45.0",
46
+ "@memberjunction/ai-bridge-twilio": "5.45.0",
47
+ "@memberjunction/ai-bridge-vonage": "5.45.0",
48
+ "@memberjunction/ai-core-plus": "5.45.0",
49
+ "@memberjunction/ai-engine-base": "5.45.0",
50
+ "@memberjunction/ai-mcp-client": "5.45.0",
51
+ "@memberjunction/ai-prompts": "5.45.0",
52
+ "@memberjunction/ai-provider-bundle": "5.45.0",
53
+ "@memberjunction/ai-vector-sync": "5.45.0",
54
+ "@memberjunction/ai-vectordb": "5.45.0",
55
+ "@memberjunction/ai-vectors-pinecone": "5.45.0",
56
+ "@memberjunction/aiengine": "5.45.0",
57
+ "@memberjunction/api-keys": "5.45.0",
58
+ "@memberjunction/auth-providers": "5.45.0",
59
+ "@memberjunction/clustering-engine": "5.45.0",
60
+ "@memberjunction/codegen-lib": "5.45.0",
61
+ "@memberjunction/communication-engine": "5.45.0",
62
+ "@memberjunction/communication-ms-graph": "5.45.0",
63
+ "@memberjunction/notifications": "5.45.0",
64
+ "@memberjunction/communication-sendgrid": "5.45.0",
65
+ "@memberjunction/communication-types": "5.45.0",
66
+ "@memberjunction/component-registry-client-sdk": "5.45.0",
67
+ "@memberjunction/computer-use": "5.45.0",
68
+ "@memberjunction/computer-use-engine": "5.45.0",
69
+ "@memberjunction/config": "5.45.0",
70
+ "@memberjunction/core": "5.45.0",
71
+ "@memberjunction/core-actions": "5.45.0",
72
+ "@memberjunction/core-entities": "5.45.0",
73
+ "@memberjunction/core-entities-server": "5.45.0",
74
+ "@memberjunction/credentials": "5.45.0",
75
+ "@memberjunction/data-context": "5.45.0",
76
+ "@memberjunction/data-context-server": "5.45.0",
77
+ "@memberjunction/doc-utils": "5.45.0",
78
+ "@memberjunction/encryption": "5.45.0",
79
+ "@memberjunction/entity-communications-base": "5.45.0",
80
+ "@memberjunction/entity-communications-server": "5.45.0",
81
+ "@memberjunction/esignature": "5.45.0",
82
+ "@memberjunction/external-change-detection": "5.45.0",
83
+ "@memberjunction/generic-database-provider": "5.45.0",
84
+ "@memberjunction/global": "5.45.0",
85
+ "@memberjunction/graphql-dataprovider": "5.45.0",
86
+ "@memberjunction/integration-engine": "5.45.0",
87
+ "@memberjunction/integration-progress-artifacts": "5.45.0",
88
+ "@memberjunction/integration-schema-builder": "5.45.0",
89
+ "@memberjunction/interactive-component-types": "5.45.0",
90
+ "@memberjunction/lists": "5.45.0",
91
+ "@memberjunction/lists-base": "5.45.0",
92
+ "@memberjunction/livekit-room-server": "5.45.0",
93
+ "@memberjunction/postgresql-dataprovider": "5.45.0",
94
+ "@memberjunction/queue": "5.45.0",
95
+ "@memberjunction/record-comparison": "5.45.0",
96
+ "@memberjunction/redis-provider": "5.45.0",
97
+ "@memberjunction/remote-browser-base": "5.45.0",
98
+ "@memberjunction/remote-browser-cdp": "5.45.0",
99
+ "@memberjunction/remote-browser-selfhost": "5.45.0",
100
+ "@memberjunction/remote-browser-server": "5.45.0",
101
+ "@memberjunction/scheduling-actions": "5.45.0",
102
+ "@memberjunction/scheduling-base-types": "5.45.0",
103
+ "@memberjunction/scheduling-engine": "5.45.0",
104
+ "@memberjunction/scheduling-engine-base": "5.45.0",
105
+ "@memberjunction/schema-engine": "5.45.0",
106
+ "@memberjunction/search-engine": "5.45.0",
107
+ "@memberjunction/server-extensions-core": "5.45.0",
108
+ "@memberjunction/sql-dialect": "5.45.0",
109
+ "@memberjunction/sqlserver-dataprovider": "5.45.0",
110
+ "@memberjunction/storage": "5.45.0",
111
+ "@memberjunction/tag-engine": "5.45.0",
112
+ "@memberjunction/tag-engine-base": "5.45.0",
113
+ "@memberjunction/templates": "5.45.0",
114
+ "@memberjunction/testing-engine": "5.45.0",
115
+ "@memberjunction/testing-engine-base": "5.45.0",
116
+ "@memberjunction/version-history": "5.45.0",
111
117
  "@octokit/auth-app": "^7.1.5",
112
118
  "@octokit/rest": "^21.1.1",
113
119
  "@types/compression": "^1.8.1",
@@ -0,0 +1,64 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { deriveSidecarPath, parsePeaksSidecar, MAX_PEAKS } from '../resolvers/peaksSidecar.js';
3
+
4
+ /**
5
+ * Tests for the pure `peaks.json` sidecar helpers that back FileResolver.CreateMediaAccessToken's
6
+ * peaks pass-through. These cover the load-bearing behavior the mutation depends on:
7
+ * - deriving the sidecar path that sits in the SAME folder as the recording, and
8
+ * - parsing + sanitizing the sidecar so valid peaks come back and garbage / wrong-shape / missing
9
+ * payloads gracefully yield `undefined` (the resolver treats that as "omit Peaks, never fail").
10
+ *
11
+ * The resolver wires these to a storage driver behind a try/catch; the helpers themselves never
12
+ * throw, which is what guarantees a sidecar problem can't block token minting.
13
+ */
14
+
15
+ describe('deriveSidecarPath', () => {
16
+ it('replaces the final path segment with peaks.json (recording in a session folder)', () => {
17
+ expect(deriveSidecarPath('realtime-recordings/sess-1/recording.wav')).toBe('realtime-recordings/sess-1/peaks.json');
18
+ });
19
+
20
+ it('uses a bare peaks.json when the key has no folder', () => {
21
+ expect(deriveSidecarPath('recording.wav')).toBe('peaks.json');
22
+ });
23
+
24
+ it('returns null when there is no ProviderKey (cannot derive a folder)', () => {
25
+ expect(deriveSidecarPath(null)).toBeNull();
26
+ expect(deriveSidecarPath(undefined)).toBeNull();
27
+ expect(deriveSidecarPath('')).toBeNull();
28
+ });
29
+ });
30
+
31
+ describe('parsePeaksSidecar', () => {
32
+ it('returns sanitized peaks from a valid JSON array (clamped to 0..1)', () => {
33
+ const bytes = Buffer.from(JSON.stringify([0, 0.5, 1, 1.7, -0.3]), 'utf8');
34
+ expect(parsePeaksSidecar(bytes)).toEqual([0, 0.5, 1, 1, 0]);
35
+ });
36
+
37
+ it('returns undefined for non-JSON garbage (never throws)', () => {
38
+ expect(parsePeaksSidecar(Buffer.from('this is not json', 'utf8'))).toBeUndefined();
39
+ });
40
+
41
+ it('returns undefined when the JSON is not an array', () => {
42
+ expect(parsePeaksSidecar(Buffer.from(JSON.stringify({ not: 'an array' }), 'utf8'))).toBeUndefined();
43
+ });
44
+
45
+ it('returns undefined for an empty array', () => {
46
+ expect(parsePeaksSidecar(Buffer.from('[]', 'utf8'))).toBeUndefined();
47
+ });
48
+
49
+ it('drops non-finite / non-number entries, keeping the finite numbers', () => {
50
+ const bytes = Buffer.from(JSON.stringify([0.2, 'x', null, NaN, 0.8]), 'utf8');
51
+ // NaN serializes to null in JSON; both 'x' and the nulls are dropped, leaving the two numbers.
52
+ expect(parsePeaksSidecar(bytes)).toEqual([0.2, 0.8]);
53
+ });
54
+
55
+ it('returns undefined when nothing finite survives sanitization', () => {
56
+ expect(parsePeaksSidecar(Buffer.from(JSON.stringify(['a', 'b', null]), 'utf8'))).toBeUndefined();
57
+ });
58
+
59
+ it('caps the array length at MAX_PEAKS', () => {
60
+ const huge = new Array(MAX_PEAKS + 500).fill(0.5);
61
+ const result = parsePeaksSidecar(Buffer.from(JSON.stringify(huge), 'utf8'));
62
+ expect(result).toHaveLength(MAX_PEAKS);
63
+ });
64
+ });
@@ -0,0 +1,68 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import jwt from 'jsonwebtoken';
3
+ import { MediaAccessKeyManager, MEDIA_ACCESS_TOKEN_TYPE } from '../rest/MediaAccessKeys.js';
4
+
5
+ /**
6
+ * The manager is a process-wide BaseSingleton whose secret is resolved lazily on first
7
+ * Sign/Verify. Tests share that single ephemeral secret (no MJ_BASE_ENCRYPTION_KEY in the
8
+ * test env), which is exactly what production does within one process.
9
+ */
10
+ describe('MediaAccessKeyManager', () => {
11
+ it('signs a token that round-trips through Verify with the correct claims', () => {
12
+ const km = MediaAccessKeyManager.Instance;
13
+ const { Token, ExpiresAt } = km.Sign('FILE-1', 'USER-1');
14
+
15
+ expect(typeof Token).toBe('string');
16
+ expect(ExpiresAt.getTime()).toBeGreaterThan(Date.now());
17
+
18
+ const result = km.Verify(Token);
19
+ expect(result.Valid).toBe(true);
20
+ if (result.Valid) {
21
+ expect(result.Claims.fileId).toBe('FILE-1');
22
+ expect(result.Claims.userId).toBe('USER-1');
23
+ expect(result.Claims.typ).toBe(MEDIA_ACCESS_TOKEN_TYPE);
24
+ }
25
+ });
26
+
27
+ it('honors the requested TTL on ExpiresAt', () => {
28
+ const km = MediaAccessKeyManager.Instance;
29
+ const { ExpiresAt } = km.Sign('FILE-2', 'USER-2', 2); // 2 hours
30
+ const deltaMs = ExpiresAt.getTime() - Date.now();
31
+ // ~2h, allow a generous window for execution time.
32
+ expect(deltaMs).toBeGreaterThan(1.9 * 3600 * 1000);
33
+ expect(deltaMs).toBeLessThan(2.1 * 3600 * 1000);
34
+ });
35
+
36
+ it('rejects a tampered token', () => {
37
+ const km = MediaAccessKeyManager.Instance;
38
+ const { Token } = km.Sign('FILE-3', 'USER-3');
39
+ const tampered = Token.slice(0, -2) + (Token.slice(-2) === 'AA' ? 'BB' : 'AA');
40
+ expect(km.Verify(tampered).Valid).toBe(false);
41
+ });
42
+
43
+ it('rejects a malformed / empty token', () => {
44
+ const km = MediaAccessKeyManager.Instance;
45
+ expect(km.Verify('').Valid).toBe(false);
46
+ expect(km.Verify('not.a.jwt').Valid).toBe(false);
47
+ });
48
+
49
+ it('rejects an expired token', () => {
50
+ const km = MediaAccessKeyManager.Instance;
51
+ // Negative TTL → already expired when minted.
52
+ const { Token } = km.Sign('FILE-4', 'USER-4', -1);
53
+ expect(km.Verify(Token).Valid).toBe(false);
54
+ });
55
+
56
+ it('rejects a token with the wrong typ even if validly signed', () => {
57
+ const km = MediaAccessKeyManager.Instance;
58
+ // Mint a valid token, then read the real secret out of a known-good token by
59
+ // re-signing through the manager is not possible; instead assert that a token
60
+ // whose typ differs is rejected by signing a structurally-valid JWT with a
61
+ // foreign secret (signature failure also yields false, which is the safe outcome).
62
+ const foreign = jwt.sign({ fileId: 'FILE-5', userId: 'USER-5', typ: 'something-else' }, 'foreign-secret', {
63
+ algorithm: 'HS256',
64
+ expiresIn: 3600,
65
+ });
66
+ expect(km.Verify(foreign).Valid).toBe(false);
67
+ });
68
+ });
@@ -0,0 +1,91 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { parseRangeHeaderLoose, parseRange } from '../rest/mediaRange.js';
3
+
4
+ /**
5
+ * Tests for the pure HTTP-Range parsing helpers behind the authenticated `/media/:fileId`
6
+ * stream route. They decide whether a request gets a 206 partial body (and over which byte
7
+ * window) or a 200/416 — the load-bearing logic for native `<audio>`/`<video>` seeking. Both
8
+ * helpers are deliberately strict: anything malformed/multi-range yields `undefined` so the
9
+ * handler falls back to a full read (streaming path) or a 416 (buffer path).
10
+ *
11
+ * - `parseRangeHeaderLoose` — streaming path, NO known total: open-ended `bytes=start-` leaves
12
+ * `end` undefined so the driver streams to EOF.
13
+ * - `parseRange` — buffer path WITH a known total: clamps `end` to the last byte and rejects
14
+ * unsatisfiable ranges (start past EOF) so the caller returns 416.
15
+ */
16
+
17
+ describe('parseRangeHeaderLoose (streaming path — no known total)', () => {
18
+ it('parses a closed range bytes=start-end into inclusive offsets', () => {
19
+ expect(parseRangeHeaderLoose('bytes=0-499')).toEqual({ start: 0, end: 499 });
20
+ expect(parseRangeHeaderLoose('bytes=200-1023')).toEqual({ start: 200, end: 1023 });
21
+ });
22
+
23
+ it('leaves end undefined for an open-ended range bytes=start- (stream to EOF)', () => {
24
+ expect(parseRangeHeaderLoose('bytes=500-')).toEqual({ start: 500 });
25
+ expect(parseRangeHeaderLoose('bytes=0-')).toEqual({ start: 0 });
26
+ });
27
+
28
+ it('tolerates surrounding whitespace', () => {
29
+ expect(parseRangeHeaderLoose(' bytes=10-20 ')).toEqual({ start: 10, end: 20 });
30
+ });
31
+
32
+ it('returns undefined when end < start (nonsensical range)', () => {
33
+ expect(parseRangeHeaderLoose('bytes=500-100')).toBeUndefined();
34
+ });
35
+
36
+ it('returns undefined for malformed / multi-range / suffix-only headers', () => {
37
+ expect(parseRangeHeaderLoose('bytes=abc-def')).toBeUndefined();
38
+ expect(parseRangeHeaderLoose('bytes=-500')).toBeUndefined(); // suffix range — left to a full read
39
+ expect(parseRangeHeaderLoose('bytes=0-100,200-300')).toBeUndefined(); // multi-range
40
+ expect(parseRangeHeaderLoose('items=0-100')).toBeUndefined(); // wrong unit
41
+ expect(parseRangeHeaderLoose('bytes=')).toBeUndefined();
42
+ expect(parseRangeHeaderLoose('')).toBeUndefined();
43
+ });
44
+
45
+ it('accepts a zero-length point range bytes=N-N', () => {
46
+ expect(parseRangeHeaderLoose('bytes=42-42')).toEqual({ start: 42, end: 42 });
47
+ });
48
+ });
49
+
50
+ describe('parseRange (buffer path — known total)', () => {
51
+ const TOTAL = 1000;
52
+
53
+ it('parses a closed range within bounds', () => {
54
+ expect(parseRange('bytes=0-499', TOTAL)).toEqual({ start: 0, end: 499 });
55
+ });
56
+
57
+ it('clamps end to the last byte when the requested end exceeds total', () => {
58
+ // bytes=500-99999 against a 1000-byte file → end clamps to 999.
59
+ expect(parseRange('bytes=500-99999', TOTAL)).toEqual({ start: 500, end: 999 });
60
+ });
61
+
62
+ it('resolves an open-ended range to the last byte', () => {
63
+ expect(parseRange('bytes=200-', TOTAL)).toEqual({ start: 200, end: 999 });
64
+ });
65
+
66
+ it('returns undefined when start is at or past EOF (unsatisfiable → 416)', () => {
67
+ expect(parseRange('bytes=1000-1100', TOTAL)).toBeUndefined(); // start === total
68
+ expect(parseRange('bytes=2000-', TOTAL)).toBeUndefined(); // start > total
69
+ });
70
+
71
+ it('returns undefined when end < start after parsing', () => {
72
+ expect(parseRange('bytes=600-100', TOTAL)).toBeUndefined();
73
+ });
74
+
75
+ it('returns undefined for malformed / multi-range / suffix-only headers', () => {
76
+ expect(parseRange('bytes=abc-', TOTAL)).toBeUndefined();
77
+ expect(parseRange('bytes=-200', TOTAL)).toBeUndefined();
78
+ expect(parseRange('bytes=0-100,200-300', TOTAL)).toBeUndefined();
79
+ expect(parseRange('items=0-100', TOTAL)).toBeUndefined();
80
+ expect(parseRange('', TOTAL)).toBeUndefined();
81
+ });
82
+
83
+ it('handles the very first byte and the very last byte exactly', () => {
84
+ expect(parseRange('bytes=0-0', TOTAL)).toEqual({ start: 0, end: 0 });
85
+ expect(parseRange('bytes=999-999', TOTAL)).toEqual({ start: 999, end: 999 });
86
+ });
87
+
88
+ it('tolerates surrounding whitespace', () => {
89
+ expect(parseRange(' bytes=10-20 ', TOTAL)).toEqual({ start: 10, end: 20 });
90
+ });
91
+ });
@@ -33,6 +33,13 @@ vi.mock('@memberjunction/ai-agents', () => ({
33
33
  CreateBridgeRoomTranscriptSink: vi.fn(),
34
34
  }));
35
35
 
36
+ // Mock the meeting-recording registration so the thin resolver is tested in isolation (no MJStorage /
37
+ // core-entities graph). Its own behavior is covered by meetingRecordingRegistration.test.ts.
38
+ vi.mock('../resolvers/meetingRecordingRegistration', () => ({
39
+ registerMeetingRecordingFile: vi.fn(async () => ({ Success: true, RecordingFileID: 'file-1', ConversationID: 'conv-1' })),
40
+ correlateRecordingStart: vi.fn(async () => true),
41
+ }));
42
+
36
43
  import { RealtimeBridgeResolver, MintLiveKitClientTokenInput, LiveKitRecordingInput } from '../resolvers/RealtimeBridgeResolver';
37
44
  import type { AppContext } from '../types.js';
38
45
 
@@ -83,10 +90,12 @@ describe('RealtimeBridgeResolver', () => {
83
90
  expect(result.Status).toBe('EGRESS_ACTIVE');
84
91
  });
85
92
 
86
- it('stops a recording and maps the result', async () => {
93
+ it('stops a recording, registers the MP4, and returns the RecordingFileID', async () => {
87
94
  const result = await resolver.StopLiveKitRecording('eg-1', ctx);
88
95
  expect(result.Success).toBe(true);
89
96
  expect(result.Status).toBe('EGRESS_COMPLETE');
97
+ // The registration mock returns a file id, which the resolver surfaces on the result.
98
+ expect(result.RecordingFileID).toBe('file-1');
90
99
  });
91
100
 
92
101
  it('requires an authenticated user to record', async () => {