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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (463) hide show
  1. package/README.md +69 -0
  2. package/dist/.tsbuildinfo +1 -1
  3. package/dist/agentSessions/BridgeHostInstance.d.ts +8 -3
  4. package/dist/agentSessions/BridgeHostInstance.d.ts.map +1 -1
  5. package/dist/agentSessions/BridgeHostInstance.js +14 -4
  6. package/dist/agentSessions/BridgeHostInstance.js.map +1 -1
  7. package/dist/agentSessions/HandoffOfferExpiry.d.ts +10 -0
  8. package/dist/agentSessions/HandoffOfferExpiry.d.ts.map +1 -0
  9. package/dist/agentSessions/HandoffOfferExpiry.js +18 -0
  10. package/dist/agentSessions/HandoffOfferExpiry.js.map +1 -0
  11. package/dist/agentSessions/ReturningVisitorRecap.js +1 -0
  12. package/dist/agentSessions/ReturningVisitorRecap.js.map +1 -1
  13. package/dist/agentSessions/SessionJanitor.d.ts +31 -8
  14. package/dist/agentSessions/SessionJanitor.d.ts.map +1 -1
  15. package/dist/agentSessions/SessionJanitor.js +85 -41
  16. package/dist/agentSessions/SessionJanitor.js.map +1 -1
  17. package/dist/agentSessions/SessionManager.d.ts +14 -4
  18. package/dist/agentSessions/SessionManager.d.ts.map +1 -1
  19. package/dist/agentSessions/SessionManager.js +28 -4
  20. package/dist/agentSessions/SessionManager.js.map +1 -1
  21. package/dist/agentSessions/conversationBrowsers.d.ts +61 -0
  22. package/dist/agentSessions/conversationBrowsers.d.ts.map +1 -0
  23. package/dist/agentSessions/conversationBrowsers.js +118 -0
  24. package/dist/agentSessions/conversationBrowsers.js.map +1 -0
  25. package/dist/agentSessions/sessionDeadline.d.ts +43 -0
  26. package/dist/agentSessions/sessionDeadline.d.ts.map +1 -0
  27. package/dist/agentSessions/sessionDeadline.js +68 -0
  28. package/dist/agentSessions/sessionDeadline.js.map +1 -0
  29. package/dist/auth/APIKeyScopeAuth.d.ts.map +1 -1
  30. package/dist/auth/APIKeyScopeAuth.js +5 -4
  31. package/dist/auth/APIKeyScopeAuth.js.map +1 -1
  32. package/dist/auth/loginEmailResolution.d.ts +41 -0
  33. package/dist/auth/loginEmailResolution.d.ts.map +1 -0
  34. package/dist/auth/loginEmailResolution.js +47 -0
  35. package/dist/auth/loginEmailResolution.js.map +1 -0
  36. package/dist/auth/magicLink/MagicLinkKeys.d.ts +50 -2
  37. package/dist/auth/magicLink/MagicLinkKeys.d.ts.map +1 -1
  38. package/dist/auth/magicLink/MagicLinkKeys.js +129 -12
  39. package/dist/auth/magicLink/MagicLinkKeys.js.map +1 -1
  40. package/dist/auth/magicLink/MagicLinkRouter.d.ts +3 -0
  41. package/dist/auth/magicLink/MagicLinkRouter.d.ts.map +1 -1
  42. package/dist/auth/magicLink/MagicLinkRouter.js +23 -5
  43. package/dist/auth/magicLink/MagicLinkRouter.js.map +1 -1
  44. package/dist/auth/magicLink/MagicLinkService.d.ts +39 -5
  45. package/dist/auth/magicLink/MagicLinkService.d.ts.map +1 -1
  46. package/dist/auth/magicLink/MagicLinkService.js +221 -9
  47. package/dist/auth/magicLink/MagicLinkService.js.map +1 -1
  48. package/dist/auth/magicLink/index.d.ts +1 -1
  49. package/dist/auth/magicLink/index.d.ts.map +1 -1
  50. package/dist/auth/magicLink/index.js +1 -1
  51. package/dist/auth/magicLink/index.js.map +1 -1
  52. package/dist/auth/magicLink/redeemLanding.d.ts +2 -2
  53. package/dist/auth/magicLink/redeemLanding.d.ts.map +1 -1
  54. package/dist/auth/magicLink/redeemLanding.js +7 -4
  55. package/dist/auth/magicLink/redeemLanding.js.map +1 -1
  56. package/dist/auth/magicLink/types.d.ts +27 -3
  57. package/dist/auth/magicLink/types.d.ts.map +1 -1
  58. package/dist/auth/magicLink/types.js +0 -4
  59. package/dist/auth/magicLink/types.js.map +1 -1
  60. package/dist/auth/newUsers.d.ts.map +1 -1
  61. package/dist/auth/newUsers.js +1 -0
  62. package/dist/auth/newUsers.js.map +1 -1
  63. package/dist/backgroundSweepConfigUnits.d.ts +231 -0
  64. package/dist/backgroundSweepConfigUnits.d.ts.map +1 -0
  65. package/dist/backgroundSweepConfigUnits.js +180 -0
  66. package/dist/backgroundSweepConfigUnits.js.map +1 -0
  67. package/dist/broadcast/BaseCacheInvalidationBroadcastProvider.d.ts.map +1 -1
  68. package/dist/broadcast/BaseCacheInvalidationBroadcastProvider.js +2 -1
  69. package/dist/broadcast/BaseCacheInvalidationBroadcastProvider.js.map +1 -1
  70. package/dist/config.d.ts +906 -8
  71. package/dist/config.d.ts.map +1 -1
  72. package/dist/config.js +59 -0
  73. package/dist/config.js.map +1 -1
  74. package/dist/context.d.ts +2 -0
  75. package/dist/context.d.ts.map +1 -1
  76. package/dist/context.js +41 -12
  77. package/dist/context.js.map +1 -1
  78. package/dist/coreRoutes.d.ts +3 -1
  79. package/dist/coreRoutes.d.ts.map +1 -1
  80. package/dist/coreRoutes.js +6 -4
  81. package/dist/coreRoutes.js.map +1 -1
  82. package/dist/generated/graphql-schemas/__mj.d.ts +1072 -5
  83. package/dist/generated/graphql-schemas/__mj.d.ts.map +1 -1
  84. package/dist/generated/graphql-schemas/__mj.js +10174 -4618
  85. package/dist/generated/graphql-schemas/__mj.js.map +1 -1
  86. package/dist/generic/OrphanedConversationDetailReconciler.d.ts +11 -0
  87. package/dist/generic/OrphanedConversationDetailReconciler.d.ts.map +1 -1
  88. package/dist/generic/OrphanedConversationDetailReconciler.js +27 -1
  89. package/dist/generic/OrphanedConversationDetailReconciler.js.map +1 -1
  90. package/dist/generic/ResolverBase.d.ts +50 -5
  91. package/dist/generic/ResolverBase.d.ts.map +1 -1
  92. package/dist/generic/ResolverBase.js +329 -132
  93. package/dist/generic/ResolverBase.js.map +1 -1
  94. package/dist/generic/RunViewResolver.d.ts +4 -0
  95. package/dist/generic/RunViewResolver.d.ts.map +1 -1
  96. package/dist/generic/RunViewResolver.js +47 -7
  97. package/dist/generic/RunViewResolver.js.map +1 -1
  98. package/dist/globalCors.d.ts.map +1 -1
  99. package/dist/globalCors.js +1 -0
  100. package/dist/globalCors.js.map +1 -1
  101. package/dist/index.d.ts +3 -0
  102. package/dist/index.d.ts.map +1 -1
  103. package/dist/index.js +201 -43
  104. package/dist/index.js.map +1 -1
  105. package/dist/integration/ConnectionPause.d.ts +121 -0
  106. package/dist/integration/ConnectionPause.d.ts.map +1 -0
  107. package/dist/integration/ConnectionPause.js +183 -0
  108. package/dist/integration/ConnectionPause.js.map +1 -0
  109. package/dist/integration/CustomColumnPromoter.d.ts +72 -6
  110. package/dist/integration/CustomColumnPromoter.d.ts.map +1 -1
  111. package/dist/integration/CustomColumnPromoter.js +87 -46
  112. package/dist/integration/CustomColumnPromoter.js.map +1 -1
  113. package/dist/integration/EntityMapLifecycle.d.ts +78 -4
  114. package/dist/integration/EntityMapLifecycle.d.ts.map +1 -1
  115. package/dist/integration/EntityMapLifecycle.js +199 -5
  116. package/dist/integration/EntityMapLifecycle.js.map +1 -1
  117. package/dist/integration/InactiveRowWarnings.d.ts +31 -0
  118. package/dist/integration/InactiveRowWarnings.d.ts.map +1 -1
  119. package/dist/integration/InactiveRowWarnings.js +29 -0
  120. package/dist/integration/InactiveRowWarnings.js.map +1 -1
  121. package/dist/integration/ObjectNamesByID.d.ts +28 -0
  122. package/dist/integration/ObjectNamesByID.d.ts.map +1 -0
  123. package/dist/integration/ObjectNamesByID.js +87 -0
  124. package/dist/integration/ObjectNamesByID.js.map +1 -0
  125. package/dist/integration/PostRestartIntrospect.d.ts +26 -0
  126. package/dist/integration/PostRestartIntrospect.d.ts.map +1 -0
  127. package/dist/integration/PostRestartIntrospect.js +27 -0
  128. package/dist/integration/PostRestartIntrospect.js.map +1 -0
  129. package/dist/integration/RSUPostRestartProgress.d.ts +176 -0
  130. package/dist/integration/RSUPostRestartProgress.d.ts.map +1 -0
  131. package/dist/integration/RSUPostRestartProgress.js +323 -0
  132. package/dist/integration/RSUPostRestartProgress.js.map +1 -0
  133. package/dist/integration/RSUProgressBridge.d.ts +41 -2
  134. package/dist/integration/RSUProgressBridge.d.ts.map +1 -1
  135. package/dist/integration/RSUProgressBridge.js +84 -1
  136. package/dist/integration/RSUProgressBridge.js.map +1 -1
  137. package/dist/integration/RunArtifactAuthorization.d.ts +39 -0
  138. package/dist/integration/RunArtifactAuthorization.d.ts.map +1 -0
  139. package/dist/integration/RunArtifactAuthorization.js +45 -0
  140. package/dist/integration/RunArtifactAuthorization.js.map +1 -0
  141. package/dist/integration/SchemaRefreshLaunch.d.ts +9 -0
  142. package/dist/integration/SchemaRefreshLaunch.d.ts.map +1 -1
  143. package/dist/integration/SchemaRefreshLaunch.js +3 -2
  144. package/dist/integration/SchemaRefreshLaunch.js.map +1 -1
  145. package/dist/integration/ScopedFieldsForBuild.d.ts +46 -0
  146. package/dist/integration/ScopedFieldsForBuild.d.ts.map +1 -0
  147. package/dist/integration/ScopedFieldsForBuild.js +91 -0
  148. package/dist/integration/ScopedFieldsForBuild.js.map +1 -0
  149. package/dist/integration/TargetConfigGuard.d.ts +40 -0
  150. package/dist/integration/TargetConfigGuard.d.ts.map +1 -0
  151. package/dist/integration/TargetConfigGuard.js +40 -0
  152. package/dist/integration/TargetConfigGuard.js.map +1 -0
  153. package/dist/logging/StartupLogger.d.ts +50 -0
  154. package/dist/logging/StartupLogger.d.ts.map +1 -1
  155. package/dist/logging/StartupLogger.js +15 -22
  156. package/dist/logging/StartupLogger.js.map +1 -1
  157. package/dist/middleware/BaseServerMiddleware.d.ts +21 -3
  158. package/dist/middleware/BaseServerMiddleware.d.ts.map +1 -1
  159. package/dist/middleware/BaseServerMiddleware.js +4 -2
  160. package/dist/middleware/BaseServerMiddleware.js.map +1 -1
  161. package/dist/realtimeSessions/ownedRealtimeSession.d.ts.map +1 -1
  162. package/dist/realtimeSessions/ownedRealtimeSession.js +2 -3
  163. package/dist/realtimeSessions/ownedRealtimeSession.js.map +1 -1
  164. package/dist/realtimeSessions/sessionAccess.d.ts +15 -0
  165. package/dist/realtimeSessions/sessionAccess.d.ts.map +1 -1
  166. package/dist/realtimeSessions/sessionAccess.js +22 -0
  167. package/dist/realtimeSessions/sessionAccess.js.map +1 -1
  168. package/dist/realtimeWidget/WidgetSessionService.d.ts.map +1 -1
  169. package/dist/realtimeWidget/WidgetSessionService.js +2 -0
  170. package/dist/realtimeWidget/WidgetSessionService.js.map +1 -1
  171. package/dist/realtimeWidget/visitorIdentity.d.ts.map +1 -1
  172. package/dist/realtimeWidget/visitorIdentity.js +1 -0
  173. package/dist/realtimeWidget/visitorIdentity.js.map +1 -1
  174. package/dist/realtimeWidget/widgetCore.d.ts.map +1 -1
  175. package/dist/realtimeWidget/widgetCore.js +4 -7
  176. package/dist/realtimeWidget/widgetCore.js.map +1 -1
  177. package/dist/realtimeWidget/widgetGuestElevation.js +1 -0
  178. package/dist/realtimeWidget/widgetGuestElevation.js.map +1 -1
  179. package/dist/requestOrigin.d.ts +22 -0
  180. package/dist/requestOrigin.d.ts.map +1 -0
  181. package/dist/requestOrigin.js +60 -0
  182. package/dist/requestOrigin.js.map +1 -0
  183. package/dist/resolvers/AgentRunStatusPublisher.d.ts +10 -0
  184. package/dist/resolvers/AgentRunStatusPublisher.d.ts.map +1 -1
  185. package/dist/resolvers/AgentRunStatusPublisher.js +18 -2
  186. package/dist/resolvers/AgentRunStatusPublisher.js.map +1 -1
  187. package/dist/resolvers/AgentSessionResolver.d.ts.map +1 -1
  188. package/dist/resolvers/AgentSessionResolver.js +6 -2
  189. package/dist/resolvers/AgentSessionResolver.js.map +1 -1
  190. package/dist/resolvers/AutotagPipelineResolver.d.ts +1 -1
  191. package/dist/resolvers/AutotagPipelineResolver.d.ts.map +1 -1
  192. package/dist/resolvers/AutotagPipelineResolver.js +61 -16
  193. package/dist/resolvers/AutotagPipelineResolver.js.map +1 -1
  194. package/dist/resolvers/ConversationRunEventsResolver.d.ts.map +1 -1
  195. package/dist/resolvers/ConversationRunEventsResolver.js +2 -0
  196. package/dist/resolvers/ConversationRunEventsResolver.js.map +1 -1
  197. package/dist/resolvers/ExecuteRemoteOperationResolver.d.ts +6 -0
  198. package/dist/resolvers/ExecuteRemoteOperationResolver.d.ts.map +1 -1
  199. package/dist/resolvers/ExecuteRemoteOperationResolver.js +30 -7
  200. package/dist/resolvers/ExecuteRemoteOperationResolver.js.map +1 -1
  201. package/dist/resolvers/GetDataResolver.d.ts.map +1 -1
  202. package/dist/resolvers/GetDataResolver.js +34 -13
  203. package/dist/resolvers/GetDataResolver.js.map +1 -1
  204. package/dist/resolvers/InfoResolver.js +1 -1
  205. package/dist/resolvers/InfoResolver.js.map +1 -1
  206. package/dist/resolvers/IntegrationDiscoveryResolver.d.ts +401 -11
  207. package/dist/resolvers/IntegrationDiscoveryResolver.d.ts.map +1 -1
  208. package/dist/resolvers/IntegrationDiscoveryResolver.js +2690 -1305
  209. package/dist/resolvers/IntegrationDiscoveryResolver.js.map +1 -1
  210. package/dist/resolvers/MCPResolver.d.ts.map +1 -1
  211. package/dist/resolvers/MCPResolver.js +13 -2
  212. package/dist/resolvers/MCPResolver.js.map +1 -1
  213. package/dist/resolvers/QuerySystemUserResolver.d.ts +38 -1
  214. package/dist/resolvers/QuerySystemUserResolver.d.ts.map +1 -1
  215. package/dist/resolvers/QuerySystemUserResolver.js +181 -88
  216. package/dist/resolvers/QuerySystemUserResolver.js.map +1 -1
  217. package/dist/resolvers/RealtimeBridgeResolver.d.ts.map +1 -1
  218. package/dist/resolvers/RealtimeBridgeResolver.js +1 -0
  219. package/dist/resolvers/RealtimeBridgeResolver.js.map +1 -1
  220. package/dist/resolvers/RealtimeClientSessionResolver.d.ts +6 -0
  221. package/dist/resolvers/RealtimeClientSessionResolver.d.ts.map +1 -1
  222. package/dist/resolvers/RealtimeClientSessionResolver.js +36 -5
  223. package/dist/resolvers/RealtimeClientSessionResolver.js.map +1 -1
  224. package/dist/resolvers/RemoteBrowserActionResolver.d.ts +62 -10
  225. package/dist/resolvers/RemoteBrowserActionResolver.d.ts.map +1 -1
  226. package/dist/resolvers/RemoteBrowserActionResolver.js +217 -34
  227. package/dist/resolvers/RemoteBrowserActionResolver.js.map +1 -1
  228. package/dist/resolvers/RunAIAgentResolver.d.ts +24 -8
  229. package/dist/resolvers/RunAIAgentResolver.d.ts.map +1 -1
  230. package/dist/resolvers/RunAIAgentResolver.js +185 -67
  231. package/dist/resolvers/RunAIAgentResolver.js.map +1 -1
  232. package/dist/resolvers/RunAIPromptResolver.d.ts +5 -3
  233. package/dist/resolvers/RunAIPromptResolver.d.ts.map +1 -1
  234. package/dist/resolvers/RunAIPromptResolver.js +19 -18
  235. package/dist/resolvers/RunAIPromptResolver.js.map +1 -1
  236. package/dist/resolvers/RunTemplateResolver.d.ts.map +1 -1
  237. package/dist/resolvers/RunTemplateResolver.js +3 -1
  238. package/dist/resolvers/RunTemplateResolver.js.map +1 -1
  239. package/dist/resolvers/SqlLoggingConfigResolver.d.ts.map +1 -1
  240. package/dist/resolvers/SqlLoggingConfigResolver.js +7 -8
  241. package/dist/resolvers/SqlLoggingConfigResolver.js.map +1 -1
  242. package/dist/resolvers/TagGovernanceResolver.d.ts +12 -0
  243. package/dist/resolvers/TagGovernanceResolver.d.ts.map +1 -1
  244. package/dist/resolvers/TagGovernanceResolver.js +84 -0
  245. package/dist/resolvers/TagGovernanceResolver.js.map +1 -1
  246. package/dist/resolvers/TelemetryResolver.d.ts +13 -8
  247. package/dist/resolvers/TelemetryResolver.d.ts.map +1 -1
  248. package/dist/resolvers/TelemetryResolver.js +40 -9
  249. package/dist/resolvers/TelemetryResolver.js.map +1 -1
  250. package/dist/resolvers/meetingRecordingRegistration.d.ts.map +1 -1
  251. package/dist/resolvers/meetingRecordingRegistration.js +2 -0
  252. package/dist/resolvers/meetingRecordingRegistration.js.map +1 -1
  253. package/dist/resolvers/meetings/meetingMutations.d.ts.map +1 -1
  254. package/dist/resolvers/meetings/meetingMutations.js +34 -4
  255. package/dist/resolvers/meetings/meetingMutations.js.map +1 -1
  256. package/dist/rest/MediaAccessKeys.d.ts.map +1 -1
  257. package/dist/rest/MediaAccessKeys.js +6 -2
  258. package/dist/rest/MediaAccessKeys.js.map +1 -1
  259. package/dist/rest/OAuthCallbackHandler.d.ts.map +1 -1
  260. package/dist/rest/OAuthCallbackHandler.js +11 -5
  261. package/dist/rest/OAuthCallbackHandler.js.map +1 -1
  262. package/dist/rest/RealtimeRecordingHandler.d.ts +3 -3
  263. package/dist/rest/RealtimeRecordingHandler.d.ts.map +1 -1
  264. package/dist/rest/RealtimeRecordingHandler.js +2 -4
  265. package/dist/rest/RealtimeRecordingHandler.js.map +1 -1
  266. package/dist/rest/ViewOperationsHandler.d.ts.map +1 -1
  267. package/dist/rest/ViewOperationsHandler.js +2 -1
  268. package/dist/rest/ViewOperationsHandler.js.map +1 -1
  269. package/dist/rest/setupRESTEndpoints.d.ts +3 -1
  270. package/dist/rest/setupRESTEndpoints.d.ts.map +1 -1
  271. package/dist/rest/setupRESTEndpoints.js +11 -2
  272. package/dist/rest/setupRESTEndpoints.js.map +1 -1
  273. package/dist/services/DurableEntityActionTaskSubmitter.d.ts.map +1 -1
  274. package/dist/services/DurableEntityActionTaskSubmitter.js +6 -1
  275. package/dist/services/DurableEntityActionTaskSubmitter.js.map +1 -1
  276. package/dist/services/TaskGraphActionRunner.d.ts.map +1 -1
  277. package/dist/services/TaskGraphActionRunner.js +2 -26
  278. package/dist/services/TaskGraphActionRunner.js.map +1 -1
  279. package/dist/services/TaskGraphAgentRunner.d.ts +6 -0
  280. package/dist/services/TaskGraphAgentRunner.d.ts.map +1 -1
  281. package/dist/services/TaskGraphAgentRunner.js +38 -0
  282. package/dist/services/TaskGraphAgentRunner.js.map +1 -1
  283. package/dist/services/TaskGraphContinuationDeliverer.d.ts.map +1 -1
  284. package/dist/services/TaskGraphContinuationDeliverer.js +1 -0
  285. package/dist/services/TaskGraphContinuationDeliverer.js.map +1 -1
  286. package/dist/services/WorkQueueHostService.d.ts +2 -1
  287. package/dist/services/WorkQueueHostService.d.ts.map +1 -1
  288. package/dist/services/WorkQueueHostService.js +19 -4
  289. package/dist/services/WorkQueueHostService.js.map +1 -1
  290. package/dist/services/workQueueConfig.d.ts +2 -2
  291. package/dist/services/workQueueConfig.js +5 -5
  292. package/dist/services/workQueueConfig.js.map +1 -1
  293. package/dist/sharedCache.js +1 -1
  294. package/dist/sharedCache.js.map +1 -1
  295. package/dist/types.d.ts +17 -0
  296. package/dist/types.d.ts.map +1 -1
  297. package/dist/types.js.map +1 -1
  298. package/dist/util.d.ts.map +1 -1
  299. package/dist/util.js +55 -2
  300. package/dist/util.js.map +1 -1
  301. package/package.json +107 -100
  302. package/src/__tests__/APIKeyScopeAuth.test.ts +4 -8
  303. package/src/__tests__/ActiveOperations.test.ts +68 -0
  304. package/src/__tests__/AgentSessionResolver.ownership.test.ts +13 -0
  305. package/src/__tests__/AssertTargetConfigs.test.ts +75 -0
  306. package/src/__tests__/BatchApplyParity.test.ts +109 -0
  307. package/src/__tests__/BridgeHostInstance.test.ts +42 -0
  308. package/src/__tests__/CollectInactiveRowWarnings.test.ts +115 -0
  309. package/src/__tests__/ConnectionPause.test.ts +192 -0
  310. package/src/__tests__/ConnectionPauseResolver.test.ts +166 -0
  311. package/src/__tests__/ConnectionPauseSave.test.ts +143 -0
  312. package/src/__tests__/CustomColumnPromoterCompletion.test.ts +142 -0
  313. package/src/__tests__/DeleteConnectionRemovesPerConnectionCatalog.test.ts +51 -0
  314. package/src/__tests__/DetachedDiscoveryStart.test.ts +65 -0
  315. package/src/__tests__/DurableEntityActionTaskSubmitter.test.ts +73 -0
  316. package/src/__tests__/EntityMapLifecycle.rekey.test.ts +252 -0
  317. package/src/__tests__/ExecuteRemoteOperationResolver.test.ts +213 -0
  318. package/src/__tests__/HandoffOfferExpiry.test.ts +40 -0
  319. package/src/__tests__/IntegrationRunArtifactAuthorization.test.ts +126 -0
  320. package/src/__tests__/MagicLinkKeys.test.ts +102 -0
  321. package/src/__tests__/MediaAccessKeys.test.ts +57 -10
  322. package/src/__tests__/NewArrivalsDefaultDisabled.test.ts +50 -0
  323. package/src/__tests__/ObjectNamesByID.test.ts +176 -0
  324. package/src/__tests__/PostRestartMapsInCatalogScope.test.ts +146 -0
  325. package/src/__tests__/QuerySystemUserResolver.childRows.test.ts +80 -0
  326. package/src/__tests__/QuerySystemUserResolver.collision.test.ts +200 -0
  327. package/src/__tests__/QuerySystemUserResolver.permissions.test.ts +132 -0
  328. package/src/__tests__/RSUPostRestartProgress.test.ts +343 -0
  329. package/src/__tests__/RSUProgressBridge.test.ts +188 -5
  330. package/src/__tests__/RSUStartupSweepWiring.test.ts +30 -0
  331. package/src/__tests__/ReactivateRefreshMessage.test.ts +10 -3
  332. package/src/__tests__/RealtimeClientSessionResolver.test.ts +22 -0
  333. package/src/__tests__/{RefreshAdoptsNewColumns.test.ts → RefreshReportsNewColumns.test.ts} +25 -16
  334. package/src/__tests__/RemoteBrowserActionRecovery.test.ts +287 -0
  335. package/src/__tests__/RemoteBrowserActionResolver.goalRunLink.test.ts +3 -0
  336. package/src/__tests__/RemoteBrowserActionResolver.ownership.test.ts +6 -4
  337. package/src/__tests__/RemoteBrowserAudioStream.test.ts +3 -0
  338. package/src/__tests__/RemoteBrowserSnapshot.test.ts +3 -0
  339. package/src/__tests__/ResolverBase.clientOldValues.test.ts +327 -26
  340. package/src/__tests__/ResolverBase.diagnosticSenderRegistrations.test.ts +26 -0
  341. package/src/__tests__/ResolverBase.encryptedApiZeroReencrypt.test.ts +307 -0
  342. package/src/__tests__/ResolverBase.entityMessageRelay.test.ts +53 -2
  343. package/src/__tests__/ResolverBase.includeBinaryFields.test.ts +60 -0
  344. package/src/__tests__/RunAIAgentResolver.branch.test.ts +4 -0
  345. package/src/__tests__/RunAIAgentResolver.consultPredecessor.test.ts +289 -0
  346. package/src/__tests__/RunAIAgentResolver.handoff.test.ts +649 -0
  347. package/src/__tests__/RunAIAgentResolver.historyFrom.test.ts +2 -0
  348. package/src/__tests__/RunAIAgentResolver.runDataGuard.test.ts +2 -0
  349. package/src/__tests__/RunAIAgentResolver.stoppedPredecessor.test.ts +87 -12
  350. package/src/__tests__/RunAIAgentResolver.streaming.test.ts +28 -4
  351. package/src/__tests__/SchemaEvolutionCustomColumns.test.ts +119 -0
  352. package/src/__tests__/SchemaRefreshLaunch.test.ts +24 -2
  353. package/src/__tests__/ScopedFieldsForBuild.test.ts +192 -0
  354. package/src/__tests__/SessionJanitor.test.ts +195 -4
  355. package/src/__tests__/SessionManager.test.ts +69 -0
  356. package/src/__tests__/TagGovernanceAndAutotagResolvers.test.ts +261 -0
  357. package/src/__tests__/TagGovernanceResolver.test.ts +60 -2
  358. package/src/__tests__/TaskGraphActionRunner.durablePayload.test.ts +2 -2
  359. package/src/__tests__/TaskGraphAgentRunner.test.ts +272 -16
  360. package/src/__tests__/ViewOperationsHandler.test.ts +2 -5
  361. package/src/__tests__/WorkQueueHostService.test.ts +25 -7
  362. package/src/__tests__/backgroundSweepConfigUnits.test.ts +153 -0
  363. package/src/__tests__/contextRequestOrigin.test.ts +105 -0
  364. package/src/__tests__/conversationBrowsers.test.ts +62 -0
  365. package/src/__tests__/globalCors.test.ts +12 -0
  366. package/src/__tests__/magicLinkExtensionCapabilities.test.ts +413 -0
  367. package/src/__tests__/orphanedConversationDetailReconciler.start.test.ts +82 -0
  368. package/src/__tests__/ownedRealtimeSession.test.ts +14 -0
  369. package/src/__tests__/realtimeRecordingHandler.test.ts +7 -2
  370. package/src/__tests__/realtimeSessions/sessionAccess.test.ts +79 -1
  371. package/src/__tests__/requestOrigin.test.ts +104 -0
  372. package/src/__tests__/review-capture-valid-token-log.test.ts +98 -0
  373. package/src/__tests__/runStreams.fieldSecurity.test.ts +5 -5
  374. package/src/__tests__/securityHardeningPr6034.test.ts +120 -0
  375. package/src/__tests__/sessionDeadline.test.ts +51 -0
  376. package/src/__tests__/sharedCache.test.ts +11 -0
  377. package/src/__tests__/unifiedAuth.test.ts +73 -1
  378. package/src/agentSessions/BridgeHostInstance.ts +20 -5
  379. package/src/agentSessions/HandoffOfferExpiry.ts +18 -0
  380. package/src/agentSessions/ReturningVisitorRecap.ts +1 -0
  381. package/src/agentSessions/SessionJanitor.ts +92 -42
  382. package/src/agentSessions/SessionManager.ts +38 -8
  383. package/src/agentSessions/conversationBrowsers.ts +143 -0
  384. package/src/agentSessions/sessionDeadline.ts +81 -0
  385. package/src/auth/APIKeyScopeAuth.ts +5 -4
  386. package/src/auth/__tests__/loginEmailResolution.test.ts +67 -0
  387. package/src/auth/loginEmailResolution.ts +67 -0
  388. package/src/auth/magicLink/MagicLinkKeys.ts +146 -14
  389. package/src/auth/magicLink/MagicLinkRouter.ts +33 -6
  390. package/src/auth/magicLink/MagicLinkService.ts +250 -10
  391. package/src/auth/magicLink/index.ts +1 -1
  392. package/src/auth/magicLink/redeemLanding.ts +7 -4
  393. package/src/auth/magicLink/types.ts +28 -3
  394. package/src/auth/newUsers.ts +1 -0
  395. package/src/backgroundSweepConfigUnits.ts +213 -0
  396. package/src/broadcast/BaseCacheInvalidationBroadcastProvider.ts +2 -1
  397. package/src/config.ts +69 -0
  398. package/src/context.ts +46 -15
  399. package/src/coreRoutes.ts +14 -7
  400. package/src/generated/graphql-schemas/__mj.ts +10360 -6479
  401. package/src/generic/OrphanedConversationDetailReconciler.ts +32 -1
  402. package/src/generic/ResolverBase.ts +422 -143
  403. package/src/generic/RunViewResolver.ts +96 -49
  404. package/src/globalCors.ts +1 -0
  405. package/src/index.ts +246 -44
  406. package/src/integration/ConnectionPause.ts +222 -0
  407. package/src/integration/CustomColumnPromoter.ts +123 -53
  408. package/src/integration/EntityMapLifecycle.ts +229 -6
  409. package/src/integration/InactiveRowWarnings.ts +50 -0
  410. package/src/integration/ObjectNamesByID.ts +97 -0
  411. package/src/integration/PostRestartIntrospect.ts +49 -0
  412. package/src/integration/RSUPostRestartProgress.ts +359 -0
  413. package/src/integration/RSUProgressBridge.ts +85 -2
  414. package/src/integration/RunArtifactAuthorization.ts +65 -0
  415. package/src/integration/SchemaRefreshLaunch.ts +12 -2
  416. package/src/integration/ScopedFieldsForBuild.ts +113 -0
  417. package/src/integration/TargetConfigGuard.ts +71 -0
  418. package/src/logging/StartupLogger.ts +24 -0
  419. package/src/middleware/BaseServerMiddleware.ts +20 -3
  420. package/src/realtimeSessions/ownedRealtimeSession.ts +2 -6
  421. package/src/realtimeSessions/sessionAccess.ts +30 -0
  422. package/src/realtimeWidget/WidgetSessionService.ts +2 -0
  423. package/src/realtimeWidget/visitorIdentity.ts +1 -0
  424. package/src/realtimeWidget/widgetCore.ts +4 -8
  425. package/src/realtimeWidget/widgetGuestElevation.ts +1 -0
  426. package/src/requestOrigin.ts +64 -0
  427. package/src/resolvers/AgentRunStatusPublisher.ts +27 -2
  428. package/src/resolvers/AgentSessionResolver.ts +6 -2
  429. package/src/resolvers/AutotagPipelineResolver.ts +59 -14
  430. package/src/resolvers/ConversationRunEventsResolver.ts +2 -0
  431. package/src/resolvers/ExecuteRemoteOperationResolver.ts +41 -9
  432. package/src/resolvers/GetDataResolver.ts +35 -13
  433. package/src/resolvers/InfoResolver.ts +1 -1
  434. package/src/resolvers/IntegrationDiscoveryResolver.ts +1522 -203
  435. package/src/resolvers/MCPResolver.ts +15 -2
  436. package/src/resolvers/QuerySystemUserResolver.ts +211 -100
  437. package/src/resolvers/RealtimeBridgeResolver.ts +1 -0
  438. package/src/resolvers/RealtimeClientSessionResolver.ts +40 -7
  439. package/src/resolvers/RemoteBrowserActionResolver.ts +242 -32
  440. package/src/resolvers/RunAIAgentResolver.ts +265 -86
  441. package/src/resolvers/RunAIPromptResolver.ts +18 -18
  442. package/src/resolvers/RunTemplateResolver.ts +3 -1
  443. package/src/resolvers/SqlLoggingConfigResolver.ts +7 -9
  444. package/src/resolvers/TagGovernanceResolver.ts +65 -0
  445. package/src/resolvers/TelemetryResolver.ts +44 -9
  446. package/src/resolvers/__tests__/GetDataResolver.tokens.test.ts +52 -0
  447. package/src/resolvers/meetingRecordingRegistration.ts +2 -0
  448. package/src/resolvers/meetings/meetingMutations.ts +37 -7
  449. package/src/rest/MediaAccessKeys.ts +6 -2
  450. package/src/rest/OAuthCallbackHandler.ts +13 -5
  451. package/src/rest/RealtimeRecordingHandler.ts +5 -9
  452. package/src/rest/ViewOperationsHandler.ts +2 -1
  453. package/src/rest/setupRESTEndpoints.ts +11 -2
  454. package/src/services/DurableEntityActionTaskSubmitter.ts +10 -2
  455. package/src/services/TaskGraphActionRunner.ts +2 -23
  456. package/src/services/TaskGraphAgentRunner.ts +43 -0
  457. package/src/services/TaskGraphContinuationDeliverer.ts +1 -0
  458. package/src/services/WorkQueueHostService.ts +26 -4
  459. package/src/services/workQueueConfig.ts +5 -5
  460. package/src/sharedCache.ts +1 -1
  461. package/src/types.ts +18 -0
  462. package/src/util.ts +55 -3
  463. package/src/services/TaskOrchestration-Integration.md +0 -188
