@memberjunction/server 6.2.0-edge.6 → 6.2.0-edge.8

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 (463) hide show
  1. package/README.md +69 -0
  2. package/dist/.tsbuildinfo +1 -1
  3. package/dist/agentSessions/BridgeHostInstance.d.ts +8 -3
  4. package/dist/agentSessions/BridgeHostInstance.d.ts.map +1 -1
  5. package/dist/agentSessions/BridgeHostInstance.js +14 -4
  6. package/dist/agentSessions/BridgeHostInstance.js.map +1 -1
  7. package/dist/agentSessions/HandoffOfferExpiry.d.ts +10 -0
  8. package/dist/agentSessions/HandoffOfferExpiry.d.ts.map +1 -0
  9. package/dist/agentSessions/HandoffOfferExpiry.js +18 -0
  10. package/dist/agentSessions/HandoffOfferExpiry.js.map +1 -0
  11. package/dist/agentSessions/ReturningVisitorRecap.js +1 -0
  12. package/dist/agentSessions/ReturningVisitorRecap.js.map +1 -1
  13. package/dist/agentSessions/SessionJanitor.d.ts +31 -8
  14. package/dist/agentSessions/SessionJanitor.d.ts.map +1 -1
  15. package/dist/agentSessions/SessionJanitor.js +85 -41
  16. package/dist/agentSessions/SessionJanitor.js.map +1 -1
  17. package/dist/agentSessions/SessionManager.d.ts +14 -4
  18. package/dist/agentSessions/SessionManager.d.ts.map +1 -1
  19. package/dist/agentSessions/SessionManager.js +28 -4
  20. package/dist/agentSessions/SessionManager.js.map +1 -1
  21. package/dist/agentSessions/conversationBrowsers.d.ts +61 -0
  22. package/dist/agentSessions/conversationBrowsers.d.ts.map +1 -0
  23. package/dist/agentSessions/conversationBrowsers.js +118 -0
  24. package/dist/agentSessions/conversationBrowsers.js.map +1 -0
  25. package/dist/agentSessions/sessionDeadline.d.ts +43 -0
  26. package/dist/agentSessions/sessionDeadline.d.ts.map +1 -0
  27. package/dist/agentSessions/sessionDeadline.js +68 -0
  28. package/dist/agentSessions/sessionDeadline.js.map +1 -0
  29. package/dist/auth/APIKeyScopeAuth.d.ts.map +1 -1
  30. package/dist/auth/APIKeyScopeAuth.js +5 -4
  31. package/dist/auth/APIKeyScopeAuth.js.map +1 -1
  32. package/dist/auth/loginEmailResolution.d.ts +41 -0
  33. package/dist/auth/loginEmailResolution.d.ts.map +1 -0
  34. package/dist/auth/loginEmailResolution.js +47 -0
  35. package/dist/auth/loginEmailResolution.js.map +1 -0
  36. package/dist/auth/magicLink/MagicLinkKeys.d.ts +50 -2
  37. package/dist/auth/magicLink/MagicLinkKeys.d.ts.map +1 -1
  38. package/dist/auth/magicLink/MagicLinkKeys.js +129 -12
  39. package/dist/auth/magicLink/MagicLinkKeys.js.map +1 -1
  40. package/dist/auth/magicLink/MagicLinkRouter.d.ts +3 -0
  41. package/dist/auth/magicLink/MagicLinkRouter.d.ts.map +1 -1
  42. package/dist/auth/magicLink/MagicLinkRouter.js +23 -5
  43. package/dist/auth/magicLink/MagicLinkRouter.js.map +1 -1
  44. package/dist/auth/magicLink/MagicLinkService.d.ts +39 -5
  45. package/dist/auth/magicLink/MagicLinkService.d.ts.map +1 -1
  46. package/dist/auth/magicLink/MagicLinkService.js +221 -9
  47. package/dist/auth/magicLink/MagicLinkService.js.map +1 -1
  48. package/dist/auth/magicLink/index.d.ts +1 -1
  49. package/dist/auth/magicLink/index.d.ts.map +1 -1
  50. package/dist/auth/magicLink/index.js +1 -1
  51. package/dist/auth/magicLink/index.js.map +1 -1
  52. package/dist/auth/magicLink/redeemLanding.d.ts +2 -2
  53. package/dist/auth/magicLink/redeemLanding.d.ts.map +1 -1
  54. package/dist/auth/magicLink/redeemLanding.js +7 -4
  55. package/dist/auth/magicLink/redeemLanding.js.map +1 -1
  56. package/dist/auth/magicLink/types.d.ts +27 -3
  57. package/dist/auth/magicLink/types.d.ts.map +1 -1
  58. package/dist/auth/magicLink/types.js +0 -4
  59. package/dist/auth/magicLink/types.js.map +1 -1
  60. package/dist/auth/newUsers.d.ts.map +1 -1
  61. package/dist/auth/newUsers.js +1 -0
  62. package/dist/auth/newUsers.js.map +1 -1
  63. package/dist/backgroundSweepConfigUnits.d.ts +231 -0
  64. package/dist/backgroundSweepConfigUnits.d.ts.map +1 -0
  65. package/dist/backgroundSweepConfigUnits.js +180 -0
  66. package/dist/backgroundSweepConfigUnits.js.map +1 -0
  67. package/dist/broadcast/BaseCacheInvalidationBroadcastProvider.d.ts.map +1 -1
  68. package/dist/broadcast/BaseCacheInvalidationBroadcastProvider.js +2 -1
  69. package/dist/broadcast/BaseCacheInvalidationBroadcastProvider.js.map +1 -1
  70. package/dist/config.d.ts +906 -8
  71. package/dist/config.d.ts.map +1 -1
  72. package/dist/config.js +59 -0
  73. package/dist/config.js.map +1 -1
  74. package/dist/context.d.ts +2 -0
  75. package/dist/context.d.ts.map +1 -1
  76. package/dist/context.js +41 -12
  77. package/dist/context.js.map +1 -1
  78. package/dist/coreRoutes.d.ts +3 -1
  79. package/dist/coreRoutes.d.ts.map +1 -1
  80. package/dist/coreRoutes.js +6 -4
  81. package/dist/coreRoutes.js.map +1 -1
  82. package/dist/generated/graphql-schemas/__mj.d.ts +1072 -5
  83. package/dist/generated/graphql-schemas/__mj.d.ts.map +1 -1
  84. package/dist/generated/graphql-schemas/__mj.js +10174 -4618
  85. package/dist/generated/graphql-schemas/__mj.js.map +1 -1
  86. package/dist/generic/OrphanedConversationDetailReconciler.d.ts +11 -0
  87. package/dist/generic/OrphanedConversationDetailReconciler.d.ts.map +1 -1
  88. package/dist/generic/OrphanedConversationDetailReconciler.js +27 -1
  89. package/dist/generic/OrphanedConversationDetailReconciler.js.map +1 -1
  90. package/dist/generic/ResolverBase.d.ts +50 -5
  91. package/dist/generic/ResolverBase.d.ts.map +1 -1
  92. package/dist/generic/ResolverBase.js +329 -132
  93. package/dist/generic/ResolverBase.js.map +1 -1
  94. package/dist/generic/RunViewResolver.d.ts +4 -0
  95. package/dist/generic/RunViewResolver.d.ts.map +1 -1
  96. package/dist/generic/RunViewResolver.js +47 -7
  97. package/dist/generic/RunViewResolver.js.map +1 -1
  98. package/dist/globalCors.d.ts.map +1 -1
  99. package/dist/globalCors.js +1 -0
  100. package/dist/globalCors.js.map +1 -1
  101. package/dist/index.d.ts +3 -0
  102. package/dist/index.d.ts.map +1 -1
  103. package/dist/index.js +201 -43
  104. package/dist/index.js.map +1 -1
  105. package/dist/integration/ConnectionPause.d.ts +121 -0
  106. package/dist/integration/ConnectionPause.d.ts.map +1 -0
  107. package/dist/integration/ConnectionPause.js +183 -0
  108. package/dist/integration/ConnectionPause.js.map +1 -0
  109. package/dist/integration/CustomColumnPromoter.d.ts +72 -6
  110. package/dist/integration/CustomColumnPromoter.d.ts.map +1 -1
  111. package/dist/integration/CustomColumnPromoter.js +87 -46
  112. package/dist/integration/CustomColumnPromoter.js.map +1 -1
  113. package/dist/integration/EntityMapLifecycle.d.ts +78 -4
  114. package/dist/integration/EntityMapLifecycle.d.ts.map +1 -1
  115. package/dist/integration/EntityMapLifecycle.js +199 -5
  116. package/dist/integration/EntityMapLifecycle.js.map +1 -1
  117. package/dist/integration/InactiveRowWarnings.d.ts +31 -0
  118. package/dist/integration/InactiveRowWarnings.d.ts.map +1 -1
  119. package/dist/integration/InactiveRowWarnings.js +29 -0
  120. package/dist/integration/InactiveRowWarnings.js.map +1 -1
  121. package/dist/integration/ObjectNamesByID.d.ts +28 -0
  122. package/dist/integration/ObjectNamesByID.d.ts.map +1 -0
  123. package/dist/integration/ObjectNamesByID.js +87 -0
  124. package/dist/integration/ObjectNamesByID.js.map +1 -0
  125. package/dist/integration/PostRestartIntrospect.d.ts +26 -0
  126. package/dist/integration/PostRestartIntrospect.d.ts.map +1 -0
  127. package/dist/integration/PostRestartIntrospect.js +27 -0
  128. package/dist/integration/PostRestartIntrospect.js.map +1 -0
  129. package/dist/integration/RSUPostRestartProgress.d.ts +176 -0
  130. package/dist/integration/RSUPostRestartProgress.d.ts.map +1 -0
  131. package/dist/integration/RSUPostRestartProgress.js +323 -0
  132. package/dist/integration/RSUPostRestartProgress.js.map +1 -0
  133. package/dist/integration/RSUProgressBridge.d.ts +41 -2
  134. package/dist/integration/RSUProgressBridge.d.ts.map +1 -1
  135. package/dist/integration/RSUProgressBridge.js +84 -1
  136. package/dist/integration/RSUProgressBridge.js.map +1 -1
  137. package/dist/integration/RunArtifactAuthorization.d.ts +39 -0
  138. package/dist/integration/RunArtifactAuthorization.d.ts.map +1 -0
  139. package/dist/integration/RunArtifactAuthorization.js +45 -0
  140. package/dist/integration/RunArtifactAuthorization.js.map +1 -0
  141. package/dist/integration/SchemaRefreshLaunch.d.ts +9 -0
  142. package/dist/integration/SchemaRefreshLaunch.d.ts.map +1 -1
  143. package/dist/integration/SchemaRefreshLaunch.js +3 -2
  144. package/dist/integration/SchemaRefreshLaunch.js.map +1 -1
  145. package/dist/integration/ScopedFieldsForBuild.d.ts +46 -0
  146. package/dist/integration/ScopedFieldsForBuild.d.ts.map +1 -0
  147. package/dist/integration/ScopedFieldsForBuild.js +91 -0
  148. package/dist/integration/ScopedFieldsForBuild.js.map +1 -0
  149. package/dist/integration/TargetConfigGuard.d.ts +40 -0
  150. package/dist/integration/TargetConfigGuard.d.ts.map +1 -0
  151. package/dist/integration/TargetConfigGuard.js +40 -0
  152. package/dist/integration/TargetConfigGuard.js.map +1 -0
  153. package/dist/logging/StartupLogger.d.ts +50 -0
  154. package/dist/logging/StartupLogger.d.ts.map +1 -1
  155. package/dist/logging/StartupLogger.js +15 -22
  156. package/dist/logging/StartupLogger.js.map +1 -1
  157. package/dist/middleware/BaseServerMiddleware.d.ts +21 -3
  158. package/dist/middleware/BaseServerMiddleware.d.ts.map +1 -1
  159. package/dist/middleware/BaseServerMiddleware.js +4 -2
  160. package/dist/middleware/BaseServerMiddleware.js.map +1 -1
  161. package/dist/realtimeSessions/ownedRealtimeSession.d.ts.map +1 -1
  162. package/dist/realtimeSessions/ownedRealtimeSession.js +2 -3
  163. package/dist/realtimeSessions/ownedRealtimeSession.js.map +1 -1
  164. package/dist/realtimeSessions/sessionAccess.d.ts +15 -0
  165. package/dist/realtimeSessions/sessionAccess.d.ts.map +1 -1
  166. package/dist/realtimeSessions/sessionAccess.js +22 -0
  167. package/dist/realtimeSessions/sessionAccess.js.map +1 -1
  168. package/dist/realtimeWidget/WidgetSessionService.d.ts.map +1 -1
  169. package/dist/realtimeWidget/WidgetSessionService.js +2 -0
  170. package/dist/realtimeWidget/WidgetSessionService.js.map +1 -1
  171. package/dist/realtimeWidget/visitorIdentity.d.ts.map +1 -1
  172. package/dist/realtimeWidget/visitorIdentity.js +1 -0
  173. package/dist/realtimeWidget/visitorIdentity.js.map +1 -1
  174. package/dist/realtimeWidget/widgetCore.d.ts.map +1 -1
  175. package/dist/realtimeWidget/widgetCore.js +4 -7
  176. package/dist/realtimeWidget/widgetCore.js.map +1 -1
  177. package/dist/realtimeWidget/widgetGuestElevation.js +1 -0
  178. package/dist/realtimeWidget/widgetGuestElevation.js.map +1 -1
  179. package/dist/requestOrigin.d.ts +22 -0
  180. package/dist/requestOrigin.d.ts.map +1 -0
  181. package/dist/requestOrigin.js +60 -0
  182. package/dist/requestOrigin.js.map +1 -0
  183. package/dist/resolvers/AgentRunStatusPublisher.d.ts +10 -0
  184. package/dist/resolvers/AgentRunStatusPublisher.d.ts.map +1 -1
  185. package/dist/resolvers/AgentRunStatusPublisher.js +18 -2
  186. package/dist/resolvers/AgentRunStatusPublisher.js.map +1 -1
  187. package/dist/resolvers/AgentSessionResolver.d.ts.map +1 -1
  188. package/dist/resolvers/AgentSessionResolver.js +6 -2
  189. package/dist/resolvers/AgentSessionResolver.js.map +1 -1
  190. package/dist/resolvers/AutotagPipelineResolver.d.ts +1 -1
  191. package/dist/resolvers/AutotagPipelineResolver.d.ts.map +1 -1
  192. package/dist/resolvers/AutotagPipelineResolver.js +61 -16
  193. package/dist/resolvers/AutotagPipelineResolver.js.map +1 -1
  194. package/dist/resolvers/ConversationRunEventsResolver.d.ts.map +1 -1
  195. package/dist/resolvers/ConversationRunEventsResolver.js +2 -0
  196. package/dist/resolvers/ConversationRunEventsResolver.js.map +1 -1
  197. package/dist/resolvers/ExecuteRemoteOperationResolver.d.ts +6 -0
  198. package/dist/resolvers/ExecuteRemoteOperationResolver.d.ts.map +1 -1
  199. package/dist/resolvers/ExecuteRemoteOperationResolver.js +30 -7
  200. package/dist/resolvers/ExecuteRemoteOperationResolver.js.map +1 -1
  201. package/dist/resolvers/GetDataResolver.d.ts.map +1 -1
  202. package/dist/resolvers/GetDataResolver.js +34 -13
  203. package/dist/resolvers/GetDataResolver.js.map +1 -1
  204. package/dist/resolvers/InfoResolver.js +1 -1
  205. package/dist/resolvers/InfoResolver.js.map +1 -1
  206. package/dist/resolvers/IntegrationDiscoveryResolver.d.ts +401 -11
  207. package/dist/resolvers/IntegrationDiscoveryResolver.d.ts.map +1 -1
  208. package/dist/resolvers/IntegrationDiscoveryResolver.js +2690 -1305
  209. package/dist/resolvers/IntegrationDiscoveryResolver.js.map +1 -1
  210. package/dist/resolvers/MCPResolver.d.ts.map +1 -1
  211. package/dist/resolvers/MCPResolver.js +13 -2
  212. package/dist/resolvers/MCPResolver.js.map +1 -1
  213. package/dist/resolvers/QuerySystemUserResolver.d.ts +38 -1
  214. package/dist/resolvers/QuerySystemUserResolver.d.ts.map +1 -1
  215. package/dist/resolvers/QuerySystemUserResolver.js +181 -88
  216. package/dist/resolvers/QuerySystemUserResolver.js.map +1 -1
  217. package/dist/resolvers/RealtimeBridgeResolver.d.ts.map +1 -1
  218. package/dist/resolvers/RealtimeBridgeResolver.js +1 -0
  219. package/dist/resolvers/RealtimeBridgeResolver.js.map +1 -1
  220. package/dist/resolvers/RealtimeClientSessionResolver.d.ts +6 -0
  221. package/dist/resolvers/RealtimeClientSessionResolver.d.ts.map +1 -1
  222. package/dist/resolvers/RealtimeClientSessionResolver.js +36 -5
  223. package/dist/resolvers/RealtimeClientSessionResolver.js.map +1 -1
  224. package/dist/resolvers/RemoteBrowserActionResolver.d.ts +62 -10
  225. package/dist/resolvers/RemoteBrowserActionResolver.d.ts.map +1 -1
  226. package/dist/resolvers/RemoteBrowserActionResolver.js +217 -34
  227. package/dist/resolvers/RemoteBrowserActionResolver.js.map +1 -1
  228. package/dist/resolvers/RunAIAgentResolver.d.ts +24 -8
  229. package/dist/resolvers/RunAIAgentResolver.d.ts.map +1 -1
  230. package/dist/resolvers/RunAIAgentResolver.js +185 -67
  231. package/dist/resolvers/RunAIAgentResolver.js.map +1 -1
  232. package/dist/resolvers/RunAIPromptResolver.d.ts +5 -3
  233. package/dist/resolvers/RunAIPromptResolver.d.ts.map +1 -1
  234. package/dist/resolvers/RunAIPromptResolver.js +19 -18
  235. package/dist/resolvers/RunAIPromptResolver.js.map +1 -1
  236. package/dist/resolvers/RunTemplateResolver.d.ts.map +1 -1
  237. package/dist/resolvers/RunTemplateResolver.js +3 -1
  238. package/dist/resolvers/RunTemplateResolver.js.map +1 -1
  239. package/dist/resolvers/SqlLoggingConfigResolver.d.ts.map +1 -1
  240. package/dist/resolvers/SqlLoggingConfigResolver.js +7 -8
  241. package/dist/resolvers/SqlLoggingConfigResolver.js.map +1 -1
  242. package/dist/resolvers/TagGovernanceResolver.d.ts +12 -0
  243. package/dist/resolvers/TagGovernanceResolver.d.ts.map +1 -1
  244. package/dist/resolvers/TagGovernanceResolver.js +84 -0
  245. package/dist/resolvers/TagGovernanceResolver.js.map +1 -1
  246. package/dist/resolvers/TelemetryResolver.d.ts +13 -8
  247. package/dist/resolvers/TelemetryResolver.d.ts.map +1 -1
  248. package/dist/resolvers/TelemetryResolver.js +40 -9
  249. package/dist/resolvers/TelemetryResolver.js.map +1 -1
  250. package/dist/resolvers/meetingRecordingRegistration.d.ts.map +1 -1
  251. package/dist/resolvers/meetingRecordingRegistration.js +2 -0
  252. package/dist/resolvers/meetingRecordingRegistration.js.map +1 -1
  253. package/dist/resolvers/meetings/meetingMutations.d.ts.map +1 -1
  254. package/dist/resolvers/meetings/meetingMutations.js +34 -4
  255. package/dist/resolvers/meetings/meetingMutations.js.map +1 -1
  256. package/dist/rest/MediaAccessKeys.d.ts.map +1 -1
  257. package/dist/rest/MediaAccessKeys.js +6 -2
  258. package/dist/rest/MediaAccessKeys.js.map +1 -1
  259. package/dist/rest/OAuthCallbackHandler.d.ts.map +1 -1
  260. package/dist/rest/OAuthCallbackHandler.js +11 -5
  261. package/dist/rest/OAuthCallbackHandler.js.map +1 -1
  262. package/dist/rest/RealtimeRecordingHandler.d.ts +3 -3
  263. package/dist/rest/RealtimeRecordingHandler.d.ts.map +1 -1
  264. package/dist/rest/RealtimeRecordingHandler.js +2 -4
  265. package/dist/rest/RealtimeRecordingHandler.js.map +1 -1
  266. package/dist/rest/ViewOperationsHandler.d.ts.map +1 -1
  267. package/dist/rest/ViewOperationsHandler.js +2 -1
  268. package/dist/rest/ViewOperationsHandler.js.map +1 -1
  269. package/dist/rest/setupRESTEndpoints.d.ts +3 -1
  270. package/dist/rest/setupRESTEndpoints.d.ts.map +1 -1
  271. package/dist/rest/setupRESTEndpoints.js +11 -2
  272. package/dist/rest/setupRESTEndpoints.js.map +1 -1
  273. package/dist/services/DurableEntityActionTaskSubmitter.d.ts.map +1 -1
  274. package/dist/services/DurableEntityActionTaskSubmitter.js +6 -1
  275. package/dist/services/DurableEntityActionTaskSubmitter.js.map +1 -1
  276. package/dist/services/TaskGraphActionRunner.d.ts.map +1 -1
  277. package/dist/services/TaskGraphActionRunner.js +2 -26
  278. package/dist/services/TaskGraphActionRunner.js.map +1 -1
  279. package/dist/services/TaskGraphAgentRunner.d.ts +6 -0
  280. package/dist/services/TaskGraphAgentRunner.d.ts.map +1 -1
  281. package/dist/services/TaskGraphAgentRunner.js +38 -0
  282. package/dist/services/TaskGraphAgentRunner.js.map +1 -1
  283. package/dist/services/TaskGraphContinuationDeliverer.d.ts.map +1 -1
  284. package/dist/services/TaskGraphContinuationDeliverer.js +1 -0
  285. package/dist/services/TaskGraphContinuationDeliverer.js.map +1 -1
  286. package/dist/services/WorkQueueHostService.d.ts +2 -1
  287. package/dist/services/WorkQueueHostService.d.ts.map +1 -1
  288. package/dist/services/WorkQueueHostService.js +19 -4
  289. package/dist/services/WorkQueueHostService.js.map +1 -1
  290. package/dist/services/workQueueConfig.d.ts +2 -2
  291. package/dist/services/workQueueConfig.js +5 -5
  292. package/dist/services/workQueueConfig.js.map +1 -1
  293. package/dist/sharedCache.js +1 -1
  294. package/dist/sharedCache.js.map +1 -1
  295. package/dist/types.d.ts +17 -0
  296. package/dist/types.d.ts.map +1 -1
  297. package/dist/types.js.map +1 -1
  298. package/dist/util.d.ts.map +1 -1
  299. package/dist/util.js +55 -2
  300. package/dist/util.js.map +1 -1
  301. package/package.json +107 -100
  302. package/src/__tests__/APIKeyScopeAuth.test.ts +4 -8
  303. package/src/__tests__/ActiveOperations.test.ts +68 -0
  304. package/src/__tests__/AgentSessionResolver.ownership.test.ts +13 -0
  305. package/src/__tests__/AssertTargetConfigs.test.ts +75 -0
  306. package/src/__tests__/BatchApplyParity.test.ts +109 -0
  307. package/src/__tests__/BridgeHostInstance.test.ts +42 -0
  308. package/src/__tests__/CollectInactiveRowWarnings.test.ts +115 -0
  309. package/src/__tests__/ConnectionPause.test.ts +192 -0
  310. package/src/__tests__/ConnectionPauseResolver.test.ts +166 -0
  311. package/src/__tests__/ConnectionPauseSave.test.ts +143 -0
  312. package/src/__tests__/CustomColumnPromoterCompletion.test.ts +142 -0
  313. package/src/__tests__/DeleteConnectionRemovesPerConnectionCatalog.test.ts +51 -0
  314. package/src/__tests__/DetachedDiscoveryStart.test.ts +65 -0
  315. package/src/__tests__/DurableEntityActionTaskSubmitter.test.ts +73 -0
  316. package/src/__tests__/EntityMapLifecycle.rekey.test.ts +252 -0
  317. package/src/__tests__/ExecuteRemoteOperationResolver.test.ts +213 -0
  318. package/src/__tests__/HandoffOfferExpiry.test.ts +40 -0
  319. package/src/__tests__/IntegrationRunArtifactAuthorization.test.ts +126 -0
  320. package/src/__tests__/MagicLinkKeys.test.ts +102 -0
  321. package/src/__tests__/MediaAccessKeys.test.ts +57 -10
  322. package/src/__tests__/NewArrivalsDefaultDisabled.test.ts +50 -0
  323. package/src/__tests__/ObjectNamesByID.test.ts +176 -0
  324. package/src/__tests__/PostRestartMapsInCatalogScope.test.ts +146 -0
  325. package/src/__tests__/QuerySystemUserResolver.childRows.test.ts +80 -0
  326. package/src/__tests__/QuerySystemUserResolver.collision.test.ts +200 -0
  327. package/src/__tests__/QuerySystemUserResolver.permissions.test.ts +132 -0
  328. package/src/__tests__/RSUPostRestartProgress.test.ts +343 -0
  329. package/src/__tests__/RSUProgressBridge.test.ts +188 -5
  330. package/src/__tests__/RSUStartupSweepWiring.test.ts +30 -0
  331. package/src/__tests__/ReactivateRefreshMessage.test.ts +10 -3
  332. package/src/__tests__/RealtimeClientSessionResolver.test.ts +22 -0
  333. package/src/__tests__/{RefreshAdoptsNewColumns.test.ts → RefreshReportsNewColumns.test.ts} +25 -16
  334. package/src/__tests__/RemoteBrowserActionRecovery.test.ts +287 -0
  335. package/src/__tests__/RemoteBrowserActionResolver.goalRunLink.test.ts +3 -0
  336. package/src/__tests__/RemoteBrowserActionResolver.ownership.test.ts +6 -4
  337. package/src/__tests__/RemoteBrowserAudioStream.test.ts +3 -0
  338. package/src/__tests__/RemoteBrowserSnapshot.test.ts +3 -0
  339. package/src/__tests__/ResolverBase.clientOldValues.test.ts +327 -26
  340. package/src/__tests__/ResolverBase.diagnosticSenderRegistrations.test.ts +26 -0
  341. package/src/__tests__/ResolverBase.encryptedApiZeroReencrypt.test.ts +307 -0
  342. package/src/__tests__/ResolverBase.entityMessageRelay.test.ts +53 -2
  343. package/src/__tests__/ResolverBase.includeBinaryFields.test.ts +60 -0
  344. package/src/__tests__/RunAIAgentResolver.branch.test.ts +4 -0
  345. package/src/__tests__/RunAIAgentResolver.consultPredecessor.test.ts +289 -0
  346. package/src/__tests__/RunAIAgentResolver.handoff.test.ts +649 -0
  347. package/src/__tests__/RunAIAgentResolver.historyFrom.test.ts +2 -0
  348. package/src/__tests__/RunAIAgentResolver.runDataGuard.test.ts +2 -0
  349. package/src/__tests__/RunAIAgentResolver.stoppedPredecessor.test.ts +87 -12
  350. package/src/__tests__/RunAIAgentResolver.streaming.test.ts +28 -4
  351. package/src/__tests__/SchemaEvolutionCustomColumns.test.ts +119 -0
  352. package/src/__tests__/SchemaRefreshLaunch.test.ts +24 -2
  353. package/src/__tests__/ScopedFieldsForBuild.test.ts +192 -0
  354. package/src/__tests__/SessionJanitor.test.ts +195 -4
  355. package/src/__tests__/SessionManager.test.ts +69 -0
  356. package/src/__tests__/TagGovernanceAndAutotagResolvers.test.ts +261 -0
  357. package/src/__tests__/TagGovernanceResolver.test.ts +60 -2
  358. package/src/__tests__/TaskGraphActionRunner.durablePayload.test.ts +2 -2
  359. package/src/__tests__/TaskGraphAgentRunner.test.ts +272 -16
  360. package/src/__tests__/ViewOperationsHandler.test.ts +2 -5
  361. package/src/__tests__/WorkQueueHostService.test.ts +25 -7
  362. package/src/__tests__/backgroundSweepConfigUnits.test.ts +153 -0
  363. package/src/__tests__/contextRequestOrigin.test.ts +105 -0
  364. package/src/__tests__/conversationBrowsers.test.ts +62 -0
  365. package/src/__tests__/globalCors.test.ts +12 -0
  366. package/src/__tests__/magicLinkExtensionCapabilities.test.ts +413 -0
  367. package/src/__tests__/orphanedConversationDetailReconciler.start.test.ts +82 -0
  368. package/src/__tests__/ownedRealtimeSession.test.ts +14 -0
  369. package/src/__tests__/realtimeRecordingHandler.test.ts +7 -2
  370. package/src/__tests__/realtimeSessions/sessionAccess.test.ts +79 -1
  371. package/src/__tests__/requestOrigin.test.ts +104 -0
  372. package/src/__tests__/review-capture-valid-token-log.test.ts +98 -0
  373. package/src/__tests__/runStreams.fieldSecurity.test.ts +5 -5
  374. package/src/__tests__/securityHardeningPr6034.test.ts +120 -0
  375. package/src/__tests__/sessionDeadline.test.ts +51 -0
  376. package/src/__tests__/sharedCache.test.ts +11 -0
  377. package/src/__tests__/unifiedAuth.test.ts +73 -1
  378. package/src/agentSessions/BridgeHostInstance.ts +20 -5
  379. package/src/agentSessions/HandoffOfferExpiry.ts +18 -0
  380. package/src/agentSessions/ReturningVisitorRecap.ts +1 -0
  381. package/src/agentSessions/SessionJanitor.ts +92 -42
  382. package/src/agentSessions/SessionManager.ts +38 -8
  383. package/src/agentSessions/conversationBrowsers.ts +143 -0
  384. package/src/agentSessions/sessionDeadline.ts +81 -0
  385. package/src/auth/APIKeyScopeAuth.ts +5 -4
  386. package/src/auth/__tests__/loginEmailResolution.test.ts +67 -0
  387. package/src/auth/loginEmailResolution.ts +67 -0
  388. package/src/auth/magicLink/MagicLinkKeys.ts +146 -14
  389. package/src/auth/magicLink/MagicLinkRouter.ts +33 -6
  390. package/src/auth/magicLink/MagicLinkService.ts +250 -10
  391. package/src/auth/magicLink/index.ts +1 -1
  392. package/src/auth/magicLink/redeemLanding.ts +7 -4
  393. package/src/auth/magicLink/types.ts +28 -3
  394. package/src/auth/newUsers.ts +1 -0
  395. package/src/backgroundSweepConfigUnits.ts +213 -0
  396. package/src/broadcast/BaseCacheInvalidationBroadcastProvider.ts +2 -1
  397. package/src/config.ts +69 -0
  398. package/src/context.ts +46 -15
  399. package/src/coreRoutes.ts +14 -7
  400. package/src/generated/graphql-schemas/__mj.ts +10360 -6479
  401. package/src/generic/OrphanedConversationDetailReconciler.ts +32 -1
  402. package/src/generic/ResolverBase.ts +422 -143
  403. package/src/generic/RunViewResolver.ts +96 -49
  404. package/src/globalCors.ts +1 -0
  405. package/src/index.ts +246 -44
  406. package/src/integration/ConnectionPause.ts +222 -0
  407. package/src/integration/CustomColumnPromoter.ts +123 -53
  408. package/src/integration/EntityMapLifecycle.ts +229 -6
  409. package/src/integration/InactiveRowWarnings.ts +50 -0
  410. package/src/integration/ObjectNamesByID.ts +97 -0
  411. package/src/integration/PostRestartIntrospect.ts +49 -0
  412. package/src/integration/RSUPostRestartProgress.ts +359 -0
  413. package/src/integration/RSUProgressBridge.ts +85 -2
  414. package/src/integration/RunArtifactAuthorization.ts +65 -0
  415. package/src/integration/SchemaRefreshLaunch.ts +12 -2
  416. package/src/integration/ScopedFieldsForBuild.ts +113 -0
  417. package/src/integration/TargetConfigGuard.ts +71 -0
  418. package/src/logging/StartupLogger.ts +24 -0
  419. package/src/middleware/BaseServerMiddleware.ts +20 -3
  420. package/src/realtimeSessions/ownedRealtimeSession.ts +2 -6
  421. package/src/realtimeSessions/sessionAccess.ts +30 -0
  422. package/src/realtimeWidget/WidgetSessionService.ts +2 -0
  423. package/src/realtimeWidget/visitorIdentity.ts +1 -0
  424. package/src/realtimeWidget/widgetCore.ts +4 -8
  425. package/src/realtimeWidget/widgetGuestElevation.ts +1 -0
  426. package/src/requestOrigin.ts +64 -0
  427. package/src/resolvers/AgentRunStatusPublisher.ts +27 -2
  428. package/src/resolvers/AgentSessionResolver.ts +6 -2
  429. package/src/resolvers/AutotagPipelineResolver.ts +59 -14
  430. package/src/resolvers/ConversationRunEventsResolver.ts +2 -0
  431. package/src/resolvers/ExecuteRemoteOperationResolver.ts +41 -9
  432. package/src/resolvers/GetDataResolver.ts +35 -13
  433. package/src/resolvers/InfoResolver.ts +1 -1
  434. package/src/resolvers/IntegrationDiscoveryResolver.ts +1522 -203
  435. package/src/resolvers/MCPResolver.ts +15 -2
  436. package/src/resolvers/QuerySystemUserResolver.ts +211 -100
  437. package/src/resolvers/RealtimeBridgeResolver.ts +1 -0
  438. package/src/resolvers/RealtimeClientSessionResolver.ts +40 -7
  439. package/src/resolvers/RemoteBrowserActionResolver.ts +242 -32
  440. package/src/resolvers/RunAIAgentResolver.ts +265 -86
  441. package/src/resolvers/RunAIPromptResolver.ts +18 -18
  442. package/src/resolvers/RunTemplateResolver.ts +3 -1
  443. package/src/resolvers/SqlLoggingConfigResolver.ts +7 -9
  444. package/src/resolvers/TagGovernanceResolver.ts +65 -0
  445. package/src/resolvers/TelemetryResolver.ts +44 -9
  446. package/src/resolvers/__tests__/GetDataResolver.tokens.test.ts +52 -0
  447. package/src/resolvers/meetingRecordingRegistration.ts +2 -0
  448. package/src/resolvers/meetings/meetingMutations.ts +37 -7
  449. package/src/rest/MediaAccessKeys.ts +6 -2
  450. package/src/rest/OAuthCallbackHandler.ts +13 -5
  451. package/src/rest/RealtimeRecordingHandler.ts +5 -9
  452. package/src/rest/ViewOperationsHandler.ts +2 -1
  453. package/src/rest/setupRESTEndpoints.ts +11 -2
  454. package/src/services/DurableEntityActionTaskSubmitter.ts +10 -2
  455. package/src/services/TaskGraphActionRunner.ts +2 -23
  456. package/src/services/TaskGraphAgentRunner.ts +43 -0
  457. package/src/services/TaskGraphContinuationDeliverer.ts +1 -0
  458. package/src/services/WorkQueueHostService.ts +26 -4
  459. package/src/services/workQueueConfig.ts +5 -5
  460. package/src/sharedCache.ts +1 -1
  461. package/src/types.ts +18 -0
  462. package/src/util.ts +55 -3
  463. package/src/services/TaskOrchestration-Integration.md +0 -188
