@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,195 @@
1
+ /**
2
+ * @fileoverview Authenticated HTTP-Range media streaming route (`GET /media/:fileId`).
3
+ *
4
+ * Streams an `MJ: Files` object's bytes (optionally a byte range) to the browser so
5
+ * `<audio>`/`<video>` elements get native progressive playback + seek-before-download
6
+ * for large media — instead of the whole file being base64'd over GraphQL.
7
+ *
8
+ * Auth model: the request carries a short-lived signed token (`?token=`) minted by
9
+ * `CreateMediaAccessToken` AFTER a per-user permission check. The token IS the
10
+ * capability: this route re-verifies its signature/expiry/`typ` and that it matches
11
+ * the `:fileId`, then loads the file under a SYSTEM context (it only needs
12
+ * `ProviderKey`/`ProviderID` to locate the bytes — no row-level re-check, since
13
+ * access was already authorized at mint).
14
+ *
15
+ * Mount unauthenticated, BEFORE the unified auth middleware:
16
+ * `app.use('/media', cors<cors.CorsRequest>(), createMediaStreamRouter())`
17
+ *
18
+ * @module @memberjunction/server/rest/MediaStreamHandler
19
+ */
20
+ import express from 'express';
21
+ import { LogError, Metadata } from '@memberjunction/core';
22
+ import { FileStorageEngine } from '@memberjunction/storage';
23
+ import { getSystemUser } from '../auth/index.js';
24
+ import { MediaAccessKeyManager } from './MediaAccessKeys.js';
25
+ import { parseRange, parseRangeHeaderLoose } from './mediaRange.js';
26
+ /**
27
+ * Builds the Express router exposing `GET /media/:fileId`. Stateless — verification
28
+ * and byte-source resolution happen per request.
29
+ */
30
+ export function createMediaStreamRouter() {
31
+ const router = express.Router();
32
+ router.get('/:fileId', async (req, res) => {
33
+ await handleMediaRequest(req, res);
34
+ });
35
+ return router;
36
+ }
37
+ /** Top-level request handler: verify token → resolve bytes → stream/buffer with Range support. */
38
+ async function handleMediaRequest(req, res) {
39
+ // Express type defs allow req.params values to be string | string[]; normalize to string
40
+ // so downstream consumers with moduleResolution:"node" type-check cleanly (see SignatureWebhookHandler).
41
+ const rawFileId = req.params.fileId;
42
+ const fileId = Array.isArray(rawFileId) ? rawFileId[0] : rawFileId;
43
+ const token = typeof req.query.token === 'string' ? req.query.token : '';
44
+ const claims = verifyMediaToken(token, fileId);
45
+ if (!claims) {
46
+ // No body leak — a bad/expired/mismatched token is indistinguishable from "forbidden".
47
+ res.status(403).end();
48
+ return;
49
+ }
50
+ // Never let CDNs or shared caches retain authorized media bytes.
51
+ res.setHeader('Cache-Control', 'private, no-store');
52
+ try {
53
+ const source = await resolveFileBytesSource(fileId);
54
+ if (!source) {
55
+ res.status(404).end();
56
+ return;
57
+ }
58
+ await streamOrBuffer(req, res, source);
59
+ }
60
+ catch (error) {
61
+ LogError(`[MediaStream] Failed to serve file ${fileId}: ${error instanceof Error ? error.message : String(error)}`);
62
+ if (!res.headersSent) {
63
+ res.status(500).end();
64
+ }
65
+ else {
66
+ res.destroy();
67
+ }
68
+ }
69
+ }
70
+ /**
71
+ * Verifies the token and that it grants access to THIS file. Returns null on any failure
72
+ * (signature, expiry, `typ`, or fileId mismatch) so the caller can 403 uniformly.
73
+ */
74
+ function verifyMediaToken(token, fileId) {
75
+ if (!token) {
76
+ return null;
77
+ }
78
+ const result = MediaAccessKeyManager.Instance.Verify(token);
79
+ if (!result.Valid) {
80
+ return null;
81
+ }
82
+ // The token's fileId must match the path — a token minted for file A cannot stream file B.
83
+ if (result.Claims.fileId !== fileId) {
84
+ return null;
85
+ }
86
+ return { fileId: result.Claims.fileId, userId: result.Claims.userId };
87
+ }
88
+ /**
89
+ * Loads the file under a SYSTEM context and resolves its storage driver + provider key.
90
+ * Returns null when the file row, its account, or its provider key can't be resolved
91
+ * (→ 404). Mirrors `readRealtimeRecordingFile`'s account-resolution (engine config with
92
+ * force-refresh fallback for newly-provisioned accounts).
93
+ */
94
+ async function resolveFileBytesSource(fileId) {
95
+ const systemUser = await getSystemUser();
96
+ // The /media route runs pre-auth on the server's own provider; access was already authorized
97
+ // at token-mint time, so this load only locates the bytes.
98
+ const md = new Metadata(); // global-provider-ok: pre-auth system-context route, no per-request provider
99
+ const file = await md.GetEntityObject('MJ: Files', systemUser);
100
+ if (!await file.Load(fileId) || !file.ProviderKey) {
101
+ return null;
102
+ }
103
+ // Resolve the storage account for the file's provider; force-refresh once if a
104
+ // newly-provisioned account isn't in the cache yet.
105
+ await FileStorageEngine.Instance.Config(false, systemUser);
106
+ let accounts = FileStorageEngine.Instance.GetAccountsByProviderID(file.ProviderID);
107
+ if (accounts.length === 0) {
108
+ await FileStorageEngine.Instance.Config(true, systemUser);
109
+ accounts = FileStorageEngine.Instance.GetAccountsByProviderID(file.ProviderID);
110
+ }
111
+ const account = accounts[0];
112
+ if (!account) {
113
+ return null;
114
+ }
115
+ const driver = await FileStorageEngine.Instance.GetDriver(account.ID, systemUser);
116
+ return {
117
+ driver,
118
+ providerKey: file.ProviderKey,
119
+ contentType: file.ContentType ?? 'application/octet-stream',
120
+ };
121
+ }
122
+ /**
123
+ * Streams the object honoring an HTTP `Range` request. Prefers true streaming
124
+ * (`GetObjectStream`) for streaming-capable drivers; gracefully falls back to a
125
+ * full buffered `GetObject` (slicing for Range) for drivers that don't.
126
+ */
127
+ async function streamOrBuffer(req, res, source) {
128
+ const rangeHeader = typeof req.headers.range === 'string' ? req.headers.range : undefined;
129
+ if (source.driver.SupportsStreaming) {
130
+ await serveViaStream(res, source, rangeHeader);
131
+ }
132
+ else {
133
+ await serveViaBuffer(res, source, rangeHeader);
134
+ }
135
+ }
136
+ /** True-streaming path: `GetObjectStream` + pipe, with 206 when a Range was honored. */
137
+ async function serveViaStream(res, source, rangeHeader) {
138
+ const range = rangeHeader ? parseRangeHeaderLoose(rangeHeader) : undefined;
139
+ // Omit End for an open-ended range so the driver streams to EOF (per ByteRange semantics).
140
+ const streamRange = range
141
+ ? (range.end == null ? { Start: range.start } : { Start: range.start, End: range.end })
142
+ : undefined;
143
+ const result = await source.driver.GetObjectStream({ fullPath: source.providerKey, Range: streamRange });
144
+ res.setHeader('Accept-Ranges', 'bytes');
145
+ res.setHeader('Content-Type', result.ContentType ?? source.contentType);
146
+ if (result.ContentLength != null) {
147
+ res.setHeader('Content-Length', String(result.ContentLength));
148
+ }
149
+ if (result.ContentRange) {
150
+ const { Start, End, Total } = result.ContentRange;
151
+ res.setHeader('Content-Range', `bytes ${Start}-${End}/${Total}`);
152
+ res.status(206);
153
+ }
154
+ else {
155
+ res.status(200);
156
+ }
157
+ // Tear down the source stream if the client aborts (closed tab, seek, etc.).
158
+ res.on('close', () => result.Stream.destroy());
159
+ result.Stream.on('error', (err) => {
160
+ LogError(`[MediaStream] Source stream error: ${err instanceof Error ? err.message : String(err)}`);
161
+ if (!res.headersSent) {
162
+ res.status(500).end();
163
+ }
164
+ else {
165
+ res.destroy();
166
+ }
167
+ });
168
+ result.Stream.pipe(res);
169
+ }
170
+ /** Fallback path: full buffered `GetObject`, slicing the buffer for Range (206) or sending whole (200). */
171
+ async function serveViaBuffer(res, source, rangeHeader) {
172
+ const buffer = await source.driver.GetObject({ fullPath: source.providerKey });
173
+ const total = buffer.length;
174
+ res.setHeader('Accept-Ranges', 'bytes');
175
+ res.setHeader('Content-Type', source.contentType);
176
+ if (!rangeHeader) {
177
+ res.status(200);
178
+ res.setHeader('Content-Length', String(total));
179
+ res.end(buffer);
180
+ return;
181
+ }
182
+ const range = parseRange(rangeHeader, total);
183
+ if (!range) {
184
+ // Unsatisfiable range — per RFC 7233, 416 + Content-Range with the total size.
185
+ res.setHeader('Content-Range', `bytes */${total}`);
186
+ res.status(416).end();
187
+ return;
188
+ }
189
+ const slice = buffer.subarray(range.start, range.end + 1);
190
+ res.status(206);
191
+ res.setHeader('Content-Range', `bytes ${range.start}-${range.end}/${total}`);
192
+ res.setHeader('Content-Length', String(slice.length));
193
+ res.end(slice);
194
+ }
195
+ //# sourceMappingURL=MediaStreamHandler.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MediaStreamHandler.js","sourceRoot":"","sources":["../../src/rest/MediaStreamHandler.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,41 @@
1
+ /**
2
+ * @fileoverview Pure HTTP `Range` header parsing for the `/media/:fileId` stream route.
3
+ *
4
+ * Split out of {@link module:@memberjunction/server/rest/MediaStreamHandler} (which pulls in
5
+ * auth/config/storage at module load) so this load-bearing parsing logic is unit-testable in
6
+ * isolation — same pattern as `peaksSidecar.ts`. These functions decide whether a request gets
7
+ * a 206 partial body (and over which byte window) vs. a full 200 / a 416.
8
+ *
9
+ * Both helpers are deliberately strict single-range parsers: anything malformed, multi-range,
10
+ * or suffix-only (`bytes=-N`) yields `undefined`, letting the caller fall back to a full read
11
+ * (streaming path) or a 416 (buffer path).
12
+ *
13
+ * @module @memberjunction/server/rest/mediaRange
14
+ */
15
+ /** A parsed, validated `Range` request against a known total size. */
16
+ export interface ParsedRange {
17
+ start: number;
18
+ end: number;
19
+ }
20
+ /**
21
+ * A parsed `Range` request WITHOUT a known total (streaming path). `end` is undefined
22
+ * for an open-ended `bytes=start-` request, matching `ByteRange`'s "omit End = EOF".
23
+ */
24
+ export interface ParsedOpenRange {
25
+ start: number;
26
+ end?: number;
27
+ }
28
+ /**
29
+ * Parses a single-range `bytes=start-end` header WITHOUT a known total (streaming path).
30
+ * Returns the inclusive offsets, or undefined when the header is malformed/multi-range/
31
+ * suffix-only (those we leave to the driver as a full read). `end` is left undefined when
32
+ * open-ended (`bytes=start-`) so the driver streams to EOF.
33
+ */
34
+ export declare function parseRangeHeaderLoose(rangeHeader: string): ParsedOpenRange | undefined;
35
+ /**
36
+ * Parses a single-range `bytes=start-end` header against a known total (buffer path),
37
+ * clamping `end` to the last byte. Returns undefined when unsatisfiable (→ 416) or
38
+ * malformed/multi-range.
39
+ */
40
+ export declare function parseRange(rangeHeader: string, total: number): ParsedRange | undefined;
41
+ //# sourceMappingURL=mediaRange.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mediaRange.d.ts","sourceRoot":"","sources":["../../src/rest/mediaRange.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,sEAAsE;AACtE,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;CACb;AAED;;;GAGG;AACH,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,WAAW,EAAE,MAAM,GAAG,eAAe,GAAG,SAAS,CAiBtF;AAED;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,WAAW,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,WAAW,GAAG,SAAS,CAetF"}
@@ -0,0 +1,60 @@
1
+ /**
2
+ * @fileoverview Pure HTTP `Range` header parsing for the `/media/:fileId` stream route.
3
+ *
4
+ * Split out of {@link module:@memberjunction/server/rest/MediaStreamHandler} (which pulls in
5
+ * auth/config/storage at module load) so this load-bearing parsing logic is unit-testable in
6
+ * isolation — same pattern as `peaksSidecar.ts`. These functions decide whether a request gets
7
+ * a 206 partial body (and over which byte window) vs. a full 200 / a 416.
8
+ *
9
+ * Both helpers are deliberately strict single-range parsers: anything malformed, multi-range,
10
+ * or suffix-only (`bytes=-N`) yields `undefined`, letting the caller fall back to a full read
11
+ * (streaming path) or a 416 (buffer path).
12
+ *
13
+ * @module @memberjunction/server/rest/mediaRange
14
+ */
15
+ /**
16
+ * Parses a single-range `bytes=start-end` header WITHOUT a known total (streaming path).
17
+ * Returns the inclusive offsets, or undefined when the header is malformed/multi-range/
18
+ * suffix-only (those we leave to the driver as a full read). `end` is left undefined when
19
+ * open-ended (`bytes=start-`) so the driver streams to EOF.
20
+ */
21
+ export function parseRangeHeaderLoose(rangeHeader) {
22
+ const match = /^bytes=(\d+)-(\d*)$/.exec(rangeHeader.trim());
23
+ if (!match) {
24
+ return undefined;
25
+ }
26
+ const start = Number(match[1]);
27
+ if (!Number.isFinite(start)) {
28
+ return undefined;
29
+ }
30
+ if (match[2] === '') {
31
+ return { start };
32
+ }
33
+ const end = Number(match[2]);
34
+ if (!Number.isFinite(end) || end < start) {
35
+ return undefined;
36
+ }
37
+ return { start, end };
38
+ }
39
+ /**
40
+ * Parses a single-range `bytes=start-end` header against a known total (buffer path),
41
+ * clamping `end` to the last byte. Returns undefined when unsatisfiable (→ 416) or
42
+ * malformed/multi-range.
43
+ */
44
+ export function parseRange(rangeHeader, total) {
45
+ const match = /^bytes=(\d+)-(\d*)$/.exec(rangeHeader.trim());
46
+ if (!match) {
47
+ return undefined;
48
+ }
49
+ const start = Number(match[1]);
50
+ if (!Number.isFinite(start) || start >= total) {
51
+ return undefined; // start past EOF → unsatisfiable
52
+ }
53
+ const requestedEnd = match[2] === '' ? total - 1 : Number(match[2]);
54
+ const end = Math.min(requestedEnd, total - 1);
55
+ if (end < start) {
56
+ return undefined;
57
+ }
58
+ return { start, end };
59
+ }
60
+ //# sourceMappingURL=mediaRange.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mediaRange.js","sourceRoot":"","sources":["../../src/rest/mediaRange.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAiBH;;;;;GAKG;AACH,MAAM,UAAU,qBAAqB,CAAC,WAAmB;IACvD,MAAM,KAAK,GAAG,qBAAqB,CAAC,IAAI,CAAC,WAAW,CAAC,IAAI,EAAE,CAAC,CAAC;IAC7D,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IAC/B,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;QAC5B,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,IAAI,KAAK,CAAC,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC;QACpB,OAAO,EAAE,KAAK,EAAE,CAAC;IACnB,CAAC;IACD,MAAM,GAAG,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IAC7B,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,GAAG,GAAG,KAAK,EAAE,CAAC;QACzC,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,OAAO,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC;AACxB,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,UAAU,CAAC,WAAmB,EAAE,KAAa;IAC3D,MAAM,KAAK,GAAG,qBAAqB,CAAC,IAAI,CAAC,WAAW,CAAC,IAAI,EAAE,CAAC,CAAC;IAC7D,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IAC/B,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,KAAK,IAAI,KAAK,EAAE,CAAC;QAC9C,OAAO,SAAS,CAAC,CAAC,iCAAiC;IACrD,CAAC;IACD,MAAM,YAAY,GAAG,KAAK,CAAC,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IACpE,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,YAAY,EAAE,KAAK,GAAG,CAAC,CAAC,CAAC;IAC9C,IAAI,GAAG,GAAG,KAAK,EAAE,CAAC;QAChB,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,OAAO,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC;AACxB,CAAC"}
@@ -0,0 +1,115 @@
1
+ /**
2
+ * @fileoverview Orchestrates RingCentral telephony bridge sessions (inbound + outbound) for MJAPI over the
3
+ * **SIP softphone** transport.
4
+ *
5
+ * RingCentral's only product that carries bidirectional call audio is a registered SIP softphone (the
6
+ * `ringcentral-softphone` SDK over SIP/TLS + RTP/SRTP) — its WebSocket "Call Streaming" product is
7
+ * receive-only and cannot inject the agent's voice. So unlike the Twilio/Vonage ingresses (public webhook
8
+ * + media WSS), this service owns a **long-lived SIP registration**: one
9
+ * {@link RingCentralSoftphoneHandle} created + registered at boot, shared across all calls. Inbound calls
10
+ * arrive as SIP INVITEs on that registration (no HTTP webhook); outbound calls place SIP calls through it.
11
+ *
12
+ * It does NOT re-implement bridge logic: it resolves the RingCentral provider + the agent identity, opens a
13
+ * server-side realtime session via the agent layer's `CreateBridgeRealtimeSession` factory, binds a
14
+ * {@link RingCentralSoftphoneCallSdk} (drawing its session from the shared handle) onto the driver, and
15
+ * hands everything to `AIBridgeEngine.StartBridgeSession` — the same seam the Twilio/Vonage services use.
16
+ *
17
+ * @module @memberjunction/server/telephony
18
+ */
19
+ import { UserInfo, IMetadataProvider } from '@memberjunction/core';
20
+ import { AIBridgeEngine } from '@memberjunction/ai-bridge-server';
21
+ import { CreateBridgeRealtimeSession } from '@memberjunction/ai-agents';
22
+ import { type BridgeNativeSdkBinding } from '@memberjunction/ai-bridge-base';
23
+ import { createRingCentralSoftphone } from '@memberjunction/ai-bridge-ringcentral';
24
+ import type { RingCentralTelephonyConfig } from '../config.js';
25
+ import { SessionManager } from '../agentSessions/SessionManager.js';
26
+ /** A resolved inbound call's identifying fields (mapped from a RingCentral SIP INVITE). */
27
+ export interface InboundCallInput {
28
+ /** The SIP `Call-ID` (the per-call id the handle parked the INVITE under + the bridge's inbound id). */
29
+ sessionId: string;
30
+ /** The caller's number (the INVITE's `From`). */
31
+ from: string;
32
+ /** The dialed DID (the INVITE's `To`) — resolved to an agent identity. */
33
+ to: string;
34
+ }
35
+ /** Result of starting an inbound bridge session. */
36
+ export interface InboundCallResult {
37
+ /** Whether a pinned agent was resolved + a bridge session started. */
38
+ accepted: boolean;
39
+ /** Why it was rejected (no agent identity for the DID, provider missing, etc.). */
40
+ reason?: string;
41
+ }
42
+ /** Injectable collaborators (production defaults wired in the constructor; fakes in tests). */
43
+ export interface RingCentralTelephonyServiceDeps {
44
+ engine?: Pick<AIBridgeEngine, 'ProviderByName' | 'ProviderByDriverClass' | 'StartBridgeSession' | 'Config'>;
45
+ sessionFactory?: typeof CreateBridgeRealtimeSession;
46
+ sessionManager?: Pick<SessionManager, 'CreateSession'>;
47
+ /** Test seam: a factory that builds the softphone handle (inject a fake to avoid the real SIP SDK). */
48
+ createHandle?: typeof createRingCentralSoftphone;
49
+ }
50
+ /**
51
+ * Starts RingCentral bridge sessions over a shared SIP-softphone registration. One instance per server,
52
+ * constructed at boot and held by the runtime holder so the outbound `PlaceRingCentralCall` resolver and the
53
+ * inbound INVITE coordinator share the same registration.
54
+ */
55
+ export declare class RingCentralTelephonyService {
56
+ private readonly config;
57
+ private readonly engine;
58
+ private readonly sessionFactory;
59
+ private readonly sessionManager;
60
+ private readonly createHandle;
61
+ /** The shared SIP registration — created + registered by {@link start}; null until then. */
62
+ private handle;
63
+ /** The carrier PCM16 rate for the negotiated codec (set in {@link start}). */
64
+ private carrierSampleRate;
65
+ constructor(config: RingCentralTelephonyConfig, deps?: RingCentralTelephonyServiceDeps);
66
+ /**
67
+ * Creates + registers the shared SIP softphone and wires inbound INVITE handling. Called once at boot
68
+ * (fire-and-forget so SIP registration never blocks server startup); logs success/failure. Inbound calls
69
+ * only flow once this resolves.
70
+ */
71
+ start(): Promise<void>;
72
+ /** Best-effort teardown of the SIP registration (server shutdown). */
73
+ dispose(): void;
74
+ /**
75
+ * Resolves the dialed DID to a pinned agent and starts an INBOUND bridge session. The bound
76
+ * {@link RingCentralSoftphoneCallSdk} answers the parked INVITE for `sessionId`. Returns
77
+ * `{ accepted:false }` (never throws) when no agent identity matches the DID, so the coordinator can
78
+ * decline the INVITE cleanly.
79
+ */
80
+ HandleInboundCall(input: InboundCallInput, contextUser: UserInfo, provider: IMetadataProvider): Promise<InboundCallResult>;
81
+ /**
82
+ * Places an OUTBOUND call from a given agent identity to a destination number. The bound
83
+ * {@link RingCentralSoftphoneCallSdk} places the SIP call through the shared registration; the returned
84
+ * SIP `Call-ID` is the bridge's external connection id.
85
+ *
86
+ * @returns The placed call's SIP `Call-ID`.
87
+ */
88
+ PlaceOutboundCall(agentIdentityId: string, toNumber: string, contextUser: UserInfo, provider: IMetadataProvider): Promise<string>;
89
+ /** Inbound INVITE coordinator: resolve the server principal, start a bridge, decline if no agent matched. */
90
+ private onInboundInvite;
91
+ /** Shared inbound/outbound bridge-start: resolve provider, open the realtime session, bind the SDK, start. */
92
+ private startBridge;
93
+ /** Resolves the seeded RingCentral provider row (by driver class, falling back to display name). */
94
+ private resolveProvider;
95
+ /**
96
+ * Builds the per-session SDK binding that wires a {@link RingCentralSoftphoneCallSdk} (drawing its
97
+ * session from the shared SIP registration handle) onto the telephony driver — overriding the package's
98
+ * default unbound SDK. Reads the handle lazily at call time so a call placed during the brief boot window
99
+ * before registration fails loudly rather than binding a null handle.
100
+ */
101
+ buildBindSdk(): BridgeNativeSdkBinding;
102
+ /** Returns the live registration handle, or throws if the softphone hasn't registered yet. */
103
+ private requireHandle;
104
+ /** Assembles the per-session bridge configuration (direction / from-number / inbound-call-id / carrier rate). */
105
+ private buildSessionConfiguration;
106
+ /** Maps the server config block to the softphone SDK's SIP-device config (credentials resolved upstream). */
107
+ private toSoftphoneConfig;
108
+ /** Resolves the server-side principal + provider for an inbound call (no MJ JWT on a SIP INVITE). */
109
+ private resolveServerContext;
110
+ /** Finds the active agent identity whose phone number matches the dialed DID. */
111
+ private resolveAgentIdentityByPhone;
112
+ /** Loads a specific agent identity by id (used by outbound PlaceCall), requiring it be active. */
113
+ private loadAgentIdentity;
114
+ }
115
+ //# sourceMappingURL=RingCentralTelephonyService.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RingCentralTelephonyService.d.ts","sourceRoot":"","sources":["../../src/telephony/RingCentralTelephonyService.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,EAAW,QAAQ,EAAE,iBAAiB,EAAiC,MAAM,sBAAsB,CAAC;AAG3G,OAAO,EAAE,cAAc,EAAE,MAAM,kCAAkC,CAAC;AAClE,OAAO,EAAE,2BAA2B,EAAE,MAAM,2BAA2B,CAAC;AACxE,OAAO,EAEH,KAAK,sBAAsB,EAK9B,MAAM,gCAAgC,CAAC;AACxC,OAAO,EACH,0BAA0B,EAK7B,MAAM,uCAAuC,CAAC;AAC/C,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,cAAc,CAAC;AAC/D,OAAO,EAAE,cAAc,EAAE,MAAM,oCAAoC,CAAC;AAapE,2FAA2F;AAC3F,MAAM,WAAW,gBAAgB;IAC7B,wGAAwG;IACxG,SAAS,EAAE,MAAM,CAAC;IAClB,iDAAiD;IACjD,IAAI,EAAE,MAAM,CAAC;IACb,0EAA0E;IAC1E,EAAE,EAAE,MAAM,CAAC;CACd;AAED,oDAAoD;AACpD,MAAM,WAAW,iBAAiB;IAC9B,sEAAsE;IACtE,QAAQ,EAAE,OAAO,CAAC;IAClB,mFAAmF;IACnF,MAAM,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,+FAA+F;AAC/F,MAAM,WAAW,+BAA+B;IAC5C,MAAM,CAAC,EAAE,IAAI,CAAC,cAAc,EAAE,gBAAgB,GAAG,uBAAuB,GAAG,oBAAoB,GAAG,QAAQ,CAAC,CAAC;IAC5G,cAAc,CAAC,EAAE,OAAO,2BAA2B,CAAC;IACpD,cAAc,CAAC,EAAE,IAAI,CAAC,cAAc,EAAE,eAAe,CAAC,CAAC;IACvD,uGAAuG;IACvG,YAAY,CAAC,EAAE,OAAO,0BAA0B,CAAC;CACpD;AAED;;;;GAIG;AACH,qBAAa,2BAA2B;IAahC,OAAO,CAAC,QAAQ,CAAC,MAAM;IAZ3B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAqG;IAC5H,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAqC;IACpE,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAwC;IACvE,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAoC;IAEjE,4FAA4F;IAC5F,OAAO,CAAC,MAAM,CAA2C;IAEzD,8EAA8E;IAC9E,OAAO,CAAC,iBAAiB,CAA4C;gBAGhD,MAAM,EAAE,0BAA0B,EACnD,IAAI,GAAE,+BAAoC;IAQ9C;;;;OAIG;IACU,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAanC,sEAAsE;IAC/D,OAAO,IAAI,IAAI;IAKtB;;;;;OAKG;IACU,iBAAiB,CAAC,KAAK,EAAE,gBAAgB,EAAE,WAAW,EAAE,QAAQ,EAAE,QAAQ,EAAE,iBAAiB,GAAG,OAAO,CAAC,iBAAiB,CAAC;IAqBvI;;;;;;OAMG;IACU,iBAAiB,CAAC,eAAe,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,WAAW,EAAE,QAAQ,EAAE,QAAQ,EAAE,iBAAiB,GAAG,OAAO,CAAC,MAAM,CAAC;IAkB9I,6GAA6G;YAC/F,eAAe;IAc7B,8GAA8G;YAChG,WAAW;IAsCzB,oGAAoG;IACpG,OAAO,CAAC,eAAe;IAQvB;;;;;OAKG;IACI,YAAY,IAAI,sBAAsB;IAO7C,8FAA8F;IAC9F,OAAO,CAAC,aAAa;IAOrB,iHAAiH;IACjH,OAAO,CAAC,yBAAyB;IAiBjC,6GAA6G;IAC7G,OAAO,CAAC,iBAAiB;IAYzB,qGAAqG;IACrG,OAAO,CAAC,oBAAoB;IAS5B,iFAAiF;YACnE,2BAA2B;IAsBzC,kGAAkG;YACpF,iBAAiB;CASlC"}