@memberjunction/server 6.2.0-edge.3 → 6.2.0-edge.5

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 (543) hide show
  1. package/README.md +20 -0
  2. package/dist/.tsbuildinfo +1 -1
  3. package/dist/agentSessions/BridgeHostInstance.d.ts +25 -0
  4. package/dist/agentSessions/BridgeHostInstance.d.ts.map +1 -0
  5. package/dist/agentSessions/BridgeHostInstance.js +30 -0
  6. package/dist/agentSessions/BridgeHostInstance.js.map +1 -0
  7. package/dist/agentSessions/HostInstance.d.ts +75 -11
  8. package/dist/agentSessions/HostInstance.d.ts.map +1 -1
  9. package/dist/agentSessions/HostInstance.js +99 -28
  10. package/dist/agentSessions/HostInstance.js.map +1 -1
  11. package/dist/agentSessions/ReturningVisitorRecap.d.ts.map +1 -1
  12. package/dist/agentSessions/ReturningVisitorRecap.js +7 -3
  13. package/dist/agentSessions/ReturningVisitorRecap.js.map +1 -1
  14. package/dist/agentSessions/SessionJanitor.d.ts +236 -27
  15. package/dist/agentSessions/SessionJanitor.d.ts.map +1 -1
  16. package/dist/agentSessions/SessionJanitor.js +682 -54
  17. package/dist/agentSessions/SessionJanitor.js.map +1 -1
  18. package/dist/agentSessions/SessionManager.d.ts +48 -3
  19. package/dist/agentSessions/SessionManager.d.ts.map +1 -1
  20. package/dist/agentSessions/SessionManager.js +112 -9
  21. package/dist/agentSessions/SessionManager.js.map +1 -1
  22. package/dist/agentSessions/SessionRunIDVerifier.d.ts +63 -0
  23. package/dist/agentSessions/SessionRunIDVerifier.d.ts.map +1 -0
  24. package/dist/agentSessions/SessionRunIDVerifier.js +129 -0
  25. package/dist/agentSessions/SessionRunIDVerifier.js.map +1 -0
  26. package/dist/agentSessions/index.d.ts +2 -0
  27. package/dist/agentSessions/index.d.ts.map +1 -1
  28. package/dist/agentSessions/index.js +10 -3
  29. package/dist/agentSessions/index.js.map +1 -1
  30. package/dist/apiRoot.d.ts +32 -0
  31. package/dist/apiRoot.d.ts.map +1 -0
  32. package/dist/apiRoot.js +67 -0
  33. package/dist/apiRoot.js.map +1 -0
  34. package/dist/auth/AuthProviderCatalogRouter.d.ts.map +1 -1
  35. package/dist/auth/AuthProviderCatalogRouter.js +25 -0
  36. package/dist/auth/AuthProviderCatalogRouter.js.map +1 -1
  37. package/dist/auth/LoginScreenEngine.d.ts +22 -0
  38. package/dist/auth/LoginScreenEngine.d.ts.map +1 -0
  39. package/dist/auth/LoginScreenEngine.js +57 -0
  40. package/dist/auth/LoginScreenEngine.js.map +1 -0
  41. package/dist/auth/actionAuthorization.d.ts +40 -0
  42. package/dist/auth/actionAuthorization.d.ts.map +1 -0
  43. package/dist/auth/actionAuthorization.js +94 -0
  44. package/dist/auth/actionAuthorization.js.map +1 -0
  45. package/dist/auth/index.d.ts +1 -0
  46. package/dist/auth/index.d.ts.map +1 -1
  47. package/dist/auth/index.js +4 -10
  48. package/dist/auth/index.js.map +1 -1
  49. package/dist/auth/magicLink/MagicLinkRouter.d.ts.map +1 -1
  50. package/dist/auth/magicLink/MagicLinkRouter.js +3 -2
  51. package/dist/auth/magicLink/MagicLinkRouter.js.map +1 -1
  52. package/dist/auth/magicLink/MagicLinkService.d.ts.map +1 -1
  53. package/dist/auth/magicLink/MagicLinkService.js +6 -3
  54. package/dist/auth/magicLink/MagicLinkService.js.map +1 -1
  55. package/dist/auth/newUsers.d.ts +10 -0
  56. package/dist/auth/newUsers.d.ts.map +1 -1
  57. package/dist/auth/newUsers.js +80 -11
  58. package/dist/auth/newUsers.js.map +1 -1
  59. package/dist/config.d.ts +2514 -22
  60. package/dist/config.d.ts.map +1 -1
  61. package/dist/config.js +166 -4
  62. package/dist/config.js.map +1 -1
  63. package/dist/context.d.ts +8 -0
  64. package/dist/context.d.ts.map +1 -1
  65. package/dist/context.js +14 -11
  66. package/dist/context.js.map +1 -1
  67. package/dist/coreRoutes.d.ts +29 -0
  68. package/dist/coreRoutes.d.ts.map +1 -0
  69. package/dist/coreRoutes.js +142 -0
  70. package/dist/coreRoutes.js.map +1 -0
  71. package/dist/generated/graphql-schemas/__mj.d.ts +504 -3
  72. package/dist/generated/graphql-schemas/__mj.d.ts.map +1 -1
  73. package/dist/generated/graphql-schemas/__mj.js +2456 -7
  74. package/dist/generated/graphql-schemas/__mj.js.map +1 -1
  75. package/dist/generic/CacheInvalidationResolver.d.ts +43 -8
  76. package/dist/generic/CacheInvalidationResolver.d.ts.map +1 -1
  77. package/dist/generic/CacheInvalidationResolver.js +59 -19
  78. package/dist/generic/CacheInvalidationResolver.js.map +1 -1
  79. package/dist/generic/OrphanedConversationDetailReconciler.js +3 -3
  80. package/dist/generic/OrphanedConversationDetailReconciler.js.map +1 -1
  81. package/dist/generic/PushStatusResolver.js +1 -1
  82. package/dist/generic/PushStatusResolver.js.map +1 -1
  83. package/dist/generic/ResolverBase.d.ts +123 -48
  84. package/dist/generic/ResolverBase.d.ts.map +1 -1
  85. package/dist/generic/ResolverBase.js +293 -305
  86. package/dist/generic/ResolverBase.js.map +1 -1
  87. package/dist/generic/RunViewResolver.d.ts.map +1 -1
  88. package/dist/generic/RunViewResolver.js +16 -0
  89. package/dist/generic/RunViewResolver.js.map +1 -1
  90. package/dist/globalCors.d.ts +33 -0
  91. package/dist/globalCors.d.ts.map +1 -0
  92. package/dist/globalCors.js +58 -0
  93. package/dist/globalCors.js.map +1 -0
  94. package/dist/index.d.ts +17 -0
  95. package/dist/index.d.ts.map +1 -1
  96. package/dist/index.js +219 -278
  97. package/dist/index.js.map +1 -1
  98. package/dist/isolatedProvider.d.ts +20 -0
  99. package/dist/isolatedProvider.d.ts.map +1 -0
  100. package/dist/isolatedProvider.js +22 -0
  101. package/dist/isolatedProvider.js.map +1 -0
  102. package/dist/multiTenancy/index.d.ts.map +1 -1
  103. package/dist/multiTenancy/index.js +22 -2
  104. package/dist/multiTenancy/index.js.map +1 -1
  105. package/dist/realtimeProxy/RealtimeProxyServer.d.ts +44 -4
  106. package/dist/realtimeProxy/RealtimeProxyServer.d.ts.map +1 -1
  107. package/dist/realtimeProxy/RealtimeProxyServer.js +121 -16
  108. package/dist/realtimeProxy/RealtimeProxyServer.js.map +1 -1
  109. package/dist/realtimeProxy/RealtimeRelayTunnel.d.ts +171 -0
  110. package/dist/realtimeProxy/RealtimeRelayTunnel.d.ts.map +1 -0
  111. package/dist/realtimeProxy/RealtimeRelayTunnel.js +532 -0
  112. package/dist/realtimeProxy/RealtimeRelayTunnel.js.map +1 -0
  113. package/dist/realtimeSessions/RealtimeSessionEventService.d.ts +148 -0
  114. package/dist/realtimeSessions/RealtimeSessionEventService.d.ts.map +1 -0
  115. package/dist/realtimeSessions/RealtimeSessionEventService.js +191 -0
  116. package/dist/realtimeSessions/RealtimeSessionEventService.js.map +1 -0
  117. package/dist/realtimeSessions/RealtimeSessionVerificationService.d.ts +66 -0
  118. package/dist/realtimeSessions/RealtimeSessionVerificationService.d.ts.map +1 -0
  119. package/dist/realtimeSessions/RealtimeSessionVerificationService.js +139 -0
  120. package/dist/realtimeSessions/RealtimeSessionVerificationService.js.map +1 -0
  121. package/dist/realtimeSessions/RealtimeVerifyRouter.d.ts +38 -0
  122. package/dist/realtimeSessions/RealtimeVerifyRouter.d.ts.map +1 -0
  123. package/dist/realtimeSessions/RealtimeVerifyRouter.js +99 -0
  124. package/dist/realtimeSessions/RealtimeVerifyRouter.js.map +1 -0
  125. package/dist/realtimeSessions/RecordingAssemblyScheduler.d.ts +100 -0
  126. package/dist/realtimeSessions/RecordingAssemblyScheduler.d.ts.map +1 -0
  127. package/dist/realtimeSessions/RecordingAssemblyScheduler.js +173 -0
  128. package/dist/realtimeSessions/RecordingAssemblyScheduler.js.map +1 -0
  129. package/dist/realtimeSessions/SessionVerificationPortsBase.d.ts +61 -0
  130. package/dist/realtimeSessions/SessionVerificationPortsBase.d.ts.map +1 -0
  131. package/dist/realtimeSessions/SessionVerificationPortsBase.js +149 -0
  132. package/dist/realtimeSessions/SessionVerificationPortsBase.js.map +1 -0
  133. package/dist/realtimeSessions/SlidingWindowRateLimiter.d.ts +57 -0
  134. package/dist/realtimeSessions/SlidingWindowRateLimiter.d.ts.map +1 -0
  135. package/dist/realtimeSessions/SlidingWindowRateLimiter.js +69 -0
  136. package/dist/realtimeSessions/SlidingWindowRateLimiter.js.map +1 -0
  137. package/dist/realtimeSessions/index.d.ts +19 -0
  138. package/dist/realtimeSessions/index.d.ts.map +1 -0
  139. package/dist/realtimeSessions/index.js +19 -0
  140. package/dist/realtimeSessions/index.js.map +1 -0
  141. package/dist/realtimeSessions/ownedRealtimeSession.d.ts +30 -0
  142. package/dist/realtimeSessions/ownedRealtimeSession.d.ts.map +1 -0
  143. package/dist/realtimeSessions/ownedRealtimeSession.js +47 -0
  144. package/dist/realtimeSessions/ownedRealtimeSession.js.map +1 -0
  145. package/dist/realtimeSessions/sessionAccess.d.ts +92 -0
  146. package/dist/realtimeSessions/sessionAccess.d.ts.map +1 -0
  147. package/dist/realtimeSessions/sessionAccess.js +94 -0
  148. package/dist/realtimeSessions/sessionAccess.js.map +1 -0
  149. package/dist/realtimeSessions/sessionConfigStamps.d.ts +70 -0
  150. package/dist/realtimeSessions/sessionConfigStamps.d.ts.map +1 -0
  151. package/dist/realtimeSessions/sessionConfigStamps.js +93 -0
  152. package/dist/realtimeSessions/sessionConfigStamps.js.map +1 -0
  153. package/dist/realtimeSessions/subscriptionAuthorization.d.ts +65 -0
  154. package/dist/realtimeSessions/subscriptionAuthorization.d.ts.map +1 -0
  155. package/dist/realtimeSessions/subscriptionAuthorization.js +71 -0
  156. package/dist/realtimeSessions/subscriptionAuthorization.js.map +1 -0
  157. package/dist/realtimeSessions/verificationCore.d.ts +243 -0
  158. package/dist/realtimeSessions/verificationCore.d.ts.map +1 -0
  159. package/dist/realtimeSessions/verificationCore.js +356 -0
  160. package/dist/realtimeSessions/verificationCore.js.map +1 -0
  161. package/dist/realtimeSessions/verificationPages.d.ts +66 -0
  162. package/dist/realtimeSessions/verificationPages.d.ts.map +1 -0
  163. package/dist/realtimeSessions/verificationPages.js +110 -0
  164. package/dist/realtimeSessions/verificationPages.js.map +1 -0
  165. package/dist/realtimeSessions/verificationPolicy.d.ts +147 -0
  166. package/dist/realtimeSessions/verificationPolicy.d.ts.map +1 -0
  167. package/dist/realtimeSessions/verificationPolicy.js +249 -0
  168. package/dist/realtimeSessions/verificationPolicy.js.map +1 -0
  169. package/dist/realtimeSessions/verificationWorkflow.d.ts +263 -0
  170. package/dist/realtimeSessions/verificationWorkflow.d.ts.map +1 -0
  171. package/dist/realtimeSessions/verificationWorkflow.js +435 -0
  172. package/dist/realtimeSessions/verificationWorkflow.js.map +1 -0
  173. package/dist/realtimeWidget/widgetGuestElevation.d.ts +20 -2
  174. package/dist/realtimeWidget/widgetGuestElevation.d.ts.map +1 -1
  175. package/dist/realtimeWidget/widgetGuestElevation.js +31 -2
  176. package/dist/realtimeWidget/widgetGuestElevation.js.map +1 -1
  177. package/dist/resolvers/ActionResolver.d.ts +31 -5
  178. package/dist/resolvers/ActionResolver.d.ts.map +1 -1
  179. package/dist/resolvers/ActionResolver.js +75 -14
  180. package/dist/resolvers/ActionResolver.js.map +1 -1
  181. package/dist/resolvers/AdhocQueryResolver.d.ts +14 -1
  182. package/dist/resolvers/AdhocQueryResolver.d.ts.map +1 -1
  183. package/dist/resolvers/AdhocQueryResolver.js +21 -5
  184. package/dist/resolvers/AdhocQueryResolver.js.map +1 -1
  185. package/dist/resolvers/ArtifactFileResolver.d.ts +5 -1
  186. package/dist/resolvers/ArtifactFileResolver.d.ts.map +1 -1
  187. package/dist/resolvers/ArtifactFileResolver.js +9 -7
  188. package/dist/resolvers/ArtifactFileResolver.js.map +1 -1
  189. package/dist/resolvers/CacheStatsResolver.d.ts +27 -0
  190. package/dist/resolvers/CacheStatsResolver.d.ts.map +1 -1
  191. package/dist/resolvers/CacheStatsResolver.js +113 -2
  192. package/dist/resolvers/CacheStatsResolver.js.map +1 -1
  193. package/dist/resolvers/ClientToolRequestResolver.d.ts +4 -2
  194. package/dist/resolvers/ClientToolRequestResolver.d.ts.map +1 -1
  195. package/dist/resolvers/ClientToolRequestResolver.js +28 -10
  196. package/dist/resolvers/ClientToolRequestResolver.js.map +1 -1
  197. package/dist/resolvers/ConversationRunEventsResolver.d.ts +8 -4
  198. package/dist/resolvers/ConversationRunEventsResolver.d.ts.map +1 -1
  199. package/dist/resolvers/ConversationRunEventsResolver.js +6 -6
  200. package/dist/resolvers/ConversationRunEventsResolver.js.map +1 -1
  201. package/dist/resolvers/DatasetResolver.d.ts +24 -3
  202. package/dist/resolvers/DatasetResolver.d.ts.map +1 -1
  203. package/dist/resolvers/DatasetResolver.js +57 -8
  204. package/dist/resolvers/DatasetResolver.js.map +1 -1
  205. package/dist/resolvers/EntityRecordNameResolver.d.ts +2 -1
  206. package/dist/resolvers/EntityRecordNameResolver.d.ts.map +1 -1
  207. package/dist/resolvers/EntityRecordNameResolver.js +3 -2
  208. package/dist/resolvers/EntityRecordNameResolver.js.map +1 -1
  209. package/dist/resolvers/FetchEntityVectorsResolver.d.ts +27 -1
  210. package/dist/resolvers/FetchEntityVectorsResolver.d.ts.map +1 -1
  211. package/dist/resolvers/FetchEntityVectorsResolver.js +148 -2
  212. package/dist/resolvers/FetchEntityVectorsResolver.js.map +1 -1
  213. package/dist/resolvers/FileResolver.d.ts +82 -11
  214. package/dist/resolvers/FileResolver.d.ts.map +1 -1
  215. package/dist/resolvers/FileResolver.js +279 -101
  216. package/dist/resolvers/FileResolver.js.map +1 -1
  217. package/dist/resolvers/GetDataContextDataResolver.d.ts +33 -7
  218. package/dist/resolvers/GetDataContextDataResolver.d.ts.map +1 -1
  219. package/dist/resolvers/GetDataContextDataResolver.js +117 -38
  220. package/dist/resolvers/GetDataContextDataResolver.js.map +1 -1
  221. package/dist/resolvers/GetDataResolver.d.ts.map +1 -1
  222. package/dist/resolvers/GetDataResolver.js +6 -3
  223. package/dist/resolvers/GetDataResolver.js.map +1 -1
  224. package/dist/resolvers/HumanHandoffResolver.d.ts.map +1 -1
  225. package/dist/resolvers/HumanHandoffResolver.js +7 -1
  226. package/dist/resolvers/HumanHandoffResolver.js.map +1 -1
  227. package/dist/resolvers/IntegrationDiscoveryResolver.d.ts.map +1 -1
  228. package/dist/resolvers/IntegrationDiscoveryResolver.js +4 -2
  229. package/dist/resolvers/IntegrationDiscoveryResolver.js.map +1 -1
  230. package/dist/resolvers/MCPResolver.d.ts.map +1 -1
  231. package/dist/resolvers/MCPResolver.js +9 -4
  232. package/dist/resolvers/MCPResolver.js.map +1 -1
  233. package/dist/resolvers/QueryResolver.d.ts +2 -0
  234. package/dist/resolvers/QueryResolver.d.ts.map +1 -1
  235. package/dist/resolvers/QueryResolver.js +13 -0
  236. package/dist/resolvers/QueryResolver.js.map +1 -1
  237. package/dist/resolvers/RealtimeBridgeResolver.d.ts +54 -2
  238. package/dist/resolvers/RealtimeBridgeResolver.d.ts.map +1 -1
  239. package/dist/resolvers/RealtimeBridgeResolver.js +155 -10
  240. package/dist/resolvers/RealtimeBridgeResolver.js.map +1 -1
  241. package/dist/resolvers/RealtimeClientSessionResolver.d.ts +157 -33
  242. package/dist/resolvers/RealtimeClientSessionResolver.d.ts.map +1 -1
  243. package/dist/resolvers/RealtimeClientSessionResolver.js +404 -116
  244. package/dist/resolvers/RealtimeClientSessionResolver.js.map +1 -1
  245. package/dist/resolvers/RealtimeSessionEventsResolver.d.ts +61 -0
  246. package/dist/resolvers/RealtimeSessionEventsResolver.d.ts.map +1 -0
  247. package/dist/resolvers/RealtimeSessionEventsResolver.js +130 -0
  248. package/dist/resolvers/RealtimeSessionEventsResolver.js.map +1 -0
  249. package/dist/resolvers/RealtimeSessionVerificationResolver.d.ts +85 -0
  250. package/dist/resolvers/RealtimeSessionVerificationResolver.d.ts.map +1 -0
  251. package/dist/resolvers/RealtimeSessionVerificationResolver.js +188 -0
  252. package/dist/resolvers/RealtimeSessionVerificationResolver.js.map +1 -0
  253. package/dist/resolvers/RemoteBrowserActionResolver.d.ts +2 -0
  254. package/dist/resolvers/RemoteBrowserActionResolver.d.ts.map +1 -1
  255. package/dist/resolvers/RemoteBrowserActionResolver.js +5 -1
  256. package/dist/resolvers/RemoteBrowserActionResolver.js.map +1 -1
  257. package/dist/resolvers/RunAIAgentResolver.d.ts +10 -0
  258. package/dist/resolvers/RunAIAgentResolver.d.ts.map +1 -1
  259. package/dist/resolvers/RunAIAgentResolver.js +100 -14
  260. package/dist/resolvers/RunAIAgentResolver.js.map +1 -1
  261. package/dist/resolvers/RunTemplateResolver.d.ts.map +1 -1
  262. package/dist/resolvers/RunTemplateResolver.js +12 -11
  263. package/dist/resolvers/RunTemplateResolver.js.map +1 -1
  264. package/dist/resolvers/SearchKnowledgeStreamResolver.d.ts +12 -1
  265. package/dist/resolvers/SearchKnowledgeStreamResolver.d.ts.map +1 -1
  266. package/dist/resolvers/SearchKnowledgeStreamResolver.js +15 -2
  267. package/dist/resolvers/SearchKnowledgeStreamResolver.js.map +1 -1
  268. package/dist/resolvers/SqlLoggingConfigResolver.d.ts.map +1 -1
  269. package/dist/resolvers/SqlLoggingConfigResolver.js +2 -3
  270. package/dist/resolvers/SqlLoggingConfigResolver.js.map +1 -1
  271. package/dist/resolvers/SyncDataResolver.d.ts.map +1 -1
  272. package/dist/resolvers/SyncDataResolver.js +6 -2
  273. package/dist/resolvers/SyncDataResolver.js.map +1 -1
  274. package/dist/resolvers/SyncRolesUsersResolver.d.ts +2 -0
  275. package/dist/resolvers/SyncRolesUsersResolver.d.ts.map +1 -1
  276. package/dist/resolvers/SyncRolesUsersResolver.js +23 -6
  277. package/dist/resolvers/SyncRolesUsersResolver.js.map +1 -1
  278. package/dist/resolvers/TaskGraphFrameResolver.d.ts +2 -1
  279. package/dist/resolvers/TaskGraphFrameResolver.d.ts.map +1 -1
  280. package/dist/resolvers/TaskGraphFrameResolver.js +28 -1
  281. package/dist/resolvers/TaskGraphFrameResolver.js.map +1 -1
  282. package/dist/resolvers/TestQuerySQLResolver.d.ts +24 -0
  283. package/dist/resolvers/TestQuerySQLResolver.d.ts.map +1 -1
  284. package/dist/resolvers/TestQuerySQLResolver.js +65 -13
  285. package/dist/resolvers/TestQuerySQLResolver.js.map +1 -1
  286. package/dist/resolvers/TransactionGroupResolver.d.ts +19 -2
  287. package/dist/resolvers/TransactionGroupResolver.d.ts.map +1 -1
  288. package/dist/resolvers/TransactionGroupResolver.js +102 -24
  289. package/dist/resolvers/TransactionGroupResolver.js.map +1 -1
  290. package/dist/resolvers/UserAvatarResolver.d.ts +16 -0
  291. package/dist/resolvers/UserAvatarResolver.d.ts.map +1 -0
  292. package/dist/resolvers/UserAvatarResolver.js +124 -0
  293. package/dist/resolvers/UserAvatarResolver.js.map +1 -0
  294. package/dist/resolvers/UserResolver.d.ts.map +1 -1
  295. package/dist/resolvers/UserResolver.js +2 -17
  296. package/dist/resolvers/UserResolver.js.map +1 -1
  297. package/dist/resolvers/agent-run-data-guard.d.ts +31 -0
  298. package/dist/resolvers/agent-run-data-guard.d.ts.map +1 -0
  299. package/dist/resolvers/agent-run-data-guard.js +72 -0
  300. package/dist/resolvers/agent-run-data-guard.js.map +1 -0
  301. package/dist/resolvers/agentVisionConsentAudit.d.ts +37 -0
  302. package/dist/resolvers/agentVisionConsentAudit.d.ts.map +1 -0
  303. package/dist/resolvers/agentVisionConsentAudit.js +63 -0
  304. package/dist/resolvers/agentVisionConsentAudit.js.map +1 -0
  305. package/dist/resolvers/avatarInputValidation.d.ts +40 -0
  306. package/dist/resolvers/avatarInputValidation.d.ts.map +1 -0
  307. package/dist/resolvers/avatarInputValidation.js +97 -0
  308. package/dist/resolvers/avatarInputValidation.js.map +1 -0
  309. package/dist/resolvers/currentUserRoles.d.ts +29 -0
  310. package/dist/resolvers/currentUserRoles.d.ts.map +1 -1
  311. package/dist/resolvers/currentUserRoles.js +30 -0
  312. package/dist/resolvers/currentUserRoles.js.map +1 -1
  313. package/dist/resolvers/meetings/meetingMutations.d.ts +6 -0
  314. package/dist/resolvers/meetings/meetingMutations.d.ts.map +1 -1
  315. package/dist/resolvers/meetings/meetingMutations.js +21 -0
  316. package/dist/resolvers/meetings/meetingMutations.js.map +1 -1
  317. package/dist/rest/RealtimeRecordingHandler.d.ts +49 -0
  318. package/dist/rest/RealtimeRecordingHandler.d.ts.map +1 -0
  319. package/dist/rest/RealtimeRecordingHandler.js +383 -0
  320. package/dist/rest/RealtimeRecordingHandler.js.map +1 -0
  321. package/dist/rest/ViewOperationsHandler.d.ts +3 -1
  322. package/dist/rest/ViewOperationsHandler.d.ts.map +1 -1
  323. package/dist/rest/ViewOperationsHandler.js +21 -12
  324. package/dist/rest/ViewOperationsHandler.js.map +1 -1
  325. package/dist/rest/setupRESTEndpoints.d.ts +6 -0
  326. package/dist/rest/setupRESTEndpoints.d.ts.map +1 -1
  327. package/dist/rest/setupRESTEndpoints.js +6 -0
  328. package/dist/rest/setupRESTEndpoints.js.map +1 -1
  329. package/dist/serverExtensionReservedRoots.d.ts +12 -10
  330. package/dist/serverExtensionReservedRoots.d.ts.map +1 -1
  331. package/dist/serverExtensionReservedRoots.js +14 -17
  332. package/dist/serverExtensionReservedRoots.js.map +1 -1
  333. package/dist/services/MJStorageBlobStore.d.ts +8 -1
  334. package/dist/services/MJStorageBlobStore.d.ts.map +1 -1
  335. package/dist/services/MJStorageBlobStore.js +25 -19
  336. package/dist/services/MJStorageBlobStore.js.map +1 -1
  337. package/dist/services/StartTaskGraphDispatcher.js +2 -2
  338. package/dist/services/StartTaskGraphDispatcher.js.map +1 -1
  339. package/dist/services/TaskGraphActionRunner.d.ts +5 -3
  340. package/dist/services/TaskGraphActionRunner.d.ts.map +1 -1
  341. package/dist/services/TaskGraphActionRunner.js +27 -5
  342. package/dist/services/TaskGraphActionRunner.js.map +1 -1
  343. package/dist/services/TaskGraphAgentRunner.d.ts +14 -2
  344. package/dist/services/TaskGraphAgentRunner.d.ts.map +1 -1
  345. package/dist/services/TaskGraphAgentRunner.js +157 -9
  346. package/dist/services/TaskGraphAgentRunner.js.map +1 -1
  347. package/dist/services/TaskGraphContinuationDeliverer.d.ts +57 -4
  348. package/dist/services/TaskGraphContinuationDeliverer.d.ts.map +1 -1
  349. package/dist/services/TaskGraphContinuationDeliverer.js +607 -18
  350. package/dist/services/TaskGraphContinuationDeliverer.js.map +1 -1
  351. package/dist/sharedCache.d.ts +105 -0
  352. package/dist/sharedCache.d.ts.map +1 -0
  353. package/dist/sharedCache.js +279 -0
  354. package/dist/sharedCache.js.map +1 -0
  355. package/dist/types.d.ts +6 -0
  356. package/dist/types.d.ts.map +1 -1
  357. package/dist/types.js.map +1 -1
  358. package/package.json +99 -96
  359. package/src/__tests__/ActionResolver.authorization.test.ts +353 -0
  360. package/src/__tests__/AdhocQueryResolver.sqlAuthorizer.test.ts +104 -0
  361. package/src/__tests__/BridgeHostInstance.test.ts +232 -0
  362. package/src/__tests__/ClientToolRequestResolver.test.ts +116 -0
  363. package/src/__tests__/DatasetResolver.itemFilters.test.ts +201 -0
  364. package/src/__tests__/EntityRecordNameResolver.rowSecurity.test.ts +152 -0
  365. package/src/__tests__/FetchEntityVectorsResolver.test.ts +433 -0
  366. package/src/__tests__/FileResolver.storageAccess.test.ts +562 -0
  367. package/src/__tests__/GetDataContextDataResolver.sqlItems.test.ts +416 -0
  368. package/src/__tests__/HostInstance.test.ts +84 -0
  369. package/src/__tests__/HumanHandoffResolver.test.ts +17 -0
  370. package/src/__tests__/ListOperationsResolver.adhocScreen.test.ts +137 -0
  371. package/src/__tests__/LoginScreenEngine.test.ts +86 -0
  372. package/src/__tests__/MJStorageBlobStore.storageGate.test.ts +63 -0
  373. package/src/__tests__/MeetingResolver.test.ts +75 -1
  374. package/src/__tests__/RealtimeBridgeResolver.test.ts +415 -6
  375. package/src/__tests__/RealtimeClientSessionResolver.configTamper.test.ts +383 -0
  376. package/src/__tests__/RealtimeClientSessionResolver.test.ts +682 -58
  377. package/src/__tests__/RealtimeProxyServer.test.ts +16 -1
  378. package/src/__tests__/RealtimeRelay.test.ts +876 -0
  379. package/src/__tests__/RecordingAssemblyScheduler.test.ts +178 -0
  380. package/src/__tests__/RemoteBrowserActionResolver.goalRunLink.test.ts +129 -0
  381. package/src/__tests__/ResolverBase.adhocReadScope.test.ts +133 -2
  382. package/src/__tests__/ResolverBase.aggregateScreen.test.ts +174 -0
  383. package/src/__tests__/ResolverBase.balancedClause.test.ts +155 -0
  384. package/src/__tests__/ResolverBase.clauseUnscopedRead.test.ts +127 -0
  385. package/src/__tests__/ResolverBase.clientOldValues.test.ts +212 -6
  386. package/src/__tests__/ResolverBase.filterEscaping.test.ts +5 -2
  387. package/src/__tests__/ReturningVisitorRecap.test.ts +115 -0
  388. package/src/__tests__/RunAIAgentResolver.branch.test.ts +170 -0
  389. package/src/__tests__/RunAIAgentResolver.historyFrom.test.ts +6 -4
  390. package/src/__tests__/RunAIAgentResolver.runDataGuard.test.ts +258 -0
  391. package/src/__tests__/RunAIAgentResolver.stoppedPredecessor.test.ts +166 -0
  392. package/src/__tests__/RunTemplateResolver.test.ts +132 -0
  393. package/src/__tests__/RunViewResolver.cacheCheckForwarding.test.ts +63 -0
  394. package/src/__tests__/RunViewResolver.cacheCheckScreen.test.ts +165 -0
  395. package/src/__tests__/SearchKnowledgeStreamResolver.test.ts +161 -0
  396. package/src/__tests__/SessionJanitor.test.ts +1203 -21
  397. package/src/__tests__/SessionManager.test.ts +240 -17
  398. package/src/__tests__/SessionRunIDVerifier.test.ts +251 -0
  399. package/src/__tests__/SyncRolesUsersResolver.userRoles.test.ts +97 -0
  400. package/src/__tests__/TaskGraphActionRunner.durablePayload.test.ts +25 -0
  401. package/src/__tests__/TaskGraphAgentRunner.test.ts +266 -0
  402. package/src/__tests__/TaskGraphContinuationDeliverer.test.ts +403 -22
  403. package/src/__tests__/TaskGraphFrameResolver.test.ts +22 -0
  404. package/src/__tests__/TestQuerySQLResolver.test.ts +274 -0
  405. package/src/__tests__/TransactionGroupResolver.refusals.test.ts +98 -1
  406. package/src/__tests__/UpdateMJDataContextItem.oldValues.test.ts +220 -0
  407. package/src/__tests__/UserAvatarResolver.test.ts +255 -0
  408. package/src/__tests__/UserResolver.currentUser.test.ts +16 -1
  409. package/src/__tests__/ViewOperationsHandler.clauseScreen.test.ts +157 -0
  410. package/src/__tests__/actionAuthorization.test.ts +130 -0
  411. package/src/__tests__/agent-run-data-guard.test.ts +130 -0
  412. package/src/__tests__/agentVisionConsentAudit.test.ts +118 -0
  413. package/src/__tests__/apiRootBoot.test.ts +202 -0
  414. package/src/__tests__/avatarInputValidation.test.ts +160 -0
  415. package/src/__tests__/cacheInvalidationRecordData.test.ts +64 -0
  416. package/src/__tests__/conversationRunEvents.test.ts +56 -14
  417. package/src/__tests__/globalCors.test.ts +217 -0
  418. package/src/__tests__/isolatedProviderTransactions.test.ts +169 -0
  419. package/src/__tests__/magicLinkService.consume.test.ts +6 -0
  420. package/src/__tests__/magicLinkService.provisionLog.test.ts +5 -0
  421. package/src/__tests__/multiTenancy.postgresRewrite.test.ts +181 -0
  422. package/src/__tests__/multiTenancy.security.test.ts +51 -0
  423. package/src/__tests__/multiTenancy.test.ts +1 -1
  424. package/src/__tests__/newUsers.template.test.ts +151 -0
  425. package/src/__tests__/newUsers.test.ts +74 -5
  426. package/src/__tests__/orphanedConversationDetailReconciler.test.ts +17 -2
  427. package/src/__tests__/ownedRealtimeSession.test.ts +39 -0
  428. package/src/__tests__/pushStatusResolver.filter.test.ts +7 -0
  429. package/src/__tests__/realtimeRecordingHandler.test.ts +652 -0
  430. package/src/__tests__/realtimeSessions/RealtimeSessionEventService.test.ts +201 -0
  431. package/src/__tests__/realtimeSessions/RealtimeSessionEventsResolver.test.ts +215 -0
  432. package/src/__tests__/realtimeSessions/RealtimeSessionVerificationResolver.test.ts +102 -0
  433. package/src/__tests__/realtimeSessions/RealtimeSessionVerificationService.test.ts +349 -0
  434. package/src/__tests__/realtimeSessions/RealtimeVerifyRouter.test.ts +131 -0
  435. package/src/__tests__/realtimeSessions/SlidingWindowRateLimiter.test.ts +56 -0
  436. package/src/__tests__/realtimeSessions/helpers.ts +4 -0
  437. package/src/__tests__/realtimeSessions/sessionAccess.test.ts +129 -0
  438. package/src/__tests__/realtimeSessions/sessionConfigStamps.test.ts +124 -0
  439. package/src/__tests__/realtimeSessions/verificationCore.test.ts +355 -0
  440. package/src/__tests__/realtimeSessions/verificationPages.test.ts +84 -0
  441. package/src/__tests__/realtimeSessions/verificationPolicy.test.ts +226 -0
  442. package/src/__tests__/realtimeSessions/verificationWorkflow.test.ts +633 -0
  443. package/src/__tests__/resolverBase.fls.test.ts +87 -4
  444. package/src/__tests__/serverExtensionHostOverlay.test.ts +66 -0
  445. package/src/__tests__/serverExtensionReservedRoots.test.ts +11 -96
  446. package/src/__tests__/sharedCache.test.ts +312 -0
  447. package/src/__tests__/widgetGuestElevation.test.ts +44 -0
  448. package/src/agentSessions/BridgeHostInstance.ts +32 -0
  449. package/src/agentSessions/HostInstance.ts +124 -28
  450. package/src/agentSessions/ReturningVisitorRecap.ts +7 -3
  451. package/src/agentSessions/SessionJanitor.ts +821 -59
  452. package/src/agentSessions/SessionManager.ts +138 -26
  453. package/src/agentSessions/SessionRunIDVerifier.ts +171 -0
  454. package/src/agentSessions/index.ts +10 -3
  455. package/src/apiRoot.ts +78 -0
  456. package/src/auth/AuthProviderCatalogRouter.ts +26 -0
  457. package/src/auth/LoginScreenEngine.ts +50 -0
  458. package/src/auth/actionAuthorization.ts +116 -0
  459. package/src/auth/index.ts +4 -8
  460. package/src/auth/magicLink/MagicLinkRouter.ts +3 -2
  461. package/src/auth/magicLink/MagicLinkService.ts +6 -3
  462. package/src/auth/newUsers.ts +99 -11
  463. package/src/config.ts +171 -5
  464. package/src/context.ts +15 -10
  465. package/src/coreRoutes.ts +166 -0
  466. package/src/generated/graphql-schemas/__mj.ts +1815 -57
  467. package/src/generic/CacheInvalidationResolver.ts +73 -18
  468. package/src/generic/OrphanedConversationDetailReconciler.ts +3 -3
  469. package/src/generic/PushStatusResolver.ts +1 -1
  470. package/src/generic/ResolverBase.ts +335 -326
  471. package/src/generic/RunViewResolver.ts +21 -0
  472. package/src/globalCors.ts +71 -0
  473. package/src/index.ts +231 -300
  474. package/src/isolatedProvider.ts +22 -0
  475. package/src/multiTenancy/index.ts +24 -2
  476. package/src/realtimeProxy/RealtimeProxyServer.ts +148 -16
  477. package/src/realtimeProxy/RealtimeRelayTunnel.ts +645 -0
  478. package/src/realtimeSessions/README.md +179 -0
  479. package/src/realtimeSessions/RealtimeSessionEventService.ts +274 -0
  480. package/src/realtimeSessions/RealtimeSessionVerificationService.ts +168 -0
  481. package/src/realtimeSessions/RealtimeVerifyRouter.ts +109 -0
  482. package/src/realtimeSessions/RecordingAssemblyScheduler.ts +234 -0
  483. package/src/realtimeSessions/SessionVerificationPortsBase.ts +189 -0
  484. package/src/realtimeSessions/SlidingWindowRateLimiter.ts +92 -0
  485. package/src/realtimeSessions/index.ts +18 -0
  486. package/src/realtimeSessions/ownedRealtimeSession.ts +55 -0
  487. package/src/realtimeSessions/sessionAccess.ts +145 -0
  488. package/src/realtimeSessions/sessionConfigStamps.ts +123 -0
  489. package/src/realtimeSessions/subscriptionAuthorization.ts +116 -0
  490. package/src/realtimeSessions/verificationCore.ts +502 -0
  491. package/src/realtimeSessions/verificationPages.ts +149 -0
  492. package/src/realtimeSessions/verificationPolicy.ts +321 -0
  493. package/src/realtimeSessions/verificationWorkflow.ts +702 -0
  494. package/src/realtimeWidget/widgetGuestElevation.ts +34 -2
  495. package/src/resolvers/ActionResolver.ts +84 -21
  496. package/src/resolvers/AdhocQueryResolver.ts +24 -6
  497. package/src/resolvers/ArtifactFileResolver.ts +9 -8
  498. package/src/resolvers/CacheStatsResolver.ts +89 -2
  499. package/src/resolvers/ClientToolRequestResolver.ts +25 -7
  500. package/src/resolvers/ConversationRunEventsResolver.ts +15 -11
  501. package/src/resolvers/DatasetResolver.ts +70 -10
  502. package/src/resolvers/EntityRecordNameResolver.ts +3 -2
  503. package/src/resolvers/FetchEntityVectorsResolver.ts +179 -2
  504. package/src/resolvers/FileResolver.ts +321 -106
  505. package/src/resolvers/GetDataContextDataResolver.ts +139 -41
  506. package/src/resolvers/GetDataResolver.ts +6 -3
  507. package/src/resolvers/HumanHandoffResolver.ts +8 -0
  508. package/src/resolvers/IntegrationDiscoveryResolver.ts +4 -2
  509. package/src/resolvers/MCPResolver.ts +8 -5
  510. package/src/resolvers/QueryResolver.ts +11 -0
  511. package/src/resolvers/RealtimeBridgeResolver.ts +163 -11
  512. package/src/resolvers/RealtimeClientSessionResolver.ts +511 -129
  513. package/src/resolvers/RealtimeSessionEventsResolver.ts +133 -0
  514. package/src/resolvers/RealtimeSessionVerificationResolver.ts +174 -0
  515. package/src/resolvers/RemoteBrowserActionResolver.ts +11 -1
  516. package/src/resolvers/RunAIAgentResolver.ts +116 -14
  517. package/src/resolvers/RunTemplateResolver.ts +13 -12
  518. package/src/resolvers/SearchKnowledgeStreamResolver.ts +19 -3
  519. package/src/resolvers/SqlLoggingConfigResolver.ts +2 -4
  520. package/src/resolvers/SyncDataResolver.ts +6 -2
  521. package/src/resolvers/SyncRolesUsersResolver.ts +24 -6
  522. package/src/resolvers/TaskGraphFrameResolver.ts +33 -1
  523. package/src/resolvers/TestQuerySQLResolver.ts +68 -13
  524. package/src/resolvers/TransactionGroupResolver.ts +131 -27
  525. package/src/resolvers/UserAvatarResolver.ts +111 -0
  526. package/src/resolvers/UserResolver.ts +2 -16
  527. package/src/resolvers/__tests__/GetDataResolver.validation.test.ts +95 -0
  528. package/src/resolvers/agent-run-data-guard.ts +89 -0
  529. package/src/resolvers/agentVisionConsentAudit.ts +82 -0
  530. package/src/resolvers/avatarInputValidation.ts +123 -0
  531. package/src/resolvers/currentUserRoles.ts +32 -1
  532. package/src/resolvers/meetings/meetingMutations.ts +21 -0
  533. package/src/rest/RealtimeRecordingHandler.ts +464 -0
  534. package/src/rest/ViewOperationsHandler.ts +29 -13
  535. package/src/rest/setupRESTEndpoints.ts +6 -0
  536. package/src/serverExtensionReservedRoots.ts +14 -17
  537. package/src/services/MJStorageBlobStore.ts +31 -30
  538. package/src/services/StartTaskGraphDispatcher.ts +2 -2
  539. package/src/services/TaskGraphActionRunner.ts +25 -5
  540. package/src/services/TaskGraphAgentRunner.ts +188 -11
  541. package/src/services/TaskGraphContinuationDeliverer.ts +713 -17
  542. package/src/sharedCache.ts +305 -0
  543. package/src/types.ts +7 -0
