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

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 (560) hide show
  1. package/README.md +47 -1
  2. package/dist/.tsbuildinfo +1 -1
  3. package/dist/agentSessions/SessionJanitor.d.ts +91 -30
  4. package/dist/agentSessions/SessionJanitor.d.ts.map +1 -1
  5. package/dist/agentSessions/SessionJanitor.js +311 -81
  6. package/dist/agentSessions/SessionJanitor.js.map +1 -1
  7. package/dist/agentSessions/SessionManager.d.ts +17 -3
  8. package/dist/agentSessions/SessionManager.d.ts.map +1 -1
  9. package/dist/agentSessions/SessionManager.js +44 -10
  10. package/dist/agentSessions/SessionManager.js.map +1 -1
  11. package/dist/apiRoot.d.ts +32 -0
  12. package/dist/apiRoot.d.ts.map +1 -0
  13. package/dist/apiRoot.js +67 -0
  14. package/dist/apiRoot.js.map +1 -0
  15. package/dist/auth/AuthProviderCatalogRouter.d.ts.map +1 -1
  16. package/dist/auth/AuthProviderCatalogRouter.js +27 -0
  17. package/dist/auth/AuthProviderCatalogRouter.js.map +1 -1
  18. package/dist/auth/LoginScreenEngine.d.ts +22 -0
  19. package/dist/auth/LoginScreenEngine.d.ts.map +1 -0
  20. package/dist/auth/LoginScreenEngine.js +57 -0
  21. package/dist/auth/LoginScreenEngine.js.map +1 -0
  22. package/dist/auth/index.d.ts +1 -0
  23. package/dist/auth/index.d.ts.map +1 -1
  24. package/dist/auth/index.js +52 -10
  25. package/dist/auth/index.js.map +1 -1
  26. package/dist/auth/initializeProviders.d.ts +10 -0
  27. package/dist/auth/initializeProviders.d.ts.map +1 -1
  28. package/dist/auth/initializeProviders.js +37 -1
  29. package/dist/auth/initializeProviders.js.map +1 -1
  30. package/dist/auth/localDevAuth.d.ts +79 -0
  31. package/dist/auth/localDevAuth.d.ts.map +1 -0
  32. package/dist/auth/localDevAuth.js +439 -0
  33. package/dist/auth/localDevAuth.js.map +1 -0
  34. package/dist/auth/magicLink/MagicLinkKeys.d.ts +2 -2
  35. package/dist/auth/magicLink/MagicLinkKeys.d.ts.map +1 -1
  36. package/dist/auth/magicLink/MagicLinkKeys.js +1 -1
  37. package/dist/auth/magicLink/MagicLinkKeys.js.map +1 -1
  38. package/dist/auth/magicLink/MagicLinkRouter.d.ts.map +1 -1
  39. package/dist/auth/magicLink/MagicLinkRouter.js +3 -2
  40. package/dist/auth/magicLink/MagicLinkRouter.js.map +1 -1
  41. package/dist/auth/magicLink/types.d.ts +12 -0
  42. package/dist/auth/magicLink/types.d.ts.map +1 -1
  43. package/dist/auth/sessionUserClone.d.ts +1 -1
  44. package/dist/auth/sessionUserClone.d.ts.map +1 -1
  45. package/dist/auth/sessionUserClone.js +3 -2
  46. package/dist/auth/sessionUserClone.js.map +1 -1
  47. package/dist/broadcast/BaseCacheInvalidationBroadcastProvider.d.ts +292 -0
  48. package/dist/broadcast/BaseCacheInvalidationBroadcastProvider.d.ts.map +1 -0
  49. package/dist/broadcast/BaseCacheInvalidationBroadcastProvider.js +442 -0
  50. package/dist/broadcast/BaseCacheInvalidationBroadcastProvider.js.map +1 -0
  51. package/dist/broadcast/index.d.ts +6 -0
  52. package/dist/broadcast/index.d.ts.map +1 -0
  53. package/dist/broadcast/index.js +6 -0
  54. package/dist/broadcast/index.js.map +1 -0
  55. package/dist/broadcast/providers/AWSIoTCoreBroadcastProvider.d.ts +43 -0
  56. package/dist/broadcast/providers/AWSIoTCoreBroadcastProvider.d.ts.map +1 -0
  57. package/dist/broadcast/providers/AWSIoTCoreBroadcastProvider.js +144 -0
  58. package/dist/broadcast/providers/AWSIoTCoreBroadcastProvider.js.map +1 -0
  59. package/dist/broadcast/providers/AzureWebPubSubBroadcastProvider.d.ts +46 -0
  60. package/dist/broadcast/providers/AzureWebPubSubBroadcastProvider.d.ts.map +1 -0
  61. package/dist/broadcast/providers/AzureWebPubSubBroadcastProvider.js +149 -0
  62. package/dist/broadcast/providers/AzureWebPubSubBroadcastProvider.js.map +1 -0
  63. package/dist/broadcast/providers/GCPPubSubBroadcastProvider.d.ts +49 -0
  64. package/dist/broadcast/providers/GCPPubSubBroadcastProvider.d.ts.map +1 -0
  65. package/dist/broadcast/providers/GCPPubSubBroadcastProvider.js +131 -0
  66. package/dist/broadcast/providers/GCPPubSubBroadcastProvider.js.map +1 -0
  67. package/dist/broadcast/providers/SequencedRoleBucketBroadcastProvider.d.ts +24 -0
  68. package/dist/broadcast/providers/SequencedRoleBucketBroadcastProvider.d.ts.map +1 -0
  69. package/dist/broadcast/providers/SequencedRoleBucketBroadcastProvider.js +51 -0
  70. package/dist/broadcast/providers/SequencedRoleBucketBroadcastProvider.js.map +1 -0
  71. package/dist/cache.d.ts +3 -2
  72. package/dist/cache.d.ts.map +1 -1
  73. package/dist/cache.js.map +1 -1
  74. package/dist/config.d.ts +632 -22
  75. package/dist/config.d.ts.map +1 -1
  76. package/dist/config.js +104 -4
  77. package/dist/config.js.map +1 -1
  78. package/dist/context.d.ts +3 -1
  79. package/dist/context.d.ts.map +1 -1
  80. package/dist/context.js +29 -8
  81. package/dist/context.js.map +1 -1
  82. package/dist/coreRoutes.d.ts +29 -0
  83. package/dist/coreRoutes.d.ts.map +1 -0
  84. package/dist/coreRoutes.js +156 -0
  85. package/dist/coreRoutes.js.map +1 -0
  86. package/dist/generated/graphql-schemas/__mj.d.ts +1016 -29
  87. package/dist/generated/graphql-schemas/__mj.d.ts.map +1 -1
  88. package/dist/generated/graphql-schemas/__mj.js +5125 -434
  89. package/dist/generated/graphql-schemas/__mj.js.map +1 -1
  90. package/dist/generic/CacheInvalidationResolver.d.ts +85 -30
  91. package/dist/generic/CacheInvalidationResolver.d.ts.map +1 -1
  92. package/dist/generic/CacheInvalidationResolver.js +175 -60
  93. package/dist/generic/CacheInvalidationResolver.js.map +1 -1
  94. package/dist/generic/OrphanedConversationDetailReconciler.d.ts.map +1 -1
  95. package/dist/generic/OrphanedConversationDetailReconciler.js +2 -0
  96. package/dist/generic/OrphanedConversationDetailReconciler.js.map +1 -1
  97. package/dist/generic/PubSubManager.d.ts +10 -0
  98. package/dist/generic/PubSubManager.d.ts.map +1 -1
  99. package/dist/generic/PubSubManager.js +44 -1
  100. package/dist/generic/PubSubManager.js.map +1 -1
  101. package/dist/generic/PushStatusResolver.d.ts +39 -6
  102. package/dist/generic/PushStatusResolver.d.ts.map +1 -1
  103. package/dist/generic/PushStatusResolver.js +94 -12
  104. package/dist/generic/PushStatusResolver.js.map +1 -1
  105. package/dist/generic/ResolverBase.d.ts +168 -69
  106. package/dist/generic/ResolverBase.d.ts.map +1 -1
  107. package/dist/generic/ResolverBase.js +464 -405
  108. package/dist/generic/ResolverBase.js.map +1 -1
  109. package/dist/generic/RunViewResolver.d.ts +11 -3
  110. package/dist/generic/RunViewResolver.d.ts.map +1 -1
  111. package/dist/generic/RunViewResolver.js +91 -13
  112. package/dist/generic/RunViewResolver.js.map +1 -1
  113. package/dist/generic/actionOutputFieldSecurity.d.ts +26 -0
  114. package/dist/generic/actionOutputFieldSecurity.d.ts.map +1 -0
  115. package/dist/generic/actionOutputFieldSecurity.js +47 -0
  116. package/dist/generic/actionOutputFieldSecurity.js.map +1 -0
  117. package/dist/generic/fieldSecurityTransport.d.ts +30 -0
  118. package/dist/generic/fieldSecurityTransport.d.ts.map +1 -0
  119. package/dist/generic/fieldSecurityTransport.js +30 -0
  120. package/dist/generic/fieldSecurityTransport.js.map +1 -0
  121. package/dist/globalCors.d.ts +33 -0
  122. package/dist/globalCors.d.ts.map +1 -0
  123. package/dist/globalCors.js +58 -0
  124. package/dist/globalCors.js.map +1 -0
  125. package/dist/index.d.ts +14 -0
  126. package/dist/index.d.ts.map +1 -1
  127. package/dist/index.js +230 -222
  128. package/dist/index.js.map +1 -1
  129. package/dist/integration/CustomColumnPromoter.d.ts.map +1 -1
  130. package/dist/integration/CustomColumnPromoter.js +6 -1
  131. package/dist/integration/CustomColumnPromoter.js.map +1 -1
  132. package/dist/logging/variablesLoggingMiddleware.d.ts +12 -0
  133. package/dist/logging/variablesLoggingMiddleware.d.ts.map +1 -1
  134. package/dist/logging/variablesLoggingMiddleware.js +50 -1
  135. package/dist/logging/variablesLoggingMiddleware.js.map +1 -1
  136. package/dist/multiTenancy/index.d.ts.map +1 -1
  137. package/dist/multiTenancy/index.js +22 -2
  138. package/dist/multiTenancy/index.js.map +1 -1
  139. package/dist/realtimeSessions/RealtimeSessionVerificationService.d.ts.map +1 -1
  140. package/dist/realtimeSessions/RealtimeSessionVerificationService.js +2 -1
  141. package/dist/realtimeSessions/RealtimeSessionVerificationService.js.map +1 -1
  142. package/dist/realtimeSessions/RealtimeVerifyRouter.d.ts.map +1 -1
  143. package/dist/realtimeSessions/RealtimeVerifyRouter.js +2 -1
  144. package/dist/realtimeSessions/RealtimeVerifyRouter.js.map +1 -1
  145. package/dist/realtimeSessions/RecordingAssemblyScheduler.d.ts +100 -0
  146. package/dist/realtimeSessions/RecordingAssemblyScheduler.d.ts.map +1 -0
  147. package/dist/realtimeSessions/RecordingAssemblyScheduler.js +173 -0
  148. package/dist/realtimeSessions/RecordingAssemblyScheduler.js.map +1 -0
  149. package/dist/realtimeSessions/SessionVerificationPortsBase.d.ts.map +1 -1
  150. package/dist/realtimeSessions/SessionVerificationPortsBase.js +1 -0
  151. package/dist/realtimeSessions/SessionVerificationPortsBase.js.map +1 -1
  152. package/dist/realtimeSessions/index.d.ts +2 -0
  153. package/dist/realtimeSessions/index.d.ts.map +1 -1
  154. package/dist/realtimeSessions/index.js +2 -0
  155. package/dist/realtimeSessions/index.js.map +1 -1
  156. package/dist/realtimeSessions/ownedRealtimeSession.d.ts +32 -0
  157. package/dist/realtimeSessions/ownedRealtimeSession.d.ts.map +1 -0
  158. package/dist/realtimeSessions/ownedRealtimeSession.js +56 -0
  159. package/dist/realtimeSessions/ownedRealtimeSession.js.map +1 -0
  160. package/dist/realtimeSessions/sessionAccess.d.ts +91 -8
  161. package/dist/realtimeSessions/sessionAccess.d.ts.map +1 -1
  162. package/dist/realtimeSessions/sessionAccess.js +127 -20
  163. package/dist/realtimeSessions/sessionAccess.js.map +1 -1
  164. package/dist/realtimeSessions/sessionConfigStamps.js +1 -1
  165. package/dist/realtimeSessions/sessionConfigStamps.js.map +1 -1
  166. package/dist/realtimeSessions/subscriptionAuthorization.d.ts.map +1 -1
  167. package/dist/realtimeSessions/subscriptionAuthorization.js +5 -2
  168. package/dist/realtimeSessions/subscriptionAuthorization.js.map +1 -1
  169. package/dist/realtimeSessions/verificationWorkflow.d.ts +2 -0
  170. package/dist/realtimeSessions/verificationWorkflow.d.ts.map +1 -1
  171. package/dist/realtimeSessions/verificationWorkflow.js +1 -1
  172. package/dist/realtimeSessions/verificationWorkflow.js.map +1 -1
  173. package/dist/realtimeWidget/WidgetRouter.d.ts.map +1 -1
  174. package/dist/realtimeWidget/WidgetRouter.js +19 -3
  175. package/dist/realtimeWidget/WidgetRouter.js.map +1 -1
  176. package/dist/realtimeWidget/WidgetSessionService.d.ts +67 -7
  177. package/dist/realtimeWidget/WidgetSessionService.d.ts.map +1 -1
  178. package/dist/realtimeWidget/WidgetSessionService.js +201 -19
  179. package/dist/realtimeWidget/WidgetSessionService.js.map +1 -1
  180. package/dist/realtimeWidget/host-identity.d.ts +1 -1
  181. package/dist/realtimeWidget/host-identity.js +1 -1
  182. package/dist/realtimeWidget/widgetCore.d.ts +20 -1
  183. package/dist/realtimeWidget/widgetCore.d.ts.map +1 -1
  184. package/dist/realtimeWidget/widgetCore.js +17 -1
  185. package/dist/realtimeWidget/widgetCore.js.map +1 -1
  186. package/dist/realtimeWidget/widgetGuestElevation.d.ts +1 -1
  187. package/dist/realtimeWidget/widgetGuestElevation.d.ts.map +1 -1
  188. package/dist/realtimeWidget/widgetGuestElevation.js +2 -1
  189. package/dist/realtimeWidget/widgetGuestElevation.js.map +1 -1
  190. package/dist/resolvers/ActionResolver.d.ts +19 -1
  191. package/dist/resolvers/ActionResolver.d.ts.map +1 -1
  192. package/dist/resolvers/ActionResolver.js +63 -12
  193. package/dist/resolvers/ActionResolver.js.map +1 -1
  194. package/dist/resolvers/AdhocQueryResolver.d.ts.map +1 -1
  195. package/dist/resolvers/AdhocQueryResolver.js +1 -0
  196. package/dist/resolvers/AdhocQueryResolver.js.map +1 -1
  197. package/dist/resolvers/AgentRunStatusPublisher.d.ts +18 -4
  198. package/dist/resolvers/AgentRunStatusPublisher.d.ts.map +1 -1
  199. package/dist/resolvers/AgentRunStatusPublisher.js +32 -8
  200. package/dist/resolvers/AgentRunStatusPublisher.js.map +1 -1
  201. package/dist/resolvers/AgentSessionResolver.d.ts +10 -4
  202. package/dist/resolvers/AgentSessionResolver.d.ts.map +1 -1
  203. package/dist/resolvers/AgentSessionResolver.js +40 -17
  204. package/dist/resolvers/AgentSessionResolver.js.map +1 -1
  205. package/dist/resolvers/ArtifactFileResolver.d.ts +5 -1
  206. package/dist/resolvers/ArtifactFileResolver.d.ts.map +1 -1
  207. package/dist/resolvers/ArtifactFileResolver.js +9 -7
  208. package/dist/resolvers/ArtifactFileResolver.js.map +1 -1
  209. package/dist/resolvers/ConversationRunEventsResolver.d.ts +8 -4
  210. package/dist/resolvers/ConversationRunEventsResolver.d.ts.map +1 -1
  211. package/dist/resolvers/ConversationRunEventsResolver.js +6 -6
  212. package/dist/resolvers/ConversationRunEventsResolver.js.map +1 -1
  213. package/dist/resolvers/DatasetResolver.d.ts +24 -3
  214. package/dist/resolvers/DatasetResolver.d.ts.map +1 -1
  215. package/dist/resolvers/DatasetResolver.js +57 -8
  216. package/dist/resolvers/DatasetResolver.js.map +1 -1
  217. package/dist/resolvers/EntityCommunicationsResolver.d.ts +13 -1
  218. package/dist/resolvers/EntityCommunicationsResolver.d.ts.map +1 -1
  219. package/dist/resolvers/EntityCommunicationsResolver.js +50 -14
  220. package/dist/resolvers/EntityCommunicationsResolver.js.map +1 -1
  221. package/dist/resolvers/EntityRecordNameResolver.d.ts +2 -1
  222. package/dist/resolvers/EntityRecordNameResolver.d.ts.map +1 -1
  223. package/dist/resolvers/EntityRecordNameResolver.js +3 -2
  224. package/dist/resolvers/EntityRecordNameResolver.js.map +1 -1
  225. package/dist/resolvers/FetchEntityVectorsResolver.d.ts +46 -3
  226. package/dist/resolvers/FetchEntityVectorsResolver.d.ts.map +1 -1
  227. package/dist/resolvers/FetchEntityVectorsResolver.js +211 -50
  228. package/dist/resolvers/FetchEntityVectorsResolver.js.map +1 -1
  229. package/dist/resolvers/FileCategoryResolver.d.ts.map +1 -1
  230. package/dist/resolvers/FileCategoryResolver.js +1 -0
  231. package/dist/resolvers/FileCategoryResolver.js.map +1 -1
  232. package/dist/resolvers/FileResolver.d.ts +87 -11
  233. package/dist/resolvers/FileResolver.d.ts.map +1 -1
  234. package/dist/resolvers/FileResolver.js +292 -105
  235. package/dist/resolvers/FileResolver.js.map +1 -1
  236. package/dist/resolvers/GetDataContextDataResolver.d.ts +33 -7
  237. package/dist/resolvers/GetDataContextDataResolver.d.ts.map +1 -1
  238. package/dist/resolvers/GetDataContextDataResolver.js +117 -38
  239. package/dist/resolvers/GetDataContextDataResolver.js.map +1 -1
  240. package/dist/resolvers/GetDataResolver.d.ts.map +1 -1
  241. package/dist/resolvers/GetDataResolver.js +6 -3
  242. package/dist/resolvers/GetDataResolver.js.map +1 -1
  243. package/dist/resolvers/IntegrationDiscoveryResolver.d.ts.map +1 -1
  244. package/dist/resolvers/IntegrationDiscoveryResolver.js +49 -9
  245. package/dist/resolvers/IntegrationDiscoveryResolver.js.map +1 -1
  246. package/dist/resolvers/MergeRecordsResolver.d.ts +8 -1
  247. package/dist/resolvers/MergeRecordsResolver.d.ts.map +1 -1
  248. package/dist/resolvers/MergeRecordsResolver.js +31 -3
  249. package/dist/resolvers/MergeRecordsResolver.js.map +1 -1
  250. package/dist/resolvers/PipelineProgressResolver.d.ts +6 -0
  251. package/dist/resolvers/PipelineProgressResolver.d.ts.map +1 -1
  252. package/dist/resolvers/PipelineProgressResolver.js +5 -3
  253. package/dist/resolvers/PipelineProgressResolver.js.map +1 -1
  254. package/dist/resolvers/QueryResolver.d.ts +2 -0
  255. package/dist/resolvers/QueryResolver.d.ts.map +1 -1
  256. package/dist/resolvers/QueryResolver.js +33 -6
  257. package/dist/resolvers/QueryResolver.js.map +1 -1
  258. package/dist/resolvers/QuerySystemUserResolver.d.ts +2 -0
  259. package/dist/resolvers/QuerySystemUserResolver.d.ts.map +1 -1
  260. package/dist/resolvers/QuerySystemUserResolver.js +15 -6
  261. package/dist/resolvers/QuerySystemUserResolver.js.map +1 -1
  262. package/dist/resolvers/RSUResolver.d.ts.map +1 -1
  263. package/dist/resolvers/RSUResolver.js +12 -0
  264. package/dist/resolvers/RSUResolver.js.map +1 -1
  265. package/dist/resolvers/RealtimeClientSessionResolver.d.ts +18 -10
  266. package/dist/resolvers/RealtimeClientSessionResolver.d.ts.map +1 -1
  267. package/dist/resolvers/RealtimeClientSessionResolver.js +92 -54
  268. package/dist/resolvers/RealtimeClientSessionResolver.js.map +1 -1
  269. package/dist/resolvers/RemoteBrowserActionResolver.d.ts.map +1 -1
  270. package/dist/resolvers/RemoteBrowserActionResolver.js +11 -9
  271. package/dist/resolvers/RemoteBrowserActionResolver.js.map +1 -1
  272. package/dist/resolvers/RunAIAgentResolver.d.ts +56 -8
  273. package/dist/resolvers/RunAIAgentResolver.d.ts.map +1 -1
  274. package/dist/resolvers/RunAIAgentResolver.js +241 -67
  275. package/dist/resolvers/RunAIAgentResolver.js.map +1 -1
  276. package/dist/resolvers/RunTemplateResolver.d.ts.map +1 -1
  277. package/dist/resolvers/RunTemplateResolver.js +4 -4
  278. package/dist/resolvers/RunTemplateResolver.js.map +1 -1
  279. package/dist/resolvers/RunTestResolver.d.ts.map +1 -1
  280. package/dist/resolvers/RunTestResolver.js +3 -1
  281. package/dist/resolvers/RunTestResolver.js.map +1 -1
  282. package/dist/resolvers/SearchKnowledgeStreamResolver.d.ts +12 -1
  283. package/dist/resolvers/SearchKnowledgeStreamResolver.d.ts.map +1 -1
  284. package/dist/resolvers/SearchKnowledgeStreamResolver.js +15 -2
  285. package/dist/resolvers/SearchKnowledgeStreamResolver.js.map +1 -1
  286. package/dist/resolvers/SqlLoggingConfigResolver.d.ts.map +1 -1
  287. package/dist/resolvers/SqlLoggingConfigResolver.js +2 -3
  288. package/dist/resolvers/SqlLoggingConfigResolver.js.map +1 -1
  289. package/dist/resolvers/SyncDataResolver.d.ts.map +1 -1
  290. package/dist/resolvers/SyncDataResolver.js +4 -0
  291. package/dist/resolvers/SyncDataResolver.js.map +1 -1
  292. package/dist/resolvers/TaskGraphFrameResolver.d.ts +2 -1
  293. package/dist/resolvers/TaskGraphFrameResolver.d.ts.map +1 -1
  294. package/dist/resolvers/TaskGraphFrameResolver.js +28 -1
  295. package/dist/resolvers/TaskGraphFrameResolver.js.map +1 -1
  296. package/dist/resolvers/TestQuerySQLResolver.d.ts +24 -0
  297. package/dist/resolvers/TestQuerySQLResolver.d.ts.map +1 -1
  298. package/dist/resolvers/TestQuerySQLResolver.js +66 -13
  299. package/dist/resolvers/TestQuerySQLResolver.js.map +1 -1
  300. package/dist/resolvers/TransactionGroupResolver.d.ts +34 -2
  301. package/dist/resolvers/TransactionGroupResolver.d.ts.map +1 -1
  302. package/dist/resolvers/TransactionGroupResolver.js +150 -27
  303. package/dist/resolvers/TransactionGroupResolver.js.map +1 -1
  304. package/dist/resolvers/UserViewResolver.d.ts +1 -1
  305. package/dist/resolvers/UserViewResolver.d.ts.map +1 -1
  306. package/dist/resolvers/UserViewResolver.js +2 -1
  307. package/dist/resolvers/UserViewResolver.js.map +1 -1
  308. package/dist/resolvers/agent-run-data-guard.d.ts +31 -0
  309. package/dist/resolvers/agent-run-data-guard.d.ts.map +1 -0
  310. package/dist/resolvers/agent-run-data-guard.js +72 -0
  311. package/dist/resolvers/agent-run-data-guard.js.map +1 -0
  312. package/dist/rest/EntityCRUDHandler.d.ts +6 -0
  313. package/dist/rest/EntityCRUDHandler.d.ts.map +1 -1
  314. package/dist/rest/EntityCRUDHandler.js +19 -5
  315. package/dist/rest/EntityCRUDHandler.js.map +1 -1
  316. package/dist/rest/RESTEndpointHandler.d.ts.map +1 -1
  317. package/dist/rest/RESTEndpointHandler.js +8 -5
  318. package/dist/rest/RESTEndpointHandler.js.map +1 -1
  319. package/dist/rest/RealtimeRecordingHandler.d.ts +49 -0
  320. package/dist/rest/RealtimeRecordingHandler.d.ts.map +1 -0
  321. package/dist/rest/RealtimeRecordingHandler.js +383 -0
  322. package/dist/rest/RealtimeRecordingHandler.js.map +1 -0
  323. package/dist/rest/ViewOperationsHandler.d.ts +9 -1
  324. package/dist/rest/ViewOperationsHandler.d.ts.map +1 -1
  325. package/dist/rest/ViewOperationsHandler.js +43 -17
  326. package/dist/rest/ViewOperationsHandler.js.map +1 -1
  327. package/dist/rest/setupRESTEndpoints.d.ts +6 -0
  328. package/dist/rest/setupRESTEndpoints.d.ts.map +1 -1
  329. package/dist/rest/setupRESTEndpoints.js +6 -0
  330. package/dist/rest/setupRESTEndpoints.js.map +1 -1
  331. package/dist/serverExtensionReservedRoots.d.ts +12 -10
  332. package/dist/serverExtensionReservedRoots.d.ts.map +1 -1
  333. package/dist/serverExtensionReservedRoots.js +14 -17
  334. package/dist/serverExtensionReservedRoots.js.map +1 -1
  335. package/dist/serverRoleConfigUnits.d.ts +29 -0
  336. package/dist/serverRoleConfigUnits.d.ts.map +1 -0
  337. package/dist/serverRoleConfigUnits.js +33 -0
  338. package/dist/serverRoleConfigUnits.js.map +1 -0
  339. package/dist/services/MJStorageBlobStore.d.ts +8 -1
  340. package/dist/services/MJStorageBlobStore.d.ts.map +1 -1
  341. package/dist/services/MJStorageBlobStore.js +25 -19
  342. package/dist/services/MJStorageBlobStore.js.map +1 -1
  343. package/dist/services/StartTaskGraphDispatcher.js +2 -2
  344. package/dist/services/StartTaskGraphDispatcher.js.map +1 -1
  345. package/dist/services/TaskGraphActionRunner.d.ts +5 -3
  346. package/dist/services/TaskGraphActionRunner.d.ts.map +1 -1
  347. package/dist/services/TaskGraphActionRunner.js +27 -5
  348. package/dist/services/TaskGraphActionRunner.js.map +1 -1
  349. package/dist/services/TaskGraphAgentRunner.d.ts +14 -2
  350. package/dist/services/TaskGraphAgentRunner.d.ts.map +1 -1
  351. package/dist/services/TaskGraphAgentRunner.js +157 -9
  352. package/dist/services/TaskGraphAgentRunner.js.map +1 -1
  353. package/dist/services/TaskGraphContinuationDeliverer.d.ts +59 -2
  354. package/dist/services/TaskGraphContinuationDeliverer.d.ts.map +1 -1
  355. package/dist/services/TaskGraphContinuationDeliverer.js +626 -16
  356. package/dist/services/TaskGraphContinuationDeliverer.js.map +1 -1
  357. package/dist/sharedCache.d.ts.map +1 -1
  358. package/dist/sharedCache.js +3 -0
  359. package/dist/sharedCache.js.map +1 -1
  360. package/dist/types.d.ts +23 -1
  361. package/dist/types.d.ts.map +1 -1
  362. package/dist/types.js.map +1 -1
  363. package/package.json +99 -98
  364. package/src/__tests__/ActionResolver.fieldSecurity.test.ts +237 -0
  365. package/src/__tests__/AdhocQueryResolver.dialect.test.ts +137 -0
  366. package/src/__tests__/AgentRunStatusPublisher.test.ts +49 -0
  367. package/src/__tests__/AgentSessionResolver.ownership.test.ts +137 -0
  368. package/src/__tests__/CustomColumnPromoterOnePass.test.ts +15 -1
  369. package/src/__tests__/DatasetResolver.itemFilters.test.ts +201 -0
  370. package/src/__tests__/EntityCommunicationsResolver.actingUser.test.ts +151 -0
  371. package/src/__tests__/EntityRecordNameResolver.rowSecurity.test.ts +152 -0
  372. package/src/__tests__/EvolutionActsOnWhatItDetects.test.ts +113 -0
  373. package/src/__tests__/FetchEntityVectorsResolver.test.ts +369 -18
  374. package/src/__tests__/FileResolver.fieldSecurity.test.ts +82 -0
  375. package/src/__tests__/FileResolver.storageAccess.test.ts +562 -0
  376. package/src/__tests__/GetDataContextDataResolver.sqlItems.test.ts +416 -0
  377. package/src/__tests__/ListOperationsResolver.adhocScreen.test.ts +137 -0
  378. package/src/__tests__/LoginScreenEngine.test.ts +86 -0
  379. package/src/__tests__/MJStorageBlobStore.storageGate.test.ts +63 -0
  380. package/src/__tests__/PipelineProgressResolver.filter.test.ts +58 -0
  381. package/src/__tests__/QuerySystemUserResolver.fieldSecurity.test.ts +49 -0
  382. package/src/__tests__/RESTEndpointHandler.test.ts +25 -0
  383. package/src/__tests__/RealtimeClientSessionResolver.configTamper.test.ts +23 -3
  384. package/src/__tests__/RealtimeClientSessionResolver.test.ts +324 -46
  385. package/src/__tests__/RecordingAssemblyScheduler.test.ts +178 -0
  386. package/src/__tests__/RemoteBrowserActionResolver.ownership.test.ts +98 -0
  387. package/src/__tests__/ResolverBase.adhocReadScope.test.ts +133 -2
  388. package/src/__tests__/ResolverBase.balancedClause.test.ts +155 -0
  389. package/src/__tests__/ResolverBase.clauseUnscopedRead.test.ts +127 -0
  390. package/src/__tests__/ResolverBase.clientOldValues.test.ts +243 -7
  391. package/src/__tests__/ResolverBase.deleteReturnProjection.test.ts +52 -0
  392. package/src/__tests__/ResolverBase.entityMessageRelay.test.ts +176 -0
  393. package/src/__tests__/ResolverBase.filterEscaping.test.ts +5 -2
  394. package/src/__tests__/ResolverBase.includeBinaryFields.test.ts +23 -1
  395. package/src/__tests__/ResolverBase.publishStatusUpdate.test.ts +68 -0
  396. package/src/__tests__/ResolverBase.userViewCacheAndDedup.test.ts +130 -0
  397. package/src/__tests__/RunAIAgentResolver.branch.test.ts +69 -13
  398. package/src/__tests__/RunAIAgentResolver.conversationWriter.test.ts +210 -0
  399. package/src/__tests__/RunAIAgentResolver.fireAndForgetCompletion.test.ts +209 -0
  400. package/src/__tests__/RunAIAgentResolver.historyFrom.test.ts +20 -28
  401. package/src/__tests__/RunAIAgentResolver.runDataGuard.test.ts +264 -0
  402. package/src/__tests__/RunAIAgentResolver.serverReply.test.ts +358 -0
  403. package/src/__tests__/RunAIAgentResolver.stoppedPredecessor.test.ts +4 -1
  404. package/src/__tests__/RunTemplateResolver.test.ts +35 -2
  405. package/src/__tests__/RunViewResolver.cacheCheckForwarding.test.ts +11 -0
  406. package/src/__tests__/RunViewResolver.cacheCheckScreen.test.ts +165 -0
  407. package/src/__tests__/SearchKnowledgeStreamResolver.test.ts +161 -0
  408. package/src/__tests__/SessionJanitor.test.ts +365 -10
  409. package/src/__tests__/SessionManager.test.ts +122 -0
  410. package/src/__tests__/TaskGraphActionRunner.durablePayload.test.ts +25 -0
  411. package/src/__tests__/TaskGraphAgentRunner.test.ts +266 -0
  412. package/src/__tests__/TaskGraphContinuationDeliverer.test.ts +468 -23
  413. package/src/__tests__/TaskGraphFrameResolver.test.ts +22 -0
  414. package/src/__tests__/TestQuerySQLResolver.test.ts +274 -0
  415. package/src/__tests__/TransactionGroupResolver.fieldSecurity.test.ts +199 -0
  416. package/src/__tests__/TransactionGroupResolver.refusals.test.ts +98 -1
  417. package/src/__tests__/UpdateMJDataContextItem.oldValues.test.ts +220 -0
  418. package/src/__tests__/UserViewResolver.updateWhereClause.test.ts +15 -0
  419. package/src/__tests__/ViewOperationsHandler.clauseScreen.test.ts +157 -0
  420. package/src/__tests__/ViewOperationsHandler.test.ts +7 -2
  421. package/src/__tests__/WidgetSessionService.test.ts +583 -4
  422. package/src/__tests__/agent-run-data-guard.test.ts +130 -0
  423. package/src/__tests__/apiRootBoot.test.ts +202 -0
  424. package/src/__tests__/buildMagicLinkSessionUser.test.ts +96 -0
  425. package/src/__tests__/cacheBroadcastProviders.test.ts +403 -0
  426. package/src/__tests__/cacheInvalidationRecordData.test.ts +109 -0
  427. package/src/__tests__/conversationRunEvents.test.ts +56 -14
  428. package/src/__tests__/fieldSecurityFixtures.ts +91 -0
  429. package/src/__tests__/globalCors.test.ts +217 -0
  430. package/src/__tests__/localDevAuth.test.ts +196 -0
  431. package/src/__tests__/multiTenancy.postgresRewrite.test.ts +181 -0
  432. package/src/__tests__/multiTenancy.security.test.ts +51 -0
  433. package/src/__tests__/multiTenancy.test.ts +1 -1
  434. package/src/__tests__/orphanedConversationDetailReconciler.test.ts +10 -0
  435. package/src/__tests__/ownedRealtimeSession.test.ts +39 -0
  436. package/src/__tests__/pushStatusResolver.filter.test.ts +64 -1
  437. package/src/__tests__/realtimeRecordingHandler.test.ts +669 -0
  438. package/src/__tests__/realtimeSessions/RealtimeSessionEventsResolver.test.ts +29 -1
  439. package/src/__tests__/realtimeSessions/ownedRealtimeSession.anonymous.test.ts +31 -0
  440. package/src/__tests__/realtimeSessions/ownedRealtimeSession.namedScoped.test.ts +37 -0
  441. package/src/__tests__/realtimeSessions/sessionAccess.test.ts +146 -7
  442. package/src/__tests__/realtimeSessions/sessionConfigStamps.test.ts +1 -1
  443. package/src/__tests__/realtimeSessions/verificationWorkflow.test.ts +25 -4
  444. package/src/__tests__/resolverBase.cloudEventChangedFields.test.ts +157 -0
  445. package/src/__tests__/resolverBase.fieldSecurityTransport.test.ts +222 -0
  446. package/src/__tests__/resolverBase.fls.test.ts +146 -97
  447. package/src/__tests__/restTransport.fieldSecurity.test.ts +148 -0
  448. package/src/__tests__/runParamsTimeZone.test.ts +179 -0
  449. package/src/__tests__/runStreams.fieldSecurity.test.ts +100 -0
  450. package/src/__tests__/runViewProperties.test.ts +114 -0
  451. package/src/__tests__/scalabilityBenchmarks.test.ts +765 -0
  452. package/src/__tests__/scalabilityDatabaseIntegration.test.ts +207 -0
  453. package/src/__tests__/serverExtensionHostOverlay.test.ts +66 -0
  454. package/src/__tests__/serverExtensionReservedRoots.test.ts +11 -98
  455. package/src/__tests__/sessionUserClone.test.ts +5 -1
  456. package/src/__tests__/sharedCache.test.ts +5 -3
  457. package/src/__tests__/startupSweepOrder.test.ts +40 -0
  458. package/src/__tests__/unifiedAuth.test.ts +28 -0
  459. package/src/__tests__/widgetGuestElevation.test.ts +2 -0
  460. package/src/agentSessions/SessionJanitor.ts +369 -93
  461. package/src/agentSessions/SessionManager.ts +45 -10
  462. package/src/apiRoot.ts +78 -0
  463. package/src/auth/AuthProviderCatalogRouter.ts +29 -0
  464. package/src/auth/LoginScreenEngine.ts +50 -0
  465. package/src/auth/index.ts +57 -12
  466. package/src/auth/initializeProviders.ts +43 -1
  467. package/src/auth/localDevAuth.ts +528 -0
  468. package/src/auth/magicLink/MagicLinkKeys.ts +2 -2
  469. package/src/auth/magicLink/MagicLinkRouter.ts +3 -2
  470. package/src/auth/magicLink/types.ts +10 -0
  471. package/src/auth/sessionUserClone.ts +3 -2
  472. package/src/broadcast/BaseCacheInvalidationBroadcastProvider.ts +674 -0
  473. package/src/broadcast/index.ts +5 -0
  474. package/src/broadcast/providers/AWSIoTCoreBroadcastProvider.ts +180 -0
  475. package/src/broadcast/providers/AzureWebPubSubBroadcastProvider.ts +182 -0
  476. package/src/broadcast/providers/GCPPubSubBroadcastProvider.ts +169 -0
  477. package/src/broadcast/providers/SequencedRoleBucketBroadcastProvider.ts +57 -0
  478. package/src/cache.ts +2 -1
  479. package/src/config.ts +115 -5
  480. package/src/context.ts +32 -8
  481. package/src/coreRoutes.ts +181 -0
  482. package/src/generated/graphql-schemas/__mj.ts +3684 -284
  483. package/src/generic/CacheInvalidationResolver.ts +172 -60
  484. package/src/generic/OrphanedConversationDetailReconciler.ts +4 -2
  485. package/src/generic/PubSubManager.ts +54 -1
  486. package/src/generic/PushStatusResolver.ts +109 -12
  487. package/src/generic/ResolverBase.ts +540 -431
  488. package/src/generic/RunViewResolver.ts +97 -15
  489. package/src/generic/actionOutputFieldSecurity.ts +75 -0
  490. package/src/generic/fieldSecurityTransport.ts +62 -0
  491. package/src/globalCors.ts +71 -0
  492. package/src/index.ts +239 -238
  493. package/src/integration/CustomColumnPromoter.ts +6 -1
  494. package/src/logging/variablesLoggingMiddleware.ts +59 -3
  495. package/src/multiTenancy/index.ts +24 -2
  496. package/src/realtimeSessions/README.md +7 -3
  497. package/src/realtimeSessions/RealtimeSessionVerificationService.ts +2 -1
  498. package/src/realtimeSessions/RealtimeVerifyRouter.ts +2 -1
  499. package/src/realtimeSessions/RecordingAssemblyScheduler.ts +234 -0
  500. package/src/realtimeSessions/SessionVerificationPortsBase.ts +1 -0
  501. package/src/realtimeSessions/index.ts +2 -0
  502. package/src/realtimeSessions/ownedRealtimeSession.ts +69 -0
  503. package/src/realtimeSessions/sessionAccess.ts +183 -23
  504. package/src/realtimeSessions/sessionConfigStamps.ts +1 -1
  505. package/src/realtimeSessions/subscriptionAuthorization.ts +4 -2
  506. package/src/realtimeSessions/verificationWorkflow.ts +3 -1
  507. package/src/realtimeWidget/WidgetRouter.ts +22 -4
  508. package/src/realtimeWidget/WidgetSessionService.ts +252 -17
  509. package/src/realtimeWidget/host-identity.ts +1 -1
  510. package/src/realtimeWidget/widgetCore.ts +30 -1
  511. package/src/realtimeWidget/widgetGuestElevation.ts +2 -1
  512. package/src/resolvers/ActionResolver.ts +78 -15
  513. package/src/resolvers/AdhocQueryResolver.ts +1 -0
  514. package/src/resolvers/AgentRunStatusPublisher.ts +36 -8
  515. package/src/resolvers/AgentSessionResolver.ts +46 -18
  516. package/src/resolvers/ArtifactFileResolver.ts +9 -8
  517. package/src/resolvers/ConversationRunEventsResolver.ts +15 -11
  518. package/src/resolvers/DatasetResolver.ts +70 -10
  519. package/src/resolvers/EntityCommunicationsResolver.ts +56 -15
  520. package/src/resolvers/EntityRecordNameResolver.ts +3 -2
  521. package/src/resolvers/FetchEntityVectorsResolver.ts +248 -55
  522. package/src/resolvers/FileCategoryResolver.ts +1 -0
  523. package/src/resolvers/FileResolver.ts +334 -109
  524. package/src/resolvers/GetDataContextDataResolver.ts +139 -41
  525. package/src/resolvers/GetDataResolver.ts +6 -3
  526. package/src/resolvers/IntegrationDiscoveryResolver.ts +53 -9
  527. package/src/resolvers/MergeRecordsResolver.ts +23 -2
  528. package/src/resolvers/PipelineProgressResolver.ts +12 -3
  529. package/src/resolvers/QueryResolver.ts +31 -6
  530. package/src/resolvers/QuerySystemUserResolver.ts +15 -6
  531. package/src/resolvers/RSUResolver.ts +12 -0
  532. package/src/resolvers/RealtimeClientSessionResolver.ts +114 -66
  533. package/src/resolvers/RemoteBrowserActionResolver.ts +15 -10
  534. package/src/resolvers/RunAIAgentResolver.ts +296 -85
  535. package/src/resolvers/RunTemplateResolver.ts +6 -4
  536. package/src/resolvers/RunTestResolver.ts +3 -1
  537. package/src/resolvers/SearchKnowledgeStreamResolver.ts +19 -3
  538. package/src/resolvers/SqlLoggingConfigResolver.ts +2 -4
  539. package/src/resolvers/SyncDataResolver.ts +4 -0
  540. package/src/resolvers/TaskGraphFrameResolver.ts +33 -1
  541. package/src/resolvers/TestQuerySQLResolver.ts +69 -13
  542. package/src/resolvers/TransactionGroupResolver.ts +185 -30
  543. package/src/resolvers/UserViewResolver.ts +2 -1
  544. package/src/resolvers/__tests__/GetDataResolver.validation.test.ts +95 -0
  545. package/src/resolvers/__tests__/MergeRecordsResolver.test.ts +43 -0
  546. package/src/resolvers/agent-run-data-guard.ts +89 -0
  547. package/src/rest/EntityCRUDHandler.ts +22 -5
  548. package/src/rest/RESTEndpointHandler.ts +8 -5
  549. package/src/rest/RealtimeRecordingHandler.ts +467 -0
  550. package/src/rest/ViewOperationsHandler.ts +52 -18
  551. package/src/rest/setupRESTEndpoints.ts +6 -0
  552. package/src/serverExtensionReservedRoots.ts +14 -17
  553. package/src/serverRoleConfigUnits.ts +48 -0
  554. package/src/services/MJStorageBlobStore.ts +31 -30
  555. package/src/services/StartTaskGraphDispatcher.ts +2 -2
  556. package/src/services/TaskGraphActionRunner.ts +25 -5
  557. package/src/services/TaskGraphAgentRunner.ts +188 -11
  558. package/src/services/TaskGraphContinuationDeliverer.ts +738 -16
  559. package/src/sharedCache.ts +3 -0
  560. package/src/types.ts +23 -1
