@memberjunction/server 6.2.0-edge.2 → 6.2.0-edge.4

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 (435) hide show
  1. package/README.md +30 -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 +173 -25
  15. package/dist/agentSessions/SessionJanitor.d.ts.map +1 -1
  16. package/dist/agentSessions/SessionJanitor.js +452 -54
  17. package/dist/agentSessions/SessionJanitor.js.map +1 -1
  18. package/dist/agentSessions/SessionManager.d.ts +43 -2
  19. package/dist/agentSessions/SessionManager.d.ts.map +1 -1
  20. package/dist/agentSessions/SessionManager.js +99 -8
  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/auth/actionAuthorization.d.ts +40 -0
  31. package/dist/auth/actionAuthorization.d.ts.map +1 -0
  32. package/dist/auth/actionAuthorization.js +94 -0
  33. package/dist/auth/actionAuthorization.js.map +1 -0
  34. package/dist/auth/index.d.ts.map +1 -1
  35. package/dist/auth/index.js +3 -10
  36. package/dist/auth/index.js.map +1 -1
  37. package/dist/auth/magicLink/MagicLinkService.d.ts.map +1 -1
  38. package/dist/auth/magicLink/MagicLinkService.js +6 -3
  39. package/dist/auth/magicLink/MagicLinkService.js.map +1 -1
  40. package/dist/auth/newUsers.d.ts +10 -0
  41. package/dist/auth/newUsers.d.ts.map +1 -1
  42. package/dist/auth/newUsers.js +80 -11
  43. package/dist/auth/newUsers.js.map +1 -1
  44. package/dist/config.d.ts +4567 -71
  45. package/dist/config.d.ts.map +1 -1
  46. package/dist/config.js +246 -3
  47. package/dist/config.js.map +1 -1
  48. package/dist/context.d.ts +8 -0
  49. package/dist/context.d.ts.map +1 -1
  50. package/dist/context.js +25 -34
  51. package/dist/context.js.map +1 -1
  52. package/dist/generated/graphql-schemas/__mj.d.ts +1073 -10
  53. package/dist/generated/graphql-schemas/__mj.d.ts.map +1 -1
  54. package/dist/generated/graphql-schemas/__mj.js +9188 -3649
  55. package/dist/generated/graphql-schemas/__mj.js.map +1 -1
  56. package/dist/generic/OrphanedConversationDetailReconciler.js +3 -3
  57. package/dist/generic/OrphanedConversationDetailReconciler.js.map +1 -1
  58. package/dist/generic/ResolverBase.d.ts +62 -6
  59. package/dist/generic/ResolverBase.d.ts.map +1 -1
  60. package/dist/generic/ResolverBase.js +179 -35
  61. package/dist/generic/ResolverBase.js.map +1 -1
  62. package/dist/generic/RunViewResolver.d.ts +4 -0
  63. package/dist/generic/RunViewResolver.d.ts.map +1 -1
  64. package/dist/generic/RunViewResolver.js +37 -1
  65. package/dist/generic/RunViewResolver.js.map +1 -1
  66. package/dist/index.d.ts +7 -0
  67. package/dist/index.d.ts.map +1 -1
  68. package/dist/index.js +214 -159
  69. package/dist/index.js.map +1 -1
  70. package/dist/isolatedProvider.d.ts +20 -0
  71. package/dist/isolatedProvider.d.ts.map +1 -0
  72. package/dist/isolatedProvider.js +22 -0
  73. package/dist/isolatedProvider.js.map +1 -0
  74. package/dist/postgresqlCompat.d.ts +8 -0
  75. package/dist/postgresqlCompat.d.ts.map +1 -0
  76. package/dist/postgresqlCompat.js +19 -0
  77. package/dist/postgresqlCompat.js.map +1 -0
  78. package/dist/postgresqlPoolSettings.d.ts +108 -0
  79. package/dist/postgresqlPoolSettings.d.ts.map +1 -0
  80. package/dist/postgresqlPoolSettings.js +134 -0
  81. package/dist/postgresqlPoolSettings.js.map +1 -0
  82. package/dist/realtimeProxy/RealtimeProxyServer.d.ts +44 -4
  83. package/dist/realtimeProxy/RealtimeProxyServer.d.ts.map +1 -1
  84. package/dist/realtimeProxy/RealtimeProxyServer.js +121 -16
  85. package/dist/realtimeProxy/RealtimeProxyServer.js.map +1 -1
  86. package/dist/realtimeProxy/RealtimeRelayTunnel.d.ts +171 -0
  87. package/dist/realtimeProxy/RealtimeRelayTunnel.d.ts.map +1 -0
  88. package/dist/realtimeProxy/RealtimeRelayTunnel.js +532 -0
  89. package/dist/realtimeProxy/RealtimeRelayTunnel.js.map +1 -0
  90. package/dist/realtimeSessions/RealtimeSessionEventService.d.ts +148 -0
  91. package/dist/realtimeSessions/RealtimeSessionEventService.d.ts.map +1 -0
  92. package/dist/realtimeSessions/RealtimeSessionEventService.js +191 -0
  93. package/dist/realtimeSessions/RealtimeSessionEventService.js.map +1 -0
  94. package/dist/realtimeSessions/RealtimeSessionVerificationService.d.ts +66 -0
  95. package/dist/realtimeSessions/RealtimeSessionVerificationService.d.ts.map +1 -0
  96. package/dist/realtimeSessions/RealtimeSessionVerificationService.js +138 -0
  97. package/dist/realtimeSessions/RealtimeSessionVerificationService.js.map +1 -0
  98. package/dist/realtimeSessions/RealtimeVerifyRouter.d.ts +38 -0
  99. package/dist/realtimeSessions/RealtimeVerifyRouter.d.ts.map +1 -0
  100. package/dist/realtimeSessions/RealtimeVerifyRouter.js +98 -0
  101. package/dist/realtimeSessions/RealtimeVerifyRouter.js.map +1 -0
  102. package/dist/realtimeSessions/SessionVerificationPortsBase.d.ts +61 -0
  103. package/dist/realtimeSessions/SessionVerificationPortsBase.d.ts.map +1 -0
  104. package/dist/realtimeSessions/SessionVerificationPortsBase.js +149 -0
  105. package/dist/realtimeSessions/SessionVerificationPortsBase.js.map +1 -0
  106. package/dist/realtimeSessions/SlidingWindowRateLimiter.d.ts +57 -0
  107. package/dist/realtimeSessions/SlidingWindowRateLimiter.d.ts.map +1 -0
  108. package/dist/realtimeSessions/SlidingWindowRateLimiter.js +69 -0
  109. package/dist/realtimeSessions/SlidingWindowRateLimiter.js.map +1 -0
  110. package/dist/realtimeSessions/index.d.ts +17 -0
  111. package/dist/realtimeSessions/index.d.ts.map +1 -0
  112. package/dist/realtimeSessions/index.js +17 -0
  113. package/dist/realtimeSessions/index.js.map +1 -0
  114. package/dist/realtimeSessions/sessionAccess.d.ts +86 -0
  115. package/dist/realtimeSessions/sessionAccess.d.ts.map +1 -0
  116. package/dist/realtimeSessions/sessionAccess.js +90 -0
  117. package/dist/realtimeSessions/sessionAccess.js.map +1 -0
  118. package/dist/realtimeSessions/sessionConfigStamps.d.ts +70 -0
  119. package/dist/realtimeSessions/sessionConfigStamps.d.ts.map +1 -0
  120. package/dist/realtimeSessions/sessionConfigStamps.js +93 -0
  121. package/dist/realtimeSessions/sessionConfigStamps.js.map +1 -0
  122. package/dist/realtimeSessions/subscriptionAuthorization.d.ts +65 -0
  123. package/dist/realtimeSessions/subscriptionAuthorization.d.ts.map +1 -0
  124. package/dist/realtimeSessions/subscriptionAuthorization.js +71 -0
  125. package/dist/realtimeSessions/subscriptionAuthorization.js.map +1 -0
  126. package/dist/realtimeSessions/verificationCore.d.ts +243 -0
  127. package/dist/realtimeSessions/verificationCore.d.ts.map +1 -0
  128. package/dist/realtimeSessions/verificationCore.js +356 -0
  129. package/dist/realtimeSessions/verificationCore.js.map +1 -0
  130. package/dist/realtimeSessions/verificationPages.d.ts +66 -0
  131. package/dist/realtimeSessions/verificationPages.d.ts.map +1 -0
  132. package/dist/realtimeSessions/verificationPages.js +110 -0
  133. package/dist/realtimeSessions/verificationPages.js.map +1 -0
  134. package/dist/realtimeSessions/verificationPolicy.d.ts +147 -0
  135. package/dist/realtimeSessions/verificationPolicy.d.ts.map +1 -0
  136. package/dist/realtimeSessions/verificationPolicy.js +249 -0
  137. package/dist/realtimeSessions/verificationPolicy.js.map +1 -0
  138. package/dist/realtimeSessions/verificationWorkflow.d.ts +263 -0
  139. package/dist/realtimeSessions/verificationWorkflow.d.ts.map +1 -0
  140. package/dist/realtimeSessions/verificationWorkflow.js +435 -0
  141. package/dist/realtimeSessions/verificationWorkflow.js.map +1 -0
  142. package/dist/realtimeWidget/widgetGuestElevation.d.ts +20 -2
  143. package/dist/realtimeWidget/widgetGuestElevation.d.ts.map +1 -1
  144. package/dist/realtimeWidget/widgetGuestElevation.js +31 -2
  145. package/dist/realtimeWidget/widgetGuestElevation.js.map +1 -1
  146. package/dist/resolvers/ActionResolver.d.ts +31 -5
  147. package/dist/resolvers/ActionResolver.d.ts.map +1 -1
  148. package/dist/resolvers/ActionResolver.js +75 -14
  149. package/dist/resolvers/ActionResolver.js.map +1 -1
  150. package/dist/resolvers/AdhocQueryResolver.d.ts +35 -17
  151. package/dist/resolvers/AdhocQueryResolver.d.ts.map +1 -1
  152. package/dist/resolvers/AdhocQueryResolver.js +92 -166
  153. package/dist/resolvers/AdhocQueryResolver.js.map +1 -1
  154. package/dist/resolvers/CacheStatsResolver.d.ts +27 -0
  155. package/dist/resolvers/CacheStatsResolver.d.ts.map +1 -1
  156. package/dist/resolvers/CacheStatsResolver.js +113 -2
  157. package/dist/resolvers/CacheStatsResolver.js.map +1 -1
  158. package/dist/resolvers/ClientToolRequestResolver.d.ts +4 -2
  159. package/dist/resolvers/ClientToolRequestResolver.d.ts.map +1 -1
  160. package/dist/resolvers/ClientToolRequestResolver.js +28 -10
  161. package/dist/resolvers/ClientToolRequestResolver.js.map +1 -1
  162. package/dist/resolvers/EntityRecordNameResolver.d.ts +7 -23
  163. package/dist/resolvers/EntityRecordNameResolver.d.ts.map +1 -1
  164. package/dist/resolvers/EntityRecordNameResolver.js +9 -47
  165. package/dist/resolvers/EntityRecordNameResolver.js.map +1 -1
  166. package/dist/resolvers/FetchEntityVectorsResolver.d.ts +12 -1
  167. package/dist/resolvers/FetchEntityVectorsResolver.d.ts.map +1 -1
  168. package/dist/resolvers/FetchEntityVectorsResolver.js +83 -2
  169. package/dist/resolvers/FetchEntityVectorsResolver.js.map +1 -1
  170. package/dist/resolvers/GetDataResolver.d.ts.map +1 -1
  171. package/dist/resolvers/GetDataResolver.js +9 -0
  172. package/dist/resolvers/GetDataResolver.js.map +1 -1
  173. package/dist/resolvers/HumanHandoffResolver.d.ts +107 -0
  174. package/dist/resolvers/HumanHandoffResolver.d.ts.map +1 -0
  175. package/dist/resolvers/HumanHandoffResolver.js +330 -0
  176. package/dist/resolvers/HumanHandoffResolver.js.map +1 -0
  177. package/dist/resolvers/MCPResolver.d.ts.map +1 -1
  178. package/dist/resolvers/MCPResolver.js +9 -4
  179. package/dist/resolvers/MCPResolver.js.map +1 -1
  180. package/dist/resolvers/MeetingResolver.d.ts +69 -0
  181. package/dist/resolvers/MeetingResolver.d.ts.map +1 -0
  182. package/dist/resolvers/MeetingResolver.js +381 -0
  183. package/dist/resolvers/MeetingResolver.js.map +1 -0
  184. package/dist/resolvers/RealtimeBridgeResolver.d.ts +88 -3
  185. package/dist/resolvers/RealtimeBridgeResolver.d.ts.map +1 -1
  186. package/dist/resolvers/RealtimeBridgeResolver.js +352 -12
  187. package/dist/resolvers/RealtimeBridgeResolver.js.map +1 -1
  188. package/dist/resolvers/RealtimeClientSessionResolver.d.ts +156 -108
  189. package/dist/resolvers/RealtimeClientSessionResolver.d.ts.map +1 -1
  190. package/dist/resolvers/RealtimeClientSessionResolver.js +395 -293
  191. package/dist/resolvers/RealtimeClientSessionResolver.js.map +1 -1
  192. package/dist/resolvers/RealtimeSessionEventsResolver.d.ts +61 -0
  193. package/dist/resolvers/RealtimeSessionEventsResolver.d.ts.map +1 -0
  194. package/dist/resolvers/RealtimeSessionEventsResolver.js +130 -0
  195. package/dist/resolvers/RealtimeSessionEventsResolver.js.map +1 -0
  196. package/dist/resolvers/RealtimeSessionVerificationResolver.d.ts +85 -0
  197. package/dist/resolvers/RealtimeSessionVerificationResolver.d.ts.map +1 -0
  198. package/dist/resolvers/RealtimeSessionVerificationResolver.js +188 -0
  199. package/dist/resolvers/RealtimeSessionVerificationResolver.js.map +1 -0
  200. package/dist/resolvers/RemoteBrowserActionResolver.d.ts +2 -0
  201. package/dist/resolvers/RemoteBrowserActionResolver.d.ts.map +1 -1
  202. package/dist/resolvers/RemoteBrowserActionResolver.js +5 -1
  203. package/dist/resolvers/RemoteBrowserActionResolver.js.map +1 -1
  204. package/dist/resolvers/RunAIAgentResolver.d.ts +10 -0
  205. package/dist/resolvers/RunAIAgentResolver.d.ts.map +1 -1
  206. package/dist/resolvers/RunAIAgentResolver.js +68 -11
  207. package/dist/resolvers/RunAIAgentResolver.js.map +1 -1
  208. package/dist/resolvers/RunTemplateResolver.d.ts.map +1 -1
  209. package/dist/resolvers/RunTemplateResolver.js +12 -11
  210. package/dist/resolvers/RunTemplateResolver.js.map +1 -1
  211. package/dist/resolvers/SyncDataResolver.d.ts.map +1 -1
  212. package/dist/resolvers/SyncDataResolver.js +6 -2
  213. package/dist/resolvers/SyncDataResolver.js.map +1 -1
  214. package/dist/resolvers/SyncRolesUsersResolver.d.ts +2 -0
  215. package/dist/resolvers/SyncRolesUsersResolver.d.ts.map +1 -1
  216. package/dist/resolvers/SyncRolesUsersResolver.js +23 -6
  217. package/dist/resolvers/SyncRolesUsersResolver.js.map +1 -1
  218. package/dist/resolvers/UserAvatarResolver.d.ts +16 -0
  219. package/dist/resolvers/UserAvatarResolver.d.ts.map +1 -0
  220. package/dist/resolvers/UserAvatarResolver.js +124 -0
  221. package/dist/resolvers/UserAvatarResolver.js.map +1 -0
  222. package/dist/resolvers/UserResolver.d.ts.map +1 -1
  223. package/dist/resolvers/UserResolver.js +2 -17
  224. package/dist/resolvers/UserResolver.js.map +1 -1
  225. package/dist/resolvers/adhocTimeout.d.ts +7 -0
  226. package/dist/resolvers/adhocTimeout.d.ts.map +1 -0
  227. package/dist/resolvers/adhocTimeout.js +12 -0
  228. package/dist/resolvers/adhocTimeout.js.map +1 -0
  229. package/dist/resolvers/agentVisionConsentAudit.d.ts +37 -0
  230. package/dist/resolvers/agentVisionConsentAudit.d.ts.map +1 -0
  231. package/dist/resolvers/agentVisionConsentAudit.js +63 -0
  232. package/dist/resolvers/agentVisionConsentAudit.js.map +1 -0
  233. package/dist/resolvers/avatarInputValidation.d.ts +40 -0
  234. package/dist/resolvers/avatarInputValidation.d.ts.map +1 -0
  235. package/dist/resolvers/avatarInputValidation.js +97 -0
  236. package/dist/resolvers/avatarInputValidation.js.map +1 -0
  237. package/dist/resolvers/currentUserRoles.d.ts +29 -0
  238. package/dist/resolvers/currentUserRoles.d.ts.map +1 -1
  239. package/dist/resolvers/currentUserRoles.js +30 -0
  240. package/dist/resolvers/currentUserRoles.js.map +1 -1
  241. package/dist/resolvers/meetings/index.d.ts +5 -0
  242. package/dist/resolvers/meetings/index.d.ts.map +1 -0
  243. package/dist/resolvers/meetings/index.js +5 -0
  244. package/dist/resolvers/meetings/index.js.map +1 -0
  245. package/dist/resolvers/meetings/meetingHelpers.d.ts +17 -0
  246. package/dist/resolvers/meetings/meetingHelpers.d.ts.map +1 -0
  247. package/dist/resolvers/meetings/meetingHelpers.js +212 -0
  248. package/dist/resolvers/meetings/meetingHelpers.js.map +1 -0
  249. package/dist/resolvers/meetings/meetingMutations.d.ts +18 -0
  250. package/dist/resolvers/meetings/meetingMutations.d.ts.map +1 -0
  251. package/dist/resolvers/meetings/meetingMutations.js +439 -0
  252. package/dist/resolvers/meetings/meetingMutations.js.map +1 -0
  253. package/dist/resolvers/meetings/meetingQueries.d.ts +7 -0
  254. package/dist/resolvers/meetings/meetingQueries.d.ts.map +1 -0
  255. package/dist/resolvers/meetings/meetingQueries.js +113 -0
  256. package/dist/resolvers/meetings/meetingQueries.js.map +1 -0
  257. package/dist/resolvers/meetings/meetingTypes.d.ts +89 -0
  258. package/dist/resolvers/meetings/meetingTypes.d.ts.map +1 -0
  259. package/dist/resolvers/meetings/meetingTypes.js +345 -0
  260. package/dist/resolvers/meetings/meetingTypes.js.map +1 -0
  261. package/dist/rest/ViewOperationsHandler.d.ts.map +1 -1
  262. package/dist/rest/ViewOperationsHandler.js +8 -5
  263. package/dist/rest/ViewOperationsHandler.js.map +1 -1
  264. package/dist/services/TaskGraphActionRunner.d.ts +8 -0
  265. package/dist/services/TaskGraphActionRunner.d.ts.map +1 -1
  266. package/dist/services/TaskGraphActionRunner.js +27 -1
  267. package/dist/services/TaskGraphActionRunner.js.map +1 -1
  268. package/dist/services/TaskGraphContinuationDeliverer.d.ts +4 -3
  269. package/dist/services/TaskGraphContinuationDeliverer.d.ts.map +1 -1
  270. package/dist/services/TaskGraphContinuationDeliverer.js +13 -7
  271. package/dist/services/TaskGraphContinuationDeliverer.js.map +1 -1
  272. package/dist/services/WorkQueueHostService.d.ts +36 -0
  273. package/dist/services/WorkQueueHostService.d.ts.map +1 -0
  274. package/dist/services/WorkQueueHostService.js +97 -0
  275. package/dist/services/WorkQueueHostService.js.map +1 -0
  276. package/dist/services/workQueueConfig.d.ts +103 -0
  277. package/dist/services/workQueueConfig.d.ts.map +1 -0
  278. package/dist/services/workQueueConfig.js +48 -0
  279. package/dist/services/workQueueConfig.js.map +1 -0
  280. package/dist/sharedCache.d.ts +105 -0
  281. package/dist/sharedCache.d.ts.map +1 -0
  282. package/dist/sharedCache.js +276 -0
  283. package/dist/sharedCache.js.map +1 -0
  284. package/dist/types.d.ts +11 -0
  285. package/dist/types.d.ts.map +1 -1
  286. package/dist/types.js.map +1 -1
  287. package/package.json +97 -94
  288. package/src/__tests__/ActionResolver.authorization.test.ts +353 -0
  289. package/src/__tests__/AdhocQueryResolver.bugs.test.ts +14 -35
  290. package/src/__tests__/AdhocQueryResolver.pagination.test.ts +6 -2
  291. package/src/__tests__/AdhocQueryResolver.scopeGuard.test.ts +3 -3
  292. package/src/__tests__/AdhocQueryResolver.sqlAuthorizer.test.ts +104 -0
  293. package/src/__tests__/BridgeHostInstance.test.ts +232 -0
  294. package/src/__tests__/ClientToolRequestResolver.test.ts +116 -0
  295. package/src/__tests__/EntityRecordNameResolver.fieldSecurity.test.ts +27 -72
  296. package/src/__tests__/FetchEntityVectorsResolver.test.ts +285 -0
  297. package/src/__tests__/HostInstance.test.ts +84 -0
  298. package/src/__tests__/HumanHandoffResolver.test.ts +250 -0
  299. package/src/__tests__/MeetingResolver.test.ts +448 -0
  300. package/src/__tests__/RealtimeBridgeResolver.test.ts +651 -4
  301. package/src/__tests__/RealtimeClientSessionResolver.configTamper.test.ts +383 -0
  302. package/src/__tests__/RealtimeClientSessionResolver.test.ts +682 -58
  303. package/src/__tests__/RealtimeProxyServer.test.ts +16 -1
  304. package/src/__tests__/RealtimeRelay.test.ts +876 -0
  305. package/src/__tests__/RemoteBrowserActionResolver.goalRunLink.test.ts +129 -0
  306. package/src/__tests__/ResolverBase.adhocReadScope.test.ts +96 -0
  307. package/src/__tests__/ResolverBase.aggregateScreen.test.ts +174 -0
  308. package/src/__tests__/ResolverBase.clientOldValues.test.ts +6 -3
  309. package/src/__tests__/ResolverBase.filterEscaping.test.ts +21 -2
  310. package/src/__tests__/ResolverBase.includeBinaryFields.test.ts +291 -0
  311. package/src/__tests__/ReturningVisitorRecap.test.ts +115 -0
  312. package/src/__tests__/RunAIAgentResolver.branch.test.ts +169 -0
  313. package/src/__tests__/RunAIAgentResolver.historyFrom.test.ts +4 -4
  314. package/src/__tests__/RunAIAgentResolver.stoppedPredecessor.test.ts +166 -0
  315. package/src/__tests__/RunTemplateResolver.test.ts +132 -0
  316. package/src/__tests__/RunViewResolver.cacheCheckForwarding.test.ts +63 -0
  317. package/src/__tests__/SessionJanitor.test.ts +845 -18
  318. package/src/__tests__/SessionManager.test.ts +213 -17
  319. package/src/__tests__/SessionRunIDVerifier.test.ts +251 -0
  320. package/src/__tests__/SyncRolesUsersResolver.userRoles.test.ts +97 -0
  321. package/src/__tests__/TaskGraphActionRunner.durablePayload.test.ts +41 -0
  322. package/src/__tests__/TaskGraphContinuationDeliverer.test.ts +50 -3
  323. package/src/__tests__/UserAvatarResolver.test.ts +255 -0
  324. package/src/__tests__/UserResolver.currentUser.test.ts +16 -1
  325. package/src/__tests__/WorkQueueHostService.test.ts +133 -0
  326. package/src/__tests__/actionAuthorization.test.ts +130 -0
  327. package/src/__tests__/adhocTimeoutClamp.test.ts +26 -0
  328. package/src/__tests__/agentVisionConsentAudit.test.ts +118 -0
  329. package/src/__tests__/avatarInputValidation.test.ts +160 -0
  330. package/src/__tests__/isolatedProviderTransactions.test.ts +169 -0
  331. package/src/__tests__/magicLinkService.consume.test.ts +6 -0
  332. package/src/__tests__/magicLinkService.provisionLog.test.ts +5 -0
  333. package/src/__tests__/newUsers.template.test.ts +151 -0
  334. package/src/__tests__/newUsers.test.ts +74 -5
  335. package/src/__tests__/orphanedConversationDetailReconciler.test.ts +17 -2
  336. package/src/__tests__/postgresqlPoolSettings.test.ts +131 -0
  337. package/src/__tests__/realtimeSessions/RealtimeSessionEventService.test.ts +201 -0
  338. package/src/__tests__/realtimeSessions/RealtimeSessionEventsResolver.test.ts +215 -0
  339. package/src/__tests__/realtimeSessions/RealtimeSessionVerificationResolver.test.ts +102 -0
  340. package/src/__tests__/realtimeSessions/RealtimeSessionVerificationService.test.ts +349 -0
  341. package/src/__tests__/realtimeSessions/RealtimeVerifyRouter.test.ts +131 -0
  342. package/src/__tests__/realtimeSessions/SlidingWindowRateLimiter.test.ts +56 -0
  343. package/src/__tests__/realtimeSessions/helpers.ts +4 -0
  344. package/src/__tests__/realtimeSessions/sessionAccess.test.ts +129 -0
  345. package/src/__tests__/realtimeSessions/sessionConfigStamps.test.ts +124 -0
  346. package/src/__tests__/realtimeSessions/verificationCore.test.ts +355 -0
  347. package/src/__tests__/realtimeSessions/verificationPages.test.ts +84 -0
  348. package/src/__tests__/realtimeSessions/verificationPolicy.test.ts +226 -0
  349. package/src/__tests__/realtimeSessions/verificationWorkflow.test.ts +633 -0
  350. package/src/__tests__/resolverBase.fls.test.ts +87 -4
  351. package/src/__tests__/serverExtensionReservedRoots.test.ts +2 -0
  352. package/src/__tests__/sharedCache.test.ts +310 -0
  353. package/src/__tests__/translateBracketsToPG.test.ts +20 -0
  354. package/src/__tests__/widgetGuestElevation.test.ts +44 -0
  355. package/src/agentSessions/BridgeHostInstance.ts +32 -0
  356. package/src/agentSessions/HostInstance.ts +124 -28
  357. package/src/agentSessions/ReturningVisitorRecap.ts +7 -3
  358. package/src/agentSessions/SessionJanitor.ts +543 -57
  359. package/src/agentSessions/SessionManager.ts +125 -25
  360. package/src/agentSessions/SessionRunIDVerifier.ts +171 -0
  361. package/src/agentSessions/index.ts +10 -3
  362. package/src/auth/actionAuthorization.ts +116 -0
  363. package/src/auth/index.ts +3 -8
  364. package/src/auth/magicLink/MagicLinkService.ts +6 -3
  365. package/src/auth/newUsers.ts +99 -11
  366. package/src/config.ts +255 -3
  367. package/src/context.ts +26 -34
  368. package/src/generated/graphql-schemas/__mj.ts +22701 -18838
  369. package/src/generic/OrphanedConversationDetailReconciler.ts +3 -3
  370. package/src/generic/ResolverBase.ts +210 -36
  371. package/src/generic/RunViewResolver.ts +37 -2
  372. package/src/index.ts +236 -170
  373. package/src/isolatedProvider.ts +22 -0
  374. package/src/postgresqlCompat.ts +21 -0
  375. package/src/postgresqlPoolSettings.ts +213 -0
  376. package/src/realtimeProxy/RealtimeProxyServer.ts +148 -16
  377. package/src/realtimeProxy/RealtimeRelayTunnel.ts +645 -0
  378. package/src/realtimeSessions/README.md +179 -0
  379. package/src/realtimeSessions/RealtimeSessionEventService.ts +274 -0
  380. package/src/realtimeSessions/RealtimeSessionVerificationService.ts +167 -0
  381. package/src/realtimeSessions/RealtimeVerifyRouter.ts +108 -0
  382. package/src/realtimeSessions/SessionVerificationPortsBase.ts +189 -0
  383. package/src/realtimeSessions/SlidingWindowRateLimiter.ts +92 -0
  384. package/src/realtimeSessions/index.ts +16 -0
  385. package/src/realtimeSessions/sessionAccess.ts +141 -0
  386. package/src/realtimeSessions/sessionConfigStamps.ts +123 -0
  387. package/src/realtimeSessions/subscriptionAuthorization.ts +116 -0
  388. package/src/realtimeSessions/verificationCore.ts +502 -0
  389. package/src/realtimeSessions/verificationPages.ts +149 -0
  390. package/src/realtimeSessions/verificationPolicy.ts +321 -0
  391. package/src/realtimeSessions/verificationWorkflow.ts +702 -0
  392. package/src/realtimeWidget/widgetGuestElevation.ts +34 -2
  393. package/src/resolvers/ActionResolver.ts +84 -21
  394. package/src/resolvers/AdhocQueryResolver.ts +105 -187
  395. package/src/resolvers/CacheStatsResolver.ts +89 -2
  396. package/src/resolvers/ClientToolRequestResolver.ts +25 -7
  397. package/src/resolvers/EntityRecordNameResolver.ts +9 -51
  398. package/src/resolvers/FetchEntityVectorsResolver.ts +97 -2
  399. package/src/resolvers/GetDataResolver.ts +10 -0
  400. package/src/resolvers/HumanHandoffResolver.ts +312 -0
  401. package/src/resolvers/MCPResolver.ts +8 -5
  402. package/src/resolvers/MeetingResolver.ts +438 -0
  403. package/src/resolvers/RealtimeBridgeResolver.ts +385 -19
  404. package/src/resolvers/RealtimeClientSessionResolver.ts +503 -344
  405. package/src/resolvers/RealtimeSessionEventsResolver.ts +133 -0
  406. package/src/resolvers/RealtimeSessionVerificationResolver.ts +174 -0
  407. package/src/resolvers/RemoteBrowserActionResolver.ts +11 -1
  408. package/src/resolvers/RunAIAgentResolver.ts +78 -11
  409. package/src/resolvers/RunTemplateResolver.ts +13 -12
  410. package/src/resolvers/SyncDataResolver.ts +6 -2
  411. package/src/resolvers/SyncRolesUsersResolver.ts +24 -6
  412. package/src/resolvers/UserAvatarResolver.ts +111 -0
  413. package/src/resolvers/UserResolver.ts +2 -16
  414. package/src/resolvers/adhocTimeout.ts +12 -0
  415. package/src/resolvers/agentVisionConsentAudit.ts +82 -0
  416. package/src/resolvers/avatarInputValidation.ts +123 -0
  417. package/src/resolvers/currentUserRoles.ts +32 -1
  418. package/src/resolvers/meetings/index.ts +4 -0
  419. package/src/resolvers/meetings/meetingHelpers.ts +307 -0
  420. package/src/resolvers/meetings/meetingMutations.ts +556 -0
  421. package/src/resolvers/meetings/meetingQueries.ts +172 -0
  422. package/src/resolvers/meetings/meetingTypes.ts +238 -0
  423. package/src/rest/ViewOperationsHandler.ts +8 -5
  424. package/src/services/TaskGraphActionRunner.ts +29 -1
  425. package/src/services/TaskGraphContinuationDeliverer.ts +13 -7
  426. package/src/services/WorkQueueHostService.ts +129 -0
  427. package/src/services/workQueueConfig.ts +52 -0
  428. package/src/sharedCache.ts +302 -0
  429. package/src/types.ts +12 -0
  430. package/dist/resolvers/adhoc-query-helpers.d.ts +0 -33
  431. package/dist/resolvers/adhoc-query-helpers.d.ts.map +0 -1
  432. package/dist/resolvers/adhoc-query-helpers.js +0 -50
  433. package/dist/resolvers/adhoc-query-helpers.js.map +0 -1
  434. package/src/__tests__/adhoc-query-helpers.test.ts +0 -50
  435. package/src/resolvers/adhoc-query-helpers.ts +0 -67