@@ -0,0 +1,113 @@
1
+ /**
2
+ * The FIELD rows a schema build materializes, for a connection with its own catalog.
3
+ *
4
+ * A schema build (ApplySchema, ApplyAll, ApplyAllBatch, the connector build, schema evolution)
5
+ * reconstructs the source schema from persisted catalog rows and hands it to SchemaBuilder. It used
6
+ * to read each object's fields from the engine's in-memory catalog. Once a connection's field rows
7
+ * are no longer held resident — they are warmed per object on demand, and the only site that warms
8
+ * them is the sync loop — that read answers `[]` for every object of the connection, and an object
9
+ * with no fields materializes nothing. Observed on a per-connection catalog (364 objects, 7,230
10
+ * active fields): the apply logged "Reusing 364 persisted IOs", then ran a migration with an empty
11
+ * table list in 4 ms — no CREATE, no ALTER, no error.
12
+ *
13
+ * So the build reads the connection's field rows itself: one read per {@link SCOPED_FIELDS_CHUNK}
14
+ * objects, plain rows, bypassing the query cache (an answer cached before discovery's persist is the
15
+ * empty set), and without the row cap (one object can carry thousands of fields). A failed read
16
+ * THROWS: an empty answer here is exactly what produced the zero-table apply.
17
+ *
18
+ * Outside a per-connection scope — or for a connection without its own catalog — this returns
19
+ * `null`, and the build reads the shared catalog from memory exactly as before.
20
+ */
21
+ import { RunView, type UserInfo } from '@memberjunction/core';
22
+ import type { MJIntegrationObjectFieldEntity } from '@memberjunction/core-entities';
23
+ import {
24
+ IntegrationEngineBase,
25
+ ENTITY_COMPANY_INTEGRATION_OBJECT_FIELDS,
26
+ type CompanyIntegrationObjectFieldRow,
27
+ } from '@memberjunction/integration-engine-base';
28
+ import { CurrentCatalogCI } from '@memberjunction/integration-engine';
29
+
30
+ /**
31
+ * What a schema build — and a refresh's re-key check — reads off a field row. The shared entity and
32
+ * the per-connection row both satisfy it.
33
+ */
34
+ export type BuildFieldRow = Pick<
35
+ MJIntegrationObjectFieldEntity,
36
+ 'Name' | 'DisplayName' | 'Description' | 'Type' | 'IsRequired' | 'AllowsNull' | 'Length' | 'Precision'
37
+ | 'Scale' | 'DefaultValue' | 'IsPrimaryKey' | 'IsUniqueKey' | 'IsReadOnly' | 'RelatedIntegrationObjectID' | 'Status'
38
+ | 'Sequence'
39
+ >;
40
+
41
+ /** A connection's field rows, keyed by lower-cased per-connection object id. */
42
+ export type ScopedBuildFields = Map<string, CompanyIntegrationObjectFieldRow[]>;
43
+
44
+ /** Objects per read. Each read is one `IN (…)` list, so this bounds the filter's length. */
45
+ export const SCOPED_FIELDS_CHUNK = 200;
46
+
47
+ /** A per-connection field row as the database returns it — before the legacy read names are derived. */
48
+ type StoredFieldRow = Omit<CompanyIntegrationObjectFieldRow, 'IntegrationObjectID' | 'RelatedIntegrationObjectID'>;
49
+
50
+ /**
51
+ * Loads the field rows of the connection in catalog scope for a schema build, or returns `null`
52
+ * when the build should read the shared catalog (no scope, or no per-connection catalog).
53
+ *
54
+ * @throws PER_CONNECTION_FIELDS_UNREADABLE when a read fails — never an empty answer.
55
+ */
56
+ export async function LoadScopedFieldsForBuild(integrationID: string, user: UserInfo): Promise<ScopedBuildFields | null> {
57
+ const ciID = CurrentCatalogCI();
58
+ if (!ciID) return null;
59
+ const engine = IntegrationEngineBase.Instance;
60
+ if (!engine.HasCompanyIntegrationCatalog(ciID)) return null;
61
+
62
+ const objects = engine.GetActiveIntegrationObjects(integrationID);
63
+ const byObject: ScopedBuildFields = new Map();
64
+ const rv = new RunView();
65
+ for (let i = 0; i < objects.length; i += SCOPED_FIELDS_CHUNK) {
66
+ const chunk = objects.slice(i, i + SCOPED_FIELDS_CHUNK);
67
+ const ids = chunk.map(o => `'${String(o.ID).replace(/'/g, "''")}'`).join(',');
68
+ const res = await rv.RunView<StoredFieldRow>({
69
+ EntityName: ENTITY_COMPANY_INTEGRATION_OBJECT_FIELDS,
70
+ ExtraFilter: `CompanyIntegrationObjectID IN (${ids})`,
71
+ ResultType: 'simple',
72
+ BypassCache: true,
73
+ IgnoreMaxRows: true,
74
+ }, user);
75
+ if (!res?.Success) {
76
+ throw new Error(
77
+ `PER_CONNECTION_FIELDS_UNREADABLE: could not read the field rows of connection ${ciID} ` +
78
+ `(objects ${i + 1}-${i + chunk.length} of ${objects.length}): ${res?.ErrorMessage ?? 'unknown error'}. ` +
79
+ `Refusing to build a schema from an empty field list.`
80
+ );
81
+ }
82
+ for (const row of res.Results ?? []) addRow(byObject, row);
83
+ }
84
+ const rowCount = [...byObject.values()].reduce((n, rows) => n + rows.length, 0);
85
+ console.log(`[LoadScopedFieldsForBuild] connection ${ciID}: ${rowCount} field rows for ${byObject.size} of ${objects.length} active objects`);
86
+ return byObject;
87
+ }
88
+
89
+ /**
90
+ * The fields a build reads for one object: the connection's rows when they were loaded, the
91
+ * engine's catalog otherwise. With loaded rows the engine is not consulted at all, so a cold
92
+ * per-connection field cache cannot empty the build.
93
+ */
94
+ export function FieldsForBuild(objectID: string, scoped: ScopedBuildFields | null | undefined, engine: IntegrationEngineBase): BuildFieldRow[] {
95
+ if (scoped) return scoped.get(String(objectID).trim().toLowerCase()) ?? [];
96
+ return engine.GetIntegrationObjectFields(objectID);
97
+ }
98
+
99
+ /**
100
+ * Files one stored row under its object, deriving the two legacy names the build reads
101
+ * (`IntegrationObjectID`, `RelatedIntegrationObjectID`) exactly as the engine's projection does.
102
+ */
103
+ function addRow(byObject: ScopedBuildFields, row: StoredFieldRow): void {
104
+ const key = String(row.CompanyIntegrationObjectID).trim().toLowerCase();
105
+ const projected: CompanyIntegrationObjectFieldRow = {
106
+ ...row,
107
+ IntegrationObjectID: row.CompanyIntegrationObjectID,
108
+ RelatedIntegrationObjectID: row.RelatedCompanyIntegrationObjectID ?? null,
109
+ };
110
+ const bucket = byObject.get(key);
111
+ if (bucket) bucket.push(projected);
112
+ else byObject.set(key, [projected]);
113
+ }
@@ -0,0 +1,71 @@
1
+ /**
2
+ * Refuses a schema build whose selected objects produced no target table.
3
+ *
4
+ * `buildTargetConfigs` drops a selected object that is not in the source schema, has no fields or
5
+ * has no primary key, and logs each drop — but every execution caller then built and ran a migration
6
+ * anyway. With nothing accepted that migration is a header-only file: RSU wrote it, opened and merged
7
+ * a PR, ran CodeGen, redeployed Explorer, and the sync "completed" over zero entity maps, every signal
8
+ * green. Seen 2026-10-29 on a PropFuel connection: three selections requested, three dropped as
9
+ * notInSchema, three empty migrations committed.
10
+ *
11
+ * A caller that asked for tables and gets none has failed, and should hear so with the names it asked
12
+ * for and why each was dropped — in the error itself, because an API or UI caller cannot read the
13
+ * server log line that used to be the only place the reasons appeared. Preview is deliberately not
14
+ * gated: an empty preview is an honest answer.
15
+ */
16
+ import type { TargetTableConfig } from '@memberjunction/integration-schema-builder';
17
+
18
+ /** Why `buildTargetConfigs` left each selected object out. */
19
+ export interface TargetConfigDrops {
20
+ NotInSchema: string[];
21
+ NoFields: string[];
22
+ NoPrimaryKey: string[];
23
+ }
24
+
25
+ /** How many names one list in the error shows before summarising the rest — the summary log's cap. */
26
+ export const TARGET_CONFIG_NAME_SAMPLE = 5;
27
+
28
+ /** An empty set of drop lists, for `buildTargetConfigs` to fill. */
29
+ export function NewTargetConfigDrops(): TargetConfigDrops {
30
+ return { NotInSchema: [], NoFields: [], NoPrimaryKey: [] };
31
+ }
32
+
33
+ /** `a, b, c, d, e and 163 more` — so a large selection cannot make a multi-KB error. */
34
+ export function SampleNames(names: ReadonlyArray<string>): string {
35
+ const head = names.slice(0, TARGET_CONFIG_NAME_SAMPLE).join(', ');
36
+ const rest = names.length - TARGET_CONFIG_NAME_SAMPLE;
37
+ return rest > 0 ? `${head} and ${rest} more` : head;
38
+ }
39
+
40
+ /**
41
+ * Throws `NO_TARGET_TABLES` when objects were selected and none produced a target table.
42
+ *
43
+ * @param drops why each object was dropped; without it the error lists the three possible reasons.
44
+ * @param alreadyCommitted what the caller had already written before the build, for a caller that
45
+ * cannot honestly say "Nothing was applied" (schema evolution persists the catalog and disables the
46
+ * removed objects' maps first).
47
+ */
48
+ export function AssertTargetConfigs(
49
+ objects: ReadonlyArray<{ SourceObjectName: string }>,
50
+ targetConfigs: ReadonlyArray<TargetTableConfig>,
51
+ sourceType: string,
52
+ drops?: TargetConfigDrops,
53
+ alreadyCommitted?: string,
54
+ ): void {
55
+ if (objects.length === 0 || targetConfigs.length > 0) return;
56
+ const reasons = drops ? describeDrops(drops) : '';
57
+ throw new Error(
58
+ `NO_TARGET_TABLES: none of the ${objects.length} selected object(s) for ${sourceType} produced a target table ` +
59
+ `(${SampleNames(objects.map(o => o.SourceObjectName))}). ` +
60
+ (reasons || 'Each was dropped as not in the source schema, without fields, or without a primary key.') +
61
+ ` ${alreadyCommitted ?? 'Nothing was applied.'} Re-run discovery for this connection and apply again.`
62
+ );
63
+ }
64
+
65
+ function describeDrops(drops: TargetConfigDrops): string {
66
+ const parts: string[] = [];
67
+ if (drops.NotInSchema.length > 0) parts.push(`not in the source schema: ${SampleNames(drops.NotInSchema)}`);
68
+ if (drops.NoFields.length > 0) parts.push(`no fields: ${SampleNames(drops.NoFields)}`);
69
+ if (drops.NoPrimaryKey.length > 0) parts.push(`no primary key: ${SampleNames(drops.NoPrimaryKey)}`);
70
+ return parts.length > 0 ? `Dropped as ${parts.join('; ')}.` : '';
71
+ }
@@ -82,7 +82,25 @@ type StartupSummaryData = {
82
82
  * logger.PrintSummary();
83
83
  * ```
84
84
  */
85
+ export type StartupSummarySnapshot = {
86
+ Level: ServerLogLevel;
87
+ Summary: Readonly<StartupSummaryData>;
88
+ PhaseTimings: ReadonlyArray<PhaseTiming>;
89
+ TotalStartupMs: number;
90
+ };
91
+
85
92
  export class StartupLogger {
93
+ private static _lastSnapshot: StartupSummarySnapshot | null = null;
94
+
95
+ /**
96
+ * Returns a read-only snapshot of the most recent server startup summary,
97
+ * used by diagnostic health snapshots to report startup phase durations without
98
+ * mutating or re-running startup logic.
99
+ */
100
+ public static GetLastStartupSnapshot(): StartupSummarySnapshot | null {
101
+ return StartupLogger._lastSnapshot;
102
+ }
103
+
86
104
  private readonly resolvedLevel: ServerLogLevel;
87
105
  private readonly phaseTimings: PhaseTiming[] = [];
88
106
  private readonly summary: StartupSummaryData = { AuthProviders: [] };
@@ -302,6 +320,12 @@ export class StartupLogger {
302
320
  // Clear the transient spinner before any block output so the line it
303
321
  // occupied is replaced cleanly by the summary.
304
322
  this.StopBoot();
323
+ StartupLogger._lastSnapshot = {
324
+ Level: this.resolvedLevel,
325
+ Summary: { ...this.summary, AuthProviders: [...this.summary.AuthProviders] },
326
+ PhaseTimings: this.phaseTimings.map((p) => ({ ...p })),
327
+ TotalStartupMs: performance.now(),
328
+ };
305
329
  if (this.resolvedLevel === 'minimal') {
306
330
  this.printReadyLineOnly();
307
331
  return;
@@ -18,6 +18,20 @@ import type { ApolloServerPlugin } from '@apollo/server';
18
18
  import type { GraphQLSchema } from 'graphql';
19
19
  import type { RunViewParams, UserInfo, BaseEntity, RunViewResult } from '@memberjunction/core';
20
20
 
21
+ /**
22
+ * What the server hands each middleware at startup.
23
+ */
24
+ export interface ServerMiddlewareContext {
25
+ /**
26
+ * Builds the path of a route under MJAPI's base path (`MJAPI_BASE_PATH`).
27
+ * `subpath` must start with `/`. Use it for every route a middleware registers or compares against.
28
+ * With `{ absolute: true }` it returns the public URL of the route (`MJAPI_PUBLIC_URL` + subpath).
29
+ * The options type is written out here, not imported from `apiRoot.ts`: packages that read this
30
+ * file's types from source must not compile `config.ts`.
31
+ */
32
+ ApiRoute: (subpath: string, options?: { absolute?: boolean }) => string;
33
+ }
34
+
21
35
  export abstract class BaseServerMiddleware {
22
36
  // --- Identity ---
23
37
 
@@ -37,9 +51,11 @@ export abstract class BaseServerMiddleware {
37
51
 
38
52
  /**
39
53
  * Optional async initialization (read config, warm caches, etc.).
40
- * Called by serve() after instantiation, before middleware/hooks are extracted.
54
+ * Called by serve() after instantiation, before middleware/hooks are extracted,
55
+ * so a context stored here is available to every other hook.
56
+ * @param context - The server's route builder. serve() always passes it.
41
57
  */
42
- async Initialize(): Promise<void> { /* no-op by default */ }
58
+ async Initialize(context?: ServerMiddlewareContext): Promise<void> { /* no-op by default */ }
43
59
 
44
60
  // --- Express Middleware ---
45
61
  // Each method corresponds to a named slot in the request pipeline.
@@ -73,8 +89,9 @@ export abstract class BaseServerMiddleware {
73
89
  * Called once during server setup with the Express app instance.
74
90
  * Use for: trust proxy settings, custom CORS, body parser config.
75
91
  * Return undefined to skip (default).
92
+ * @param context - The server's route builder. serve() always passes it.
76
93
  */
77
- ConfigureExpressApp?(app: Application): void | Promise<void>;
94
+ ConfigureExpressApp?(app: Application, context?: ServerMiddlewareContext): void | Promise<void>;
78
95
 
79
96
  // --- Apollo / GraphQL Extensions ---
80
97
 
@@ -8,7 +8,7 @@
8
8
  import { IMetadataProvider, LogError, UserInfo } from '@memberjunction/core';
9
9
  import { UUIDsEqual } from '@memberjunction/global';
10
10
  import type { MJAIAgentSessionEntity } from '@memberjunction/core-entities';
11
- import { AuthorizeSessionAccess, LoadSessionConversationScope } from './sessionAccess.js';
11
+ import { AuthorizeLoadedSession } from './sessionAccess.js';
12
12
 
13
13
  const SESSION_ENTITY = 'MJ: AI Agent Sessions';
14
14
 
@@ -54,11 +54,7 @@ export async function LoadOwnedRealtimeSession(
54
54
  );
55
55
  throw new RealtimeSessionOwnershipError(agentSessionId);
56
56
  }
57
- const conversation = await LoadSessionConversationScope(session, contextUser, provider);
58
- const access = AuthorizeSessionAccess(
59
- { SessionUserID: session.UserID, ConversationExternalID: conversation.ExternalID, ConversationReadable: conversation.Readable },
60
- contextUser,
61
- );
57
+ const access = await AuthorizeLoadedSession(session, contextUser, provider);
62
58
  if (!access.Allowed) {
63
59
  LogError(
64
60
  `LoadOwnedRealtimeSession: session access refused (${access.Reason}) — user ${contextUser.ID} attempted to operate on session ${agentSessionId}`,
@@ -199,6 +199,36 @@ export async function LoadSessionConversationScope(
199
199
  return { ExternalID, Readable };
200
200
  }
201
201
 
202
+ /**
203
+ * Decides whether `contextUser` may address an already-loaded `session`: the {@link AuthorizeSessionAccess}
204
+ * decision, reading the session's conversation only when that decision needs it.
205
+ *
206
+ * {@link AuthorizeSessionAccess} decides a principal without a scope identity — a named user with no magic-link
207
+ * scope, the usual signed-in caller — from the owner check alone, and refuses a non-owner before it looks at
208
+ * anything else, so neither needs the conversation read. This function and that one encode the same rule on
209
+ * purpose and live together for that reason: change the rule there, change the read here. The
210
+ * `AuthorizeLoadedSession` table test in `sessionAccess.test.ts` fails if the two ever disagree.
211
+ *
212
+ * @param session - the session, loaded through the caller's own gate
213
+ * @param contextUser - the authenticated caller; the conversation, when read, is read as this user (RLS applies)
214
+ * @param provider - the request-scoped provider
215
+ */
216
+ export async function AuthorizeLoadedSession(
217
+ session: MJAIAgentSessionEntity,
218
+ contextUser: UserInfo,
219
+ provider: IMetadataProvider,
220
+ ): Promise<SessionAccessDecision> {
221
+ const ownsSession = !!contextUser?.ID && !!session.UserID && UUIDsEqual(session.UserID, contextUser.ID);
222
+ if (!ownsSession || !ResolveSessionScopeIdentity(contextUser)) {
223
+ return AuthorizeSessionAccess({ SessionUserID: session.UserID, ConversationExternalID: null, ConversationReadable: false }, contextUser);
224
+ }
225
+ const conversation = await LoadSessionConversationScope(session, contextUser, provider);
226
+ return AuthorizeSessionAccess(
227
+ { SessionUserID: session.UserID, ConversationExternalID: conversation.ExternalID, ConversationReadable: conversation.Readable },
228
+ contextUser,
229
+ );
230
+ }
231
+
202
232
  /**
203
233
  * Reads a conversation's owner and scope id under `contextUser` (so its row-level security applies), for a
204
234
  * caller that addresses the conversation directly rather than through a session — a text agent turn, for
@@ -861,6 +861,7 @@ export class WidgetSessionService {
861
861
  ExtraFilter: `VisitorKey = '${visitorKey}' AND ApplicationID = '${applicationId.replace(/'/g, "''")}'`,