@@ -5,6 +5,8 @@ import { afterEach, describe, expect, it, vi } from 'vitest';
5
5
  import {
6
6
  ConfigureRecordDataBroadcast,
7
7
  MayBroadcastRecordData,
8
+ ReadIsRowFiltered,
9
+ ShapeCacheInvalidationEvent,
8
10
  } from '../generic/CacheInvalidationResolver.js';
9
11
 
10
12
  /**
@@ -175,3 +177,65 @@ describe('cacheInvalidationFilter', () => {
175
177
  expect(filter({ payload: { ...event(''), entityName: '' } as never, context: session({ ID: 'u1' }) })).toBe(false);
176
178
  });
177
179
  });
180
+
181
+ /**
182
+ * Row-level narrowing (MJ#5241): a subscriber who reads the entity only through a row-level security filter learns that a
183
+ * row changed, not which one; a subscriber with an unfiltered read keeps the key; a subscriber with no read is the entity-level
184
+ * filter's business and gets nothing here to shape.
185
+ */
186
+ describe('ReadIsRowFiltered and ShapeCacheInvalidationEvent', () => {
187
+ const FILTERED_ROLE = 'role-participant';
188
+ const OPEN_ROLE = 'role-staff';
189
+ const entity = {
190
+ Permissions: [
191
+ { RoleID: FILTERED_ROLE, CanRead: true, Type: 'Allow', ReadRLSFilterID: 'filter-1' },
192
+ { RoleID: OPEN_ROLE, CanRead: true, Type: 'Allow', ReadRLSFilterID: null },
193
+ { RoleID: 'role-denied', CanRead: true, Type: 'Deny', ReadRLSFilterID: null },
194
+ ],
195
+ };
196
+ const payload = {
197
+ entityName: 'Spaces',
198
+ primaryKeyValues: 'ID|abc',
199
+ action: 'save',
200
+ sourceServerId: 'srv',
201
+ timestamp: new Date('2026-10-07T00:00:00Z'),
202
+ originSessionId: 'sess',
203
+ recordData: '{"Name":"x"}',
204
+ };
205
+
206
+ it('is row-filtered when every read the user holds carries a filter', () => {
207
+ expect(ReadIsRowFiltered(entity, { UserRoles: [{ RoleID: FILTERED_ROLE }] })).toBe(true);
208
+ });
209
+
210
+ it('is not row-filtered when any read the user holds is unfiltered, whichever role brings it', () => {
211
+ expect(ReadIsRowFiltered(entity, { UserRoles: [{ RoleID: OPEN_ROLE }] })).toBe(false);
212
+ expect(ReadIsRowFiltered(entity, { UserRoles: [{ RoleID: FILTERED_ROLE }, { RoleID: OPEN_ROLE }] })).toBe(false);
213
+ });
214
+
215
+ it('is not row-filtered when the user holds no read at all (the entity-level filter owns that case), and a Deny row grants nothing', () => {
216
+ expect(ReadIsRowFiltered(entity, { UserRoles: [{ RoleID: 'role-stranger' }] })).toBe(false);
217
+ expect(ReadIsRowFiltered(entity, { UserRoles: [{ RoleID: 'role-denied' }] })).toBe(false);
218
+ expect(ReadIsRowFiltered(entity, { UserRoles: null })).toBe(false);
219
+ });
220
+
221
+ it('matches role ids without regard to case or whitespace', () => {
222
+ expect(ReadIsRowFiltered(entity, { UserRoles: [{ RoleID: ` ${FILTERED_ROLE.toUpperCase()} ` }] })).toBe(true);
223
+ });
224
+
225
+ it('withholds the key and the row from a row-filtered subscriber, and keeps the rest of the event', () => {
226
+ const shaped = ShapeCacheInvalidationEvent(payload, { UserRoles: [{ RoleID: FILTERED_ROLE }] }, entity);
227
+ expect(shaped.PrimaryKeyValues).toBeUndefined();
228
+ expect(shaped.RecordData).toBeUndefined();
229
+ expect(shaped.EntityName).toBe('Spaces');
230
+ expect(shaped.Action).toBe('save');
231
+ expect(shaped.SourceServerID).toBe('srv');
232
+ expect(shaped.OriginSessionID).toBe('sess');
233
+ });
234
+
235
+ it('delivers the event whole to an unfiltered reader, and when the entity or the user is unknown', () => {
236
+ expect(ShapeCacheInvalidationEvent(payload, { UserRoles: [{ RoleID: OPEN_ROLE }] }, entity).PrimaryKeyValues).toBe('ID|abc');
237
+ expect(ShapeCacheInvalidationEvent(payload, { UserRoles: [{ RoleID: OPEN_ROLE }] }, entity).RecordData).toBe('{"Name":"x"}');
238
+ expect(ShapeCacheInvalidationEvent(payload, { UserRoles: [{ RoleID: FILTERED_ROLE }] }, null).PrimaryKeyValues).toBe('ID|abc');
239
+ expect(ShapeCacheInvalidationEvent(payload, undefined, entity).PrimaryKeyValues).toBe('ID|abc');
240
+ });
241
+ });
@@ -128,7 +128,7 @@ describe('TailConversationEvents', () => {
128
128
 
129
129
  const runCall = mockRunView.mock.calls.find((c) => c[0].EntityName === 'MJ: AI Agent Runs');
130
130
  expect(runCall?.[0].ResultType).toBe('simple');
131
- expect([...(runCall?.[0].Fields ?? [])].sort()).toEqual(['ID', 'Result', 'Status']);
131
+ expect([...(runCall?.[0].Fields ?? [])].sort()).toEqual(['FinalPayload', 'ID', 'Result', 'Status']);
132
132
  });