@@ -0,0 +1,129 @@
1
+ /**
2
+ * `ExecuteRemoteBrowserGoal` attaches a "Browser goal" step to the realtime co-agent run named by the
3
+ * session's `coAgentRunID`. The session owner can edit `Config`, so the run must be a record of the
4
+ * session before a step is attached to it.
5
+ */
6
+ // type-graphql decorators on the resolver need the reflect-metadata polyfill loaded first.
7
+ import 'reflect-metadata';
8
+
9
+ import { describe, it, expect, vi, beforeEach } from 'vitest';
10
+
11
+ // As in RemoteBrowserActionRecovery.test.ts: the decorators become no-ops so the resolver's plain logic runs.
12
+ vi.mock('type-graphql', () => {
13
+ const noopDecorator = () => () => undefined;
14
+ return {
15
+ Resolver: noopDecorator,
16
+ Mutation: noopDecorator,
17
+ Query: noopDecorator,
18
+ Subscription: noopDecorator,
19
+ ObjectType: noopDecorator,
20
+ InputType: noopDecorator,
21
+ Field: noopDecorator,
22
+ Arg: noopDecorator,
23
+ Args: noopDecorator,
24
+ Ctx: noopDecorator,
25
+ PubSub: noopDecorator,
26
+ Root: noopDecorator,
27
+ Float: class {},
28
+ Int: class {},
29
+ ID: class {},
30
+ };
31
+ });
32
+
33
+ const achieveGoalMock = vi.fn(async (_sessionID: string, _goal: string, _options: Record<string, unknown>) => ({ Success: true, Status: 'Done' }));
34
+ vi.mock('@memberjunction/remote-browser-server', () => ({
35
+ NormalizeInstanceKey: (key?: string) => key,
36
+ RemoteBrowserEngine: {
37
+ Instance: {
38
+ AchieveGoal: (sessionID: string, goal: string, options: Record<string, unknown>) => achieveGoalMock(sessionID, goal, options),
39
+ },
40
+ },
41
+ }));
42
+
43
+ const beginStepMock = vi.fn(async (_provider: unknown, _user: unknown, _coAgentRunID: string | undefined, _goal: string) => null);
44
+ vi.mock('../agentSessions/remoteBrowserGoalEngine.js', async (importOriginal) => ({
45
+ ...(await importOriginal<typeof import('../agentSessions/remoteBrowserGoalEngine.js')>()),
46
+ BeginBrowserGoalStep: (provider: unknown, user: unknown, coAgentRunID: string | undefined, goal: string) =>
47
+ beginStepMock(provider, user, coAgentRunID, goal),
48
+ FinalizeBrowserGoalStep: vi.fn(async () => undefined),
49
+ }));
50
+
51
+ vi.mock('@memberjunction/aiengine', () => ({ AIEngine: { Instance: { Config: vi.fn(), Prompts: [] } } }));
52
+ vi.mock('@memberjunction/ai-prompts', async (importOriginal) => ({
53
+ ...(await importOriginal<typeof import('@memberjunction/ai-prompts')>()),
54
+ AIPromptRunner: class {},
55
+ }));
56
+ vi.mock('@memberjunction/ai-core-plus', async (importOriginal) => ({
57
+ ...(await importOriginal<typeof import('@memberjunction/ai-core-plus')>()),
58
+ AIPromptParams: class {},
59
+ }));
60
+
61
+ import { RemoteBrowserActionResolver } from '../resolvers/RemoteBrowserActionResolver.js';
62
+ import type { AppContext } from '../types.js';
63
+ import type { IMetadataProvider, UserInfo } from '@memberjunction/core';
64
+ import type { MJAIAgentSessionEntity } from '@memberjunction/core-entities';
65
+
66
+ const SESSION_ID = 'c3c3c3c3-2000-4000-8000-000000000001';
67
+ const CO_RUN_ID = 'c3c3c3c3-2000-4000-8000-000000000002';
68
+ /** A run that exists, but belongs to someone else's session. */
69
+ const FOREIGN_RUN_ID = 'c3c3c3c3-2000-4000-8000-0000000000f1';
70
+
71
+ /** The runs the fake lookup reports as records of the session. */
72
+ let sessionRunIDs: string[] = [];
73
+ const runViewsMock = vi.fn(async (paramsList: Array<{ EntityName?: string }>) =>
74
+ paramsList.map((params) => ({
75
+ Success: true,
76
+ Results: params.EntityName === 'MJ: AI Agent Runs' ? sessionRunIDs.map((ID) => ({ ID })) : [],
77
+ })),
78
+ );
79
+
80
+ /** Bypasses the ownership/provider plumbing (covered elsewhere); the session carries `ConfigJson`. */
81
+ class TestableResolver extends RemoteBrowserActionResolver {
82
+ public ConfigJson = '';
83
+ protected override requireUserAndProvider(): { contextUser: UserInfo; provider: IMetadataProvider } {
84
+ return {
85
+ contextUser: { ID: 'c3c3c3c3-2000-4000-8000-0000000000aa' } as unknown as UserInfo,
86
+ provider: { RunViews: runViewsMock } as unknown as IMetadataProvider,
87
+ };
88
+ }
89
+ protected override async loadOwnedSession(): Promise<MJAIAgentSessionEntity> {
90
+ return { ID: SESSION_ID, AgentID: 'agent-1', Config_: this.ConfigJson } as unknown as MJAIAgentSessionEntity;
91
+ }
92
+ protected override async resolveProviderName(): Promise<string | undefined> {
93
+ return 'Self-Hosted Chrome';
94
+ }
95
+ }
96
+
97
+ const ctx = { userPayload: { sessionId: 'push-sess-1' }, providers: [] } as unknown as AppContext;
98
+
99
+ async function startGoal(config: Record<string, unknown>): Promise<void> {
100
+ const resolver = new TestableResolver();
101
+ resolver.ConfigJson = JSON.stringify(config);
102
+ await resolver.ExecuteRemoteBrowserGoal(SESSION_ID, 'open the docs', ctx);
103
+ }
104
+
105
+ beforeEach(() => {
106
+ sessionRunIDs = [];
107
+ runViewsMock.mockClear();
108
+ beginStepMock.mockClear();
109
+ achieveGoalMock.mockClear();
110
+ });
111
+
112
+ describe('RemoteBrowserActionResolver.ExecuteRemoteBrowserGoal — co-agent run from Config', () => {
113
+ it('does not attach the goal to a run that is not a record of the session', async () => {
114
+ await startGoal({ targetAgentID: 'target-1', coAgentRunID: FOREIGN_RUN_ID });
115
+
116
+ expect(beginStepMock).toHaveBeenCalledTimes(1);
117
+ expect(beginStepMock.mock.calls[0][2]).toBeUndefined();
118
+ expect(achieveGoalMock.mock.calls[0][2].AgentRunID).toBeUndefined();
119
+ });
120
+
121
+ it('attaches the goal to the session\'s co-agent run', async () => {
122
+ sessionRunIDs = [CO_RUN_ID];
123
+
124
+ await startGoal({ targetAgentID: 'target-1', coAgentRunID: CO_RUN_ID });
125
+
126
+ expect(beginStepMock.mock.calls[0][2]).toBe(CO_RUN_ID);
127
+ expect(achieveGoalMock.mock.calls[0][2].AgentRunID).toBe(CO_RUN_ID);
128
+ });
129
+ });
@@ -0,0 +1,96 @@
1
+ // ResolverBase transitively pulls in type-graphql decorators, which need the
2
+ // Reflect.metadata polyfill at import time.
3
+ import 'reflect-metadata';
4
+ import { describe, it, expect } from 'vitest';
5
+ import type { IMetadataProvider, UserInfo } from '@memberjunction/core';
6
+ import { ResolverBase } from '../generic/ResolverBase.js';
7
+
8
+ /**
9
+ * The ad-hoc SQL screen (`assertFullQueryUsesReadableEntityViews`) admits a table reference
10
+ * only when it resolves to an entity base view the caller can read UNSCOPED. These tests pin
11
+ * the three ways that resolution was looser than it looked (PR #4782 review):
12
+ *
13
+ * - a schema-qualified reference fell back to a bare-name match in another schema;
14
+ * - an unqualified name present in two schemas resolved to whichever loaded last;
15
+ * - entity CanRead was treated as unscoped read authority, though raw SQL applies neither the
16
+ * row-level filter nor the denied-field projection RunView does.
17
+ */
18
+
19
+ type EntityShape = {
20
+ Name: string;
21
+ SchemaName: string;
22
+ BaseView: string;
23
+ canRead?: boolean;
24
+ rowFilter?: string;
25
+ deniedFields?: string[];
26
+ };
27
+
28
+ function fakeProvider(entities: EntityShape[]): IMetadataProvider {
29
+ return {
30
+ Entities: entities.map((e) => ({
31
+ Name: e.Name,
32
+ SchemaName: e.SchemaName,
33
+ BaseView: e.BaseView,
34
+ GetUserPermisions: () => ({ CanRead: e.canRead ?? true }),
35
+ GetEffectiveRowFilterWhereClause: () => e.rowFilter ?? '',
36
+ GetDeniedReadFields: () => new Set(e.deniedFields ?? []),
37
+ })),
38
+ } as unknown as IMetadataProvider;
39
+ }
40
+
41
+ const USER = { ID: 'U1', Email: 'u@example.com' } as unknown as UserInfo;
42
+
43
+ class Probe extends ResolverBase {
44
+ public Screen(sql: string, provider: IMetadataProvider, user: UserInfo | undefined = USER) {
45
+ return this.assertFullQueryUsesReadableEntityViews(sql, provider, user, 'ad-hoc SQL');
46
+ }
47
+ }
48
+
49
+ const PUBLIC_FOO: EntityShape = { Name: 'Public Foo', SchemaName: 'public', BaseView: 'vwFoo' };
50
+
51
+ describe('assertFullQueryUsesReadableEntityViews', () => {
52
+ it('admits an unscoped readable base view', () => {
53
+ expect(() => new Probe().Screen('SELECT ID FROM [public].[vwFoo]', fakeProvider([PUBLIC_FOO]))).not.toThrow();
54
+ });
55
+
56
+ it('does not resolve a schema-qualified reference through another schema', () => {
57
+ expect(() => new Probe().Screen('SELECT ID FROM secret.vwFoo', fakeProvider([PUBLIC_FOO]))).toThrow(
58
+ /entity base view, not 'secret.vwFoo'/,
59
+ );
60
+ });
61
+
62
+ it('refuses an unqualified view name that exists in more than one schema', () => {
63
+ const provider = fakeProvider([PUBLIC_FOO, { Name: 'Other Foo', SchemaName: 'other', BaseView: 'vwFoo' }]);
64
+ expect(() => new Probe().Screen('SELECT ID FROM vwFoo', provider)).toThrow(/more than one schema/);
65
+ expect(() => new Probe().Screen('SELECT ID FROM other.vwFoo', provider)).not.toThrow();
66
+ });
67
+
68
+ it('refuses an entity the caller cannot read', () => {
69
+ expect(() => new Probe().Screen('SELECT ID FROM public.vwFoo', fakeProvider([{ ...PUBLIC_FOO, canRead: false }]))).toThrow(
70
+ /read permission/,
71
+ );
72
+ });
73
+
74
+ it('refuses an entity that is row-level-security filtered for the caller', () => {
75
+ const provider = fakeProvider([{ ...PUBLIC_FOO, rowFilter: "(UserID = 'U1')" }]);
76
+ expect(() => new Probe().Screen('SELECT ID, UserID FROM public.vwFoo', provider)).toThrow(/row-level-security/);
77
+ });
78
+
79
+ it('refuses an entity with a field the caller is denied', () => {
80
+ const provider = fakeProvider([{ ...PUBLIC_FOO, deniedFields: ['secret'] }]);
81
+ expect(() => new Probe().Screen('SELECT ID FROM public.vwFoo', provider)).toThrow(/not permitted to read/);
82
+ });
83
+
84
+ it('applies the scope check to every reference, including joins and subqueries', () => {
85
+ const provider = fakeProvider([PUBLIC_FOO, { Name: 'Bar', SchemaName: 'public', BaseView: 'vwBar', rowFilter: '(1=0)' }]);
86
+ expect(() =>
87
+ new Probe().Screen('SELECT f.ID FROM public.vwFoo f WHERE f.ID IN (SELECT FooID FROM public.vwBar)', provider),
88
+ ).toThrow(/'Bar' is row-level-security filtered/);
89
+ });
90
+
91
+ it('exempts CTE names the statement defines', () => {
92
+ expect(() =>
93
+ new Probe().Screen('WITH x AS (SELECT ID FROM public.vwFoo) SELECT ID FROM x', fakeProvider([PUBLIC_FOO])),
94
+ ).not.toThrow();
95
+ });
96
+ });
@@ -0,0 +1,174 @@
1
+ // ResolverBase transitively pulls in type-graphql decorators, which need the
2
+ // Reflect.metadata polyfill at import time.
3
+ import 'reflect-metadata';
4
+ import { describe, it, expect } from 'vitest';
5
+ import type { AggregateExpression, DatabaseProviderBase, IMetadataProvider, RunViewParams, RunViewResult, UserInfo } from '@memberjunction/core';
6
+ import type { PubSubEngine } from 'type-graphql';
7
+ import { ResolverBase } from '../generic/ResolverBase.js';
8
+ import { RunViewResolver, type RunDynamicViewInput } from '../generic/RunViewResolver.js';
9
+ import type { AppContext, UserPayload } from '../types.js';
10
+
11
+ /**
12
+ * `Aggregates[].expression` is spliced into `SELECT <expression> AS [Agg_0] FROM <view> WHERE …`.
13
+ * The GraphQL-boundary screen must treat it like the other client SQL fragments (ExtraFilter,
14
+ * OrderBy, OverrideExcludeFilter): one statement, no writes, and subqueries only against entity
15
+ * base views the caller can read.
16
+ *
17
+ * The payloads below are harmless (`SELECT 1`).
18
+ */
19
+
20
+ const ENTITY_NAME = 'MJ: Users';
21
+
22
+ /** An aggregate that carries a second statement. */
23
+ const STACKED_AGGREGATE = "COUNT(*) AS [a'] ; SELECT 1 AS [b'], COUNT(*)";
24
+
25
+ /** A subquery against a base table rather than an entity base view. */
26
+ const BASE_TABLE_AGGREGATE = 'COUNT(*) + (SELECT COUNT(*) FROM __mj.[User])';
27
+
28
+ type Captured = { params: RunViewParams | null };
29
+
30
+ /** Entity metadata the screen reads, plus a `RunView` that records what reached it. */
31
+ function fakeProvider(captured: Captured): DatabaseProviderBase {
32
+ return {
33
+ Entities: [
34
+ {
35
+ ID: 'E1',
36
+ Name: ENTITY_NAME,
37
+ SchemaName: '__mj',
38
+ BaseView: 'vwUsers',
39
+ BaseTable: 'User',
40
+ Fields: [
41
+ { Name: 'ID', NeedsQuotes: true },
42
+ { Name: 'Name', NeedsQuotes: true },
43
+ { Name: 'Sequence', NeedsQuotes: false },
44
+ ],
45
+ PrimaryKeys: [{ Name: 'ID' }],
46
+ GetUserPermisions: () => ({ CanRead: true }),
47
+ },
48
+ ],
49
+ EntityByName: (name: string) => (name === ENTITY_NAME ? { ID: 'E1', Name: ENTITY_NAME, PrimaryKeys: [{ Name: 'ID' }] } : undefined),
50
+ RunView: async (params: RunViewParams): Promise<RunViewResult> => {
51
+ captured.params = params;
52
+ return { Success: true, Results: [], RowCount: 0, TotalRowCount: 0, ErrorMessage: '' } as RunViewResult;
53
+ },
54
+ RunViews: async (params: RunViewParams[]): Promise<RunViewResult[]> => {
55
+ captured.params = params[0] ?? null;
56
+ return params.map(() => ({ Success: true, Results: [], RowCount: 0, TotalRowCount: 0, ErrorMessage: '' } as RunViewResult));
57
+ },
58
+ } as unknown as DatabaseProviderBase;
59
+ }
60
+
61
+ const fakeUser = () => ({ Email: 'tester@example.com' } as UserInfo);
62
+ const fakePayload = () => ({ email: 'tester@example.com', userRecord: fakeUser() } as UserPayload);
63
+
64
+ /** Reaches the boundary screen and the dynamic-view entry point. */
65
+ class Probe extends ResolverBase {
66
+ public ScreenAll(
67
+ clauses: { extraFilter?: string | null; aggregates?: AggregateExpression[] | null },
68
+ provider: DatabaseProviderBase,
69
+ ) {
70
+ return this.screenClientViewClauses(clauses, provider as unknown as IMetadataProvider);
71
+ }
72
+
73
+ public RunDynamic(input: RunDynamicViewInput, provider: DatabaseProviderBase) {
74
+ return this.RunDynamicViewGeneric(input, provider, fakePayload(), undefined as unknown as PubSubEngine);
75
+ }
76
+ }
77
+
78
+ describe('ResolverBase.screenClientViewClauses — aggregate expressions', () => {
79
+ it('rejects an aggregate that carries a second statement', () => {
80
+ expect(() =>
81
+ new Probe().ScreenAll({ aggregates: [{ expression: STACKED_AGGREGATE }] }, fakeProvider({ params: null })),
82
+ ).toThrow(/Invalid Aggregate: multiple statements/);
83
+ });
84
+
85
+ it('rejects an aggregate whose subquery reads a base table', () => {
86
+ expect(() =>
87
+ new Probe().ScreenAll({ aggregates: [{ expression: BASE_TABLE_AGGREGATE }] }, fakeProvider({ params: null })),
88
+ ).toThrow(/Invalid Aggregate: subquery must use an entity base view/);
89
+ });
90
+
91
+ it('screens every aggregate in the list, not only the first', () => {
92
+ expect(() =>
93
+ new Probe().ScreenAll(
94
+ { aggregates: [{ expression: 'COUNT(*)' }, { expression: STACKED_AGGREGATE }] },
95
+ fakeProvider({ params: null }),
96
+ ),
97
+ ).toThrow(/Invalid Aggregate: multiple statements/);
98
+ });
99
+
100
+ it('leaves an aggregate the parser cannot read to the provider, which refuses it per aggregate', () => {
101
+ expect(() =>
102
+ new Probe().ScreenAll({ aggregates: [{ expression: 'COUNT_BIG(*)' }] }, fakeProvider({ params: null })),
103
+ ).not.toThrow();
104
+ });
105
+
106
+ it('lets ordinary aggregate expressions through', () => {
107
+ const ordinary = [
108
+ 'COUNT(*)',
109
+ 'SUM(Sequence)',
110
+ 'AVG(Sequence * 2)',
111
+ 'COUNT(DISTINCT Name)',
112
+ "COUNT(CASE WHEN Name = 'x' THEN 1 END)",
113
+ 'MIN([Name])',
114
+ ];
115
+ expect(() =>
116
+ new Probe().ScreenAll({ aggregates: ordinary.map((expression) => ({ expression })) }, fakeProvider({ params: null })),
117
+ ).not.toThrow();
118
+ });
119
+ });
120
+
121
+ describe('ResolverBase RunView entry points — aggregate expressions', () => {
122
+ it('RunDynamicViewGeneric never reaches RunView with a stacked aggregate', async () => {
123
+ const captured: Captured = { params: null };
124
+ const input = { EntityName: ENTITY_NAME, Aggregates: [{ expression: STACKED_AGGREGATE }] } as RunDynamicViewInput;
125
+
126
+ await expect(new Probe().RunDynamic(input, fakeProvider(captured))).rejects.toThrow(/Invalid Aggregate: multiple statements/);
127
+ expect(captured.params).toBeNull();
128
+ });
129
+
130
+ it('RunDynamicViewGeneric passes ordinary aggregates through to RunView', async () => {
131
+ const captured: Captured = { params: null };
132
+ const aggregates = [{ expression: 'COUNT(*)', alias: 'Total' }, { expression: 'SUM(Sequence)' }];
133
+ const input = { EntityName: ENTITY_NAME, Aggregates: aggregates } as RunDynamicViewInput;
134
+
135
+ await new Probe().RunDynamic(input, fakeProvider(captured));
136
+
137
+ expect(captured.params?.Aggregates).toEqual(aggregates);
138
+ });
139
+
140
+ it('RunViews returns a failure result, and never reaches the provider, for a stacked aggregate', async () => {
141
+ const captured: Captured = { params: null };
142
+ const provider = fakeProvider(captured);
143
+ const input = [{ EntityName: ENTITY_NAME, Aggregates: [{ expression: STACKED_AGGREGATE }] }] as Parameters<RunViewResolver['RunViews']>[0];
144
+
145
+ const results = await new RunViewResolver().RunViews(
146
+ input,
147
+ { providers: [{ type: 'Read-Only', provider }] as unknown as AppContext['providers'], userPayload: fakePayload() } as AppContext,
148
+ undefined as unknown as PubSubEngine,
149
+ );
150
+
151
+ expect(results).toHaveLength(1);
152
+ expect(results[0].Success).toBe(false);
153
+ expect(results[0].ErrorMessage).toMatch(/Invalid Aggregate: multiple statements/);
154
+ expect(captured.params).toBeNull();
155
+ });
156
+
157
+ it('RunViews does not fail the batch because one aggregate cannot be parsed', async () => {
158
+ const captured: Captured = { params: null };
159
+ const provider = fakeProvider(captured);
160
+ const input = [
161
+ { EntityName: ENTITY_NAME, Aggregates: [{ expression: 'COUNT_BIG(*)' }] },
162
+ { EntityName: ENTITY_NAME },
163
+ ] as Parameters<RunViewResolver['RunViews']>[0];
164
+
165
+ const results = await new RunViewResolver().RunViews(
166
+ input,
167
+ { providers: [{ type: 'Read-Only', provider }] as unknown as AppContext['providers'], userPayload: fakePayload() } as AppContext,
168
+ undefined as unknown as PubSubEngine,
169
+ );
170
+
171
+ expect(results.map((r) => r.Success)).toEqual([true, true]);
172
+ expect(captured.params?.Aggregates).toEqual([{ expression: 'COUNT_BIG(*)' }]);
173
+ });
174
+ });
@@ -1,7 +1,7 @@
1
1
  // Reflect.metadata polyfill at import time.