862
862
  OrderBy: '__mj_CreatedAt DESC',
863
863
  MaxRows: 1,
864
+ SkipTotalRowCount: true,
864
865
  Fields: ['ID'],
865
866
  ResultType: 'simple',
866
867
  },
@@ -885,6 +886,7 @@ export class WidgetSessionService {
885
886
  EntityName: WIDGET_ENTITY,
886
887
  ExtraFilter: `PublicKey = '${key.replace(/'/g, "''")}'`,
887
888
  MaxRows: 1,
889
+ SkipTotalRowCount: true,
888
890
  ResultType: 'entity_object',
889
891
  },
890
892
  contextUser,
@@ -79,6 +79,7 @@ export async function ResolveIdentityByEmail(
79
79
  ExtraFilter: `${emailField} = '${trimmed.replace(/'/g, "''")}'`,
80
80
  Fields: pkNames,
81
81
  MaxRows: 1,
82
+ SkipTotalRowCount: true,
82
83
  ResultType: 'simple',
83
84
  },
84
85
  contextUser,
@@ -14,6 +14,7 @@
14
14
 
15
15
  import { BuildSessionClaims, IsRoleGrantable } from '../auth/magicLink/magicLinkCore.js';
16
16
  import type { MagicLinkJWTClaims } from '../auth/magicLink/types.js';