@@ -0,0 +1,201 @@
1
+ // The resolvers carry type-graphql decorators, which need the Reflect.metadata polyfill at import time.
2
+ import 'reflect-metadata';
3
+ import { describe, it, expect, vi } from 'vitest';
4
+ import {
5
+ DatabaseProviderBase,
6
+ DatasetItemFilterType,
7
+ DatasetResultType,
8
+ DatasetStatusResultType,
9
+ EntityInfo,
10
+ UserInfo,
11
+ } from '@memberjunction/core';
12
+ import { ENCRYPTED_SENTINEL } from '@memberjunction/global';
13
+ import { DatasetResolverExtended, DatasetStatusResolver } from '../resolvers/DatasetResolver.js';
14
+ import type { AppContext, UserPayload } from '../types.js';
15
+
16
+ // The encrypted-field policy configures the encryption engine before it inspects a value; there is
17
+ // no key store in a unit test.
18
+ vi.mock('@memberjunction/encryption', () => ({
19
+ EncryptionEngine: {
20
+ Instance: {
21
+ Config: async () => undefined,
22
+ GetKeyByID: () => ({ Marker: '$ENC$' }),
23
+ Encrypt: async (value: string) => `$ENC$${value}`,
24
+ },
25
+ },
26
+ }));
27
+
28
+ /**
29
+ * The GraphQL dataset queries are the network edge of security finding C6.
30
+ *
31
+ * `GetDatasetByName` / `GetDatasetStatusByName` took `ItemFilters[].Filter` from any authenticated
32
+ * principal and handed it to the provider unscreened, and passed no context user, so the provider
33
+ * could neither refuse an unreadable item nor apply row-level security. These tests pin that the
34
+ * resolvers screen every ItemFilter with the same AST screen as ExtraFilter before the provider is
35
+ * reached, pass the session user down, and apply the API encryption policy to the rows they return.
36
+ */
37
+
38
+ const ROLE_ID = 'R0000000-0000-0000-0000-00000000000A';
39
+ const OTHER_ROLE_ID = 'R0000000-0000-0000-0000-00000000000C';
40
+
41
+ function entity(name: string, baseView: string, fields: Record<string, unknown>[] = []): EntityInfo {
42
+ const id = `E-${name}`;
43
+ return new EntityInfo({
44
+ ID: id, Name: name, SchemaName: '__mj', BaseTable: name, BaseView: baseView,
45
+ Permissions: [{ ID: `p-${name}`, EntityID: id, RoleID: ROLE_ID, CanRead: true, Type: 'Allow' }],
46
+ Fields: fields.map((f, i) => ({ ID: `${id}-f${i}`, EntityID: id, Entity: name, Sequence: i + 1, Type: 'nvarchar', ...f })),
47
+ });
48
+ }
49
+
50
+ const THINGS = entity('Things', 'vwThings', [{ Name: 'ID', IsPrimaryKey: true }, { Name: 'Name' }]);
51
+ const SECRETS = entity('Secrets', 'vwSecrets', [
52
+ { Name: 'ID', IsPrimaryKey: true },
53
+ { Name: 'Token', Encrypt: true, EncryptionKeyID: 'K1', AllowDecryptInAPI: false, SendEncryptedValue: false },
54
+ ]);
55
+
56
+ const USER = new UserInfo(undefined, {
57
+ ID: 'U1', Name: 'Reader', Email: 'reader@example.com', IsActive: true,
58
+ UserRoles: [{ UserID: 'U1', RoleID: ROLE_ID, Role: 'Reader' }],
59
+ });
60
+
61
+ type ProviderCall = {
62
+ Method: 'GetDatasetByName' | 'GetDatasetStatusByName';
63
+ DatasetName: string;
64
+ ItemFilters: DatasetItemFilterType[] | undefined;
65
+ ContextUser: UserInfo | undefined;
66
+ };
67
+
68
+ /** A provider double that records what reached it and answers with fixed rows. */
69
+ function fakeProvider(itemEntity: EntityInfo = THINGS, rows: Record<string, unknown>[] = []) {
70
+ const calls: ProviderCall[] = [];
71
+ const provider = {
72
+ Entities: [THINGS, SECRETS],
73
+ EntityByName: (name: string) => [THINGS, SECRETS].find(e => e.Name.toLowerCase() === name?.trim().toLowerCase()),
74
+ GetDatasetByName: async (DatasetName: string, ItemFilters?: DatasetItemFilterType[], ContextUser?: UserInfo): Promise<DatasetResultType> => {
75
+ calls.push({ Method: 'GetDatasetByName', DatasetName, ItemFilters, ContextUser });
76
+ return {
77
+ DatasetID: 'DS1', DatasetName, Success: true, Status: '', LatestUpdateDate: new Date(0),
78
+ Results: [{ Code: itemEntity.Name, EntityName: itemEntity.Name, EntityID: itemEntity.ID, Results: rows, Success: true }],
79
+ };
80
+ },
81
+ GetDatasetStatusByName: async (DatasetName: string, ItemFilters?: DatasetItemFilterType[], ContextUser?: UserInfo): Promise<DatasetStatusResultType> => {
82
+ calls.push({ Method: 'GetDatasetStatusByName', DatasetName, ItemFilters, ContextUser });
83
+ return { DatasetID: 'DS1', DatasetName, Success: true, Status: '', LatestUpdateDate: new Date(0), EntityUpdateDates: [] };
84
+ },
85
+ };
86
+ return { provider: provider as unknown as DatabaseProviderBase, calls };
87
+ }
88
+
89
+ function context(provider: DatabaseProviderBase, userPayload: Partial<UserPayload> = { email: USER.Email, userRecord: USER }): AppContext {
90
+ return {
91
+ providers: [{ provider, type: 'Read-Write' }],
92
+ userPayload: { sessionId: 'S1', ...userPayload } as UserPayload,
93
+ } as AppContext;
94
+ }
95
+
96
+ const STACKED = [{ ItemCode: 'Things', Filter: '1=0; SELECT 1' }];
97
+ // A quote inside a bracket identifier opens a fake literal for the string-literal stripper that
98
+ // guards the provider denylist; the AST screen tokenizes brackets and still sees the second statement.
99
+ const STACKED_BEHIND_BRACKET = [{ ItemCode: 'Things', Filter: "[a'] = 1; SELECT 1 --'" }];
100
+
101
+ describe('GetDatasetByName', () => {
102
+ it('refuses a stacked-statement ItemFilter before the provider is reached', async () => {
103
+ const { provider, calls } = fakeProvider();
104
+
105
+ await expect(new DatasetResolverExtended().GetDatasetByName('MJ_Metadata', context(provider), STACKED))
106
+ .rejects.toThrow(/multiple statements are not permitted/);
107
+ expect(calls).toEqual([]);
108
+ });
109
+
110
+ it('refuses a stacked statement hidden behind a bracket identifier', async () => {
111
+ const { provider, calls } = fakeProvider();
112
+
113
+ await expect(new DatasetResolverExtended().GetDatasetByName('MJ_Metadata', context(provider), STACKED_BEHIND_BRACKET))
114
+ .rejects.toThrow(/multiple statements are not permitted/);
115
+ expect(calls).toEqual([]);
116
+ });
117
+
118
+ it('refuses an ItemFilter whose subquery reads a table that is not an entity base view', async () => {
119
+ const { provider, calls } = fakeProvider();
120
+ const filters = [{ ItemCode: 'Things', Filter: 'ID IN (SELECT ID FROM __mj.[User])' }];
121
+
122
+ await expect(new DatasetResolverExtended().GetDatasetByName('MJ_Metadata', context(provider), filters))
123
+ .rejects.toThrow(/entity base view/);
124
+ expect(calls).toEqual([]);
125
+ });
126
+
127
+ it('passes the session user to the provider, with the screened filters', async () => {
128
+ const { provider, calls } = fakeProvider();
129
+ const filters = [{ ItemCode: 'Things', Filter: "Name = 'x'" }];
130
+
131
+ const result = await new DatasetResolverExtended().GetDatasetByName('Things_Dataset', context(provider), filters);
132
+
133
+ expect(result.Success).toBe(true);
134
+ expect(calls).toHaveLength(1);
135
+ expect(calls[0].ContextUser).toBe(USER);
136
+ expect(calls[0].ItemFilters).toEqual(filters);
137
+ });
138
+
139
+ it('refuses a session that resolves to no user rather than reading as nobody', async () => {
140
+ const { provider, calls } = fakeProvider();
141
+
142
+ await expect(new DatasetResolverExtended().GetDatasetByName('Things_Dataset', context(provider, { email: '', userRecord: null })))
143
+ .rejects.toThrow(/no user/i);
144
+ expect(calls).toEqual([]);
145
+ });
146
+
147
+ it('masks an encrypted field the entity does not allow decrypted over the API, without touching the provider rows', async () => {
148
+ // A context user makes the provider decrypt; the API policy decides what leaves the server.
149
+ const providerRow = Object.freeze({ ID: 'S1', Token: 'plain-secret' });
150
+ const { provider } = fakeProvider(SECRETS, [providerRow]);
151
+
152
+ const result = await new DatasetResolverExtended().GetDatasetByName('Secrets_Dataset', context(provider));
153
+
154
+ const items = JSON.parse(result.Results) as DatasetResultType['Results'];
155
+ expect(items[0].Results[0].Token).toBe(ENCRYPTED_SENTINEL);
156
+ expect(result.Results).not.toContain('plain-secret');
157
+ expect(providerRow.Token).toBe('plain-secret');
158
+ });
159
+ });
160
+
161
+ describe('GetDatasetStatusByName', () => {
162
+ it('refuses a stacked-statement ItemFilter before the provider is reached', async () => {
163
+ const { provider, calls } = fakeProvider();
164
+
165
+ await expect(new DatasetStatusResolver().GetDatasetStatusByName('MJ_Metadata', context(provider), STACKED))
166
+ .rejects.toThrow(/multiple statements are not permitted/);
167
+ expect(calls).toEqual([]);
168
+ });
169
+
170
+ it('passes the session user to the provider', async () => {
171
+ const { provider, calls } = fakeProvider();
172
+
173
+ await new DatasetStatusResolver().GetDatasetStatusByName('Things_Dataset', context(provider), [{ ItemCode: 'Things', Filter: "Name = 'x'" }]);
174
+
175
+ expect(calls).toHaveLength(1);
176
+ expect(calls[0].ContextUser).toBe(USER);
177
+ });
178
+
179
+ it('refuses an ItemFilter that reads an entity the user has no read permission on', async () => {
180
+ const { provider, calls } = fakeProvider();
181
+ const outsider = new UserInfo(undefined, {
182
+ ID: 'U2', Name: 'Outsider', Email: 'outsider@example.com', IsActive: true,
183
+ UserRoles: [{ UserID: 'U2', RoleID: OTHER_ROLE_ID, Role: 'Other' }],
184
+ });
185
+ const filters = [{ ItemCode: 'Things', Filter: 'ID IN (SELECT ID FROM __mj.vwSecrets)' }];
186
+
187
+ await expect(new DatasetStatusResolver().GetDatasetStatusByName('Things_Dataset', context(provider, { email: outsider.Email, userRecord: outsider }), filters))
188
+ .rejects.toThrow(/read permission/);
189
+ expect(calls).toEqual([]);
190
+ });
191
+ });
192
+
193
+ describe('GetMultipleDatasetStatusByName', () => {
194
+ it('passes the session user to the provider for every dataset', async () => {
195
+ const { provider, calls } = fakeProvider();
196
+
197
+ await new DatasetStatusResolver().GetMultipleDatasetStatusByName(['A', 'B'], context(provider));
198
+
199
+ expect(calls.map(c => c.ContextUser)).toEqual([USER, USER]);
200
+ });
201
+ });
@@ -0,0 +1,151 @@
1
+ // The resolver module pulls in type-graphql decorators, which need the Reflect.metadata polyfill.
2
+ import 'reflect-metadata';
3
+ import { describe, it, expect, vi, beforeEach } from 'vitest';
4
+ import type { DatabaseProviderBase, UserInfo } from '@memberjunction/core';
5
+ import type { EntityCommunicationParams } from '@memberjunction/entity-communications-base';
6
+
7
+ /**
8
+ * `RunEntityCommunicationByViewID` with `previewOnly` + `includeProcessedMessages` returns the rows
9
+ * it read. It must hand the engine the authenticated caller as the acting user, and screen the
10
+ * view's client-supplied clauses the way a RunView request is screened, because this entry point
11
+ * does not pass through the RunView resolvers.
12
+ */
13
+
14
+ const { captured } = vi.hoisted(() => ({ captured: { params: [] as EntityCommunicationParams[] } }));
15
+
16
+ // The resolver's input types declare fields without explicit GraphQL types; the test runner emits no
17
+ // decorator metadata, so the decorators are stubbed out.
18
+ vi.mock('type-graphql', () => {
19
+ const noopDecorator = () => () => undefined;
20
+ return {
21
+ Resolver: noopDecorator, Mutation: noopDecorator, Query: noopDecorator, Subscription: noopDecorator,
22
+ ObjectType: noopDecorator, InputType: noopDecorator, Field: noopDecorator, Arg: noopDecorator,
23
+ Args: noopDecorator, Ctx: noopDecorator, PubSub: noopDecorator, Root: noopDecorator,
24
+ Directive: noopDecorator, Authorized: noopDecorator, UseMiddleware: noopDecorator,
25
+ createUnionType: () => class {},
26
+ AuthorizationError: class AuthorizationError extends Error {},
27
+ Float: class {}, Int: class {}, ID: class {},
28
+ };
29
+ });
30
+
31
+ vi.mock('@memberjunction/entity-communications-server', () => ({
32
+ EntityCommunicationsEngine: {
33
+ Instance: {
34
+ Config: vi.fn(),
35
+ RunEntityCommunication: vi.fn(async (params: EntityCommunicationParams) => {
36
+ captured.params.push(params);
37
+ return { Success: true, Results: [] };
38
+ }),
39
+ },
40
+ },
41
+ }));
42
+
43
+ vi.mock('@memberjunction/templates', () => ({
44
+ TemplateEngineServer: { Instance: { Config: vi.fn(), FindTemplate: vi.fn() } },
45
+ }));
46
+
47
+ import { ReportResolver } from '../resolvers/EntityCommunicationsResolver.js';
48
+ import type { AppContext } from '../types.js';
49
+ import type { RunViewByIDInput } from '../generic/RunViewResolver.js';
50
+ import type { CommunicationMessageInput } from '../resolvers/EntityCommunicationsResolver.js';
51
+
52
+ const SENDER = { ID: 'sender', Email: 'sender@example.com' } as UserInfo;
53
+
54
+ /** Entity metadata the clause screen reads: one entity the sender can read, one it cannot. */
55
+ function fakeProvider(): DatabaseProviderBase {
56
+ const entity = (name: string, view: string, canRead: boolean) => ({
57
+ ID: name,
58
+ Name: name,
59
+ SchemaName: '__mj',
60
+ BaseView: view,
61
+ BaseTable: view.replace(/^vw/, ''),
62
+ Fields: [{ Name: 'ID', NeedsQuotes: true }, { Name: 'Email', NeedsQuotes: true }],
63
+ GetUserPermisions: () => ({ CanRead: canRead }),
64
+ GetEffectiveRowFilterWhereClause: () => '',
65
+ GetDeniedReadFields: () => new Set<string>(),
66
+ });
67
+ return {
68
+ Entities: [entity('Customers', 'vwCustomers', true), entity('Salaries', 'vwSalaries', false)],
69
+ GetEntityObject: async () => fakeView(),
70
+ } as unknown as DatabaseProviderBase;
71
+ }
72
+
73
+ /** Saved views by ID, as the caller can load them: `view-1` is a Customers view, `view-salaries` is not. */
74
+ const VIEWS: Record<string, { ID: string; EntityID: string }> = {
75
+ 'view-1': { ID: 'view-1', EntityID: 'Customers' },
76
+ 'view-salaries': { ID: 'view-salaries', EntityID: 'Salaries' },
77
+ };
78
+
79
+ function fakeView() {
80
+ const view = {
81
+ ID: undefined as string | undefined,
82
+ EntityID: undefined as string | undefined,
83
+ async Load(id: string) {
84
+ const found = VIEWS[id];
85
+ if (!found) return false;
86
+ Object.assign(view, found);
87
+ return true;
88
+ },
89
+ };
90
+ return view;
91
+ }
92
+
93
+ function context(): AppContext {
94
+ return {
95
+ userPayload: { email: SENDER.Email, userRecord: SENDER },
96
+ providers: [{ provider: fakeProvider(), type: 'Read-Write' }],
97
+ } as unknown as AppContext;
98
+ }
99
+
100
+ function run(extraFilter?: string, viewID = 'view-1') {
101
+ const viewInput = { ViewID: viewID, ExtraFilter: extraFilter } as RunViewByIDInput;
102
+ const message = { MessageType: {}, From: 'a@example.com', To: '' } as unknown as CommunicationMessageInput;
103
+ return new ReportResolver().RunEntityCommunicationByViewID('Customers', viewInput, 'SendGrid', 'Email', message, true, true, context());
104
+ }
105
+
106
+ describe('RunEntityCommunicationByViewID — runs as the caller', () => {
107
+ beforeEach(() => {
108
+ captured.params = [];
109
+ });
110
+
111
+ it('passes the authenticated caller to the engine as the acting user', async () => {
112
+ const result = await run();
113
+ expect(result.Success).toBe(true);
114
+ expect(captured.params).toHaveLength(1);
115
+ expect(captured.params[0].ContextUser).toBe(SENDER);
116
+ });
117
+
118
+ it('refuses an ExtraFilter whose subquery reads an entity the caller cannot read, before the engine runs', async () => {
119
+ const result = await run("ID IN (SELECT ID FROM __mj.vwSalaries WHERE Amount > 100000)");
120
+ expect(result.Success).toBe(false);
121
+ expect(result.ErrorMessage).toMatch(/ExtraFilter/);
122
+ expect(captured.params).toHaveLength(0);
123
+ });
124
+
125
+ it('refuses an ExtraFilter that carries a second statement', async () => {
126
+ const result = await run("1=1; SELECT 1");
127
+ expect(result.Success).toBe(false);
128
+ expect(captured.params).toHaveLength(0);
129
+ });
130
+ });
131
+
132
+ describe('RunEntityCommunicationByViewID — the view must belong to the entity', () => {
133
+ beforeEach(() => {
134
+ captured.params = [];
135
+ });
136
+
137
+ it('refuses a view of a different entity, before the engine runs', async () => {
138
+ const result = await run(undefined, 'view-salaries');
139
+ expect(result.Success).toBe(false);
140
+ expect(captured.params).toHaveLength(0);
141
+ });
142
+
143
+ it('gives the same error for a view of another entity as for a view that does not exist', async () => {
144
+ const mismatched = await run(undefined, 'view-salaries');
145
+ const missing = await run(undefined, 'view-missing');
146
+ expect(missing.Success).toBe(false);
147
+ expect(mismatched.ErrorMessage).toBe(missing.ErrorMessage);
148
+ expect(mismatched.ErrorMessage).toMatch(/does not exist or you do not have access/);
149
+ expect(captured.params).toHaveLength(0);
150
+ });
151
+ });
@@ -0,0 +1,152 @@
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, beforeEach, afterEach } from 'vitest';
5
+ import { CompositeKey, DatabaseProviderBase, EntityInfo, EntityPermissionType, Metadata, RowLevelSecurityFilterInfo, UserInfo } from '@memberjunction/core';
6
+ import type { DeleteSQLResult, IMetadataProvider, QueryExecutionSpec, RunQueryResult, SaveSQLResult } from '@memberjunction/core';
7
+ import { EntityRecordNameResolver } from '../resolvers/EntityRecordNameResolver.js';
8
+ import type { AppContext, UserPayload } from '../types.js';
9
+
10
+ /**
11
+ * The record-name queries against a real provider whose database only records what it is sent.
12
+ * The lookup the resolver causes carries the acting user's read row filter, binds the key value,
13
+ * refuses a key that is not the entity's primary key, and never runs without an acting user.
14
+ */
15
+
16
+ const CLIENTS_ID = 'E0000000-0000-0000-0000-0000000000C1';
17
+ const ACCOUNT_MANAGER_ROLE_ID = 'B0000000-0000-0000-0000-0000000000C1';
18
+ const OWN_CLIENTS_FILTER_ID = 'F0000000-0000-0000-0000-0000000000C1';
19
+ const MANAGER_ID = 'C0000000-0000-0000-0000-0000000000C1';
20
+ const CLIENT_ID_VALUE = '33333333-3333-3333-3333-333333333333';
21
+
22
+ const ROW_FILTERS: RowLevelSecurityFilterInfo[] = [
23
+ new RowLevelSecurityFilterInfo({ ID: OWN_CLIENTS_FILTER_ID, Name: 'Own clients', FilterText: "AccountManagerID = '{{UserID}}'" }),
24
+ ];
25
+
26
+ /** `Clients`: account managers read only the clients they manage. */
27
+ function clientsEntity(): EntityInfo {
28
+ return new EntityInfo({
29
+ ID: CLIENTS_ID, Name: 'Clients', SchemaName: 'dbo', BaseTable: 'Client', BaseView: 'vwClients',
30
+ Permissions: [
31
+ { ID: 'perm-manager', EntityID: CLIENTS_ID, RoleID: ACCOUNT_MANAGER_ROLE_ID, CanRead: true, ReadRLSFilterID: OWN_CLIENTS_FILTER_ID, Type: 'Allow' },
32
+ ],
33
+ Fields: [
34
+ { ID: 'cl-id', EntityID: CLIENTS_ID, Sequence: 1, Name: 'ID', Type: 'uniqueidentifier', IsPrimaryKey: true, NeedsQuotes: true },
35
+ { ID: 'cl-name', EntityID: CLIENTS_ID, Sequence: 2, Name: 'Name', Type: 'nvarchar', IsNameField: true, NeedsQuotes: true },
36
+ { ID: 'cl-tier', EntityID: CLIENTS_ID, Sequence: 3, Name: 'Tier', Type: 'int', NeedsQuotes: false },
37
+ { ID: 'cl-manager', EntityID: CLIENTS_ID, Sequence: 4, Name: 'AccountManagerID', Type: 'uniqueidentifier', NeedsQuotes: true },
38
+ ],
39
+ });
40
+ }
41
+
42
+ interface SentQuery {
43
+ SQL: string;
44
+ Parameters: unknown[] | undefined;
45
+ }
46
+
47
+ /** A SQL Server-shaped provider over `Clients`, whose database answers every query with one row. */
48
+ class RecordingProvider extends DatabaseProviderBase {
49
+ public Sent: SentQuery[] = [];
50
+ private readonly entities = [clientsEntity()];
51
+
52
+ public override get Entities(): EntityInfo[] {
53
+ return this.entities;
54
+ }
55
+
56
+ public override get RowLevelSecurityFilters(): RowLevelSecurityFilterInfo[] {
57
+ return ROW_FILTERS;
58
+ }
59
+
60
+ async ExecuteSQL<T>(sql: string, parameters?: unknown[]): Promise<Array<T>> {
61
+ this.Sent.push({ SQL: sql, Parameters: parameters });
62
+ return [{ Name: 'Acme Manufacturing' }] as T[];
63
+ }
64
+
65
+ public override BuildParameterPlaceholder(index: number): string { return `@p${index}`; }
66
+ protected get UUIDFunctionPattern(): RegExp { return /^newid\(\)$/i; }
67
+ protected get DBDefaultFunctionPattern(): RegExp { return /^getdate\(\)$/i; }
68
+ public QuoteIdentifier(name: string): string { return `[${name}]`; }
69
+ public QuoteSchemaAndView(schema: string, obj: string): string { return `[${schema}].[${obj}]`; }
70
+ protected BuildChildDiscoverySQL(): string { return ''; }
71
+ protected BuildHardLinkDependencySQL(): string { return ''; }
72
+ protected BuildSoftLinkDependencySQL(): string { return ''; }
73
+ protected async GenerateSaveSQL(): Promise<SaveSQLResult> { return { fullSQL: '' }; }
74
+ protected GenerateDeleteSQL(): DeleteSQLResult { return { fullSQL: '' }; }
75
+ protected BuildRecordChangeSQL(): { sql: string; parameters?: unknown[] } | null { return null; }
76
+ protected BuildSiblingRecordChangeSQL(): string { return ''; }
77
+ async BeginTransaction(): Promise<void> {}
78
+ async CommitTransaction(): Promise<void> {}
79
+ async RollbackTransaction(): Promise<void> {}
80
+ protected async InternalExecuteQueryFromSpec(_spec: QueryExecutionSpec): Promise<RunQueryResult> {
81
+ throw new Error('Not supported.');
82
+ }
83
+ }
84
+
85
+ const MANAGER = new UserInfo(null, {
86
+ ID: MANAGER_ID, Name: 'Morgan', Email: 'morgan@example.com', IsActive: true,
87
+ UserRoles: [{ UserID: MANAGER_ID, RoleID: ACCOUNT_MANAGER_ROLE_ID, Role: 'Account Managers' }],
88
+ });
89
+
90
+ function key(fieldName: string, value: string): CompositeKey {
91
+ return new CompositeKey([{ FieldName: fieldName, Value: value }]);
92
+ }
93
+
94
+ describe('EntityRecordNameResolver lookups under row-level security', () => {
95
+ let provider: RecordingProvider;
96
+ let savedProvider: IMetadataProvider;
97
+ let context: AppContext;
98
+ const resolver = new EntityRecordNameResolver();
99
+
100
+ beforeEach(() => {
101
+ provider = new RecordingProvider();
102
+ // Row filters resolve through the global provider.
103
+ savedProvider = Metadata.Provider;
104
+ Metadata.Provider = provider;
105
+ const userPayload: UserPayload = { email: MANAGER.Email, userRecord: MANAGER, sessionId: 'session-1' };
106
+ context = { dataSource: undefined, dataSources: [], providers: [{ provider, type: 'Read-Only' }], userPayload };
107
+ });
108
+
109
+ afterEach(() => {
110
+ Metadata.Provider = savedProvider;
111
+ });
112
+
113
+ function managerRowFilter(): string {
114
+ return provider.EntityByName('Clients').GetEffectiveRowFilterWhereClause(MANAGER, EntityPermissionType.Read, '');
115
+ }
116
+
117
+ it("looks the name up under the acting user's read row filter, with the key value bound", async () => {
118
+ const rowFilter = managerRowFilter();
119
+ expect(rowFilter).toContain(MANAGER_ID);
120
+
121
+ const result = await resolver.GetEntityRecordName('Clients', key('ID', CLIENT_ID_VALUE), context);
122
+
123
+ expect(result.Success).toBe(true);
124
+ expect(provider.Sent).toEqual([
125
+ { SQL: `SELECT [Name] FROM [dbo].[vwClients] WHERE [ID]=@p0 AND (${rowFilter})`, Parameters: [CLIENT_ID_VALUE] },
126
+ ]);
127
+ });
128
+
129
+ it('applies the row filter to each item of a batch', async () => {
130
+ const rowFilter = managerRowFilter();
131
+
132
+ const results = await resolver.GetEntityRecordNames([{ EntityName: 'Clients', CompositeKey: key('ID', CLIENT_ID_VALUE) }], context);
133
+
134
+ expect(results.map(r => r.Success)).toEqual([true]);
135
+ expect(provider.Sent.map(q => q.SQL)).toEqual([`SELECT [Name] FROM [dbo].[vwClients] WHERE [ID]=@p0 AND (${rowFilter})`]);
136
+ });
137
+
138
+ it('refuses a key field that is not a primary key, without querying', async () => {
139
+ const result = await resolver.GetEntityRecordName('Clients', key('Tier', '1'), context);
140
+
141
+ expect(result.Success).toBe(false);
142
+ expect(result.RecordName).toBeUndefined();
143
+ expect(provider.Sent).toHaveLength(0);
144
+ });
145
+
146
+ it('looks no name up when there is no acting user', async () => {
147
+ const result = await resolver.InnerGetEntityRecordName(provider, 'Clients', key('ID', CLIENT_ID_VALUE));
148
+
149
+ expect(result.Success).toBe(false);
150
+ expect(provider.Sent).toHaveLength(0);
151
+ });
152
+ });
@@ -0,0 +1,113 @@
1
+ /**
2
+ * Two ways the schema-evolution path stopped short of acting on what it already knew.
3
+ *
4
+ * - MJ-RUN-37: the persist layer has always recorded an `IncrementalWatermarkField` change and
5
+ * nothing consumed the record. `changedObjects` — the sole input to `ResetPullWatermarks` — was
6
+ * built from a physical column diff and a field-map add/disable, and a cursor swap produces
7
+ * neither. The stale Pull watermark then became a lower bound on a DIFFERENT column, and where
8
+ * the new column sorts later, every row below it was filtered out at the source, permanently and
9
+ * invisibly (an incremental's fetched-vs-expected check agrees, because the source counts
10
+ * against the filter it was handed).
11
+ *
12
+ * - MJ-APPLY-6 residual: `RunPipelineBatchWithRetry` existed and two of the six pipeline call
13
+ * sites used it. `SchemaBuilder.RunSchemaPipeline` states the reason plainly — the middle steps
14
+ * fail transiently and "the callers simply never used it".
15
+ *
16
+ * The evolution mutation needs a provider, a connector, a SchemaBuilder and an RSU pipeline to
17
+ * invoke, and the properties here are wiring, not behaviour worth standing all that up to observe.
18
+ * Read from source, the convention this directory already uses for this method. The DECISION the
19
+ * wiring feeds is unit-tested for real in the integration-engine package
20
+ * (WatermarkFieldChangeIsAChange.test.ts) — these pin that the resolver calls it, feeds it the
21
+ * right input, and does so before the watermark reset.
22
+ */
23
+ import { describe, it, expect } from 'vitest';
24
+ import { readFileSync } from 'node:fs';
25
+ import { join } from 'node:path';
26
+
27
+ const SRC = readFileSync(join(__dirname, '..', 'resolvers', 'IntegrationDiscoveryResolver.ts'), 'utf-8');
28
+ const PROMOTER = readFileSync(join(__dirname, '..', 'integration', 'CustomColumnPromoter.ts'), 'utf-8');
29
+ const RSU_RESOLVER = readFileSync(join(__dirname, '..', 'resolvers', 'RSUResolver.ts'), 'utf-8');
30
+
31
+ /** The body of a named resolver method, up to the next @Query/@Mutation at the same indent. */
32
+ function methodBody(src: string, name: string): string {
33
+ const start = src.indexOf(`async ${name}(`);
34
+ expect(start, `${name} not found`).toBeGreaterThan(-1);
35
+ const rest = src.slice(start);
36
+ const end = rest.search(/\n @(Query|Mutation)\(/);
37
+ return end === -1 ? rest : rest.slice(0, end);
38
+ }
39
+
40
+ describe('MJ-RUN-37 — a watermark-field change reaches ResetPullWatermarks', () => {
41
+ const body = () => methodBody(SRC, 'IntegrationSchemaEvolution');
42
+
43
+ it('consumes the persist layer\'s detection rather than re-deriving it', () => {
44
+ // The whole defect was that this signal existed and had no consumer. It must come from the
45
+ // refresh's OWN PersistResult — re-reading the catalog afterwards cannot tell a changed
46
+ // watermark field from one that was always that way.
47
+ expect(body()).toMatch(/ObjectsWithWatermarkFieldChange\(\s*refresh\.PersistResult\?\.ObjectMergeLog \?\? \[\]/);
48
+ });
49
+
50
+ it('imports it from the engine that produces it', () => {
51
+ expect(SRC).toMatch(/ObjectsWithWatermarkFieldChange[\s\S]{0,200}?from "@memberjunction\/integration-engine"/);
52
+ });
53
+
54
+ it('scopes it to the CONTINUING maps, so it cannot report what nothing can reset', () => {
55
+ expect(body()).toMatch(/continuingMaps\.map\(m => m\.ExternalObjectName \?\? ''\)/);
56
+ });
57
+
58
+ it('folds the result into changedObjects — the only input ResetPullWatermarks has', () => {
59
+ const b = body();
60
+ const push = b.search(/if \(!changedObjects\.some\(n => n\.toLowerCase\(\) === name\.toLowerCase\(\)\)\) changedObjects\.push\(name\)/);
61
+ const reset = b.indexOf('ResetPullWatermarks(');
62
+ expect(push, 'the watermark-changed names are never pushed onto changedObjects').toBeGreaterThan(-1);
63
+ expect(reset).toBeGreaterThan(-1);
64
+ expect(push, 'the push must happen BEFORE the reset, or it changes nothing').toBeLessThan(reset);
65
+ });
66
+
67
+ it('does not push unconditionally — an unchanged run must not re-full-sync the connection', () => {
68
+ // The guard is the expensive half. `changedObjects` drives ResetPullWatermarks AND HasChanges,
69
+ // so a rule that fires when no cursor moved turns every refresh into a full re-fetch of
70
+ // everything. The emptiness of the detection is what has to carry that, never a bare loop.
71
+ const b = body();
72
+ const i = b.indexOf('ObjectsWithWatermarkFieldChange(');
73
+ const after = b.slice(i, i + 600);
74
+ expect(after).toMatch(/for \(const name of watermarkFieldChanged\)/);
75
+ expect(after).not.toMatch(/changedObjects\.push\(\.\.\./);
76
+ });
77
+ });
78
+
79
+ describe('MJ-APPLY-6 residual — the bare pipeline calls that should retry', () => {
80
+ it('IntegrationApplySchemaBatch retries, matching its single-connector sibling', () => {
81
+ // IntegrationApplySchema retries via SchemaBuilder.RunSchemaPipeline. Same install path, same
82
+ // generated DDL, same four transient steps — the batch simply never used the wrapper.
83
+ const b = methodBody(SRC, 'IntegrationApplySchemaBatch');
84
+ expect(b).toMatch(/rsm\.RunPipelineBatchWithRetry\(pipelineInputs\)/);
85
+ expect(b).not.toMatch(/rsm\.RunPipelineBatch\(/);
86
+ });
87
+
88
+ it('IntegrationSchemaEvolution retries', () => {
89
+ const b = methodBody(SRC, 'IntegrationSchemaEvolution');
90
+ expect(b).toMatch(/rsm\.RunPipelineBatchWithRetry\(/);
91
+ expect(b).not.toMatch(/rsm\.RunPipelineBatch\(/);
92
+ });
93
+
94
+ it('the custom-column promoter retries — it runs unattended, mid-sync', () => {
95
+ expect(PROMOTER).toMatch(/RuntimeSchemaManager\.Instance\.RunPipelineBatchWithRetry\(batchInputs\)/);
96
+ expect(PROMOTER).not.toMatch(/RuntimeSchemaManager\.Instance\.RunPipelineBatch\(/);
97
+ });
98
+
99
+ it('no bare RunPipelineBatch survives in the integration resolver', () => {
100
+ // A regression here is invisible at runtime: the pipeline still works, it just stops
101
+ // absorbing the hiccup it was wrapped to absorb.
102
+ expect(SRC).not.toMatch(/RunPipelineBatch\((?!\w)/);
103
+ });
104
+
105
+ it('RunRuntimeSchemaUpdateBatch deliberately does NOT retry, and says why', () => {
106
+ // The one exception, kept explicit so it reads as a decision rather than an oversight: this
107
+ // mutation replays CALLER-AUTHORED SQL, and executeMigration is not transactional.
108
+ const b = methodBody(RSU_RESOLVER, 'RunRuntimeSchemaUpdateBatch');
109
+ expect(b).toMatch(/rsm\.RunPipelineBatch\(pipelineInputs\)/);
110
+ expect(b).toMatch(/deliberately the BARE call/);
111
+ expect(b).toMatch(/NOT transactional/);
112
+ });
113
+ });