2
2
  import 'reflect-metadata';
3
3
  import { describe, it, expect } from 'vitest';
4
- import { EntityFieldTSType, type EntityFieldInfo, type EntityInfo } from '@memberjunction/core';
4
+ import { EntityFieldTSType, EntityInfo, UserInfo, type EntityFieldInfo } from '@memberjunction/core';
5
5
  import { ResolverBase } from '../generic/ResolverBase.js';
6
6
 
7
7
  /**
@@ -13,8 +13,11 @@ class Probe extends ResolverBase {
13
13
  public Convert(field: Partial<EntityFieldInfo>, raw: unknown) {
14
14
  return this.ClientOldValueToFieldValue(field as EntityFieldInfo, raw);
15
15
  }
16
- public MustLoad(entity: Partial<EntityInfo>, oldValues: boolean) {
17
- return this.MustLoadTruthFromDatabase(entity as EntityInfo, { OldValues___: oldValues ? [{ Key: 'Comments', Value: null }] : undefined }, false, false);
16
+ public MustLoad(entity: Record<string, unknown>, oldValues: boolean) {
17
+ // A real entity with no permission rows and a user with no roles: no row filter applies.
18
+ // The Update RLS term itself is covered in resolverBase.fls.test.ts.
19
+ const user = new UserInfo(null, { ID: 'u-1', Name: 'Probe', Email: 'probe@example.test', IsActive: true, UserRoles: [] });
20
+ return this.MustLoadTruthFromDatabase(new EntityInfo({ Fields: [], ...entity }), { OldValues___: oldValues ? [{ Key: 'Comments', Value: null }] : undefined }, false, false, user);
18
21
  }
19
22
  }
20
23
  const probe = new Probe();
@@ -33,7 +33,13 @@ type Captured = { params: RunViewParams | null };
33
33
  * its params. `Sequence` stands in for a numeric field (`NeedsQuotes` is false only for
34
34
  * Number and Boolean TS types).
35
35
  */