17
+ import { NormalizeOrigin } from '../requestOrigin.js';
17
18
 
18
19
  /**
19
20
  * The magic-link resource-scope `resourceType` used for a widget guest session. Pairs with the
@@ -77,12 +78,12 @@ export function ParseAllowedOrigins(allowedOrigins: string | null | undefined):
77
78
  list = JSON.parse(raw);
78
79
  } catch {
79
80
  // Not JSON — tolerate a CSV form.
80
- return raw.split(',').map((s) => normalizeOrigin(s)).filter((s): s is string => !!s);
81
+ return raw.split(',').map((s) => NormalizeOrigin(s)).filter((s): s is string => !!s);
81
82
  }
82
83
  if (!Array.isArray(list)) {
83
84
  return [];
84
85
  }
85
- return list.filter((s): s is string => typeof s === 'string').map(normalizeOrigin).filter((s): s is string => !!s);
86
+ return list.filter((s): s is string => typeof s === 'string').map(NormalizeOrigin).filter((s): s is string => !!s);
86
87
  }
87
88
 
88
89
  /** @deprecated Use {@link ParseAllowedOrigins}. */
@@ -90,11 +91,6 @@ export function parseAllowedOrigins(allowedOrigins: string | null | undefined):
90
91
  return ParseAllowedOrigins(allowedOrigins);