133
133
 
134
134
  it('returns real dates when a plain row carries them as strings', async () => {
@@ -261,20 +261,20 @@ describe('TailConversationEvents', () => {
261
261
  it('marks a Running run in flight and withholds the payload', async () => {
262
262
  scriptRunView({
263
263
  detail: { ID: DETAIL_ID, Status: 'In-Progress' },
264
- run: { ID: RUN_ID, Status: 'Running', Result: '{"partial":true}' },
264
+ run: { ID: RUN_ID, Status: 'Running', FinalPayload: '{"partial":true}' },
265
265
  steps: [step(1)],
266
266
  });
267
267
  const out = await resolver.TailConversationEvents(DETAIL_ID, ctxFor(USER), 0);
268
268
 
269
269
  expect(out.IsInFlight).toBe(true);
270
- // A partial Result mid-run could be mistaken for the final answer.
270
+ // FinalPayload is filled during the run. A partial one could be mistaken for the final answer.
271
271
  expect(out.FinalPayload).toBeUndefined();
272
272
  });
273
273
 
274
274
  it('releases the payload once the run is terminal, so one call ends the wait', async () => {
275
275
  scriptRunView({
276
276
  detail: { ID: DETAIL_ID, Status: 'Complete' },
277
- run: { ID: RUN_ID, Status: 'Completed', Result: '{"answer":"done"}' },
277
+ run: { ID: RUN_ID, Status: 'Completed', FinalPayload: '{"answer":"done"}', Result: null },
278
278
  steps: [step(1)],
279
279
  });
280
280
  const out = await resolver.TailConversationEvents(DETAIL_ID, ctxFor(USER), 0);
@@ -302,7 +302,7 @@ describe('TailConversationEvents', () => {
302
302
  // Reporting it as terminal would tell a client to complete a message mid-workflow.
303
303
  scriptRunView({
304
304
  detail: { ID: DETAIL_ID, Status: 'In-Progress' },
305
- run: { ID: RUN_ID, Status: 'Paused', Result: '{"started":"workflow"}' },
305
+ run: { ID: RUN_ID, Status: 'Paused', FinalPayload: '{"started":"workflow"}' },
306
306
  steps: [step(1)],
307
307
  });
308
308
  const out = await resolver.TailConversationEvents(DETAIL_ID, ctxFor(USER), 0);
@@ -353,16 +353,19 @@ describe('TailConversationEvents', () => {
353
353
  });
354
354
 
355
355
  /**
356
- * REGRESSION, found in manual testing. `FinalPayload` read only `AIAgentRun.Result`, which is
357
- * agent-dependent — null on 16 of 40 completed runs in a real database, including every run of the
358
- * Loop-type agent used to reproduce #4222. A caller waiting on this field to decide a message was
359
- * finished would wait forever on those agents.
356
+ * `FinalPayload` comes from `AIAgentRun.FinalPayload`, then `Result` (some older rows have no
357
+ * `FinalPayload`), then the conversation detail's message.
358
+ *
359
+ * REGRESSION, found in manual testing: the run payload is agent-dependent — null on 16 of 40
360
+ * completed runs in a real database, including every run of the Loop-type agent used to reproduce
361
+ * #4222. Without the detail-message fallback, a caller waiting on this field to decide a message
362
+ * was finished would wait forever on those agents.
360
363
  */
361
364
  describe('TailConversationEvents FinalPayload sourcing', () => {
362
- it('falls back to the conversation detail message when the run has no Result', async () => {
365
+ it('falls back to the conversation detail message when the run has no payload', async () => {
363
366
  scriptRunView({
364
367
  detail: { ID: DETAIL_ID, Status: 'Complete', Message: 'the answer text' },
365
- run: { ID: RUN_ID, Status: 'Completed', Result: null },
368
+ run: { ID: RUN_ID, Status: 'Completed', FinalPayload: null, Result: null },
366
369
  steps: [],
367
370
  });
368
371
 
@@ -372,10 +375,34 @@ describe('TailConversationEvents FinalPayload sourcing', () => {
372
375
  expect(result.FinalPayload).toBe('the answer text');
373
376
  });
374
377
 
375
- it('prefers the run Result when the agent does write one', async () => {
378
+ it('prefers the run FinalPayload over the detail message', async () => {
379
+ scriptRunView({
380
+ detail: { ID: DETAIL_ID, Status: 'Complete', Message: 'detail message' },
381
+ run: { ID: RUN_ID, Status: 'Completed', FinalPayload: 'run payload', Result: null },
382
+ steps: [],
383
+ });
384
+
385
+ const result = await new ConversationRunEventsResolver().TailConversationEvents(DETAIL_ID, ctxFor(USER), 0);
386
+
387
+ expect(result.FinalPayload).toBe('run payload');
388
+ });
389
+
390
+ it('prefers FinalPayload over Result when a row has both', async () => {
391
+ scriptRunView({
392
+ detail: { ID: DETAIL_ID, Status: 'Complete', Message: 'detail message' },
393
+ run: { ID: RUN_ID, Status: 'Completed', FinalPayload: 'run payload', Result: 'run result' },
394
+ steps: [],
395
+ });
396
+
397
+ const result = await new ConversationRunEventsResolver().TailConversationEvents(DETAIL_ID, ctxFor(USER), 0);
398
+
399
+ expect(result.FinalPayload).toBe('run payload');
400
+ });
401
+
402
+ it('falls back to Result on an older row that has no FinalPayload', async () => {
376
403
  scriptRunView({
377
404
  detail: { ID: DETAIL_ID, Status: 'Complete', Message: 'detail message' },
378
- run: { ID: RUN_ID, Status: 'Completed', Result: 'run result' },
405
+ run: { ID: RUN_ID, Status: 'Completed', FinalPayload: null, Result: 'run result' },
379
406
  steps: [],
380
407
  });
381
408
 
@@ -384,10 +411,25 @@ describe('TailConversationEvents FinalPayload sourcing', () => {
384
411
  expect(result.FinalPayload).toBe('run result');
385
412
  });
386
413
 
414
+ it('returns the partial FinalPayload a Cancelled run saved, with RunStatus telling the caller', async () => {
415
+ // Cancelling saves the run with no Result, but it keeps the FinalPayload saved during the run.
416
+ scriptRunView({
417
+ detail: { ID: DETAIL_ID, Status: 'Error', Message: 'detail message' },
418
+ run: { ID: RUN_ID, Status: 'Cancelled', FinalPayload: '{"partial":true}', Result: null },
419
+ steps: [],
420
+ });
421
+
422
+ const result = await new ConversationRunEventsResolver().TailConversationEvents(DETAIL_ID, ctxFor(USER), 0);
423
+
424
+ expect(result.IsInFlight).toBe(false);
425
+ expect(result.RunStatus).toBe('Cancelled');
426
+ expect(result.FinalPayload).toBe('{"partial":true}');
427
+ });
428
+
387
429
  it('still withholds the payload while the run is in flight', async () => {
388
430
  scriptRunView({
389
431
  detail: { ID: DETAIL_ID, Status: 'In-Progress', Message: 'partial text' },
390
- run: { ID: RUN_ID, Status: 'Running', Result: null },
432
+ run: { ID: RUN_ID, Status: 'Running', FinalPayload: '{"partial":true}', Result: null },
391
433
  steps: [],
392
434
  });
393
435
 
@@ -0,0 +1,217 @@
1
+ /**
2
+ * MJServer's global CORS middleware vs. server extensions that own their CORS policy.
3
+ *
4
+ * Reproduces the BizApps Voting ballot-edge failure: the global middleware ran ahead of the
5
+ * extension, reflected any origin onto the extension's refusals, and answered every preflight
6
+ * with 204 so the extension's own OPTIONS handler never ran. Runs a real Express app on an
7
+ * ephemeral port so the preflight is Express's and `cors`'s own behavior, not a mock.
8
+ */
9
+ import { describe, it, expect, afterEach } from 'vitest';
10
+ import express, { type Application } from 'express';
11
+ import type { Server } from 'node:http';
12
+ import type { AddressInfo } from 'node:net';
13
+ import { readFileSync } from 'node:fs';
14
+ import { GetCorsOwningServerExtensionRoots, type ServerExtensionConfig } from '@memberjunction/server-extensions-core';
15
+ import cors from 'cors';
16
+ import { CreateExtensionOwnedPreflightFallback, CreateGlobalCorsMiddleware } from '../globalCors.js';
17
+
18
+ const ALLOWED_BALLOT_ORIGIN = 'https://vote.example.org';
19
+ const OTHER_ORIGIN = 'https://evil.example.com';
20
+
21
+ /** A ballot-style edge: its own per-resource origin policy and its own preflight answer. */
22
+ function mountBallotExtension(app: Application): void {
23
+ const router = express.Router();
24
+ router.use((req, res, next) => {
25
+ const origin = req.headers.origin;
26
+ if (origin && origin !== ALLOWED_BALLOT_ORIGIN) {
27
+ res.status(403).json({ error: 'origin not allowed for this ballot' });
28
+ return;
29
+ }
30
+ if (origin) {
31
+ res.setHeader('Access-Control-Allow-Origin', origin);
32
+ res.setHeader('Vary', 'Origin');
33
+ }
34
+ if (req.method === 'OPTIONS') {
35
+ res.setHeader('Access-Control-Allow-Methods', 'POST');
36
+ res.setHeader('X-Ballot-Preflight', 'extension');
37
+ res.status(200).end();
38
+ return;
39
+ }
40
+ next();
41
+ });
42
+ router.post('/cast', (_req, res) => { res.json({ ok: true }); });
43
+ app.use('/ballot', router);
44
+ }
45
+
46
+ let server: Server | undefined;
47
+
48
+ afterEach(async () => {
49
+ if (server) {
50
+ await new Promise<void>((resolve) => server!.close(() => resolve()));
51
+ server = undefined;
52
+ }
53
+ });
54
+
55
+ async function startApp(extensions: ServerExtensionConfig[]): Promise<string> {
56
+ const app = express();
57
+ app.disable('x-powered-by');
58
+ app.use(CreateGlobalCorsMiddleware({ allowedOrigins: ['*'] }, GetCorsOwningServerExtensionRoots(extensions)));
59
+ mountBallotExtension(app);
60
+ app.get('/core', (_req, res) => { res.json({ ok: true }); });
61
+ server = app.listen(0);
62
+ await new Promise<void>((resolve) => server!.once('listening', () => resolve()));
63
+ return `http://127.0.0.1:${(server.address() as AddressInfo).port}`;
64
+ }
65
+
66
+ const ballotConfig = (ownsCors: boolean | undefined): ServerExtensionConfig => ({
67
+ Enabled: true,
68
+ DriverClass: 'BallotEdge',
69
+ RootPath: '/ballot',
70
+ Phase: 'pre-auth',
71
+ ...(ownsCors === undefined ? {} : { OwnsCORS: ownsCors }),
72
+ Settings: {},
73
+ });
74
+
75
+ describe('CreateGlobalCorsMiddleware', () => {
76
+ it('without OwnsCORS, global CORS answers the preflight and the extension never sees it (the bug)', async () => {
77
+ const base = await startApp([ballotConfig(undefined)]);
78
+ const res = await fetch(`${base}/ballot/cast`, {
79
+ method: 'OPTIONS',
80
+ headers: { Origin: OTHER_ORIGIN, 'Access-Control-Request-Method': 'POST' },
81
+ });
82
+ expect(res.status).toBe(204);
83
+ expect(res.headers.get('access-control-allow-origin')).toBe(OTHER_ORIGIN);
84
+ expect(res.headers.get('x-ballot-preflight')).toBeNull();
85
+ });
86
+
87
+ it('with OwnsCORS, the preflight reaches the extension, which refuses a foreign origin without CORS headers', async () => {
88
+ const base = await startApp([ballotConfig(true)]);
89
+ const res = await fetch(`${base}/ballot/cast`, {
90
+ method: 'OPTIONS',
91
+ headers: { Origin: OTHER_ORIGIN, 'Access-Control-Request-Method': 'POST' },
92
+ });
93
+ expect(res.status).toBe(403);
94
+ expect(res.headers.get('access-control-allow-origin')).toBeNull();
95
+ });
96
+
97
+ it('with OwnsCORS, the extension answers its own preflight for its allowed origin', async () => {
98
+ const base = await startApp([ballotConfig(true)]);
99
+ const res = await fetch(`${base}/ballot/cast`, {
100
+ method: 'OPTIONS',
101
+ headers: { Origin: ALLOWED_BALLOT_ORIGIN, 'Access-Control-Request-Method': 'POST' },
102
+ });
103
+ expect(res.status).toBe(200);
104
+ expect(res.headers.get('x-ballot-preflight')).toBe('extension');
105
+ expect(res.headers.get('access-control-allow-origin')).toBe(ALLOWED_BALLOT_ORIGIN);
106
+ // The global policy's max-age would only be there if global CORS had run.
107
+ expect(res.headers.get('access-control-max-age')).toBeNull();
108
+ });
109
+
110
+ it('with OwnsCORS, a foreign-origin refusal on a normal request carries no reflected origin', async () => {
111
+ const base = await startApp([ballotConfig(true)]);
112
+ const res = await fetch(`${base}/ballot/cast`, { method: 'POST', headers: { Origin: OTHER_ORIGIN } });
113
+ expect(res.status).toBe(403);
114
+ expect(res.headers.get('access-control-allow-origin')).toBeNull();
115
+ });
116
+
117
+ it('keeps global CORS on every path the extension does not own', async () => {
118
+ const base = await startApp([ballotConfig(true)]);
119
+ const res = await fetch(`${base}/core`, {
120
+ method: 'OPTIONS',
121
+ headers: { Origin: OTHER_ORIGIN, 'Access-Control-Request-Method': 'GET' },
122
+ });
123
+ expect(res.status).toBe(204);
124
+ expect(res.headers.get('access-control-allow-origin')).toBe(OTHER_ORIGIN);
125
+ expect(res.headers.get('access-control-max-age')).toBe('86400');
126
+ });
127
+
128
+ it('ignores OwnsCORS on a disabled extension', async () => {
129
+ const base = await startApp([{ ...ballotConfig(true), Enabled: false }]);
130
+ const res = await fetch(`${base}/ballot/cast`, {
131
+ method: 'OPTIONS',
132
+ headers: { Origin: OTHER_ORIGIN, 'Access-Control-Request-Method': 'POST' },
133
+ });
134
+ expect(res.status).toBe(204);
135
+ });
136
+
137
+ it('never sends credentials under the wildcard, even when allowCredentials is true', async () => {
138
+ const app = express();
139
+ app.use(CreateGlobalCorsMiddleware({ allowedOrigins: ['*'], allowCredentials: true }));
140
+ app.get('/core', (_req, res) => { res.json({ ok: true }); });
141
+ server = app.listen(0);
142
+ await new Promise<void>((resolve) => server!.once('listening', () => resolve()));
143
+ const base = `http://127.0.0.1:${(server.address() as AddressInfo).port}`;
144
+ const res = await fetch(`${base}/core`, { headers: { Origin: OTHER_ORIGIN } });
145
+ expect(res.headers.get('access-control-allow-credentials')).toBeNull();
146
+ });
147
+ });
148
+
149
+ /**
150
+ * The real mount order: global CORS → extensions → (fallback) → the GraphQL mount at `/`, whose own
151
+ * `cors()` approves every origin. The webhook extension owns its CORS but has no OPTIONS handler.
152
+ */
153
+ async function startAppWithGraphQLMount(withFallback: boolean): Promise<string> {
154
+ const extensions: ServerExtensionConfig[] = [
155
+ ballotConfig(true),
156
+ { Enabled: true, DriverClass: 'Webhook', RootPath: '/webhook', Phase: 'pre-auth', OwnsCORS: true, Settings: {} },
157
+ ];
158
+ const ownedRoots = GetCorsOwningServerExtensionRoots(extensions);
159
+ const app = express();
160
+ app.use(CreateGlobalCorsMiddleware({ allowedOrigins: ['*'] }, ownedRoots));
161
+ mountBallotExtension(app);
162
+ app.post('/webhook/events', (_req, res) => { res.json({ received: true }); });
163
+ if (withFallback) {
164
+ app.use(CreateExtensionOwnedPreflightFallback(ownedRoots));
165
+ }
166
+ app.use('/', cors(), (_req, res) => { res.json({ graphql: true }); });
167
+ server = app.listen(0);
168
+ await new Promise<void>((resolve) => server!.once('listening', () => resolve()));
169
+ return `http://127.0.0.1:${(server.address() as AddressInfo).port}`;
170
+ }
171
+
172
+ async function preflight(url: string, origin: string): Promise<Response> {
173
+ return fetch(url, { method: 'OPTIONS', headers: { Origin: origin, 'Access-Control-Request-Method': 'POST' } });
174
+ }
175
+
176
+ describe('CreateExtensionOwnedPreflightFallback', () => {
177
+ it('without it, an unanswered preflight under an owned root falls through to the GraphQL cors() and is approved for any origin (the bug)', async () => {
178
+ const base = await startAppWithGraphQLMount(false);
179
+ const res = await preflight(`${base}/webhook/events`, OTHER_ORIGIN);
180
+ expect(res.status).toBe(204);
181
+ expect(res.headers.get('access-control-allow-origin')).toBe('*');
182
+ });
183
+
184
+ it('refuses an unanswered preflight under an owned root with a bare 404', async () => {
185
+ const base = await startAppWithGraphQLMount(true);
186
+ for (const path of ['/webhook/events', '/WEBHOOK/Events', '/webhook/', '/webhook/no-such-route']) {
187
+ const res = await preflight(`${base}${path}`, OTHER_ORIGIN);
188
+ expect(res.status, path).toBe(404);
189
+ expect(res.headers.get('access-control-allow-origin'), path).toBeNull();
190
+ }
191
+ });
192
+
193
+ it('leaves an extension that answers its own preflight in charge', async () => {
194
+ const base = await startAppWithGraphQLMount(true);
195
+ const res = await preflight(`${base}/ballot/cast`, ALLOWED_BALLOT_ORIGIN);
196
+ expect(res.status).toBe(200);
197
+ expect(res.headers.get('x-ballot-preflight')).toBe('extension');
198
+ });
199
+
200
+ it('touches neither non-preflight requests under an owned root nor preflights elsewhere', async () => {
201
+ const base = await startAppWithGraphQLMount(true);
202
+ const post = await fetch(`${base}/webhook/events`, { method: 'POST', headers: { Origin: OTHER_ORIGIN } });
203
+ expect(post.status).toBe(200);
204
+ const sibling = await preflight(`${base}/webhookx/events`, OTHER_ORIGIN);
205
+ expect(sibling.status).toBe(204);
206
+ expect(sibling.headers.get('access-control-allow-origin')).toBe(OTHER_ORIGIN);
207
+ });
208
+ });
209
+
210
+ describe('X-Powered-By', () => {
211
+ it('CreateApp and Serve() both disable X-Powered-By', () => {
212
+ // Read as text so this test never loads config.ts / the full server graph.
213
+ const src = readFileSync(new URL('../index.ts', import.meta.url), 'utf8');
214
+ // Both the default app factory and Serve() (for host-supplied apps) disable the header.
215
+ expect(src.match(/app\.disable\('x-powered-by'\)/g)?.length ?? 0).toBeGreaterThanOrEqual(2);
216
+ });
217
+ });
@@ -0,0 +1,169 @@
1
+ /**
2
+ * Transactions opened outside a GraphQL request run on their OWN provider, never the shared
3
+ * `Metadata.Provider`.
4
+ *
5
+ * A provider's ambient transaction is per INSTANCE, and the global provider is shared by every
6
+ * request, scheduled job and background service. A transaction opened there nests into any other
7
+ * caller's, and while it is open every global-provider write from anywhere lands inside it. One left
8
+ * open swallows them all until the process restarts (MJ#2140). These tests pin the two call sites
9
+ * that used to open their transaction on the global provider: magic-link provisioning and
10
+ * SyncData's delete-with-filter. New-user creation is pinned in newUsers.test.ts.
11
+ */
12
+ import 'reflect-metadata';
13
+ import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
14
+
15
+ vi.mock('@memberjunction/generic-database-provider', () => ({
16
+ UserCache: { Instance: { Users: [] } },
17
+ }));
18
+ vi.mock('@memberjunction/communication-engine', () => ({
19
+ CommunicationEngine: { Instance: {} },
20
+ }));
21
+ vi.mock('@memberjunction/communication-types', () => ({
22
+ Message: class {},
23
+ }));
24
+ vi.mock('../config.js', () => ({
25
+ configInfo: {},
26
+ }));
27
+
28
+ const factory = vi.hoisted(() => ({ create: vi.fn() }));
29
+ vi.mock('../isolatedProvider.js', () => ({ CreateIsolatedProvider: factory.create }));
30
+
31
+ import { Metadata, RunView, type IMetadataProvider, type RoleInfo, type UserInfo } from '@memberjunction/core';
32
+ import type { MJMagicLinkInviteEntity } from '@memberjunction/core-entities';
33
+ import type { MagicLinkConfig } from '../config.js';
34
+ import { MagicLinkService } from '../auth/magicLink/MagicLinkService.js';
35
+ import { SyncDataResolver, type ActionItemOutputType } from '../resolvers/SyncDataResolver.js';
36
+
37
+ /** A provider that records every call made on it. */
38
+ function recordingProvider(calls: string[], saveSucceeds = true) {
39
+ return {
40
+ BeginTransaction: vi.fn(async () => { calls.push('BeginTransaction'); }),
41
+ CommitTransaction: vi.fn(async () => { calls.push('CommitTransaction'); }),
42
+ RollbackTransaction: vi.fn(async () => { calls.push('RollbackTransaction'); }),
43
+ GetEntityObject: vi.fn(async (entityName: string) => {
44
+ calls.push(`GetEntityObject:${entityName}`);
45
+ const entity: Record<string, unknown> = {
46
+ NewRecord: () => { entity.ID = `id-${entityName}`; },
47
+ Save: async () => saveSucceeds,
48
+ GetAll: () => ({ ID: entity.ID }),
49
+ LatestResult: { CompleteMessage: 'save failed' },
50
+ };
51
+ return entity;
52
+ }),
53
+ };
54
+ }
55
+
56
+ let globalCalls: string[];
57
+ let isolatedCalls: string[];
58
+
59
+ beforeEach(() => {
60
+ vi.spyOn(console, 'log').mockImplementation(() => undefined);
61
+ vi.spyOn(console, 'error').mockImplementation(() => undefined);
62
+ globalCalls = [];
63
+ isolatedCalls = [];
64
+ Metadata.Provider = recordingProvider(globalCalls) as unknown as IMetadataProvider;
65
+ });
66
+
67
+ afterEach(() => {
68
+ Metadata.Provider = null as unknown as IMetadataProvider;
69
+ vi.restoreAllMocks();
70
+ factory.create.mockReset();
71
+ });
72
+
73
+ describe('MagicLinkService.createScopedUser', () => {
74
+ const invite = { Email: 'guest@example.com', RoleID: 'role-1', ApplicationID: 'app-1' } as unknown as MJMagicLinkInviteEntity;
75
+ const role = { ID: 'role-1', Name: 'External Guest' } as RoleInfo;
76
+ const contextUser = { ID: 'ctx-user' } as UserInfo;
77
+ const provision = () =>
78
+ new MagicLinkService('https://mj.example.com', {} as MagicLinkConfig)['createScopedUser'](invite, role, contextUser);
79
+
80
+ it('provisions inside a transaction on its own provider', async () => {
81
+ factory.create.mockResolvedValue(recordingProvider(isolatedCalls));
82
+
83
+ expect((await provision()).success).toBe(true);
84
+ expect(isolatedCalls).toEqual([
85
+ 'BeginTransaction',
86
+ 'GetEntityObject:MJ: Users',
87
+ 'GetEntityObject:MJ: User Roles',
88
+ 'GetEntityObject:MJ: User Applications',
89
+ 'CommitTransaction',
90
+ ]);
91
+ expect(globalCalls).toEqual([]);
92
+ });
93
+
94
+ it('rolls back on its own provider when a save fails', async () => {
95
+ factory.create.mockResolvedValue(recordingProvider(isolatedCalls, false));
96
+
97
+ expect((await provision()).success).toBe(false);
98
+ expect(isolatedCalls).toEqual(['BeginTransaction', 'GetEntityObject:MJ: Users', 'RollbackTransaction']);
99
+ expect(globalCalls).toEqual([]);
100
+ });
101
+ });
102
+
103
+ describe('SyncDataResolver.SyncSingleItemDeleteWithFilter', () => {
104
+ const user = { ID: 'system' } as UserInfo;
105
+
106
+ /** Rows the filtered view returns; each records its Delete on the shared call log. */
107
+ function stubView(rows: number, deleteSucceeds = true) {
108
+ const fromProvider = vi.spyOn(RunView, 'FromMetadataProvider').mockImplementation(
109
+ () =>
110
+ ({
111
+ RunView: async () => ({
112
+ Success: true,
113
+ Results: Array.from({ length: rows }, (_, i) => ({
114
+ Delete: async () => { isolatedCalls.push(`Delete:${i}`); return deleteSucceeds; },
115
+ LatestResult: { CompleteMessage: 'delete failed' },
116
+ })),
117
+ }),
118
+ }) as unknown as RunView,
119
+ );
120
+ return fromProvider;
121
+ }
122
+
123
+ const deleteWithFilter = (providerOverride?: IMetadataProvider) => {
124
+ const result = { Success: false } as ActionItemOutputType;
125
+ const resolver = new SyncDataResolver() as unknown as {
126
+ SyncSingleItemDeleteWithFilter: (...args: unknown[]) => Promise<void>;
127
+ };
128
+ return resolver
129
+ .SyncSingleItemDeleteWithFilter('MJ: Things', "Name = 'x'", result, user, {}, providerOverride)
130
+ .then(() => result);
131
+ };
132
+
133
+ it('with no request provider, loads AND deletes on its own provider inside one transaction', async () => {
134
+ const isolated = recordingProvider(isolatedCalls);
135
+ factory.create.mockResolvedValue(isolated);
136
+ const fromProvider = stubView(2);
137
+
138
+ const result = await deleteWithFilter();
139
+
140
+ expect(result.Success).toBe(true);
141
+ expect(fromProvider).toHaveBeenCalledWith(isolated);
142
+ expect(isolatedCalls).toEqual(['BeginTransaction', 'Delete:0', 'Delete:1', 'CommitTransaction']);
143
+ expect(globalCalls).toEqual([]);
144
+ });
145
+
146
+ it('with a request provider, uses it for both the read and the transaction', async () => {
147
+ const requestProvider = recordingProvider(isolatedCalls);
148
+ const fromProvider = stubView(1);
149
+
150
+ const result = await deleteWithFilter(requestProvider as unknown as IMetadataProvider);
151
+
152
+ expect(result.Success).toBe(true);
153
+ expect(factory.create).not.toHaveBeenCalled();
154
+ expect(fromProvider).toHaveBeenCalledWith(requestProvider);
155
+ expect(isolatedCalls).toEqual(['BeginTransaction', 'Delete:0', 'CommitTransaction']);
156
+ expect(globalCalls).toEqual([]);
157
+ });
158
+
159
+ it('rolls back on the same provider when a delete fails', async () => {
160
+ factory.create.mockResolvedValue(recordingProvider(isolatedCalls));
161
+ stubView(2, false);
162
+
163
+ const result = await deleteWithFilter();
164
+
165
+ expect(result.Success).toBe(false);
166
+ expect(isolatedCalls).toEqual(['BeginTransaction', 'Delete:0', 'RollbackTransaction']);
167
+ expect(globalCalls).toEqual([]);
168
+ });
169
+ });
@@ -15,6 +15,12 @@ vi.mock('@memberjunction/communication-types', () => ({
15
15
  vi.mock('../config.js', () => ({
16
16
  configInfo: {},
17
17
  }));
18
+ // Provisioning is stubbed in this suite; keep the real provider stack out of the module graph.
19
+ vi.mock('../isolatedProvider.js', () => ({
20
+ CreateIsolatedProvider: async () => {
21
+ throw new Error('provisioning is stubbed in this suite');
22
+ },
23
+ }));
18
24
 
19
25
  import { Metadata, RunView, type DatabaseProviderBase, type IMetadataProvider, type UserInfo } from '@memberjunction/core';
20
26
  import type { MJMagicLinkInviteEntity } from '@memberjunction/core-entities';
@@ -16,6 +16,10 @@ vi.mock('@memberjunction/communication-types', () => ({
16
16
  vi.mock('../config.js', () => ({
17
17
  configInfo: {},
18
18
  }));
19
+ const isolated = vi.hoisted(() => ({ provider: null as unknown }));
20
+ vi.mock('../isolatedProvider.js', () => ({
21
+ CreateIsolatedProvider: async () => isolated.provider,
22
+ }));
19
23
 
20
24
  import { Metadata, type IMetadataProvider, type RoleInfo, type UserInfo } from '@memberjunction/core';
21
25
  import type { MJMagicLinkInviteEntity } from '@memberjunction/core-entities';
@@ -62,6 +66,7 @@ describe('MagicLinkService provisioning log policy', () => {
62
66
  RollbackTransaction: async () => undefined,
63
67
  GetEntityObject: async (entityName: string) => makeMockEntity(`mock-${entityName}`),
64
68
  };
69
+ isolated.provider = provider;
65
70
  Metadata.Provider = provider as unknown as IMetadataProvider;
66
71
 
67
72
  service = new MagicLinkService('https://mj.example.com', {} as MagicLinkConfig);