36
- function fakeProvider(captured: Captured, rows: Record<string, unknown>[] = []): DatabaseProviderBase {
36
+ function fakeProvider(captured: Captured, rows: Record<string, unknown>[] = [], unreadableEntities: string[] = []): DatabaseProviderBase {
37
+ const withPerms = (e: Record<string, unknown>) => ({
38
+ ...e,
39
+ // Real EntityInfo aggregates per-user permissions; the boundary screen now consults
40
+ // CanRead for every subquery target, so the mock must answer it.
41
+ GetUserPermisions: () => ({ CanRead: !unreadableEntities.includes(e.Name as string) }),
42
+ });
37
43
  return {
38
44
  Entities: [
39
45
  {
@@ -67,7 +73,7 @@ function fakeProvider(captured: Captured, rows: Record<string, unknown>[] = []):
67
73
  { Name: 'Committees: Motions', SchemaName: '__mj_BizAppsCommittees', BaseView: 'vwMotions', BaseTable: 'Motion', Fields: [] },
68
74
  { Name: 'Committees: Memberships', SchemaName: '__mj_BizAppsCommittees', BaseView: 'vwMemberships', BaseTable: 'Membership', Fields: [] },
69
75
  { Name: 'Committees: Terms', SchemaName: '__mj_BizAppsCommittees', BaseView: 'vwTerms', BaseTable: 'Term', Fields: [] },
70
- ],
76
+ ].map(withPerms),
71
77
  RunView: async (params: RunViewParams): Promise<RunViewResult> => {
72
78
  captured.params = params;
73
79
  return { Success: true, Results: rows, RowCount: rows.length, TotalRowCount: rows.length, ErrorMessage: '' } as RunViewResult;
@@ -246,6 +252,19 @@ describe('ResolverBase — GraphQL-boundary ExtraFilter AST screen', () => {
246
252
  expect(captured.params?.ExtraFilter).toBe(`ID IN (SELECT ID FROM [__mj].[vwUsers] WHERE Email = 'a@b.com')`);
247
253
  });
248
254
 
255
+ it('RunDynamicViewGeneric rejects a subquery into an entity the caller cannot read', async () => {
256
+ const captured: Captured = { params: null };
257
+ const input = {
258
+ EntityName: ENTITY_NAME,
259
+ ExtraFilter: `ID IN (SELECT TaskID FROM [__mj_BizAppsTasks].[vwTaskAssignments])`,
260
+ } as RunDynamicViewInput;
261
+
262
+ await expect(
263
+ new Probe().RunDynamic(input, fakeProvider(captured, [], ['MJ_BizApps_Tasks: Task Assignments']))
264
+ ).rejects.toThrow(/read permission/);
265
+ expect(captured.params).toBeNull();
266
+ });
267
+
249
268
  it('RunDynamicViewGeneric passes a benign ExtraFilter through to RunView', async () => {
250
269
  const captured: Captured = { params: null };
251
270
  const input = {