@@ -0,0 +1,200 @@
1
+ /**
2
+ * #6007: concurrent `CreateQueryExtended` calls with the same name.
3
+ *
4
+ * Every request first checks that the name is free, so N requests that arrive together all pass
5
+ * the check and then race to insert. The database's unique index on (Name, CategoryID) lets one
6
+ * win. Before the fix, every loser found the winner's row and returned it as its own result, even
7
+ * with `AutoResolveCollision`, so N callers shared one query. Now:
8
+ *
9
+ * - with `AutoResolveCollision`, a loser picks a new unique name and inserts again (bounded), so
10
+ * every request gets its own query;
11
+ * - without it, the existing create-or-get behaviour stays: losers get the query that now exists;
12
+ * - the existence lookup reads the database, never a cache.
13
+ *
14
+ * The provider is a stub whose saves yield before inserting, so concurrent saves interleave the
15
+ * way they do against a real database, and whose insert enforces the unique name.
16
+ */
17
+ import 'reflect-metadata';
18
+ import { describe, it, expect, vi, beforeEach } from 'vitest';
19
+ import type { RunViewParams } from '@memberjunction/core';
20
+
21
+ vi.mock('type-graphql', () => {
22
+ const decoratorFactory = (..._args: unknown[]) => (..._decorated: unknown[]) => undefined;
23
+ const exportNames = ['Arg', 'Ctx', 'Field', 'InputType', 'Mutation', 'ObjectType', 'Resolver', 'PubSub', 'Int', 'Float'];
24
+ return { ...Object.fromEntries(exportNames.map((name) => [name, decoratorFactory])), registerEnumType: () => undefined };
25
+ });
26
+ vi.mock('@memberjunction/core-entities-server', () => ({ MJQueryEntityServer: class {} }));
27
+ vi.mock('../generic/DeleteOptionsInput.js', () => ({ DeleteOptionsInput: class {} }));
28
+ vi.mock('@memberjunction/core', async (importOriginal) => {
29
+ const actual = await importOriginal<typeof import('@memberjunction/core')>();
30
+ return { ...actual, AssertClientInputWritableForUser: () => undefined, LogStatus: () => undefined };
31
+ });
32
+ vi.mock('../generated/generated.js', () => ({
33
+ MJQuery_: class {}, MJQueryField_: class {}, MJQueryParameter_: class {}, MJQueryEntity_: class {}, MJQueryPermission_: class {},
34
+ MJQueryResolver: class {
35
+ protected async CheckAPIKeyScopeAuthorization(): Promise<void> { /* authorized */ }
36
+ protected async AfterCreate(): Promise<void> { /* no event */ }
37
+ protected ListenForEntityMessages(): void { /* no pub/sub */ }
38
+ protected async MapFieldNamesToCodeNames(_entityName: string, values: Record<string, unknown>) { return values; }
39
+ },
40
+ }));
41
+
42
+ import { MJQueryResolverExtended, CreateQuerySystemUserInput } from '../resolvers/QuerySystemUserResolver.js';
43
+ import type { AppContext } from '../types.js';
44
+
45
+ /** One stored query row. */
46
+ interface StoredQuery { ID: string; Name: string; CategoryID: string | null }
47
+
48
+ const CATEGORY = 'C0000000-0000-0000-0000-000000000001';
49
+ let rows: StoredQuery[];
50
+ let lookups: RunViewParams[];
51
+ /** The child-row reads the resolver made, with every cache bypassed or not. */
52
+ let childReads: RunViewParams[];
53
+ /** The field rows stored per query ID, as the database holds them (the query cache holds none). */
54
+ let storedFields: Map<string, Array<{ ID: string; QueryID: string; Name: string; Sequence: number }>>;
55
+ let nextID: number;
56
+
57
+ /** A query record as the stub provider hands out: its insert yields first and enforces the unique name. */
58
+ class StubQueryRecord {
59
+ public ID = '';
60
+ public Name = '';
61
+ public CategoryID: string | null = null;
62
+ public ParameterHints?: Map<string, string>;
63
+ public EntityInfo = {};
64
+ public LatestResult: { Message: string; CompleteMessage: string } | null = null;
65
+ public QueryFields = [];
66
+ public QueryParameters = [];
67
+ public QueryEntities = [];
68
+ public QueryPermissions = [];
69
+
70
+ SetMany(values: Record<string, unknown>): void {
71
+ if (typeof values.Name === 'string') this.Name = values.Name;
72
+ if (typeof values.CategoryID === 'string') this.CategoryID = values.CategoryID;
73
+ }
74
+
75
+ async Save(): Promise<boolean> {
76
+ await new Promise(resolve => setTimeout(resolve, 1));
77
+ if (rows.some(r => r.Name.toLowerCase() === this.Name.toLowerCase() && r.CategoryID === this.CategoryID)) {
78
+ const message = `Cannot insert duplicate key row in object '__mj.Query' with unique index 'UX_Query_Name_CategoryID'. The duplicate key value is (${this.Name}).`;
79
+ this.LatestResult = { Message: message, CompleteMessage: message };
80
+ return false;
81
+ }
82
+ this.ID = `Q${++nextID}`;
83
+ rows.push({ ID: this.ID, Name: this.Name, CategoryID: this.CategoryID });
84
+ return true;
85
+ }
86
+
87
+ async Load(id: string): Promise<boolean> {
88
+ const row = rows.find(r => r.ID === id);
89
+ if (!row) return false;
90
+ Object.assign(this, row);
91
+ return true;
92
+ }
93
+
94
+ GetAll(): Record<string, unknown> {
95
+ return { ID: this.ID, Name: this.Name, CategoryID: this.CategoryID };
96
+ }
97
+ }
98
+
99
+ const provider = {
100
+ async GetEntityObject(): Promise<StubQueryRecord> {
101
+ return new StubQueryRecord();
102
+ },
103
+ async RunViews(params: RunViewParams[]) {
104
+ childReads.push(...params);
105
+ return params.map(p => {
106
+ const queryID = /QueryID='(.*)'/.exec(p.ExtraFilter ?? '')?.[1] ?? '';
107
+ const results = p.EntityName === 'MJ: Query Fields' ? (storedFields.get(queryID) ?? []) : [];
108
+ return { Success: true, Results: results, RowCount: results.length, TotalRowCount: results.length, ExecutionTime: 0, ErrorMessage: '', UserViewRunID: '' };
109
+ });
110
+ },
111
+ async RunView(params: RunViewParams) {
112
+ lookups.push(params);
113
+ await new Promise(resolve => setTimeout(resolve, 0));
114
+ const name = /LOWER\(Name\) = LOWER\('(.*)'\)/.exec(params.ExtraFilter ?? '')?.[1]?.replace(/''/g, "'") ?? '';
115
+ const matches = rows.filter(r => r.Name.toLowerCase() === name.toLowerCase() && r.CategoryID === CATEGORY);
116
+ return { Success: true, Results: matches, RowCount: matches.length, TotalRowCount: matches.length, ExecutionTime: 0, ErrorMessage: '', UserViewRunID: '' };
117
+ },
118
+ };
119
+
120
+ const context = {
121
+ providers: [{ provider, type: 'Read-Write' }],
122
+ userPayload: { userRecord: { ID: 'user-1', Email: 'u@example.com', UserRoles: [] } },
123
+ } as unknown as AppContext;
124
+
125
+ function input(name: string, autoResolve: boolean): CreateQuerySystemUserInput {
126
+ return Object.assign(new CreateQuerySystemUserInput(), { Name: name, CategoryID: CATEGORY, SQL: 'SELECT 1 AS One', AutoResolveCollision: autoResolve });
127
+ }
128
+
129
+ async function createConcurrently(count: number, name: string, autoResolve: boolean) {
130
+ const resolver = new MJQueryResolverExtended();
131
+ const pubSub = { publish: async () => undefined } as unknown as Parameters<MJQueryResolverExtended['CreateQueryExtended']>[2];
132
+ return Promise.all(Array.from({ length: count }, () => resolver.CreateQueryExtended(input(name, autoResolve), context, pubSub)));
133
+ }
134
+
135
+ beforeEach(() => {
136
+ rows = [];
137
+ lookups = [];
138
+ childReads = [];
139
+ storedFields = new Map();
140
+ nextID = 0;
141
+ });
142
+
143
+ describe('CreateQueryExtended — concurrent creates with the same name (#6007)', () => {
144
+ it('with AutoResolveCollision, gives every concurrent request its own query with its own name', async () => {
145
+ const results = await createConcurrently(8, 'Revenue By Region', true);
146
+
147
+ expect(results.every(r => r.Success)).toBe(true);
148
+ const ids = new Set(results.map(r => r.Query?.ID));
149
+ const names = new Set(results.map(r => r.Query?.Name));
150
+ expect(ids.size).toBe(8);
151
+ expect(names.size).toBe(8);
152
+ expect(rows).toHaveLength(8);
153
+ expect([...names]).toContain('Revenue By Region');
154
+ expect([...names].filter(n => n !== 'Revenue By Region').every(n => /^Revenue By Region \(\d+\)$/.test(String(n)))).toBe(true);
155
+ });
156
+
157
+ it('without AutoResolveCollision, keeps create-or-get: the requests that lose the race get the query that now exists', async () => {
158
+ const results = await createConcurrently(5, 'Orders Today', false);
159
+
160
+ expect(results.every(r => r.Success)).toBe(true);
161
+ expect(rows).toHaveLength(1);
162
+ expect(new Set(results.map(r => r.Query?.ID))).toEqual(new Set([rows[0].ID]));
163
+ });
164
+
165
+ it('without AutoResolveCollision, a request that loses the race gets the other query\'s fields, read from the database', async () => {
166
+ const saveOnce = StubQueryRecord.prototype.Save;
167
+ vi.spyOn(StubQueryRecord.prototype, 'Save').mockImplementation(async function (this: StubQueryRecord) {
168
+ const saved = await saveOnce.call(this);
169
+ if (saved) storedFields.set(this.ID, [{ ID: `${this.ID}-f1`, QueryID: this.ID, Name: 'One', Sequence: 1 }]);
170
+ return saved;
171
+ });
172
+ const results = await createConcurrently(3, 'With Fields', false);
173
+
174
+ const winnerID = rows[0].ID;
175
+ const losers = results.filter(r => r.Fields?.length === 1);
176
+ expect(losers).toHaveLength(2);
177
+ expect(losers.every(r => r.Query?.ID === winnerID && r.Fields?.[0].Name === 'One')).toBe(true);
178
+ expect(childReads.length).toBeGreaterThan(0);
179
+ expect(childReads.every(r => r.BypassCache === true)).toBe(true);
180
+ });
181
+
182
+ it('reads every existence check from the database, never a cache', async () => {
183
+ await createConcurrently(3, 'Uncached', true);
184
+
185
+ expect(lookups.length).toBeGreaterThan(0);
186
+ expect(lookups.every(l => l.BypassCache === true)).toBe(true);
187
+ });
188
+
189
+ it('returns the save failure itself when it is not a name race', async () => {
190
+ vi.spyOn(StubQueryRecord.prototype, 'Save').mockImplementationOnce(async function (this: StubQueryRecord) {
191
+ this.LatestResult = { Message: 'Query "Bad" was not saved: MJQ_TAG_IN_STRING_LITERAL', CompleteMessage: 'Query "Bad" was not saved: MJQ_TAG_IN_STRING_LITERAL' };
192
+ return false;
193
+ });
194
+ const [result] = await createConcurrently(1, 'Bad', true);
195
+
196
+ expect(result.Success).toBe(false);
197
+ expect(result.ErrorMessage).toContain('MJQ_TAG_IN_STRING_LITERAL');
198
+ expect(rows).toHaveLength(0);
199
+ });
200
+ });
@@ -0,0 +1,132 @@
1
+ /**
2
+ * `CreateQueryExtended` / `UpdateQueryExtended`: a query-permission row that is not saved or not
3
+ * deleted fails the mutation with the entity's own reason, instead of reporting success.
4
+ *
5
+ * The provider is a stub; permission rows are stubs whose Save / Delete report failure on demand.
6
+ */
7
+ import 'reflect-metadata';
8
+ import { describe, it, expect, vi, beforeEach } from 'vitest';
9
+ import type { RunViewParams } from '@memberjunction/core';
10
+ import { QueryEngine } from '@memberjunction/core-entities';
11
+
12
+ vi.mock('type-graphql', () => {
13
+ const decoratorFactory = (..._args: unknown[]) => (..._decorated: unknown[]) => undefined;
14
+ const exportNames = ['Arg', 'Ctx', 'Field', 'InputType', 'Mutation', 'ObjectType', 'Resolver', 'PubSub', 'Int', 'Float'];
15
+ return { ...Object.fromEntries(exportNames.map((name) => [name, decoratorFactory])), registerEnumType: () => undefined };
16
+ });
17
+ vi.mock('@memberjunction/core-entities-server', () => ({ MJQueryEntityServer: class {} }));
18
+ vi.mock('../generic/DeleteOptionsInput.js', () => ({ DeleteOptionsInput: class {} }));
19
+ vi.mock('@memberjunction/core', async (importOriginal) => {
20
+ const actual = await importOriginal<typeof import('@memberjunction/core')>();
21
+ return { ...actual, AssertClientInputWritableForUser: () => undefined, LogStatus: () => undefined, LogError: () => undefined };
22
+ });
23
+ vi.mock('../generated/generated.js', () => ({
24
+ MJQuery_: class {}, MJQueryField_: class {}, MJQueryParameter_: class {}, MJQueryEntity_: class {}, MJQueryPermission_: class {},
25
+ MJQueryResolver: class {
26
+ protected async CheckAPIKeyScopeAuthorization(): Promise<void> { /* authorized */ }
27
+ protected async AfterCreate(): Promise<void> { /* no event */ }
28
+ protected ListenForEntityMessages(): void { /* no pub/sub */ }
29
+ protected async MapFieldNamesToCodeNames(_entityName: string, values: Record<string, unknown>) { return values; }
30
+ },
31
+ }));
32
+
33
+ import { MJQueryResolverExtended, CreateQuerySystemUserInput, UpdateQuerySystemUserInput } from '../resolvers/QuerySystemUserResolver.js';
34
+ import type { AppContext } from '../types.js';
35
+
36
+ const QUERY_ID = 'Q0000000-0000-0000-0000-000000000001';
37
+ const ROLE = 'R0000000-0000-0000-0000-000000000001';
38
+ const REFUSED = 'The INSERT statement conflicted with the FOREIGN KEY constraint "FK_QueryPermission_Role".';
39
+
40
+ /** A query or permission row as the stub provider hands out. */
41
+ class StubRecord {
42
+ public ID = '';
43
+ public Name = 'Revenue';
44
+ public CategoryID: string | null = null;
45
+ public QueryID = '';
46
+ public RoleID = '';
47
+ public EntityInfo = {};
48
+ public LatestResult: { Message: string; CompleteMessage: string } | null = null;
49
+ public QueryFields = [];
50
+ public QueryParameters = [];
51
+ public QueryEntities = [];
52
+ public QueryPermissions = [];
53
+ constructor(private readonly entityName: string) {}
54
+ SetMany(): void { /* fields are not inspected */ }
55
+ async Load(id: string): Promise<boolean> { this.ID = id; return true; }
56
+ async Save(): Promise<boolean> {
57
+ if (this.entityName === 'MJ: Query Permissions' && failPermissionSave) {
58
+ this.LatestResult = { Message: REFUSED, CompleteMessage: REFUSED };
59
+ return false;
60
+ }
61
+ if (!this.ID) this.ID = QUERY_ID;
62
+ permissionWrites.push(`${this.entityName}:save`);
63
+ return true;
64
+ }
65
+ GetAll(): Record<string, unknown> { return { ID: this.ID, Name: this.Name }; }
66
+ }
67
+
68
+ let failPermissionSave: boolean;
69
+ let permissionWrites: string[];
70
+
71
+ const provider = {
72
+ async GetEntityObject(entityName: string): Promise<StubRecord> { return new StubRecord(entityName); },
73
+ async RunView(_params: RunViewParams) {
74
+ return { Success: true, Results: [], RowCount: 0, TotalRowCount: 0, ExecutionTime: 0, ErrorMessage: '', UserViewRunID: '' };
75
+ },
76
+ async RunViews(params: RunViewParams[]) {
77
+ return params.map(() => ({ Success: true, Results: [], RowCount: 0, TotalRowCount: 0, ExecutionTime: 0, ErrorMessage: '', UserViewRunID: '' }));
78
+ },
79
+ };
80
+
81
+ const context = {
82
+ providers: [{ provider, type: 'Read-Write' }],
83
+ userPayload: { userRecord: { ID: 'user-1', Email: 'u@example.com', UserRoles: [] } },
84
+ } as unknown as AppContext;
85
+ const pubSub = { publish: async () => undefined } as unknown as Parameters<MJQueryResolverExtended['CreateQueryExtended']>[2];
86
+
87
+ /** A stored permission row whose Delete fails with the entity's reason. */
88
+ function undeletablePermission() {
89
+ return {
90
+ RoleID: ROLE,
91
+ LatestResult: { Message: 'Delete refused by row-level security', CompleteMessage: 'Delete refused by row-level security' },
92
+ Delete: async () => false,
93
+ };
94
+ }
95
+
96
+ beforeEach(() => {
97
+ failPermissionSave = false;
98
+ permissionWrites = [];
99
+ vi.restoreAllMocks();
100
+ vi.spyOn(QueryEngine, 'Instance', 'get').mockReturnValue({ GetQueryPermissions: () => [] } as unknown as QueryEngine);
101
+ });
102
+
103
+ describe('query permission writes report their failures', () => {
104
+ it('a create whose permission row is not saved fails, naming the query, the role and the reason', async () => {
105
+ failPermissionSave = true;
106
+ const input = Object.assign(new CreateQuerySystemUserInput(), { Name: 'Revenue', SQL: 'SELECT 1 AS One', Permissions: [{ RoleID: ROLE }] });
107
+ const result = await new MJQueryResolverExtended().CreateQueryExtended(input, context, pubSub);
108
+
109
+ expect(result.Success).toBe(false);
110
+ expect(result.ErrorMessage).toContain(`Query 'Revenue' (ID ${QUERY_ID}) was created, but its permissions were not`);
111
+ expect(result.ErrorMessage).toContain(`role ${ROLE}: ${REFUSED}`);
112
+ });
113
+
114
+ it('an update whose new permission row is not saved fails with the reason', async () => {
115
+ failPermissionSave = true;
116
+ const input = Object.assign(new UpdateQuerySystemUserInput(), { ID: QUERY_ID, Permissions: [{ RoleID: ROLE }] });
117
+ const result = await new MJQueryResolverExtended().UpdateQueryExtended(input, context, pubSub);
118
+
119
+ expect(result.Success).toBe(false);
120
+ expect(result.ErrorMessage).toBe(`Failed to update query permissions: Failed to save the query permission for role ${ROLE}: ${REFUSED}`);
121
+ });
122
+
123
+ it('an update whose old permission row is not deleted fails with the reason and saves no new row', async () => {
124
+ vi.spyOn(QueryEngine, 'Instance', 'get').mockReturnValue({ GetQueryPermissions: () => [undeletablePermission()] } as unknown as QueryEngine);
125
+ const input = Object.assign(new UpdateQuerySystemUserInput(), { ID: QUERY_ID, Permissions: [{ RoleID: ROLE }] });
126
+ const result = await new MJQueryResolverExtended().UpdateQueryExtended(input, context, pubSub);
127
+
128
+ expect(result.Success).toBe(false);
129
+ expect(result.ErrorMessage).toBe(`Failed to update query permissions: Failed to delete the query permission for role ${ROLE}: Delete refused by row-level security`);
130
+ expect(permissionWrites).not.toContain('MJ: Query Permissions:save');
131
+ });
132
+ });