91
92
  }
92
93
 
93
- /** Lowercases + trims an origin and strips a trailing slash for stable comparison. */
94
- function normalizeOrigin(origin: string): string {
95
- return origin.trim().toLowerCase().replace(/\/+$/, '');
96
- }
97
-
98
94
  /**
99
95
  * Parses the widget's `EnabledChannels` column into a clean list of channel names (Phase 2). Accepts a
100
96
  * JSON array (preferred, e.g. `["Whiteboard"]`) or a comma-separated string (tolerated). Returns an
@@ -129,7 +125,7 @@ export function IsOriginAllowed(requestOrigin: string | null | undefined, allowe
129
125
  if (!requestOrigin || allowedOrigins.length === 0) {
130
126
  return false;
131
127
  }
132
- const candidate = normalizeOrigin(requestOrigin);
128
+ const candidate = NormalizeOrigin(requestOrigin);
133
129
  return allowedOrigins.some((o) => o === candidate);
134
130
  }
135
131
 
@@ -190,6 +190,7 @@ async function loadWidgetById(
190
190
  EntityName: WIDGET_ENTITY,
191
191
  ExtraFilter: `ID = '${widgetId.replace(/'/g, "''")}'`,
192
192
  MaxRows: 1,
193
+ SkipTotalRowCount: true,
193
194
  ResultType: 'entity_object',
194
195
  },
195
196
  elevatedUser,
@@ -0,0 +1,64 @@
1
+ /**
2
+ * Request-origin helpers shared by the GraphQL context (`AppContext.RequestOrigin`) and the realtime
3
+ * widget origin allowlist, so an origin read from a request and an origin read from an allowlist are
4
+ * normalized the same way and compare with plain string equality.
5
+ */
6
+
7
+ /**
8
+ * Shape of a serialized http(s) origin: scheme, host and optional port, with no path, query,
9
+ * fragment or credentials. Applied after {@link NormalizeOrigin}, so it only needs lowercase.
10
+ */
11
+ const HTTP_ORIGIN_PATTERN = /^https?:\/\/[^/?#@\s]+$/;
12
+
13
+ /** Character code of `/`. */
14
+ const SLASH_CHAR_CODE = 47;
15
+
16
+ /**
17
+ * Normalizes an origin for stable comparison: trims whitespace, lowercases, and strips trailing
18
+ * slashes. It does not parse the value or rewrite ports, so `https://a.com:443` stays as written.
19
+ * @param origin an origin as written in a header or an allowlist entry
20
+ */
21
+ export function NormalizeOrigin(origin: string): string {
22
+ return StripTrailingSlashes(origin.trim().toLowerCase());
23
+ }
24
+
25
+ /**
26
+ * Removes trailing `/` characters with a linear scan. A `/\/+$/` regex is quadratic on input with
27
+ * many `/` not at the end, and this runs on a request header the client controls.
28
+ */
29
+ function StripTrailingSlashes(value: string): string {
30
+ let end = value.length;
31
+ while (end > 0 && value.charCodeAt(end - 1) === SLASH_CHAR_CODE) {
32
+ end--;
33
+ }
34
+ return value.slice(0, end);
35
+ }
36
+
37
+ /**
38
+ * Reads a request's `Origin` header into a normalized origin (`scheme://host[:port]`).
39
+ *
40
+ * Returns undefined when the header is absent, repeated, blank, opaque (`null`, which sandboxed
41
+ * frames and privacy-sensitive redirects send), or anything other than a parseable http(s) origin.
42
+ * Ports are kept as sent: browsers omit default ports from `Origin`, and allowlist entries pass
43
+ * through the same {@link NormalizeOrigin}, so both sides agree without rewriting.
44
+ * @param originHeader the raw `Origin` header value from the request
45
+ */
46
+ export function ReadRequestOrigin(originHeader: string | string[] | undefined): string | undefined {
47
+ if (typeof originHeader !== 'string') {
48
+ return undefined;
49
+ }
50
+ const normalized = NormalizeOrigin(originHeader);
51
+ return IsHttpOrigin(normalized) ? normalized : undefined;
52
+ }
53
+
54
+ /** True when a normalized value is a parseable http(s) origin with no path, query, fragment or credentials. */
55
+ function IsHttpOrigin(normalized: string): boolean {
56
+ if (!HTTP_ORIGIN_PATTERN.test(normalized)) {
57
+ return false;
58
+ }
59
+ try {
60
+ return new URL(normalized).host.length > 0;
61
+ } catch {
62
+ return false;
63
+ }
64
+ }
@@ -17,13 +17,18 @@ import type {
17
17
  import type { EntityInfo } from '@memberjunction/core';
18
18
  import type { UserPayload } from '../types.js';
19
19
  import { PublishStatusUpdate, StatusUpdateOwnerID } from '../generic/PushStatusResolver.js';
20
- import { ProjectEntityObjectForCaller } from '../generic/fieldSecurityTransport.js';
20
+ import { ProjectEntityObjectForCaller, ProjectValuesForCaller } from '../generic/fieldSecurityTransport.js';
21
21
 
22
22
  const SIGNIFICANT_STEPS = ['prompt_execution', 'action_execution', 'subagent_execution', 'decision_processing'];
23
23
 
24
24
  /** The run fields the published messages read. Entities and test fakes both satisfy this. */
25
25
  interface StatusRun {
26
26
  ID?: string;
27
+ AgentID?: string;
28
+ Agent?: string | null;
29
+ ParentRunID?: string | null;
30
+ Status?: string;
31
+ ConversationID?: string | null;
27
32
  ConversationDetailID?: string;
28
33
  ErrorMessage?: string;
29
34
  Steps?: Array<{ StepName?: string }>;
@@ -31,6 +36,9 @@ interface StatusRun {
31
36
  GetAll?: () => Record<string, unknown>;
32
37
  }
33
38
 
39
+ /** The run fields a streamed chunk carries. */
40
+ type RunSummary = Pick<StatusRun, 'ID' | 'AgentID' | 'Agent' | 'ParentRunID' | 'Status' | 'ConversationID' | 'ConversationDetailID'>;
41
+
34
42
  export class AgentRunStatusPublisher {
35
43
  private run: StatusRun | null = null;
36
44
 
@@ -102,7 +110,7 @@ export class AgentRunStatusPublisher {
102
110
  agentRunId: agentRun.ID,
103
111
  type: 'streaming',
104
112
  ...this.replyRow(),
105
- agentRun: this.runValues(agentRun),
113
+ agentRun: this.runSummary(agentRun),
106
114
  streaming: {
107
115
  content: chunk.content,
108
116
  isPartial: !chunk.isComplete,
@@ -188,6 +196,23 @@ export class AgentRunStatusPublisher {
188
196
  return ProjectEntityObjectForCaller({ EntityInfo: agentRun.EntityInfo, GetAll: () => getAll.call(agentRun) }, this.userPayload?.userRecord);
189
197
  }
190
198
 
199
+ /**
200
+ * The run's identifying fields as the recipient may read them. A streamed chunk carries only
201
+ * these: the run's `GetAll()` holds every step and prompt run, and a run streams many chunks.
202
+ */
203
+ private runSummary(agentRun: StatusRun): RunSummary {
204
+ const summary: RunSummary = {
205
+ ID: agentRun.ID,
206
+ AgentID: agentRun.AgentID,
207
+ Agent: agentRun.Agent,
208
+ ParentRunID: agentRun.ParentRunID,
209
+ Status: agentRun.Status,
210
+ ConversationID: agentRun.ConversationID,
211
+ ConversationDetailID: agentRun.ConversationDetailID,
212
+ };
213
+ return ProjectValuesForCaller(agentRun.EntityInfo, summary, this.userPayload?.userRecord);
214
+ }
215
+
191
216
  private publish(type: 'ExecutionProgress' | 'StreamingContent', data: unknown): void {
192
217
  PublishStatusUpdate(this.pubSub, {
193
218
  sessionId: this.userPayload.sessionId,
@@ -23,8 +23,12 @@ import {
23
23
  RealtimeSessionOwnershipError,
24
24
  } from '../realtimeSessions/ownedRealtimeSession.js';
25
25
 
26
- /** The `CloseReason` values a client may stamp on its own session; `Janitor` and `Shutdown` are the server's. */
27
- const CLIENT_CLOSE_REASONS: readonly SessionCloseReason[] = ['Explicit', 'Error'];
26
+ /**
27
+ * The `CloseReason` values a client may stamp on its own session; `Idle`, `Janitor` and `Shutdown` are the server's.
28
+ * `TimeLimit` is a claim: the browser's deadline alarm fired, and {@link SessionManager.CloseSession} verifies it
29
+ * against the session's stored deadline, recording `Explicit` instead when the deadline is not near.
30
+ */
31
+ const CLIENT_CLOSE_REASONS: readonly SessionCloseReason[] = ['Explicit', 'Error', 'TimeLimit'];
28
32
 
29
33
  /** Reads the mutation's optional `closeReason`: absent means `Explicit`; a server-only or unknown value is refused. */
30
34
  function ParseClientCloseReason(closeReason: string | null | undefined): SessionCloseReason {