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

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 (407) 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/config.d.ts +906 -8
  68. package/dist/config.d.ts.map +1 -1
  69. package/dist/config.js +59 -0
  70. package/dist/config.js.map +1 -1
  71. package/dist/context.d.ts +2 -0
  72. package/dist/context.d.ts.map +1 -1
  73. package/dist/context.js +35 -4
  74. package/dist/context.js.map +1 -1
  75. package/dist/coreRoutes.d.ts +3 -1
  76. package/dist/coreRoutes.d.ts.map +1 -1
  77. package/dist/coreRoutes.js +6 -4
  78. package/dist/coreRoutes.js.map +1 -1
  79. package/dist/generated/graphql-schemas/__mj.d.ts +228 -0
  80. package/dist/generated/graphql-schemas/__mj.d.ts.map +1 -1
  81. package/dist/generated/graphql-schemas/__mj.js +1163 -6
  82. package/dist/generated/graphql-schemas/__mj.js.map +1 -1
  83. package/dist/generic/OrphanedConversationDetailReconciler.d.ts +11 -0
  84. package/dist/generic/OrphanedConversationDetailReconciler.d.ts.map +1 -1
  85. package/dist/generic/OrphanedConversationDetailReconciler.js +27 -1
  86. package/dist/generic/OrphanedConversationDetailReconciler.js.map +1 -1
  87. package/dist/generic/ResolverBase.d.ts +50 -5
  88. package/dist/generic/ResolverBase.d.ts.map +1 -1
  89. package/dist/generic/ResolverBase.js +321 -132
  90. package/dist/generic/ResolverBase.js.map +1 -1
  91. package/dist/generic/RunViewResolver.d.ts +4 -0
  92. package/dist/generic/RunViewResolver.d.ts.map +1 -1
  93. package/dist/generic/RunViewResolver.js +47 -7
  94. package/dist/generic/RunViewResolver.js.map +1 -1
  95. package/dist/index.d.ts +1 -0
  96. package/dist/index.d.ts.map +1 -1
  97. package/dist/index.js +157 -35
  98. package/dist/index.js.map +1 -1
  99. package/dist/integration/ConnectionPause.d.ts +121 -0
  100. package/dist/integration/ConnectionPause.d.ts.map +1 -0
  101. package/dist/integration/ConnectionPause.js +183 -0
  102. package/dist/integration/ConnectionPause.js.map +1 -0
  103. package/dist/integration/CustomColumnPromoter.d.ts +72 -6
  104. package/dist/integration/CustomColumnPromoter.d.ts.map +1 -1
  105. package/dist/integration/CustomColumnPromoter.js +87 -46
  106. package/dist/integration/CustomColumnPromoter.js.map +1 -1
  107. package/dist/integration/EntityMapLifecycle.d.ts +78 -4
  108. package/dist/integration/EntityMapLifecycle.d.ts.map +1 -1
  109. package/dist/integration/EntityMapLifecycle.js +199 -5
  110. package/dist/integration/EntityMapLifecycle.js.map +1 -1
  111. package/dist/integration/InactiveRowWarnings.d.ts +31 -0
  112. package/dist/integration/InactiveRowWarnings.d.ts.map +1 -1
  113. package/dist/integration/InactiveRowWarnings.js +29 -0
  114. package/dist/integration/InactiveRowWarnings.js.map +1 -1
  115. package/dist/integration/ObjectNamesByID.d.ts +28 -0
  116. package/dist/integration/ObjectNamesByID.d.ts.map +1 -0
  117. package/dist/integration/ObjectNamesByID.js +87 -0
  118. package/dist/integration/ObjectNamesByID.js.map +1 -0
  119. package/dist/integration/PostRestartIntrospect.d.ts +26 -0
  120. package/dist/integration/PostRestartIntrospect.d.ts.map +1 -0
  121. package/dist/integration/PostRestartIntrospect.js +27 -0
  122. package/dist/integration/PostRestartIntrospect.js.map +1 -0
  123. package/dist/integration/RSUPostRestartProgress.d.ts +176 -0
  124. package/dist/integration/RSUPostRestartProgress.d.ts.map +1 -0
  125. package/dist/integration/RSUPostRestartProgress.js +323 -0
  126. package/dist/integration/RSUPostRestartProgress.js.map +1 -0
  127. package/dist/integration/RSUProgressBridge.d.ts +41 -2
  128. package/dist/integration/RSUProgressBridge.d.ts.map +1 -1
  129. package/dist/integration/RSUProgressBridge.js +84 -1
  130. package/dist/integration/RSUProgressBridge.js.map +1 -1
  131. package/dist/integration/RunArtifactAuthorization.d.ts +39 -0
  132. package/dist/integration/RunArtifactAuthorization.d.ts.map +1 -0
  133. package/dist/integration/RunArtifactAuthorization.js +45 -0
  134. package/dist/integration/RunArtifactAuthorization.js.map +1 -0
  135. package/dist/integration/SchemaRefreshLaunch.d.ts +9 -0
  136. package/dist/integration/SchemaRefreshLaunch.d.ts.map +1 -1
  137. package/dist/integration/SchemaRefreshLaunch.js +3 -2
  138. package/dist/integration/SchemaRefreshLaunch.js.map +1 -1
  139. package/dist/integration/ScopedFieldsForBuild.d.ts +46 -0
  140. package/dist/integration/ScopedFieldsForBuild.d.ts.map +1 -0
  141. package/dist/integration/ScopedFieldsForBuild.js +91 -0
  142. package/dist/integration/ScopedFieldsForBuild.js.map +1 -0
  143. package/dist/integration/TargetConfigGuard.d.ts +40 -0
  144. package/dist/integration/TargetConfigGuard.d.ts.map +1 -0
  145. package/dist/integration/TargetConfigGuard.js +40 -0
  146. package/dist/integration/TargetConfigGuard.js.map +1 -0
  147. package/dist/realtimeSessions/ownedRealtimeSession.d.ts.map +1 -1
  148. package/dist/realtimeSessions/ownedRealtimeSession.js +2 -3
  149. package/dist/realtimeSessions/ownedRealtimeSession.js.map +1 -1
  150. package/dist/realtimeSessions/sessionAccess.d.ts +15 -0
  151. package/dist/realtimeSessions/sessionAccess.d.ts.map +1 -1
  152. package/dist/realtimeSessions/sessionAccess.js +22 -0
  153. package/dist/realtimeSessions/sessionAccess.js.map +1 -1
  154. package/dist/realtimeWidget/WidgetSessionService.d.ts.map +1 -1
  155. package/dist/realtimeWidget/WidgetSessionService.js +2 -0
  156. package/dist/realtimeWidget/WidgetSessionService.js.map +1 -1
  157. package/dist/realtimeWidget/visitorIdentity.d.ts.map +1 -1
  158. package/dist/realtimeWidget/visitorIdentity.js +1 -0
  159. package/dist/realtimeWidget/visitorIdentity.js.map +1 -1
  160. package/dist/realtimeWidget/widgetCore.d.ts.map +1 -1
  161. package/dist/realtimeWidget/widgetCore.js +4 -7
  162. package/dist/realtimeWidget/widgetCore.js.map +1 -1
  163. package/dist/realtimeWidget/widgetGuestElevation.js +1 -0
  164. package/dist/realtimeWidget/widgetGuestElevation.js.map +1 -1
  165. package/dist/requestOrigin.d.ts +22 -0
  166. package/dist/requestOrigin.d.ts.map +1 -0
  167. package/dist/requestOrigin.js +60 -0
  168. package/dist/requestOrigin.js.map +1 -0
  169. package/dist/resolvers/AgentSessionResolver.d.ts.map +1 -1
  170. package/dist/resolvers/AgentSessionResolver.js +6 -2
  171. package/dist/resolvers/AgentSessionResolver.js.map +1 -1
  172. package/dist/resolvers/AutotagPipelineResolver.d.ts.map +1 -1
  173. package/dist/resolvers/AutotagPipelineResolver.js +2 -1
  174. package/dist/resolvers/AutotagPipelineResolver.js.map +1 -1
  175. package/dist/resolvers/ConversationRunEventsResolver.d.ts.map +1 -1
  176. package/dist/resolvers/ConversationRunEventsResolver.js +2 -0
  177. package/dist/resolvers/ConversationRunEventsResolver.js.map +1 -1
  178. package/dist/resolvers/ExecuteRemoteOperationResolver.d.ts +6 -0
  179. package/dist/resolvers/ExecuteRemoteOperationResolver.d.ts.map +1 -1
  180. package/dist/resolvers/ExecuteRemoteOperationResolver.js +30 -7
  181. package/dist/resolvers/ExecuteRemoteOperationResolver.js.map +1 -1
  182. package/dist/resolvers/GetDataResolver.d.ts.map +1 -1
  183. package/dist/resolvers/GetDataResolver.js +34 -13
  184. package/dist/resolvers/GetDataResolver.js.map +1 -1
  185. package/dist/resolvers/IntegrationDiscoveryResolver.d.ts +401 -11
  186. package/dist/resolvers/IntegrationDiscoveryResolver.d.ts.map +1 -1
  187. package/dist/resolvers/IntegrationDiscoveryResolver.js +2690 -1305
  188. package/dist/resolvers/IntegrationDiscoveryResolver.js.map +1 -1
  189. package/dist/resolvers/MCPResolver.d.ts.map +1 -1
  190. package/dist/resolvers/MCPResolver.js +13 -2
  191. package/dist/resolvers/MCPResolver.js.map +1 -1
  192. package/dist/resolvers/QuerySystemUserResolver.d.ts +36 -0
  193. package/dist/resolvers/QuerySystemUserResolver.d.ts.map +1 -1
  194. package/dist/resolvers/QuerySystemUserResolver.js +174 -87
  195. package/dist/resolvers/QuerySystemUserResolver.js.map +1 -1
  196. package/dist/resolvers/RealtimeBridgeResolver.d.ts.map +1 -1
  197. package/dist/resolvers/RealtimeBridgeResolver.js +1 -0
  198. package/dist/resolvers/RealtimeBridgeResolver.js.map +1 -1
  199. package/dist/resolvers/RealtimeClientSessionResolver.d.ts +6 -0
  200. package/dist/resolvers/RealtimeClientSessionResolver.d.ts.map +1 -1
  201. package/dist/resolvers/RealtimeClientSessionResolver.js +36 -5
  202. package/dist/resolvers/RealtimeClientSessionResolver.js.map +1 -1
  203. package/dist/resolvers/RemoteBrowserActionResolver.d.ts +62 -10
  204. package/dist/resolvers/RemoteBrowserActionResolver.d.ts.map +1 -1
  205. package/dist/resolvers/RemoteBrowserActionResolver.js +217 -34
  206. package/dist/resolvers/RemoteBrowserActionResolver.js.map +1 -1
  207. package/dist/resolvers/RunAIAgentResolver.d.ts +24 -8
  208. package/dist/resolvers/RunAIAgentResolver.d.ts.map +1 -1
  209. package/dist/resolvers/RunAIAgentResolver.js +185 -67
  210. package/dist/resolvers/RunAIAgentResolver.js.map +1 -1
  211. package/dist/resolvers/RunAIPromptResolver.d.ts +5 -3
  212. package/dist/resolvers/RunAIPromptResolver.d.ts.map +1 -1
  213. package/dist/resolvers/RunAIPromptResolver.js +19 -18
  214. package/dist/resolvers/RunAIPromptResolver.js.map +1 -1
  215. package/dist/resolvers/RunTemplateResolver.d.ts.map +1 -1
  216. package/dist/resolvers/RunTemplateResolver.js +3 -1
  217. package/dist/resolvers/RunTemplateResolver.js.map +1 -1
  218. package/dist/resolvers/meetingRecordingRegistration.d.ts.map +1 -1
  219. package/dist/resolvers/meetingRecordingRegistration.js +2 -0
  220. package/dist/resolvers/meetingRecordingRegistration.js.map +1 -1
  221. package/dist/rest/MediaAccessKeys.d.ts.map +1 -1
  222. package/dist/rest/MediaAccessKeys.js +6 -2
  223. package/dist/rest/MediaAccessKeys.js.map +1 -1
  224. package/dist/rest/OAuthCallbackHandler.d.ts.map +1 -1
  225. package/dist/rest/OAuthCallbackHandler.js +11 -5
  226. package/dist/rest/OAuthCallbackHandler.js.map +1 -1
  227. package/dist/rest/RealtimeRecordingHandler.d.ts +3 -3
  228. package/dist/rest/RealtimeRecordingHandler.d.ts.map +1 -1
  229. package/dist/rest/RealtimeRecordingHandler.js +2 -4
  230. package/dist/rest/RealtimeRecordingHandler.js.map +1 -1
  231. package/dist/rest/ViewOperationsHandler.d.ts.map +1 -1
  232. package/dist/rest/ViewOperationsHandler.js +2 -1
  233. package/dist/rest/ViewOperationsHandler.js.map +1 -1
  234. package/dist/rest/setupRESTEndpoints.d.ts +3 -1
  235. package/dist/rest/setupRESTEndpoints.d.ts.map +1 -1
  236. package/dist/rest/setupRESTEndpoints.js +11 -2
  237. package/dist/rest/setupRESTEndpoints.js.map +1 -1
  238. package/dist/services/DurableEntityActionTaskSubmitter.d.ts.map +1 -1
  239. package/dist/services/DurableEntityActionTaskSubmitter.js +6 -1
  240. package/dist/services/DurableEntityActionTaskSubmitter.js.map +1 -1
  241. package/dist/services/TaskGraphActionRunner.d.ts.map +1 -1
  242. package/dist/services/TaskGraphActionRunner.js +2 -26
  243. package/dist/services/TaskGraphActionRunner.js.map +1 -1
  244. package/dist/services/TaskGraphAgentRunner.d.ts +6 -0
  245. package/dist/services/TaskGraphAgentRunner.d.ts.map +1 -1
  246. package/dist/services/TaskGraphAgentRunner.js +38 -0
  247. package/dist/services/TaskGraphAgentRunner.js.map +1 -1
  248. package/dist/services/TaskGraphContinuationDeliverer.d.ts.map +1 -1
  249. package/dist/services/TaskGraphContinuationDeliverer.js +1 -0
  250. package/dist/services/TaskGraphContinuationDeliverer.js.map +1 -1
  251. package/dist/services/WorkQueueHostService.d.ts +2 -1
  252. package/dist/services/WorkQueueHostService.d.ts.map +1 -1
  253. package/dist/services/WorkQueueHostService.js +19 -4
  254. package/dist/services/WorkQueueHostService.js.map +1 -1
  255. package/dist/services/workQueueConfig.d.ts +2 -2
  256. package/dist/services/workQueueConfig.js +5 -5
  257. package/dist/services/workQueueConfig.js.map +1 -1
  258. package/dist/sharedCache.js +1 -1
  259. package/dist/sharedCache.js.map +1 -1
  260. package/dist/types.d.ts +17 -0
  261. package/dist/types.d.ts.map +1 -1
  262. package/dist/types.js.map +1 -1
  263. package/package.json +98 -98
  264. package/src/__tests__/APIKeyScopeAuth.test.ts +4 -8
  265. package/src/__tests__/ActiveOperations.test.ts +68 -0
  266. package/src/__tests__/AgentSessionResolver.ownership.test.ts +13 -0
  267. package/src/__tests__/AssertTargetConfigs.test.ts +75 -0
  268. package/src/__tests__/BatchApplyParity.test.ts +109 -0
  269. package/src/__tests__/BridgeHostInstance.test.ts +42 -0
  270. package/src/__tests__/CollectInactiveRowWarnings.test.ts +115 -0
  271. package/src/__tests__/ConnectionPause.test.ts +192 -0
  272. package/src/__tests__/ConnectionPauseResolver.test.ts +166 -0
  273. package/src/__tests__/ConnectionPauseSave.test.ts +143 -0
  274. package/src/__tests__/CustomColumnPromoterCompletion.test.ts +142 -0
  275. package/src/__tests__/DeleteConnectionRemovesPerConnectionCatalog.test.ts +51 -0
  276. package/src/__tests__/DetachedDiscoveryStart.test.ts +65 -0
  277. package/src/__tests__/DurableEntityActionTaskSubmitter.test.ts +73 -0
  278. package/src/__tests__/EntityMapLifecycle.rekey.test.ts +252 -0
  279. package/src/__tests__/ExecuteRemoteOperationResolver.test.ts +213 -0
  280. package/src/__tests__/HandoffOfferExpiry.test.ts +40 -0
  281. package/src/__tests__/IntegrationRunArtifactAuthorization.test.ts +126 -0
  282. package/src/__tests__/MagicLinkKeys.test.ts +102 -0
  283. package/src/__tests__/MediaAccessKeys.test.ts +57 -10
  284. package/src/__tests__/NewArrivalsDefaultDisabled.test.ts +50 -0
  285. package/src/__tests__/ObjectNamesByID.test.ts +176 -0
  286. package/src/__tests__/PostRestartMapsInCatalogScope.test.ts +146 -0
  287. package/src/__tests__/QuerySystemUserResolver.childRows.test.ts +80 -0
  288. package/src/__tests__/QuerySystemUserResolver.collision.test.ts +200 -0
  289. package/src/__tests__/QuerySystemUserResolver.permissions.test.ts +132 -0
  290. package/src/__tests__/RSUPostRestartProgress.test.ts +343 -0
  291. package/src/__tests__/RSUProgressBridge.test.ts +188 -5
  292. package/src/__tests__/RSUStartupSweepWiring.test.ts +30 -0
  293. package/src/__tests__/ReactivateRefreshMessage.test.ts +10 -3
  294. package/src/__tests__/RealtimeClientSessionResolver.test.ts +22 -0
  295. package/src/__tests__/{RefreshAdoptsNewColumns.test.ts → RefreshReportsNewColumns.test.ts} +25 -16
  296. package/src/__tests__/RemoteBrowserActionRecovery.test.ts +287 -0
  297. package/src/__tests__/RemoteBrowserActionResolver.goalRunLink.test.ts +3 -0
  298. package/src/__tests__/RemoteBrowserActionResolver.ownership.test.ts +6 -4
  299. package/src/__tests__/RemoteBrowserAudioStream.test.ts +3 -0
  300. package/src/__tests__/RemoteBrowserSnapshot.test.ts +3 -0
  301. package/src/__tests__/ResolverBase.clientOldValues.test.ts +323 -26
  302. package/src/__tests__/ResolverBase.encryptedApiZeroReencrypt.test.ts +307 -0
  303. package/src/__tests__/ResolverBase.entityMessageRelay.test.ts +53 -2
  304. package/src/__tests__/ResolverBase.includeBinaryFields.test.ts +60 -0
  305. package/src/__tests__/RunAIAgentResolver.branch.test.ts +4 -0
  306. package/src/__tests__/RunAIAgentResolver.consultPredecessor.test.ts +289 -0
  307. package/src/__tests__/RunAIAgentResolver.handoff.test.ts +649 -0
  308. package/src/__tests__/RunAIAgentResolver.historyFrom.test.ts +2 -0
  309. package/src/__tests__/RunAIAgentResolver.runDataGuard.test.ts +2 -0
  310. package/src/__tests__/RunAIAgentResolver.stoppedPredecessor.test.ts +87 -12
  311. package/src/__tests__/SchemaEvolutionCustomColumns.test.ts +119 -0
  312. package/src/__tests__/SchemaRefreshLaunch.test.ts +24 -2
  313. package/src/__tests__/ScopedFieldsForBuild.test.ts +192 -0
  314. package/src/__tests__/SessionJanitor.test.ts +195 -4
  315. package/src/__tests__/SessionManager.test.ts +69 -0
  316. package/src/__tests__/TaskGraphActionRunner.durablePayload.test.ts +2 -2
  317. package/src/__tests__/TaskGraphAgentRunner.test.ts +272 -16
  318. package/src/__tests__/ViewOperationsHandler.test.ts +2 -5
  319. package/src/__tests__/WorkQueueHostService.test.ts +25 -7
  320. package/src/__tests__/backgroundSweepConfigUnits.test.ts +153 -0
  321. package/src/__tests__/contextRequestOrigin.test.ts +105 -0
  322. package/src/__tests__/conversationBrowsers.test.ts +62 -0
  323. package/src/__tests__/magicLinkExtensionCapabilities.test.ts +413 -0
  324. package/src/__tests__/orphanedConversationDetailReconciler.start.test.ts +82 -0
  325. package/src/__tests__/ownedRealtimeSession.test.ts +14 -0
  326. package/src/__tests__/realtimeRecordingHandler.test.ts +7 -2
  327. package/src/__tests__/realtimeSessions/sessionAccess.test.ts +79 -1
  328. package/src/__tests__/requestOrigin.test.ts +104 -0
  329. package/src/__tests__/securityHardeningPr6034.test.ts +120 -0
  330. package/src/__tests__/sessionDeadline.test.ts +51 -0
  331. package/src/__tests__/sharedCache.test.ts +11 -0
  332. package/src/__tests__/unifiedAuth.test.ts +73 -1
  333. package/src/agentSessions/BridgeHostInstance.ts +20 -5
  334. package/src/agentSessions/HandoffOfferExpiry.ts +18 -0
  335. package/src/agentSessions/ReturningVisitorRecap.ts +1 -0
  336. package/src/agentSessions/SessionJanitor.ts +92 -42
  337. package/src/agentSessions/SessionManager.ts +38 -8
  338. package/src/agentSessions/conversationBrowsers.ts +143 -0
  339. package/src/agentSessions/sessionDeadline.ts +81 -0
  340. package/src/auth/APIKeyScopeAuth.ts +5 -4
  341. package/src/auth/__tests__/loginEmailResolution.test.ts +67 -0
  342. package/src/auth/loginEmailResolution.ts +67 -0
  343. package/src/auth/magicLink/MagicLinkKeys.ts +146 -14
  344. package/src/auth/magicLink/MagicLinkRouter.ts +33 -6
  345. package/src/auth/magicLink/MagicLinkService.ts +250 -10
  346. package/src/auth/magicLink/index.ts +1 -1
  347. package/src/auth/magicLink/redeemLanding.ts +7 -4
  348. package/src/auth/magicLink/types.ts +28 -3
  349. package/src/auth/newUsers.ts +1 -0
  350. package/src/backgroundSweepConfigUnits.ts +213 -0
  351. package/src/config.ts +69 -0
  352. package/src/context.ts +39 -6
  353. package/src/coreRoutes.ts +14 -7
  354. package/src/generated/graphql-schemas/__mj.ts +822 -7
  355. package/src/generic/OrphanedConversationDetailReconciler.ts +32 -1
  356. package/src/generic/ResolverBase.ts +414 -143
  357. package/src/generic/RunViewResolver.ts +96 -49
  358. package/src/index.ts +201 -35
  359. package/src/integration/ConnectionPause.ts +222 -0
  360. package/src/integration/CustomColumnPromoter.ts +123 -53
  361. package/src/integration/EntityMapLifecycle.ts +229 -6
  362. package/src/integration/InactiveRowWarnings.ts +50 -0
  363. package/src/integration/ObjectNamesByID.ts +97 -0
  364. package/src/integration/PostRestartIntrospect.ts +49 -0
  365. package/src/integration/RSUPostRestartProgress.ts +359 -0
  366. package/src/integration/RSUProgressBridge.ts +85 -2
  367. package/src/integration/RunArtifactAuthorization.ts +65 -0
  368. package/src/integration/SchemaRefreshLaunch.ts +12 -2
  369. package/src/integration/ScopedFieldsForBuild.ts +113 -0
  370. package/src/integration/TargetConfigGuard.ts +71 -0
  371. package/src/realtimeSessions/ownedRealtimeSession.ts +2 -6
  372. package/src/realtimeSessions/sessionAccess.ts +30 -0
  373. package/src/realtimeWidget/WidgetSessionService.ts +2 -0
  374. package/src/realtimeWidget/visitorIdentity.ts +1 -0
  375. package/src/realtimeWidget/widgetCore.ts +4 -8
  376. package/src/realtimeWidget/widgetGuestElevation.ts +1 -0
  377. package/src/requestOrigin.ts +64 -0
  378. package/src/resolvers/AgentSessionResolver.ts +6 -2
  379. package/src/resolvers/AutotagPipelineResolver.ts +2 -1
  380. package/src/resolvers/ConversationRunEventsResolver.ts +2 -0
  381. package/src/resolvers/ExecuteRemoteOperationResolver.ts +41 -9
  382. package/src/resolvers/GetDataResolver.ts +35 -13
  383. package/src/resolvers/IntegrationDiscoveryResolver.ts +1522 -203
  384. package/src/resolvers/MCPResolver.ts +15 -2
  385. package/src/resolvers/QuerySystemUserResolver.ts +197 -98
  386. package/src/resolvers/RealtimeBridgeResolver.ts +1 -0
  387. package/src/resolvers/RealtimeClientSessionResolver.ts +40 -7
  388. package/src/resolvers/RemoteBrowserActionResolver.ts +242 -32
  389. package/src/resolvers/RunAIAgentResolver.ts +265 -86
  390. package/src/resolvers/RunAIPromptResolver.ts +18 -18
  391. package/src/resolvers/RunTemplateResolver.ts +3 -1
  392. package/src/resolvers/__tests__/GetDataResolver.tokens.test.ts +52 -0
  393. package/src/resolvers/meetingRecordingRegistration.ts +2 -0
  394. package/src/rest/MediaAccessKeys.ts +6 -2
  395. package/src/rest/OAuthCallbackHandler.ts +13 -5
  396. package/src/rest/RealtimeRecordingHandler.ts +5 -9
  397. package/src/rest/ViewOperationsHandler.ts +2 -1
  398. package/src/rest/setupRESTEndpoints.ts +11 -2
  399. package/src/services/DurableEntityActionTaskSubmitter.ts +10 -2
  400. package/src/services/TaskGraphActionRunner.ts +2 -23
  401. package/src/services/TaskGraphAgentRunner.ts +43 -0
  402. package/src/services/TaskGraphContinuationDeliverer.ts +1 -0
  403. package/src/services/WorkQueueHostService.ts +26 -4
  404. package/src/services/workQueueConfig.ts +5 -5
  405. package/src/sharedCache.ts +1 -1
  406. package/src/types.ts +18 -0
  407. package/src/services/TaskOrchestration-Integration.md +0 -188
