@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,252 @@
1
+ import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
2
+ import { RunView, type DatabaseProviderBase, type UserInfo } from '@memberjunction/core';
3
+ import { CatalogIdentityKey, ClearRekeyedObjectData, DecideRekeyed, IdentityKeyFields } from '../integration/EntityMapLifecycle.js';
4
+
5
+ /**
6
+ * A connector upgrade can change what identifies a row. PheedLoop 1.4.6 makes Attendees
7
+ * `code + eventCode`, because the object is fetched once per event and an attendee at two events
8
+ * comes back twice carrying the same `code`.
9
+ *
10
+ * `ToExternalRecord` joins every IsPrimaryKey field with '|' in Sequence order, so both the
11
+ * MEMBERS and the ORDER are part of identity. These pin the two pure decisions that drive the
12
+ * evolution's clear-and-rebuild.
13
+ */
14
+ const f = (Name: string, IsPrimaryKey: boolean | null, Sequence: number | null) =>
15
+ ({ Name, IsPrimaryKey, Sequence });
16
+
17
+ describe('IdentityKeyFields', () => {
18
+ it('takes only key fields, in Sequence order — not declaration order', () => {
19
+ expect(IdentityKeyFields([
20
+ f('eventCode', true, 2),
21
+ f('is_checked_in', false, 3),
22
+ f('code', true, 1),
23
+ ])).toEqual(['code', 'eventCode']);
24
+ });
25
+
26
+ it('is empty when nothing is flagged', () => {
27
+ expect(IdentityKeyFields([f('a', false, 1), f('b', null, 2)])).toEqual([]);
28
+ });
29
+
30
+ it('treats a missing Sequence as 0 rather than dropping the field', () => {
31
+ expect(IdentityKeyFields([f('b', true, null), f('a', true, 1)])).toEqual(['b', 'a']);
32
+ });
33
+
34
+ it('does not mutate the caller array', () => {
35
+ const input = [f('z', true, 9), f('a', true, 1)];
36
+ IdentityKeyFields(input);
37
+ expect(input[0].Name).toBe('z');
38
+ });
39
+ });
40
+
41
+ describe('DecideRekeyed', () => {
42
+ it('a column joining the key is a re-key — the PheedLoop case', () => {
43
+ expect(DecideRekeyed(['code'], ['code', 'eventCode'])).toBe(true);
44
+ });
45
+
46
+ it('a column leaving the key is a re-key', () => {
47
+ expect(DecideRekeyed(['code', 'eventCode'], ['code'])).toBe(true);
48
+ });
49
+
50
+ it('the same columns in a different ORDER is a re-key — the identity string differs', () => {
51
+ expect(DecideRekeyed(['code', 'eventCode'], ['eventCode', 'code'])).toBe(true);
52
+ });
53
+
54
+ it('an unchanged key is not', () => {
55
+ expect(DecideRekeyed(['code', 'eventCode'], ['code', 'eventCode'])).toBe(false);
56
+ });
57
+
58
+ it('matches case-insensitively, as every catalog-to-map reconciliation does', () => {
59
+ expect(DecideRekeyed(['Code'], ['code'])).toBe(false);
60
+ });
61
+
62
+ // The guard that stops this from clearing the fleet. An empty BUILT key means the entity's
63
+ // fields could not be read, not that the table never had a key — and treating unknown as
64
+ // empty would re-key (and therefore WIPE) every table on a workspace whose metadata failed
65
+ // to load.
66
+ it('an unreadable built key is never a re-key, however different the catalog looks', () => {
67
+ expect(DecideRekeyed([], ['code', 'eventCode'])).toBe(false);
68
+ expect(DecideRekeyed([], [])).toBe(false);
69
+ });
70
+
71
+ it('a catalog that reports no key against a real built key IS a re-key', () => {
72
+ expect(DecideRekeyed(['code'], [])).toBe(true);
73
+ });
74
+
75
+ // The same guard on the catalog side. A refresh reading a per-connection object whose fields
76
+ // were never warmed got `[]`, and "no key" against a real built key cleared the object's
77
+ // record maps and rows on every refresh.
78
+ it('an unread catalog key is never a re-key', () => {
79
+ expect(DecideRekeyed(['code'], null)).toBe(false);
80
+ expect(DecideRekeyed(['code', 'eventCode'], CatalogIdentityKey([]))).toBe(false);
81
+ });
82
+ });
83
+
84
+ describe('CatalogIdentityKey', () => {
85
+ const row = (Name: string, IsPrimaryKey: boolean, Sequence: number, Status = 'Active') =>
86
+ ({ Name, IsPrimaryKey, Sequence, Status });
87
+
88
+ it('is null when no field row was read — a cold per-connection object answers []', () => {
89
+ expect(CatalogIdentityKey([])).toBeNull();
90
+ });
91
+
92
+ it('is null when no row is Active — nothing describes the object', () => {
93
+ expect(CatalogIdentityKey([row('code', true, 1, 'Inactive')])).toBeNull();
94
+ });
95
+
96
+ it('is the Active key fields in Sequence order', () => {
97
+ expect(CatalogIdentityKey([
98
+ row('eventCode', true, 2),
99
+ row('old_key', true, 0, 'Inactive'),
100
+ row('code', true, 1),
101
+ row('name', false, 3),
102
+ ])).toEqual(['code', 'eventCode']);
103
+ });
104
+
105
+ it('is [] when Active rows declare no key — read, and keyless, so still a re-key', () => {
106
+ const key = CatalogIdentityKey([row('name', false, 1)]);
107
+ expect(key).toEqual([]);
108
+ expect(DecideRekeyed(['code'], key)).toBe(true);
109
+ });
110
+ });
111
+
112
+ /**
113
+ * Two connections to the same vendor share one schema, so they share every entity table. The clear
114
+ * may only touch the rows THIS connection's record maps point at; the other connection's rows in
115
+ * the same table are not its to delete.
116
+ */
117
+ describe('ClearRekeyedObjectData', () => {
118
+ const USER = { ID: 'user-1' } as UserInfo;
119
+ const RECORD_MAPS = { Name: 'MJ: Company Integration Record Maps', SchemaName: '__mj', BaseTable: 'CompanyIntegrationRecordMap' };
120
+ const TARGET = { ExternalObjectName: 'attendees', EntityMapID: 'M1', EntityID: 'E1', SchemaName: 'pheedloop', BaseTable: 'attendees' };
121
+ type MapRow = { ID: string; EntityRecordID: string };
122
+ type RunViewParams = Parameters<RunView['RunView']>[0];
123
+ type RunViewResult = Awaited<ReturnType<RunView['RunView']>>;
124
+
125
+ /** A provider that records every statement and transaction step, in order. */
126
+ function fakeProvider(key: string[], failOn?: string) {
127
+ const log: string[] = [];
128
+ const provider = {
129
+ Dialect: {
130
+ QuoteIdentifier: (s: string) => `[${s}]`,
131
+ QuoteStringLiteral: (s: string) => `'${s.replace(/'/g, "''")}'`,
132
+ },
133
+ Entities: [RECORD_MAPS],
134
+ EntityByName: (name: string) => (name === RECORD_MAPS.Name ? RECORD_MAPS : undefined),
135
+ EntityByID: (id: string) => (id === 'E1' ? { PrimaryKeys: key.map(Name => ({ Name })) } : undefined),
136
+ ExecuteSQL: async (sql: string) => {
137
+ log.push(sql);
138
+ if (failOn && sql.includes(failOn)) throw new Error('deadlock victim');
139
+ return [];
140
+ },
141
+ BeginTransaction: async () => { log.push('BEGIN'); },
142
+ CommitTransaction: async () => { log.push('COMMIT'); },
143
+ RollbackTransaction: async () => { log.push('ROLLBACK'); },
144
+ };
145
+ return { provider: provider as unknown as DatabaseProviderBase, log };
146
+ }
147
+
148
+ let runView: ReturnType<typeof vi.spyOn>;
149
+ /** Answers the record-map reads one page at a time, then empty. */
150
+ function recordMapPages(...pages: MapRow[][]) {
151
+ for (const page of pages) {
152
+ runView.mockResolvedValueOnce({ Success: true, Results: page } as unknown as RunViewResult);
153
+ }
154
+ runView.mockResolvedValue({ Success: true, Results: [] } as unknown as RunViewResult);
155
+ }
156
+ const reads = () => runView.mock.calls.map(c => c[0] as RunViewParams);
157
+
158
+ beforeEach(() => { runView = vi.spyOn(RunView.prototype, 'RunView'); });
159
+ afterEach(() => { vi.restoreAllMocks(); });
160
+
161
+ it('deletes only the rows this connection\'s record maps point at — never the whole table', async () => {
162
+ const { provider, log } = fakeProvider(['ID']);
163
+ recordMapPages([{ ID: 'm1', EntityRecordID: 'r1' }, { ID: 'm2', EntityRecordID: 'r2' }]);
164
+
165
+ const cleared = await ClearRekeyedObjectData([TARGET], 'CI-A', USER, provider);
166
+
167
+ expect(cleared).toEqual(['attendees']);
168
+ expect(log).not.toContain('DELETE FROM [pheedloop].[attendees]');
169
+ expect(log).toContain("DELETE FROM [pheedloop].[attendees] WHERE [ID] IN ('r1','r2')");
170
+ expect(reads()[0].EntityName).toBe(RECORD_MAPS.Name);
171
+ expect(reads()[0].ExtraFilter).toBe("CompanyIntegrationID='CI-A' AND EntityID='E1'");
172
+ });
173
+
174
+ it('deletes the rows while their maps still say which rows they are, in one transaction with the maps', async () => {
175
+ const { provider, log } = fakeProvider(['ID']);
176
+ recordMapPages([{ ID: 'm1', EntityRecordID: 'r1' }]);
177
+
178
+ await ClearRekeyedObjectData([TARGET], 'CI-A', USER, provider);
179
+
180
+ expect(log).toEqual([
181
+ 'BEGIN',
182
+ "DELETE FROM [pheedloop].[attendees] WHERE [ID] IN ('r1')",
183
+ "DELETE FROM [__mj].[CompanyIntegrationRecordMap] WHERE [CompanyIntegrationID]='CI-A' AND [EntityID]='E1' AND [ID] IN ('m1')",
184
+ 'COMMIT',
185
+ ]);
186
+ });
187
+
188
+ it('matches every column of a composite key, split the way the engine joined it', async () => {
189
+ const { provider, log } = fakeProvider(['code', 'eventCode']);
190
+ recordMapPages([{ ID: 'm1', EntityRecordID: 'a|1' }, { ID: 'm2', EntityRecordID: "b'x|2" }]);
191
+
192
+ await ClearRekeyedObjectData([TARGET], 'CI-A', USER, provider);
193
+
194
+ expect(log).toContain(
195
+ "DELETE FROM [pheedloop].[attendees] WHERE ([code]='a' AND [eventCode]='1') OR ([code]='b''x' AND [eventCode]='2')"
196
+ );
197
+ });
198
+
199
+ it('leaves a map whose id cannot name exactly one row, and does not report the object cleared', async () => {
200
+ // 'a|1|z' against a two-column key is a value that itself contained '|': splitting it would
201
+ // name some OTHER row. An empty component is a null key value, which matches nothing safely.
202
+ const { provider, log } = fakeProvider(['code', 'eventCode']);
203
+ recordMapPages([
204
+ { ID: 'm1', EntityRecordID: 'a|1' },
205
+ { ID: 'm2', EntityRecordID: 'a|1|z' },
206
+ { ID: 'm3', EntityRecordID: '|2' },
207
+ ]);
208
+
209
+ const cleared = await ClearRekeyedObjectData([TARGET], 'CI-A', USER, provider);
210
+
211
+ expect(cleared).toEqual([]);
212
+ expect(log).toContain("DELETE FROM [pheedloop].[attendees] WHERE ([code]='a' AND [eventCode]='1')");
213
+ expect(log.some(s => s.includes('m2') || s.includes('m3'))).toBe(false);
214
+ });
215
+
216
+ it('pages through the maps by id, so a big table is never read or deleted whole', async () => {
217
+ const { provider, log } = fakeProvider(['ID']);
218
+ recordMapPages(
219
+ [{ ID: 'm1', EntityRecordID: 'r1' }, { ID: 'm2', EntityRecordID: 'r2' }],
220
+ [{ ID: 'm3', EntityRecordID: 'r3' }],
221
+ );
222
+
223
+ const cleared = await ClearRekeyedObjectData([TARGET], 'CI-A', USER, provider, 2);
224
+
225
+ expect(cleared).toEqual(['attendees']);
226
+ expect(reads()).toHaveLength(2);
227
+ expect(reads()[0]).toMatchObject({ OrderBy: 'ID', MaxRows: 2, SkipTotalRowCount: true, BypassCache: true });
228
+ expect(reads()[1].ExtraFilter).toBe("CompanyIntegrationID='CI-A' AND EntityID='E1' AND ID > 'm2'");
229
+ expect(log.filter(s => s === 'COMMIT')).toHaveLength(2);
230
+ });
231
+
232
+ it('rolls a failed chunk back and does not report the object cleared', async () => {
233
+ const { provider, log } = fakeProvider(['ID'], 'CompanyIntegrationRecordMap');
234
+ recordMapPages([{ ID: 'm1', EntityRecordID: 'r1' }]);
235
+
236
+ const cleared = await ClearRekeyedObjectData([TARGET], 'CI-A', USER, provider);
237
+
238
+ expect(cleared).toEqual([]);
239
+ expect(log).toContain('ROLLBACK');
240
+ expect(log).not.toContain('COMMIT');
241
+ });
242
+
243
+ it('deletes nothing when the record maps cannot be read', async () => {
244
+ const { provider, log } = fakeProvider(['ID']);
245
+ runView.mockResolvedValue({ Success: false, Results: [], ErrorMessage: 'timeout' } as unknown as RunViewResult);
246
+
247
+ const cleared = await ClearRekeyedObjectData([TARGET], 'CI-A', USER, provider);
248
+
249
+ expect(cleared).toEqual([]);
250
+ expect(log).toEqual([]);
251
+ });
252
+ });
@@ -0,0 +1,213 @@
1
+ import 'reflect-metadata';
2
+ import { describe, it, expect, vi, beforeEach } from 'vitest';
3
+ import type { PubSubEngine } from 'type-graphql';
4
+
5
+ vi.mock('../config.js', () => ({
6
+ configInfo: {},
7
+ }));
8
+
9
+ import { RegisterClass } from '@memberjunction/global';
10
+ import {
11
+ BaseRemotableOperation,
12
+ RemoteOperationError,
13
+ type IMetadataProvider,
14
+ type RemoteOpInvokeOptions,
15
+ type RemoteOpResult,
16
+ type RemoteOpServerContext,
17
+ type UserInfo,
18
+ } from '@memberjunction/core';
19
+ import { RemoteOperationEngineBase } from '@memberjunction/core-entities';
20
+ import { DispatchRemoteOperationInProcess } from '@memberjunction/core';
21
+ import { ExecuteRemoteOperationResolver, ExecuteRemoteOperationInput } from '../resolvers/ExecuteRemoteOperationResolver.js';
22
+ import type { AppContext } from '../types.js';
23
+
24
+ interface TestOpInput {
25
+ message: string;
26
+ }
27
+
28
+ interface TestOpOutput {
29
+ echoed: string;
30
+ origin?: string;
31
+ ip?: string;
32
+ sessionId?: string;
33
+ apiKeyHash?: string;
34
+ }
35
+
36
+ @RegisterClass(BaseRemotableOperation, 'TestServer.ContextEcho')
37
+ class ContextEchoOperation extends BaseRemotableOperation<TestOpInput, TestOpOutput> {
38
+ public readonly OperationKey = 'TestServer.ContextEcho';
39
+ public override readonly ContractFingerprint = 'ro_fp_expected12345678';
40
+
41
+ protected override async Authorize(
42
+ _input: TestOpInput,
43
+ _user: UserInfo,
44
+ context?: RemoteOpServerContext,
45
+ ): Promise<boolean> {
46
+ return context?.requestOrigin !== 'https://evil.example.com';
47
+ }
48
+
49
+ protected override async InternalExecute(
50
+ input: TestOpInput,
51
+ _provider: IMetadataProvider,
52
+ _user: UserInfo,
53
+ context: RemoteOpServerContext,
54
+ ): Promise<TestOpOutput> {
55
+ return {
56
+ echoed: input.message,
57
+ origin: context.requestOrigin,
58
+ ip: context.clientIp,
59
+ sessionId: context.sessionId,
60
+ apiKeyHash: context.apiKeyHash,
61
+ };
62
+ }
63
+ }
64
+ void ContextEchoOperation;
65
+
66
+ @RegisterClass(BaseRemotableOperation, 'TestServer.StructuredFailure')
67
+ class StructuredFailureOperation extends BaseRemotableOperation<TestOpInput, TestOpOutput> {
68
+ public readonly OperationKey = 'TestServer.StructuredFailure';
69
+
70
+ protected override async InternalExecute(): Promise<TestOpOutput> {
71
+ throw new RemoteOperationError('CUSTOM_DOMAIN_CODE', 'Structured domain validation rejected request');
72
+ }
73
+ }
74
+ void StructuredFailureOperation;
75
+
76
+ describe('ExecuteRemoteOperationResolver', () => {
77
+ const fakeUser = { ID: 'user-1', Email: 'ada@example.com' } as UserInfo;
78
+ const pubSub: PubSubEngine = {
79
+ publish: vi.fn(async () => undefined),
80
+ subscribe: vi.fn(async () => 1),
81
+ unsubscribe: vi.fn(),
82
+ asyncIterator: vi.fn(),
83
+ } as PubSubEngine;
84
+
85
+ beforeEach(() => {
86
+ vi.spyOn(RemoteOperationEngineBase.Instance, 'Config').mockResolvedValue(undefined);
87
+ vi.spyOn(RemoteOperationEngineBase.Instance, 'IsInvokable').mockReturnValue({ Invokable: true });
88
+ vi.spyOn(RemoteOperationEngineBase.Instance, 'GetContractFingerprint').mockReturnValue(undefined);
89
+ });
90
+
91
+ function buildContext(overrides: Partial<AppContext> = {}): {
92
+ ctx: AppContext;
93
+ capturedOptions: RemoteOpInvokeOptions[];
94
+ } {
95
+ const capturedOptions: RemoteOpInvokeOptions[] = [];
96
+ const provider = {
97
+ RouteOperation: async <TIn, TOut>(
98
+ key: string,
99
+ opInput: TIn,
100
+ options: RemoteOpInvokeOptions = {},
101
+ ): Promise<RemoteOpResult<TOut>> => {
102
+ capturedOptions.push(options);
103
+ return DispatchRemoteOperationInProcess<TIn, TOut>(
104
+ key,
105
+ opInput,
106
+ options,
107
+ provider as IMetadataProvider,
108
+ fakeUser,
109
+ );
110
+ },
111
+ };
112
+
113
+ const ctx: AppContext = {
114
+ dataSource: {} as AppContext['dataSource'],
115
+ dataSources: [],
116
+ providers: [{ provider: provider as AppContext['providers'][number]['provider'], type: 'Read-Write' }],
117
+ userPayload: {
118
+ email: 'ada@example.com',
119
+ userRecord: fakeUser,
120
+ sessionId: 'sess-abc-123',
121
+ apiKeyHash: 'hash-xyz-987',
122
+ },
123
+ RequestOrigin: 'https://app.example.com',
124
+ ClientIp: '198.51.100.24',
125
+ ...overrides,
126
+ };
127
+ return { ctx, capturedOptions };
128
+ }
129
+
130
+ it('forwards RequestOrigin, ClientIp, sessionId, and apiKeyHash into RouteOperation and Authorize/InternalExecute', async () => {
131
+ const resolver = new ExecuteRemoteOperationResolver();
132
+ const { ctx, capturedOptions } = buildContext();
133
+
134
+ const input: ExecuteRemoteOperationInput = {
135
+ operationKey: 'TestServer.ContextEcho',
136
+ inputJSON: JSON.stringify({ message: 'hi' }),
137
+ invokeMode: 'attached',
138
+ contractFingerprint: 'ro_fp_expected12345678',
139
+ };
140
+
141
+ const res = await resolver.ExecuteRemoteOperation(input, ctx, pubSub);
142
+ expect(res.success).toBe(true);
143
+ expect(res.resultCode).toBe('SUCCESS');
144
+ expect(capturedOptions).toHaveLength(1);
145
+ expect(capturedOptions[0].requestOrigin).toBe('https://app.example.com');
146
+ expect(capturedOptions[0].clientIp).toBe('198.51.100.24');
147
+ expect(capturedOptions[0].sessionId).toBe('sess-abc-123');
148
+ expect(capturedOptions[0].apiKeyHash).toBe('hash-xyz-987');
149
+
150
+ const parsed = JSON.parse(res.outputJSON!) as TestOpOutput;
151
+ expect(parsed).toEqual({
152
+ echoed: 'hi',
153
+ origin: 'https://app.example.com',
154
+ ip: '198.51.100.24',
155
+ sessionId: 'sess-abc-123',
156
+ apiKeyHash: 'hash-xyz-987',
157
+ });
158
+ });
159
+
160
+ it('enforces Authorize against context.requestOrigin', async () => {
161
+ const resolver = new ExecuteRemoteOperationResolver();
162
+ const { ctx } = buildContext({ RequestOrigin: 'https://evil.example.com' });
163
+
164
+ const res = await resolver.ExecuteRemoteOperation(
165
+ {
166
+ operationKey: 'TestServer.ContextEcho',
167
+ inputJSON: JSON.stringify({ message: 'hi' }),
168
+ invokeMode: 'attached',
169
+ },
170
+ ctx,
171
+ pubSub,
172
+ );
173
+ expect(res.success).toBe(false);
174
+ expect(res.resultCode).toBe('FORBIDDEN');
175
+ });
176
+
177
+ it('rejects stale client contractFingerprint with CONTRACT_MISMATCH before running operation', async () => {
178
+ const resolver = new ExecuteRemoteOperationResolver();
179
+ const { ctx, capturedOptions } = buildContext();
180
+
181
+ const res = await resolver.ExecuteRemoteOperation(
182
+ {
183
+ operationKey: 'TestServer.ContextEcho',
184
+ inputJSON: JSON.stringify({ message: 'hi' }),
185
+ invokeMode: 'attached',
186
+ contractFingerprint: 'ro_fp_stale00000000000',
187
+ },
188
+ ctx,
189
+ pubSub,
190
+ );
191
+ expect(res.success).toBe(false);
192
+ expect(res.resultCode).toBe('CONTRACT_MISMATCH');
193
+ expect(capturedOptions).toHaveLength(0);
194
+ });
195
+
196
+ it('surfaces custom RemoteOperationError ResultCodes on the GraphQL result', async () => {
197
+ const resolver = new ExecuteRemoteOperationResolver();
198
+ const { ctx } = buildContext();
199
+
200
+ const res = await resolver.ExecuteRemoteOperation(
201
+ {
202
+ operationKey: 'TestServer.StructuredFailure',
203
+ inputJSON: JSON.stringify({ message: 'check' }),
204
+ invokeMode: 'attached',
205
+ },
206
+ ctx,
207
+ pubSub,
208
+ );
209
+ expect(res.success).toBe(false);
210
+ expect(res.resultCode).toBe('CUSTOM_DOMAIN_CODE');
211
+ expect(res.errorMessage).toBe('Structured domain validation rejected request');
212
+ });
213
+ });
@@ -0,0 +1,40 @@
1
+ /**
2
+ * `agentSessions.handoffOfferExpiry`: MJServer either stops the handoff offer registry's expiry sweep
3
+ * or puts it on the configured interval. The registry starts the sweep on its own when it loads.
4
+ */
5
+ import { describe, it, expect, vi, afterEach } from 'vitest';
6
+ import { HandoffOfferRegistry } from '@memberjunction/livekit-room-server';
7
+ import { ApplyHandoffOfferExpirySettings } from '../agentSessions/HandoffOfferExpiry.js';
8
+
9
+ describe('ApplyHandoffOfferExpirySettings', () => {
10
+ afterEach(() => {
11
+ HandoffOfferRegistry.Instance.StartPeriodicSweep(15_000);
12
+ HandoffOfferRegistry.Instance.StopPeriodicSweep();
13
+ vi.restoreAllMocks();
14
+ vi.useRealTimers();
15
+ });
16
+
17
+ it('disabled: stops the sweep, and nothing is read for an hour', async () => {
18
+ vi.useFakeTimers();
19
+ const registry = HandoffOfferRegistry.Instance;
20
+ registry.StartPeriodicSweep(15_000);
21
+ const sweep = vi.spyOn(registry, 'Sweep').mockResolvedValue(0);
22
+
23
+ ApplyHandoffOfferExpirySettings({ enabled: false, sweepIntervalMs: 15_000 });
24
+ await vi.advanceTimersByTimeAsync(60 * 60_000);
25
+
26
+ expect(sweep).not.toHaveBeenCalled();
27
+ expect(registry.IsPeriodicSweepScheduled).toBe(false);
28
+ });
29
+
30
+ it('enabled: runs the sweep on the configured interval', async () => {
31
+ vi.useFakeTimers();
32
+ const registry = HandoffOfferRegistry.Instance;
33
+ const sweep = vi.spyOn(registry, 'Sweep').mockResolvedValue(0);
34
+
35
+ ApplyHandoffOfferExpirySettings({ enabled: true, sweepIntervalMs: 120_000 });
36
+ await vi.advanceTimersByTimeAsync(10 * 60_000);
37
+
38
+ expect(sweep).toHaveBeenCalledTimes(5);
39
+ });
40
+ });
@@ -0,0 +1,126 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import type { IntegrationRunManifest } from '@memberjunction/integration-progress-artifacts';
3
+ import { AuthorizeRunArtifact } from '../integration/RunArtifactAuthorization.js';
4
+
5
+ /**
6
+ * Who may read a run artifact.
7
+ *
8
+ * This is a security boundary, not plumbing. A run artifact is one undivided event stream carrying
9
+ * migration descriptions, affected table names and error text; an RSU batch can span several
10
+ * connections, and there is no mechanism to serve a caller a partial view of one. So the rule is
11
+ * AND — authorized for EVERY connection the run touched — because "any one of them" would hand
12
+ * connection A's schema-change descriptions to a user who only holds rights on connection B.
13
+ *
14
+ * The per-connection check itself (a RunView under the caller's context, so the provider applies
15
+ * row-level security) is exercised elsewhere; these tests stub it to an allowlist and lock the
16
+ * COMBINING rule, which is the part that changed.
17
+ */
18
+
19
+ /**
20
+ * A per-connection check that answers from `allowed` and records what it was asked, so the tests can
21
+ * assert the short-circuit as well as the verdict. Memoization lives in the resolver's cache, so a
22
+ * shared `asked` list across calls shows exactly how many lookups the rule performed.
23
+ */
24
+ function checkFor(allowed: string[]): { canRead: (id: string) => Promise<boolean>; asked: string[] } {
25
+ const asked: string[] = [];
26
+ const seen = new Map<string, boolean>();
27
+ return {
28
+ asked,
29
+ canRead: async (companyIntegrationID: string): Promise<boolean> => {
30
+ const cached = seen.get(companyIntegrationID);
31
+ if (cached !== undefined) return cached;
32
+ asked.push(companyIntegrationID);
33
+ const verdict = allowed.includes(companyIntegrationID);
34
+ seen.set(companyIntegrationID, verdict);
35
+ return verdict;
36
+ },
37
+ };
38
+ }
39
+
40
+ function manifestOf(over: Partial<IntegrationRunManifest>): IntegrationRunManifest {
41
+ return { runID: 'r1', runKind: 'RSU', startedAt: new Date().toISOString(), ...over };
42
+ }
43
+
44
+ describe('run-artifact authorization', () => {
45
+ it('authorizes a single-connection run for a caller who holds that connection', async () => {
46
+ const { canRead } = checkFor(['CI-1']);
47
+ const result = await AuthorizeRunArtifact(
48
+ manifestOf({ companyIntegrationID: 'CI-1', companyIntegrationIDs: ['CI-1'] }),
49
+ canRead
50
+ );
51
+ expect(result.Authorized).toBe(true);
52
+ });
53
+
54
+ it('authorizes an RSU run at all — the manifest identity is what unblocks it', async () => {
55
+ // Regression guard for the original defect: an RSU manifest carried no connection, the check
56
+ // returned false on the missing field, and every RSU run answered "not authorized" to
57
+ // IntegrationGetRun and IntegrationTailRunEvents.
58
+ const { canRead } = checkFor(['CI-1']);
59
+ const withIdentity = await AuthorizeRunArtifact(manifestOf({ companyIntegrationIDs: ['CI-1'] }), canRead);
60
+ const withoutIdentity = await AuthorizeRunArtifact(manifestOf({}), canRead);
61
+ expect(withIdentity.Authorized).toBe(true);
62
+ expect(withoutIdentity.Authorized).toBe(false);
63
+ });
64
+
65
+ it('requires EVERY connection of a multi-connection batch — AND, not any', async () => {
66
+ // The leak this prevents: a user with rights only on CI-2 reading CI-1's schema-change
67
+ // descriptions out of the shared batch stream.
68
+ const { canRead } = checkFor(['CI-2']);
69
+ const result = await AuthorizeRunArtifact(manifestOf({ companyIntegrationIDs: ['CI-1', 'CI-2'] }), canRead);
70
+ expect(result.Authorized).toBe(false);
71
+ expect(result.DeniedCompanyIntegrationID).toBe('CI-1');
72
+ });
73
+
74
+ it('denies the batch even when the caller holds all but one of its connections', async () => {
75
+ const { canRead } = checkFor(['CI-1', 'CI-2']);
76
+ const result = await AuthorizeRunArtifact(manifestOf({ companyIntegrationIDs: ['CI-1', 'CI-2', 'CI-3'] }), canRead);
77
+ expect(result.Authorized).toBe(false);
78
+ expect(result.DeniedCompanyIntegrationID).toBe('CI-3');
79
+ });
80
+
81
+ it('authorizes a multi-connection batch only for a caller who holds all of them', async () => {
82
+ const { canRead } = checkFor(['CI-1', 'CI-2']);
83
+ const result = await AuthorizeRunArtifact(manifestOf({ companyIntegrationIDs: ['CI-1', 'CI-2'] }), canRead);
84
+ expect(result.Authorized).toBe(true);
85
+ });
86
+
87
+ it('stops at the first denial, so a caller cannot probe what else a run covers', async () => {
88
+ const { canRead, asked } = checkFor([]);
89
+ const result = await AuthorizeRunArtifact(manifestOf({ companyIntegrationIDs: ['CI-1', 'CI-2', 'CI-3'] }), canRead);
90
+ expect(result.DeniedCompanyIntegrationID).toBe('CI-1');
91
+ expect(asked).toEqual(['CI-1']);
92
+ });
93
+
94
+ it('denies a run with no connection identity at all, without asking anything', async () => {
95
+ // Non-tenant-scoped artifacts are not exposed through the per-company endpoints, and an
96
+ // identity-less run must not become readable as a side effect of runs gaining a set field.
97
+ const { canRead, asked } = checkFor(['CI-1']);
98
+ for (const manifest of [{}, { companyIntegrationIDs: [] }]) {
99
+ const result = await AuthorizeRunArtifact(manifestOf(manifest), canRead);
100
+ expect(result.Authorized).toBe(false);
101
+ expect(result.DeniedCompanyIntegrationID).toBeUndefined();
102
+ }
103
+ expect(asked).toEqual([]);
104
+ });
105
+
106
+ it('falls back to the singular field for runs written before the set existed', async () => {
107
+ // Every sync/discovery run carries only `companyIntegrationID`; those must keep working.
108
+ const { canRead } = checkFor(['CI-9']);
109
+ const ok = await AuthorizeRunArtifact(manifestOf({ runKind: 'SyncRun', companyIntegrationID: 'CI-9' }), canRead);
110
+ const denied = await AuthorizeRunArtifact(manifestOf({ runKind: 'SyncRun', companyIntegrationID: 'CI-8' }), canRead);
111
+ expect(ok.Authorized).toBe(true);
112
+ expect(denied.Authorized).toBe(false);
113
+ expect(denied.DeniedCompanyIntegrationID).toBe('CI-8');
114
+ });
115
+
116
+ it('prefers the set over the singular field when both are present', async () => {
117
+ // A batch that also happens to carry a singular value must still be judged on the whole set.
118
+ const { canRead } = checkFor(['CI-1']);
119
+ const result = await AuthorizeRunArtifact(
120
+ manifestOf({ companyIntegrationID: 'CI-1', companyIntegrationIDs: ['CI-1', 'CI-2'] }),
121
+ canRead
122
+ );
123
+ expect(result.Authorized).toBe(false);
124
+ expect(result.DeniedCompanyIntegrationID).toBe('CI-2');
125
+ });
126
+ });