@@ -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
+ });
@@ -0,0 +1,102 @@
1
+ import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
2
+ import { generateKeyPairSync } from 'node:crypto';
3
+
4
+ const logDoubles = vi.hoisted(() => ({
5
+ LogWarning: vi.fn(),
6
+ LogStatusEx: vi.fn(),
7
+ }));
8
+
9
+ vi.mock('@memberjunction/core', async importOriginal => {
10
+ const actual = await importOriginal<typeof import('@memberjunction/core')>();
11
+ return { ...actual, LogWarning: logDoubles.LogWarning, LogStatusEx: logDoubles.LogStatusEx };
12
+ });
13
+
14
+ import { GetGlobalObjectStore } from '@memberjunction/global';
15
+ import {
16
+ MagicLinkKeyManager,
17
+ IsMagicLinkLocalDevEnvironment,
18
+ EPHEMERAL_MAGIC_LINK_KEY_WARNING,
19
+ } from '../auth/magicLink/MagicLinkKeys.js';
20
+
21
+ /** Subclass so each test gets its own singleton slot instead of sharing the app-wide instance. */
22
+ class IsolatedKeyManager extends MagicLinkKeyManager {}
23
+
24
+ function freshManager(): IsolatedKeyManager {
25
+ const manager = new IsolatedKeyManager();
26
+ const store = GetGlobalObjectStore();
27
+ if (store) {
28
+ delete store[manager.GlobalKey];
29
+ }
30
+ return new IsolatedKeyManager();
31
+ }
32
+
33
+ describe('IsMagicLinkLocalDevEnvironment', () => {
34
+ it('treats NODE_ENV=production (any case, padded) as not local dev', () => {
35
+ expect(IsMagicLinkLocalDevEnvironment({ NODE_ENV: 'production' })).toBe(false);
36
+ expect(IsMagicLinkLocalDevEnvironment({ NODE_ENV: ' Production ' })).toBe(false);
37
+ });
38
+
39
+ it('treats unset, development and test as local dev', () => {
40
+ expect(IsMagicLinkLocalDevEnvironment({})).toBe(true);
41
+ expect(IsMagicLinkLocalDevEnvironment({ NODE_ENV: 'development' })).toBe(true);
42
+ expect(IsMagicLinkLocalDevEnvironment({ NODE_ENV: 'test' })).toBe(true);
43
+ });
44
+ });
45
+
46
+ describe('MagicLinkKeyManager ephemeral key reporting', () => {
47
+ let manager: IsolatedKeyManager;
48
+
49
+ beforeEach(() => {
50
+ logDoubles.LogWarning.mockClear();
51
+ logDoubles.LogStatusEx.mockClear();
52
+ manager = freshManager();
53
+ });
54
+
55
+ afterEach(() => {
56
+ const store = GetGlobalObjectStore();
57
+ if (store) {
58
+ delete store[manager.GlobalKey];
59
+ }
60
+ });
61
+
62
+ it('warns in the default output when no key is configured and NODE_ENV=production', () => {
63
+ manager.Initialize(undefined, { NODE_ENV: 'production' });
64
+
65
+ expect(manager.IsEphemeral).toBe(true);
66
+ expect(logDoubles.LogWarning).toHaveBeenCalledTimes(1);
67
+ expect(logDoubles.LogWarning).toHaveBeenCalledWith(EPHEMERAL_MAGIC_LINK_KEY_WARNING);
68
+ expect(logDoubles.LogStatusEx).not.toHaveBeenCalled();
69
+ });
70
+
71
+ it('the warning names the restart and multi-instance consequences and how to configure a key', () => {
72
+ expect(EPHEMERAL_MAGIC_LINK_KEY_WARNING).toContain('restarts');
73
+ expect(EPHEMERAL_MAGIC_LINK_KEY_WARNING).toContain('other MJAPI instance');
74
+ expect(EPHEMERAL_MAGIC_LINK_KEY_WARNING).toContain('magicLink.rsaPrivateKey');
75
+ expect(EPHEMERAL_MAGIC_LINK_KEY_WARNING).toContain('MJ_MAGIC_LINK_PRIVATE_KEY');
76
+ });
77
+
78
+ it('keeps the notice verbose-only in local dev', () => {
79
+ manager.Initialize(undefined, {});
80
+
81
+ expect(manager.IsEphemeral).toBe(true);
82
+ expect(logDoubles.LogWarning).not.toHaveBeenCalled();
83
+ expect(logDoubles.LogStatusEx).toHaveBeenCalledTimes(1);
84
+ expect(logDoubles.LogStatusEx.mock.calls[0][0]).toMatchObject({ verboseOnly: true });
85
+ });
86
+
87
+ it('is not ephemeral and logs nothing when a key is configured, even in production', () => {
88
+ const { privateKey } = generateKeyPairSync('rsa', { modulusLength: 2048 });
89
+ const pem = privateKey.export({ type: 'pkcs8', format: 'pem' }) as string;
90
+
91
+ manager.Initialize(pem, { NODE_ENV: 'production' });
92
+
93
+ expect(manager.IsEphemeral).toBe(false);
94
+ expect(logDoubles.LogWarning).not.toHaveBeenCalled();
95
+ expect(logDoubles.LogStatusEx).not.toHaveBeenCalled();
96
+ });
97
+
98
+ it('reports IsEphemeral false before initialization', () => {
99
+ expect(manager.IsEphemeral).toBe(false);
100
+ expect(manager.IsInitialized).toBe(false);
101
+ });
102
+ });
@@ -1,5 +1,7 @@
1
1
  import { describe, it, expect } from 'vitest';
2
+ import { createHmac } from 'node:crypto';
2
3
  import jwt from 'jsonwebtoken';
4
+ import { GetGlobalObjectStore } from '@memberjunction/global';
3
5
  import { MediaAccessKeyManager, MEDIA_ACCESS_TOKEN_TYPE } from '../rest/MediaAccessKeys.js';
4
6
 
5
7
  /**
@@ -53,16 +55,61 @@ describe('MediaAccessKeyManager', () => {
53
55
  expect(km.Verify(Token).Valid).toBe(false);
54
56
  });
55
57
 
56
- it('rejects a token with the wrong typ even if validly signed', () => {
58
+ it('rejects a token with the wrong typ even when validly signed with the real secret', () => {
57
59
  const km = MediaAccessKeyManager.Instance;
58
- // Mint a valid token, then read the real secret out of a known-good token by
59
- // re-signing through the manager is not possible; instead assert that a token
60
- // whose typ differs is rejected by signing a structurally-valid JWT with a
61
- // foreign secret (signature failure also yields false, which is the safe outcome).
62
- const foreign = jwt.sign({ fileId: 'FILE-5', userId: 'USER-5', typ: 'something-else' }, 'foreign-secret', {
63
- algorithm: 'HS256',
64
- expiresIn: 3600,
65
- });
66
- expect(km.Verify(foreign).Valid).toBe(false);
60
+ const uploadToken = km.SignUpload('USER-5').Token;
61
+ expect(km.Verify(uploadToken).Valid).toBe(false);
62
+
63
+ const mediaToken = km.Sign('FILE-5', 'USER-5').Token;
64
+ expect(km.VerifyUpload(mediaToken).Valid).toBe(false);
65
+ });
66
+
67
+ it('derives a domain-separated HS256 signing key from MJ_BASE_ENCRYPTION_KEY and rejects tokens signed with the raw master key', () => {
68
+ const store = GetGlobalObjectStore();
69
+ const savedStoreSecret = store?.['___MEDIA_ACCESS_SECRET___'];
70
+ const savedEnvKey = process.env.MJ_BASE_ENCRYPTION_KEY;
71
+ const rawMasterKey = 'test-master-encryption-key-do-not-use-directly';
72
+
73
+ try {
74
+ if (store) {
75
+ delete store['___MEDIA_ACCESS_SECRET___'];
76
+ }
77
+ process.env.MJ_BASE_ENCRYPTION_KEY = rawMasterKey;
78
+
79
+ const isolatedKm = new MediaAccessKeyManager();
80
+ const { Token } = isolatedKm.Sign('FILE-KEY-SEP', 'USER-KEY-SEP');
81
+
82
+ // Round-trips through the manager using the derived subkey
83
+ const verified = isolatedKm.Verify(Token);
84
+ expect(verified.Valid).toBe(true);
85
+
86
+ // Matches the documented HMAC-SHA256 key derivation label
87
+ const expectedDerivedSecret = createHmac('sha256', rawMasterKey)
88
+ .update('mj-media-access-token-signing-v1')
89
+ .digest('base64');
90
+ const decoded = jwt.verify(Token, expectedDerivedSecret, { algorithms: ['HS256'] });
91
+ expect(typeof decoded).toBe('object');
92
+
93
+ // A JWT signed directly with the raw MJ_BASE_ENCRYPTION_KEY MUST be rejected
94
+ const forgedWithRawMasterKey = jwt.sign(
95
+ { fileId: 'FILE-KEY-SEP', userId: 'USER-KEY-SEP', typ: MEDIA_ACCESS_TOKEN_TYPE },
96
+ rawMasterKey,
97
+ { algorithm: 'HS256', expiresIn: 3600 },
98
+ );
99
+ expect(isolatedKm.Verify(forgedWithRawMasterKey).Valid).toBe(false);
100
+ } finally {
101
+ if (savedEnvKey === undefined) {
102
+ delete process.env.MJ_BASE_ENCRYPTION_KEY;
103
+ } else {
104
+ process.env.MJ_BASE_ENCRYPTION_KEY = savedEnvKey;
105
+ }
106
+ if (store) {
107
+ if (savedStoreSecret === undefined) {
108
+ delete store['___MEDIA_ACCESS_SECRET___'];
109
+ } else {
110
+ store['___MEDIA_ACCESS_SECRET___'] = savedStoreSecret;
111
+ }
112
+ }
113
+ }
67
114
  });
68
115
  });
@@ -0,0 +1,50 @@
1
+ /**
2
+ * everything.txt is explicit about what a schema refresh does with objects it has just found:
3
+ * "we create entity maps for all objects that are determined as things that should be now added
4
+ * but arent, we enable nothing (because the user needs to, after the refresh, then go turn them
5
+ * on)". These pin that contract at the only place it is expressed - the argument defaults.
6
+ *
7
+ * They defaulted TRUE for a period on the claim that a disabled new object never reached a
8
+ * migration. That claim was wrong: `CreateDisabled` governs only the entity/field MAP status,
9
+ * new IntegrationObject rows are written Active unconditionally, and phase 4 evolves over
10
+ * `continuingMaps + newObjects` regardless of the flag. The table is created either way; the
11
+ * flag decides only whether the object starts SYNCING unasked.
12
+ *
13
+ * Read from source rather than by invoking the resolver: the default lives in a type-graphql
14
+ * decorator, and a test that booted the schema to read it back would be testing type-graphql.
15
+ */
16
+ import { describe, it, expect } from 'vitest';
17
+ import { readFileSync } from 'node:fs';
18
+ import { join } from 'node:path';
19
+
20
+ const SRC = readFileSync(
21
+ join(__dirname, '..', 'resolvers', 'IntegrationDiscoveryResolver.ts'),
22
+ 'utf-8',
23
+ );
24
+
25
+ function argDefault(name: string): string | null {
26
+ const m = SRC.match(new RegExp(`@Arg\\("${name}",\\s*\\{\\s*defaultValue:\\s*(true|false)`));
27
+ return m ? m[1] : null;
28
+ }
29
+
30
+ describe('new arrivals default to disabled', () => {
31
+ it('autoEnableNewObjects defaults false', () => {
32
+ expect(argDefault('autoEnableNewObjects')).toBe('false');
33
+ });
34
+
35
+ it('autoEnableNewColumns defaults false', () => {
36
+ expect(argDefault('autoEnableNewColumns')).toBe('false');
37
+ });
38
+
39
+ it('the description does not promise auto-adoption', () => {
40
+ const line = SRC.split('\n').find(l => l.includes('@Arg("autoEnableNewObjects"'));
41
+ expect(line).toBeDefined();
42
+ expect(line!).toMatch(/DISABLED/);
43
+ expect(line!).not.toMatch(/created ENABLED/);
44
+ });
45
+
46
+ it('CreateDisabled is still what carries the decision to the post-restart consumer', () => {
47
+ // If this coupling is ever renamed, the defaults above stop meaning anything.
48
+ expect(SRC).toMatch(/CreateDisabled:\s*!autoEnableNewObjects/);
49
+ });
50
+ });