@memberjunction/server 6.2.0-edge.5 → 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 (401) hide show
  1. package/README.md +27 -1
  2. package/dist/.tsbuildinfo +1 -1
  3. package/dist/agentSessions/SessionJanitor.d.ts +3 -3
  4. package/dist/agentSessions/SessionJanitor.js +3 -3
  5. package/dist/agentSessions/SessionManager.d.ts +15 -5
  6. package/dist/agentSessions/SessionManager.d.ts.map +1 -1
  7. package/dist/agentSessions/SessionManager.js +34 -12
  8. package/dist/agentSessions/SessionManager.js.map +1 -1
  9. package/dist/auth/AuthProviderCatalogRouter.d.ts.map +1 -1
  10. package/dist/auth/AuthProviderCatalogRouter.js +2 -0
  11. package/dist/auth/AuthProviderCatalogRouter.js.map +1 -1
  12. package/dist/auth/index.d.ts.map +1 -1
  13. package/dist/auth/index.js +51 -10
  14. package/dist/auth/index.js.map +1 -1
  15. package/dist/auth/initializeProviders.d.ts +10 -0
  16. package/dist/auth/initializeProviders.d.ts.map +1 -1
  17. package/dist/auth/initializeProviders.js +37 -1
  18. package/dist/auth/initializeProviders.js.map +1 -1
  19. package/dist/auth/localDevAuth.d.ts +79 -0
  20. package/dist/auth/localDevAuth.d.ts.map +1 -0
  21. package/dist/auth/localDevAuth.js +439 -0
  22. package/dist/auth/localDevAuth.js.map +1 -0
  23. package/dist/auth/magicLink/MagicLinkKeys.d.ts +2 -2
  24. package/dist/auth/magicLink/MagicLinkKeys.d.ts.map +1 -1
  25. package/dist/auth/magicLink/MagicLinkKeys.js +1 -1
  26. package/dist/auth/magicLink/MagicLinkKeys.js.map +1 -1
  27. package/dist/auth/magicLink/types.d.ts +12 -0
  28. package/dist/auth/magicLink/types.d.ts.map +1 -1
  29. package/dist/auth/sessionUserClone.d.ts +1 -1
  30. package/dist/auth/sessionUserClone.d.ts.map +1 -1
  31. package/dist/auth/sessionUserClone.js +3 -2
  32. package/dist/auth/sessionUserClone.js.map +1 -1
  33. package/dist/broadcast/BaseCacheInvalidationBroadcastProvider.d.ts +292 -0
  34. package/dist/broadcast/BaseCacheInvalidationBroadcastProvider.d.ts.map +1 -0
  35. package/dist/broadcast/BaseCacheInvalidationBroadcastProvider.js +442 -0
  36. package/dist/broadcast/BaseCacheInvalidationBroadcastProvider.js.map +1 -0
  37. package/dist/broadcast/index.d.ts +6 -0
  38. package/dist/broadcast/index.d.ts.map +1 -0
  39. package/dist/broadcast/index.js +6 -0
  40. package/dist/broadcast/index.js.map +1 -0
  41. package/dist/broadcast/providers/AWSIoTCoreBroadcastProvider.d.ts +43 -0
  42. package/dist/broadcast/providers/AWSIoTCoreBroadcastProvider.d.ts.map +1 -0
  43. package/dist/broadcast/providers/AWSIoTCoreBroadcastProvider.js +144 -0
  44. package/dist/broadcast/providers/AWSIoTCoreBroadcastProvider.js.map +1 -0
  45. package/dist/broadcast/providers/AzureWebPubSubBroadcastProvider.d.ts +46 -0
  46. package/dist/broadcast/providers/AzureWebPubSubBroadcastProvider.d.ts.map +1 -0
  47. package/dist/broadcast/providers/AzureWebPubSubBroadcastProvider.js +149 -0
  48. package/dist/broadcast/providers/AzureWebPubSubBroadcastProvider.js.map +1 -0
  49. package/dist/broadcast/providers/GCPPubSubBroadcastProvider.d.ts +49 -0
  50. package/dist/broadcast/providers/GCPPubSubBroadcastProvider.d.ts.map +1 -0
  51. package/dist/broadcast/providers/GCPPubSubBroadcastProvider.js +131 -0
  52. package/dist/broadcast/providers/GCPPubSubBroadcastProvider.js.map +1 -0
  53. package/dist/broadcast/providers/SequencedRoleBucketBroadcastProvider.d.ts +24 -0
  54. package/dist/broadcast/providers/SequencedRoleBucketBroadcastProvider.d.ts.map +1 -0
  55. package/dist/broadcast/providers/SequencedRoleBucketBroadcastProvider.js +51 -0
  56. package/dist/broadcast/providers/SequencedRoleBucketBroadcastProvider.js.map +1 -0
  57. package/dist/cache.d.ts +3 -2
  58. package/dist/cache.d.ts.map +1 -1
  59. package/dist/cache.js.map +1 -1
  60. package/dist/config.d.ts +496 -0
  61. package/dist/config.d.ts.map +1 -1
  62. package/dist/config.js +61 -0
  63. package/dist/config.js.map +1 -1
  64. package/dist/context.d.ts +3 -1
  65. package/dist/context.d.ts.map +1 -1
  66. package/dist/context.js +29 -8
  67. package/dist/context.js.map +1 -1
  68. package/dist/coreRoutes.d.ts.map +1 -1
  69. package/dist/coreRoutes.js +14 -0
  70. package/dist/coreRoutes.js.map +1 -1
  71. package/dist/generated/graphql-schemas/__mj.d.ts +635 -14
  72. package/dist/generated/graphql-schemas/__mj.d.ts.map +1 -1
  73. package/dist/generated/graphql-schemas/__mj.js +3366 -418
  74. package/dist/generated/graphql-schemas/__mj.js.map +1 -1
  75. package/dist/generic/CacheInvalidationResolver.d.ts +50 -30
  76. package/dist/generic/CacheInvalidationResolver.d.ts.map +1 -1
  77. package/dist/generic/CacheInvalidationResolver.js +154 -79
  78. package/dist/generic/CacheInvalidationResolver.js.map +1 -1
  79. package/dist/generic/OrphanedConversationDetailReconciler.d.ts.map +1 -1
  80. package/dist/generic/OrphanedConversationDetailReconciler.js +2 -0
  81. package/dist/generic/OrphanedConversationDetailReconciler.js.map +1 -1
  82. package/dist/generic/PubSubManager.d.ts +10 -0
  83. package/dist/generic/PubSubManager.d.ts.map +1 -1
  84. package/dist/generic/PubSubManager.js +44 -1
  85. package/dist/generic/PubSubManager.js.map +1 -1
  86. package/dist/generic/PushStatusResolver.d.ts +39 -6
  87. package/dist/generic/PushStatusResolver.d.ts.map +1 -1
  88. package/dist/generic/PushStatusResolver.js +93 -11
  89. package/dist/generic/PushStatusResolver.js.map +1 -1
  90. package/dist/generic/ResolverBase.d.ts +65 -25
  91. package/dist/generic/ResolverBase.d.ts.map +1 -1
  92. package/dist/generic/ResolverBase.js +201 -101
  93. package/dist/generic/ResolverBase.js.map +1 -1
  94. package/dist/generic/RunViewResolver.d.ts +11 -3
  95. package/dist/generic/RunViewResolver.d.ts.map +1 -1
  96. package/dist/generic/RunViewResolver.js +79 -13
  97. package/dist/generic/RunViewResolver.js.map +1 -1
  98. package/dist/generic/actionOutputFieldSecurity.d.ts +26 -0
  99. package/dist/generic/actionOutputFieldSecurity.d.ts.map +1 -0
  100. package/dist/generic/actionOutputFieldSecurity.js +47 -0
  101. package/dist/generic/actionOutputFieldSecurity.js.map +1 -0
  102. package/dist/generic/fieldSecurityTransport.d.ts +30 -0
  103. package/dist/generic/fieldSecurityTransport.d.ts.map +1 -0
  104. package/dist/generic/fieldSecurityTransport.js +30 -0
  105. package/dist/generic/fieldSecurityTransport.js.map +1 -0
  106. package/dist/index.d.ts +3 -0
  107. package/dist/index.d.ts.map +1 -1
  108. package/dist/index.js +122 -66
  109. package/dist/index.js.map +1 -1
  110. package/dist/integration/CustomColumnPromoter.d.ts.map +1 -1
  111. package/dist/integration/CustomColumnPromoter.js +6 -1
  112. package/dist/integration/CustomColumnPromoter.js.map +1 -1
  113. package/dist/logging/variablesLoggingMiddleware.d.ts +12 -0
  114. package/dist/logging/variablesLoggingMiddleware.d.ts.map +1 -1
  115. package/dist/logging/variablesLoggingMiddleware.js +50 -1
  116. package/dist/logging/variablesLoggingMiddleware.js.map +1 -1
  117. package/dist/realtimeSessions/SessionVerificationPortsBase.d.ts.map +1 -1
  118. package/dist/realtimeSessions/SessionVerificationPortsBase.js +1 -0
  119. package/dist/realtimeSessions/SessionVerificationPortsBase.js.map +1 -1
  120. package/dist/realtimeSessions/ownedRealtimeSession.d.ts +8 -6
  121. package/dist/realtimeSessions/ownedRealtimeSession.d.ts.map +1 -1
  122. package/dist/realtimeSessions/ownedRealtimeSession.js +15 -6
  123. package/dist/realtimeSessions/ownedRealtimeSession.js.map +1 -1
  124. package/dist/realtimeSessions/sessionAccess.d.ts +88 -11
  125. package/dist/realtimeSessions/sessionAccess.d.ts.map +1 -1
  126. package/dist/realtimeSessions/sessionAccess.js +125 -22
  127. package/dist/realtimeSessions/sessionAccess.js.map +1 -1
  128. package/dist/realtimeSessions/sessionConfigStamps.js +1 -1
  129. package/dist/realtimeSessions/sessionConfigStamps.js.map +1 -1
  130. package/dist/realtimeSessions/subscriptionAuthorization.d.ts.map +1 -1
  131. package/dist/realtimeSessions/subscriptionAuthorization.js +5 -2
  132. package/dist/realtimeSessions/subscriptionAuthorization.js.map +1 -1
  133. package/dist/realtimeSessions/verificationWorkflow.d.ts +2 -0
  134. package/dist/realtimeSessions/verificationWorkflow.d.ts.map +1 -1
  135. package/dist/realtimeSessions/verificationWorkflow.js +1 -1
  136. package/dist/realtimeSessions/verificationWorkflow.js.map +1 -1
  137. package/dist/realtimeWidget/WidgetRouter.d.ts.map +1 -1
  138. package/dist/realtimeWidget/WidgetRouter.js +19 -3
  139. package/dist/realtimeWidget/WidgetRouter.js.map +1 -1
  140. package/dist/realtimeWidget/WidgetSessionService.d.ts +67 -7
  141. package/dist/realtimeWidget/WidgetSessionService.d.ts.map +1 -1
  142. package/dist/realtimeWidget/WidgetSessionService.js +201 -19
  143. package/dist/realtimeWidget/WidgetSessionService.js.map +1 -1
  144. package/dist/realtimeWidget/host-identity.d.ts +1 -1
  145. package/dist/realtimeWidget/host-identity.js +1 -1
  146. package/dist/realtimeWidget/widgetCore.d.ts +20 -1
  147. package/dist/realtimeWidget/widgetCore.d.ts.map +1 -1
  148. package/dist/realtimeWidget/widgetCore.js +17 -1
  149. package/dist/realtimeWidget/widgetCore.js.map +1 -1
  150. package/dist/realtimeWidget/widgetGuestElevation.d.ts +1 -1
  151. package/dist/realtimeWidget/widgetGuestElevation.d.ts.map +1 -1
  152. package/dist/realtimeWidget/widgetGuestElevation.js +2 -1
  153. package/dist/realtimeWidget/widgetGuestElevation.js.map +1 -1
  154. package/dist/resolvers/ActionResolver.d.ts +19 -1
  155. package/dist/resolvers/ActionResolver.d.ts.map +1 -1
  156. package/dist/resolvers/ActionResolver.js +63 -12
  157. package/dist/resolvers/ActionResolver.js.map +1 -1
  158. package/dist/resolvers/AdhocQueryResolver.d.ts.map +1 -1
  159. package/dist/resolvers/AdhocQueryResolver.js +1 -0
  160. package/dist/resolvers/AdhocQueryResolver.js.map +1 -1
  161. package/dist/resolvers/AgentRunStatusPublisher.d.ts +18 -4
  162. package/dist/resolvers/AgentRunStatusPublisher.d.ts.map +1 -1
  163. package/dist/resolvers/AgentRunStatusPublisher.js +32 -8
  164. package/dist/resolvers/AgentRunStatusPublisher.js.map +1 -1
  165. package/dist/resolvers/AgentSessionResolver.d.ts +10 -4
  166. package/dist/resolvers/AgentSessionResolver.d.ts.map +1 -1
  167. package/dist/resolvers/AgentSessionResolver.js +40 -17
  168. package/dist/resolvers/AgentSessionResolver.js.map +1 -1
  169. package/dist/resolvers/EntityCommunicationsResolver.d.ts +13 -1
  170. package/dist/resolvers/EntityCommunicationsResolver.d.ts.map +1 -1
  171. package/dist/resolvers/EntityCommunicationsResolver.js +50 -14
  172. package/dist/resolvers/EntityCommunicationsResolver.js.map +1 -1
  173. package/dist/resolvers/FetchEntityVectorsResolver.d.ts +28 -0
  174. package/dist/resolvers/FetchEntityVectorsResolver.d.ts.map +1 -1
  175. package/dist/resolvers/FetchEntityVectorsResolver.js +117 -21
  176. package/dist/resolvers/FetchEntityVectorsResolver.js.map +1 -1
  177. package/dist/resolvers/FileCategoryResolver.d.ts.map +1 -1
  178. package/dist/resolvers/FileCategoryResolver.js +1 -0
  179. package/dist/resolvers/FileCategoryResolver.js.map +1 -1
  180. package/dist/resolvers/FileResolver.d.ts +5 -0
  181. package/dist/resolvers/FileResolver.d.ts.map +1 -1
  182. package/dist/resolvers/FileResolver.js +13 -4
  183. package/dist/resolvers/FileResolver.js.map +1 -1
  184. package/dist/resolvers/IntegrationDiscoveryResolver.d.ts.map +1 -1
  185. package/dist/resolvers/IntegrationDiscoveryResolver.js +45 -7
  186. package/dist/resolvers/IntegrationDiscoveryResolver.js.map +1 -1
  187. package/dist/resolvers/MergeRecordsResolver.d.ts +8 -1
  188. package/dist/resolvers/MergeRecordsResolver.d.ts.map +1 -1
  189. package/dist/resolvers/MergeRecordsResolver.js +31 -3
  190. package/dist/resolvers/MergeRecordsResolver.js.map +1 -1
  191. package/dist/resolvers/PipelineProgressResolver.d.ts +6 -0
  192. package/dist/resolvers/PipelineProgressResolver.d.ts.map +1 -1
  193. package/dist/resolvers/PipelineProgressResolver.js +5 -3
  194. package/dist/resolvers/PipelineProgressResolver.js.map +1 -1
  195. package/dist/resolvers/QueryResolver.d.ts.map +1 -1
  196. package/dist/resolvers/QueryResolver.js +20 -6
  197. package/dist/resolvers/QueryResolver.js.map +1 -1
  198. package/dist/resolvers/QuerySystemUserResolver.d.ts +2 -0
  199. package/dist/resolvers/QuerySystemUserResolver.d.ts.map +1 -1
  200. package/dist/resolvers/QuerySystemUserResolver.js +15 -6
  201. package/dist/resolvers/QuerySystemUserResolver.js.map +1 -1
  202. package/dist/resolvers/RSUResolver.d.ts.map +1 -1
  203. package/dist/resolvers/RSUResolver.js +12 -0
  204. package/dist/resolvers/RSUResolver.js.map +1 -1
  205. package/dist/resolvers/RealtimeClientSessionResolver.d.ts +16 -2
  206. package/dist/resolvers/RealtimeClientSessionResolver.d.ts.map +1 -1
  207. package/dist/resolvers/RealtimeClientSessionResolver.js +82 -28
  208. package/dist/resolvers/RealtimeClientSessionResolver.js.map +1 -1
  209. package/dist/resolvers/RemoteBrowserActionResolver.d.ts.map +1 -1
  210. package/dist/resolvers/RemoteBrowserActionResolver.js +11 -9
  211. package/dist/resolvers/RemoteBrowserActionResolver.js.map +1 -1
  212. package/dist/resolvers/RunAIAgentResolver.d.ts +56 -8
  213. package/dist/resolvers/RunAIAgentResolver.d.ts.map +1 -1
  214. package/dist/resolvers/RunAIAgentResolver.js +209 -64
  215. package/dist/resolvers/RunAIAgentResolver.js.map +1 -1
  216. package/dist/resolvers/RunTemplateResolver.d.ts.map +1 -1
  217. package/dist/resolvers/RunTemplateResolver.js +4 -4
  218. package/dist/resolvers/RunTemplateResolver.js.map +1 -1
  219. package/dist/resolvers/RunTestResolver.d.ts.map +1 -1
  220. package/dist/resolvers/RunTestResolver.js +3 -1
  221. package/dist/resolvers/RunTestResolver.js.map +1 -1
  222. package/dist/resolvers/SyncDataResolver.d.ts.map +1 -1
  223. package/dist/resolvers/SyncDataResolver.js +4 -0
  224. package/dist/resolvers/SyncDataResolver.js.map +1 -1
  225. package/dist/resolvers/TestQuerySQLResolver.d.ts.map +1 -1
  226. package/dist/resolvers/TestQuerySQLResolver.js +1 -0
  227. package/dist/resolvers/TestQuerySQLResolver.js.map +1 -1
  228. package/dist/resolvers/TransactionGroupResolver.d.ts +17 -2
  229. package/dist/resolvers/TransactionGroupResolver.d.ts.map +1 -1
  230. package/dist/resolvers/TransactionGroupResolver.js +60 -15
  231. package/dist/resolvers/TransactionGroupResolver.js.map +1 -1
  232. package/dist/resolvers/UserViewResolver.d.ts +1 -1
  233. package/dist/resolvers/UserViewResolver.d.ts.map +1 -1
  234. package/dist/resolvers/UserViewResolver.js +2 -1
  235. package/dist/resolvers/UserViewResolver.js.map +1 -1
  236. package/dist/rest/EntityCRUDHandler.d.ts +6 -0
  237. package/dist/rest/EntityCRUDHandler.d.ts.map +1 -1
  238. package/dist/rest/EntityCRUDHandler.js +19 -5
  239. package/dist/rest/EntityCRUDHandler.js.map +1 -1
  240. package/dist/rest/RESTEndpointHandler.d.ts.map +1 -1
  241. package/dist/rest/RESTEndpointHandler.js +8 -5
  242. package/dist/rest/RESTEndpointHandler.js.map +1 -1
  243. package/dist/rest/RealtimeRecordingHandler.d.ts +3 -3
  244. package/dist/rest/RealtimeRecordingHandler.d.ts.map +1 -1
  245. package/dist/rest/RealtimeRecordingHandler.js +7 -7
  246. package/dist/rest/RealtimeRecordingHandler.js.map +1 -1
  247. package/dist/rest/ViewOperationsHandler.d.ts +6 -0
  248. package/dist/rest/ViewOperationsHandler.d.ts.map +1 -1
  249. package/dist/rest/ViewOperationsHandler.js +22 -5
  250. package/dist/rest/ViewOperationsHandler.js.map +1 -1
  251. package/dist/serverRoleConfigUnits.d.ts +29 -0
  252. package/dist/serverRoleConfigUnits.d.ts.map +1 -0
  253. package/dist/serverRoleConfigUnits.js +33 -0
  254. package/dist/serverRoleConfigUnits.js.map +1 -0
  255. package/dist/services/TaskGraphContinuationDeliverer.d.ts +5 -0
  256. package/dist/services/TaskGraphContinuationDeliverer.d.ts.map +1 -1
  257. package/dist/services/TaskGraphContinuationDeliverer.js +42 -15
  258. package/dist/services/TaskGraphContinuationDeliverer.js.map +1 -1
  259. package/dist/types.d.ts +23 -1
  260. package/dist/types.d.ts.map +1 -1
  261. package/dist/types.js.map +1 -1
  262. package/package.json +98 -98
  263. package/src/__tests__/ActionResolver.fieldSecurity.test.ts +237 -0
  264. package/src/__tests__/AdhocQueryResolver.dialect.test.ts +137 -0
  265. package/src/__tests__/AgentRunStatusPublisher.test.ts +49 -0
  266. package/src/__tests__/AgentSessionResolver.ownership.test.ts +137 -0
  267. package/src/__tests__/CustomColumnPromoterOnePass.test.ts +15 -1
  268. package/src/__tests__/EntityCommunicationsResolver.actingUser.test.ts +151 -0
  269. package/src/__tests__/EvolutionActsOnWhatItDetects.test.ts +113 -0
  270. package/src/__tests__/FetchEntityVectorsResolver.test.ts +206 -3
  271. package/src/__tests__/FileResolver.fieldSecurity.test.ts +82 -0
  272. package/src/__tests__/PipelineProgressResolver.filter.test.ts +58 -0
  273. package/src/__tests__/QuerySystemUserResolver.fieldSecurity.test.ts +49 -0
  274. package/src/__tests__/RESTEndpointHandler.test.ts +25 -0
  275. package/src/__tests__/RealtimeClientSessionResolver.configTamper.test.ts +23 -3
  276. package/src/__tests__/RealtimeClientSessionResolver.test.ts +324 -46
  277. package/src/__tests__/RemoteBrowserActionResolver.ownership.test.ts +98 -0
  278. package/src/__tests__/ResolverBase.clientOldValues.test.ts +37 -4
  279. package/src/__tests__/ResolverBase.deleteReturnProjection.test.ts +52 -0
  280. package/src/__tests__/ResolverBase.entityMessageRelay.test.ts +176 -0
  281. package/src/__tests__/ResolverBase.includeBinaryFields.test.ts +23 -1
  282. package/src/__tests__/ResolverBase.publishStatusUpdate.test.ts +68 -0
  283. package/src/__tests__/ResolverBase.userViewCacheAndDedup.test.ts +130 -0
  284. package/src/__tests__/RunAIAgentResolver.branch.test.ts +68 -13
  285. package/src/__tests__/RunAIAgentResolver.conversationWriter.test.ts +210 -0
  286. package/src/__tests__/RunAIAgentResolver.fireAndForgetCompletion.test.ts +209 -0
  287. package/src/__tests__/RunAIAgentResolver.historyFrom.test.ts +17 -27
  288. package/src/__tests__/RunAIAgentResolver.runDataGuard.test.ts +8 -2
  289. package/src/__tests__/RunAIAgentResolver.serverReply.test.ts +358 -0
  290. package/src/__tests__/RunAIAgentResolver.stoppedPredecessor.test.ts +4 -1
  291. package/src/__tests__/RunTemplateResolver.test.ts +35 -2
  292. package/src/__tests__/RunViewResolver.cacheCheckForwarding.test.ts +11 -0
  293. package/src/__tests__/SessionManager.test.ts +95 -0
  294. package/src/__tests__/TaskGraphContinuationDeliverer.test.ts +114 -3
  295. package/src/__tests__/TransactionGroupResolver.fieldSecurity.test.ts +199 -0
  296. package/src/__tests__/UserViewResolver.updateWhereClause.test.ts +15 -0
  297. package/src/__tests__/ViewOperationsHandler.test.ts +7 -2
  298. package/src/__tests__/WidgetSessionService.test.ts +583 -4
  299. package/src/__tests__/apiRootBoot.test.ts +1 -1
  300. package/src/__tests__/buildMagicLinkSessionUser.test.ts +96 -0
  301. package/src/__tests__/cacheBroadcastProviders.test.ts +403 -0
  302. package/src/__tests__/cacheInvalidationRecordData.test.ts +45 -0
  303. package/src/__tests__/fieldSecurityFixtures.ts +91 -0
  304. package/src/__tests__/globalCors.test.ts +3 -3
  305. package/src/__tests__/localDevAuth.test.ts +196 -0
  306. package/src/__tests__/orphanedConversationDetailReconciler.test.ts +10 -0
  307. package/src/__tests__/pushStatusResolver.filter.test.ts +57 -1
  308. package/src/__tests__/realtimeRecordingHandler.test.ts +19 -2
  309. package/src/__tests__/realtimeSessions/RealtimeSessionEventsResolver.test.ts +29 -1
  310. package/src/__tests__/realtimeSessions/ownedRealtimeSession.anonymous.test.ts +31 -0
  311. package/src/__tests__/realtimeSessions/ownedRealtimeSession.namedScoped.test.ts +37 -0
  312. package/src/__tests__/realtimeSessions/sessionAccess.test.ts +146 -7
  313. package/src/__tests__/realtimeSessions/sessionConfigStamps.test.ts +1 -1
  314. package/src/__tests__/realtimeSessions/verificationWorkflow.test.ts +25 -4
  315. package/src/__tests__/resolverBase.cloudEventChangedFields.test.ts +157 -0
  316. package/src/__tests__/resolverBase.fieldSecurityTransport.test.ts +222 -0
  317. package/src/__tests__/resolverBase.fls.test.ts +146 -97
  318. package/src/__tests__/restTransport.fieldSecurity.test.ts +148 -0
  319. package/src/__tests__/runParamsTimeZone.test.ts +179 -0
  320. package/src/__tests__/runStreams.fieldSecurity.test.ts +100 -0
  321. package/src/__tests__/runViewProperties.test.ts +114 -0
  322. package/src/__tests__/scalabilityBenchmarks.test.ts +765 -0
  323. package/src/__tests__/scalabilityDatabaseIntegration.test.ts +207 -0
  324. package/src/__tests__/sessionUserClone.test.ts +5 -1
  325. package/src/__tests__/startupSweepOrder.test.ts +40 -0
  326. package/src/__tests__/unifiedAuth.test.ts +28 -0
  327. package/src/__tests__/widgetGuestElevation.test.ts +2 -0
  328. package/src/agentSessions/SessionJanitor.ts +3 -3
  329. package/src/agentSessions/SessionManager.ts +35 -12
  330. package/src/auth/AuthProviderCatalogRouter.ts +3 -0
  331. package/src/auth/index.ts +56 -12
  332. package/src/auth/initializeProviders.ts +43 -1
  333. package/src/auth/localDevAuth.ts +528 -0
  334. package/src/auth/magicLink/MagicLinkKeys.ts +2 -2
  335. package/src/auth/magicLink/types.ts +10 -0
  336. package/src/auth/sessionUserClone.ts +3 -2
  337. package/src/broadcast/BaseCacheInvalidationBroadcastProvider.ts +674 -0
  338. package/src/broadcast/index.ts +5 -0
  339. package/src/broadcast/providers/AWSIoTCoreBroadcastProvider.ts +180 -0
  340. package/src/broadcast/providers/AzureWebPubSubBroadcastProvider.ts +182 -0
  341. package/src/broadcast/providers/GCPPubSubBroadcastProvider.ts +169 -0
  342. package/src/broadcast/providers/SequencedRoleBucketBroadcastProvider.ts +57 -0
  343. package/src/cache.ts +2 -1
  344. package/src/config.ts +69 -0
  345. package/src/context.ts +32 -8
  346. package/src/coreRoutes.ts +15 -0
  347. package/src/generated/graphql-schemas/__mj.ts +2239 -102
  348. package/src/generic/CacheInvalidationResolver.ts +135 -78
  349. package/src/generic/OrphanedConversationDetailReconciler.ts +4 -2
  350. package/src/generic/PubSubManager.ts +54 -1
  351. package/src/generic/PushStatusResolver.ts +108 -11
  352. package/src/generic/ResolverBase.ts +237 -107
  353. package/src/generic/RunViewResolver.ts +80 -15
  354. package/src/generic/actionOutputFieldSecurity.ts +75 -0
  355. package/src/generic/fieldSecurityTransport.ts +62 -0
  356. package/src/index.ts +127 -68
  357. package/src/integration/CustomColumnPromoter.ts +6 -1
  358. package/src/logging/variablesLoggingMiddleware.ts +59 -3
  359. package/src/realtimeSessions/README.md +7 -3
  360. package/src/realtimeSessions/SessionVerificationPortsBase.ts +1 -0
  361. package/src/realtimeSessions/ownedRealtimeSession.ts +20 -6
  362. package/src/realtimeSessions/sessionAccess.ts +181 -25
  363. package/src/realtimeSessions/sessionConfigStamps.ts +1 -1
  364. package/src/realtimeSessions/subscriptionAuthorization.ts +4 -2
  365. package/src/realtimeSessions/verificationWorkflow.ts +3 -1
  366. package/src/realtimeWidget/WidgetRouter.ts +22 -4
  367. package/src/realtimeWidget/WidgetSessionService.ts +252 -17
  368. package/src/realtimeWidget/host-identity.ts +1 -1
  369. package/src/realtimeWidget/widgetCore.ts +30 -1
  370. package/src/realtimeWidget/widgetGuestElevation.ts +2 -1
  371. package/src/resolvers/ActionResolver.ts +78 -15
  372. package/src/resolvers/AdhocQueryResolver.ts +1 -0
  373. package/src/resolvers/AgentRunStatusPublisher.ts +36 -8
  374. package/src/resolvers/AgentSessionResolver.ts +46 -18
  375. package/src/resolvers/EntityCommunicationsResolver.ts +56 -15
  376. package/src/resolvers/FetchEntityVectorsResolver.ts +132 -21
  377. package/src/resolvers/FileCategoryResolver.ts +1 -0
  378. package/src/resolvers/FileResolver.ts +14 -4
  379. package/src/resolvers/IntegrationDiscoveryResolver.ts +49 -7
  380. package/src/resolvers/MergeRecordsResolver.ts +23 -2
  381. package/src/resolvers/PipelineProgressResolver.ts +12 -3
  382. package/src/resolvers/QueryResolver.ts +20 -6
  383. package/src/resolvers/QuerySystemUserResolver.ts +15 -6
  384. package/src/resolvers/RSUResolver.ts +12 -0
  385. package/src/resolvers/RealtimeClientSessionResolver.ts +104 -33
  386. package/src/resolvers/RemoteBrowserActionResolver.ts +15 -10
  387. package/src/resolvers/RunAIAgentResolver.ts +258 -82
  388. package/src/resolvers/RunTemplateResolver.ts +6 -4
  389. package/src/resolvers/RunTestResolver.ts +3 -1
  390. package/src/resolvers/SyncDataResolver.ts +4 -0
  391. package/src/resolvers/TestQuerySQLResolver.ts +1 -0
  392. package/src/resolvers/TransactionGroupResolver.ts +66 -15
  393. package/src/resolvers/UserViewResolver.ts +2 -1
  394. package/src/resolvers/__tests__/MergeRecordsResolver.test.ts +43 -0
  395. package/src/rest/EntityCRUDHandler.ts +22 -5
  396. package/src/rest/RESTEndpointHandler.ts +8 -5
  397. package/src/rest/RealtimeRecordingHandler.ts +13 -10
  398. package/src/rest/ViewOperationsHandler.ts +24 -6
  399. package/src/serverRoleConfigUnits.ts +48 -0
  400. package/src/services/TaskGraphContinuationDeliverer.ts +52 -20
  401. package/src/types.ts +23 -1
@@ -1,4 +1,4 @@
1
- import { EntityPermissionType, FieldValueCollection, EntitySaveOptions, IMetadataProvider, LogError, UserInfo } from '@memberjunction/core';
1
+ import { AssertClientInputWritableForUser, EntityPermissionType, FieldValueCollection, EntitySaveOptions, IMetadataProvider, LogError, UserInfo } from '@memberjunction/core';
2
2
  import { NormalizeUUID } from '@memberjunction/global';
3
3
  import { MJFileEntity, MJFileStorageProviderEntity, MJFileStorageAccountEntity } from '@memberjunction/core-entities';
4
4
  import {
@@ -47,6 +47,7 @@ import {
47
47
  import { CreateMJFileInput, MJFileResolver as FileResolverBase, MJFile_, UpdateMJFileInput } from '../generated/generated.js';
48
48
  import { FieldMapper } from '@memberjunction/graphql-dataprovider';
49
49
  import { GetReadOnlyProvider } from '../util.js';
50
+ import { ProjectEntityObjectForCaller, ProjectableEntityObject } from '../generic/fieldSecurityTransport.js';
50
51
  import { MediaAccessKeyManager } from '../rest/MediaAccessKeys.js';
51
52
  import { UploadTokenManager } from '../rest/UploadTokenManager.js';
52
53
  import { DeriveSidecarPath, ParsePeaksSidecar } from './peaksSidecar.js';
@@ -666,6 +667,8 @@ export class FileResolver extends FileResolverBase {
666
667
  const fileEntity = await provider.GetEntityObject<MJFileEntity>('MJ: Files', user);
667
668
  const providerEntity = await provider.GetEntityObject<MJFileStorageProviderEntity>('MJ: File Storage Providers', user);
668
669
  fileEntity.CheckPermissions(EntityPermissionType.Create, true);
670
+ // Field-level security: refuse a key the caller may not read, or that names no field, before anything else
671
+ AssertClientInputWritableForUser(fileEntity.EntityInfo, { ...input }, user);
669
672
 
670
673
  // The upload URL signs an overwrite of `Name` (and the row will point at `ProviderKey`): Write on the account the
671
674
  // provider resolves to, and the tracked-file rule on both keys, as for every other route that writes an object.
@@ -693,7 +696,7 @@ export class FileResolver extends FileResolverBase {
693
696
  if (!saved) {
694
697
  console.error('[CreateFile] File save failed:', fileEntity.LatestResult?.CompleteMessage);
695
698
  }
696
- const File = mapper.MapFields({ ...fileEntity.GetAll() });
699
+ const File = this.fileResponse(fileEntity, user, provider);
697
700
 
698
701
  return { File, UploadUrl, NameExists };
699
702
  }
@@ -760,8 +763,7 @@ export class FileResolver extends FileResolverBase {
760
763
  });
761
764
 
762
765
  const loaded = await fileEntity.Load(uploadResult.FileID);
763
- const mapper = new FieldMapper();
764
- const File = loaded ? (mapper.MapFields({ ...fileEntity.GetAll() }) as unknown as MJFile_) : undefined;
766
+ const File = loaded ? (this.fileResponse(fileEntity, user, provider) as unknown as MJFile_) : undefined;
765
767
 
766
768
  return {
767
769
  Success: true,
@@ -778,6 +780,14 @@ export class FileResolver extends FileResolverBase {
778
780
  }
779
781
  }
780
782
 
783
+ /**
784
+ * The file record for a response: its field values as the caller may read them, in GraphQL field
785
+ * names. The record was just saved or reloaded, and a save re-reads the whole row.
786
+ */
787
+ private fileResponse(fileEntity: ProjectableEntityObject, user: UserInfo, provider: IMetadataProvider): Record<string, unknown> {
788
+ return new FieldMapper().MapFields({ ...ProjectEntityObjectForCaller(fileEntity, user, provider) });
789
+ }
790
+
781
791
  /**
782
792
  * The account an upload will land in: the requested one, else the engine's default (the first active account) —
783
793
  * returned as an explicit ID so the account the gate checks is the account `UploadFile` uses. A requested ID is
@@ -33,7 +33,8 @@ import {
33
33
  IntegrationSchemaSync,
34
34
  decideSchemaLimitViolations,
35
35
  IntegrationConnectorCreationPipeline,
36
- IntegrationActionGenerator
36
+ IntegrationActionGenerator,
37
+ ObjectsWithWatermarkFieldChange
37
38
  } from "@memberjunction/integration-engine";
38
39
  import type {
39
40
  IntegrationActionVerb,
@@ -1624,7 +1625,7 @@ export class IntegrationDiscoveryResolver extends ResolverBase {
1624
1625
  Platform: validatedPlatform,
1625
1626
  MJVersion: process.env.MJ_VERSION ?? '5.11.0',
1626
1627
  SourceType: companyIntegration.Integration,
1627
- AdditionalSchemaInfoPath: process.env.RSU_ADDITIONAL_SCHEMA_INFO_PATH ?? 'additionalSchemaInfo.json',
1628
+ AdditionalSchemaInfoPath: RuntimeSchemaManager.Instance.AdditionalSchemaInfoPath,
1628
1629
  MigrationsDir: process.env.RSU_MIGRATIONS_PATH ?? 'migrations/rsu',
1629
1630
  MetadataDir: process.env.RSU_METADATA_DIR ?? 'metadata',
1630
1631
  ExistingTables: this.buildExistingTables(targetConfigs, provider),
@@ -3431,7 +3432,7 @@ export class IntegrationDiscoveryResolver extends ResolverBase {
3431
3432
  Platform: validatedPlatform,
3432
3433
  MJVersion: process.env.MJ_VERSION ?? '5.11.0',
3433
3434
  SourceType: companyIntegration.Integration,
3434
- AdditionalSchemaInfoPath: process.env.RSU_ADDITIONAL_SCHEMA_INFO_PATH ?? 'additionalSchemaInfo.json',
3435
+ AdditionalSchemaInfoPath: RuntimeSchemaManager.Instance.AdditionalSchemaInfoPath,
3435
3436
  MigrationsDir: process.env.RSU_MIGRATIONS_PATH ?? 'migrations/rsu',
3436
3437
  MetadataDir: process.env.RSU_METADATA_DIR ?? 'metadata',
3437
3438
  ExistingTables: this.buildExistingTables(targetConfigs, provider),
@@ -3516,8 +3517,14 @@ export class IntegrationDiscoveryResolver extends ResolverBase {
3516
3517
  return { Success: false, Message: 'No valid schema inputs to process', Items: itemResults };
3517
3518
  }
3518
3519
 
3520
+ // Retrying variant, matching `IntegrationApplySchema` (which retries via
3521
+ // SchemaBuilder.RunSchemaPipeline). This is the same install path over the same
3522
+ // SchemaBuilder-generated DDL, and its expensive middle steps — ExecuteMigration,
3523
+ // RunCodeGen, CompileTypeScript, RestartMJAPI — fail transiently. The wrapper replays
3524
+ // only those four, and only while NOTHING in the batch has been applied, so a partially
3525
+ // succeeded batch is still reported as-is rather than double-applied.
3519
3526
  const rsm = RuntimeSchemaManager.Instance;
3520
- const batchResult = await rsm.RunPipelineBatch(pipelineInputs);
3527
+ const batchResult = await rsm.RunPipelineBatchWithRetry(pipelineInputs);
3521
3528
 
3522
3529
  return {
3523
3530
  Success: batchResult.SuccessCount > 0,
@@ -4205,7 +4212,7 @@ export class IntegrationDiscoveryResolver extends ResolverBase {
4205
4212
  Platform: platform,
4206
4213
  MJVersion: process.env.MJ_VERSION ?? '5.11.0',
4207
4214
  SourceType: companyIntegration.Integration,
4208
- AdditionalSchemaInfoPath: process.env.RSU_ADDITIONAL_SCHEMA_INFO_PATH ?? 'additionalSchemaInfo.json',
4215
+ AdditionalSchemaInfoPath: RuntimeSchemaManager.Instance.AdditionalSchemaInfoPath,
4209
4216
  MigrationsDir: process.env.RSU_MIGRATIONS_PATH ?? 'migrations/rsu',
4210
4217
  MetadataDir: process.env.RSU_METADATA_DIR ?? 'metadata',
4211
4218
  ExistingTables: this.buildExistingTables(targetConfigs, provider),
@@ -6186,7 +6193,7 @@ export class IntegrationDiscoveryResolver extends ResolverBase {
6186
6193
  Platform: validatedPlatform,
6187
6194
  MJVersion: process.env.MJ_VERSION ?? '5.11.0',
6188
6195
  SourceType: companyIntegration.Integration,
6189
- AdditionalSchemaInfoPath: process.env.RSU_ADDITIONAL_SCHEMA_INFO_PATH ?? 'additionalSchemaInfo.json',
6196
+ AdditionalSchemaInfoPath: RuntimeSchemaManager.Instance.AdditionalSchemaInfoPath,
6190
6197
  MigrationsDir: process.env.RSU_MIGRATIONS_PATH ?? 'migrations/rsu',
6191
6198
  MetadataDir: process.env.RSU_METADATA_DIR ?? 'metadata',
6192
6199
  ExistingTables: existingTables,
@@ -6227,6 +6234,36 @@ export class IntegrationDiscoveryResolver extends ResolverBase {
6227
6234
  }
6228
6235
  }
6229
6236
 
6237
+ // ── Phase 5b — MJ-RUN-37: a WATERMARK-FIELD swap is a change the DDL diff cannot see ──
6238
+ //
6239
+ // A connector upgrade can move an object's incremental cursor to a different source
6240
+ // column (`modified_at` → `last_updated`). Phase 1's persist already DETECTS that — the
6241
+ // overlay rewrites `IntegrationObject.IncrementalWatermarkField` and records it in the
6242
+ // merge log's `ChangedAttributes`. Nothing consumed it, and no other source of
6243
+ // `changedObjects` can: a cursor swap adds no column (so `diff.AddedColumns` and
6244
+ // `ModifiedColumns` are both empty) and touches no field map (so Phase 6 stays quiet).
6245
+ //
6246
+ // The object was therefore treated as unchanged, its Pull watermark survived, and the
6247
+ // next incremental sync applied the OLD column's stored value as a lower bound on the
6248
+ // NEW one. Where the new column sorts later, every row below that value is filtered out
6249
+ // AT THE SOURCE — permanently, and with no artifact: the source reports a total
6250
+ // consistent with the filter it was given, so fetched equals expected and the run
6251
+ // closes clean. Absence, not duplication, is what this produces.
6252
+ //
6253
+ // Scoped to CONTINUING maps deliberately. A new object has no watermark to invalidate
6254
+ // and a removed one has no sync left to protect; widening this to every persisted object
6255
+ // would inflate `ChangedObjects` (and `hasChanges`) with names Phase 7 cannot act on.
6256
+ const watermarkFieldChanged = ObjectsWithWatermarkFieldChange(
6257
+ refresh.PersistResult?.ObjectMergeLog ?? [],
6258
+ continuingMaps.map(m => m.ExternalObjectName ?? ''),
6259
+ );
6260
+ for (const name of watermarkFieldChanged) {
6261
+ if (!changedObjects.some(n => n.toLowerCase() === name.toLowerCase())) changedObjects.push(name);
6262
+ }
6263
+ if (watermarkFieldChanged.length > 0) {
6264
+ LogStatus(`[SchemaEvolution] IncrementalWatermarkField changed on ${watermarkFieldChanged.length} object(s) — resetting their Pull watermarks: ${watermarkFieldChanged.join(', ')}`);
6265
+ }
6266
+
6230
6267
  // ── Phase 6 — reconcile CONTINUING maps' field maps to the resolution ──
6231
6268
  // Added source fields get field maps (Active on enabled maps, Disabled on disabled
6232
6269
  // maps); field maps whose source field vanished from the resolution are disabled.
@@ -6301,8 +6338,13 @@ export class IntegrationDiscoveryResolver extends ResolverBase {
6301
6338
  rsuInput.ContextUser = user;
6302
6339
  }
6303
6340
 
6341
+ // Retrying variant, for the same reason every other install path uses it: a refresh
6342
+ // is unattended and long, and a transient ExecuteMigration / RunCodeGen /
6343
+ // CompileTypeScript / RestartMJAPI failure would otherwise abandon it with the
6344
+ // metadata already re-resolved and the DDL not applied — the half-state an operator
6345
+ // then has to diagnose. Replay happens only while nothing was applied.
6304
6346
  const rsm = RuntimeSchemaManager.Instance;
6305
- const batchResult = await rsm.RunPipelineBatch([rsuInput]);
6347
+ const batchResult = await rsm.RunPipelineBatchWithRetry([rsuInput]);
6306
6348
  const pipelineResult = batchResult.Results[0];
6307
6349
  pipelineSteps = pipelineResult?.Steps.map((s: RSUPipelineStep) => ({
6308
6350
  Name: s.Name, Status: s.Status, DurationMs: s.DurationMs, Message: s.Message, StepIndex: s.StepIndex, StepTotal: s.StepTotal,
@@ -38,6 +38,18 @@ export class EntityDependencyResolver {
38
38
  }
39
39
  }
40
40
 
41
+ @InputType()
42
+ export class RecordDependencyOptionsInput {
43
+ @Field(() => String, { nullable: true })
44
+ LinkType?: 'both' | 'hard' | 'soft';
45
+
46
+ @Field(() => Boolean, { nullable: true })
47
+ IncludeSystemEntities?: boolean;
48
+
49
+ @Field(() => [String], { nullable: true })
50
+ EntityNames?: string[];
51
+ }
52
+
41
53
  @ObjectType()
42
54
  export class RecordDependencyResult {
43
55
  @Field(() => String)
@@ -49,6 +61,9 @@ export class RecordDependencyResult {
49
61
  @Field(() => String)
50
62
  FieldName: string; // required
51
63
 
64
+ @Field(() => String, { nullable: true })
65
+ FieldDisplayName?: string;
66
+
52
67
  @Field(() => CompositeKeyOutputType)
53
68
  PrimaryKey: CompositeKey;
54
69
 
@@ -70,17 +85,23 @@ export class RecordDependencyResolver {
70
85
  @Arg('entityName', () => String) entityName: string,
71
86
  @Arg('CompositeKey', () => CompositeKeyInputType) ckInput: CompositeKeyInputType,
72
87
  @Ctx() { dataSource, userPayload, providers }: AppContext,
73
- @PubSub() pubSub: PubSubEngine
88
+ @PubSub() pubSub: PubSubEngine,
89
+ @Arg('options', () => RecordDependencyOptionsInput, { nullable: true }) options?: RecordDependencyOptionsInput
74
90
  ) {
75
91
  try {
76
92
  const md = GetReadOnlyProvider(providers);
77
93
  const ck = new CompositeKey(ckInput.KeyValuePairs);
78
- const result = await md.GetRecordDependencies(entityName, ck);
94
+ const contextUser = userPayload?.userRecord;
95
+ const result =
96
+ options !== undefined || contextUser !== undefined
97
+ ? await md.GetRecordDependencies(entityName, ck, contextUser, options)
98
+ : await md.GetRecordDependencies(entityName, ck);
79
99
 
80
100
  return result.map(dep => ({
81
101
  EntityName: dep.EntityName,
82
102
  RelatedEntityName: dep.RelatedEntityName,
83
103
  FieldName: dep.FieldName,
104
+ FieldDisplayName: dep.FieldDisplayName,
84
105
  PrimaryKey: dep.PrimaryKey,
85
106
  CompositeKey: dep.PrimaryKey,
86
107
  IsSoftLink: dep.IsSoftLink,
@@ -1,6 +1,7 @@
1
1
  import { Resolver, Subscription, Root, ObjectType, Field, Float, Mutation, Arg, Ctx, PubSub, PubSubEngine } from 'type-graphql';
2
2
  import { AppContext } from '../types.js';
3
3
  import { LogStatus, LogError } from '@memberjunction/core';
4
+ import { UUIDsEqual } from '@memberjunction/global';
4
5
  import { ResolverBase } from '../generic/ResolverBase.js';
5
6
 
6
7
  const PIPELINE_PROGRESS_TOPIC = 'PIPELINE_PROGRESS';
@@ -49,6 +50,16 @@ export class PipelineStartResult {
49
50
  ErrorMessage?: string;
50
51
  }
51
52
 
53
+ export function PipelineProgressFilter({
54
+ payload,
55
+ args,
56
+ }: {
57
+ payload: PipelineProgressNotification;
58
+ args: { pipelineRunID: string };
59
+ }): boolean {
60
+ return UUIDsEqual(payload.PipelineRunID, args.pipelineRunID) || payload.PipelineRunID === args.pipelineRunID;
61
+ }
62
+
52
63
  @Resolver()
53
64
  export class PipelineProgressResolver extends ResolverBase {
54
65
  /**
@@ -56,9 +67,7 @@ export class PipelineProgressResolver extends ResolverBase {
56
67
  */
57
68
  @Subscription(() => PipelineProgressNotification, {
58
69
  topics: PIPELINE_PROGRESS_TOPIC,
59
- filter: ({ payload, args }: { payload: PipelineProgressNotification; args: { pipelineRunID: string } }) => {
60
- return payload.PipelineRunID === args.pipelineRunID;
61
- },
70
+ filter: PipelineProgressFilter,
62
71
  })
63
72
  PipelineProgress(
64
73
  @Root() notification: PipelineProgressNotification,
@@ -226,7 +226,8 @@ export class RunQueryResolver extends ResolverBase {
226
226
  ForceAuditLog: ForceAuditLog,
227
227
  AuditLogDescription: AuditLogDescription,
228
228
  Enrichment: Enrichment,
229
- DataSource: DataSource
229
+ DataSource: DataSource,
230
+ TimeZone: context.userPayload.TimeZone
230
231
  },
231
232
  context.userPayload.userRecord);
232
233
 
@@ -247,6 +248,7 @@ export class RunQueryResolver extends ResolverBase {
247
248
  QueryID: QueryID,
248
249
  QueryName: queryName || 'Unknown Query',
249
250
  Success: result.Success ?? false,
251
+ // fls-guard: saved-query rows. Its SQL is refused before it runs if it reads a field the caller is denied, unless the query has `MJ: Query Permissions` rows, which authorize it by role alone
250
252
  Results: JSON.stringify(result.Results ?? null),
251
253
  RowCount: result.RowCount ?? 0,
252
254
  TotalRowCount: result.TotalRowCount ?? 0,
@@ -288,7 +290,8 @@ export class RunQueryResolver extends ResolverBase {
288
290
  ForceAuditLog: ForceAuditLog,
289
291
  AuditLogDescription: AuditLogDescription,
290
292
  Enrichment: Enrichment,
291
- DataSource: DataSource
293
+ DataSource: DataSource,
294
+ TimeZone: context.userPayload.TimeZone
292
295
  },
293
296
  context.userPayload.userRecord);
294
297
 
@@ -296,6 +299,7 @@ export class RunQueryResolver extends ResolverBase {
296
299
  QueryID: result.QueryID || '',
297
300
  QueryName: QueryName,
298
301
  Success: result.Success ?? false,
302
+ // fls-guard: saved-query rows. Its SQL is refused before it runs if it reads a field the caller is denied, unless the query has `MJ: Query Permissions` rows, which authorize it by role alone
299
303
  Results: JSON.stringify(result.Results ?? null),
300
304
  RowCount: result.RowCount ?? 0,
301
305
  TotalRowCount: result.TotalRowCount ?? 0,
@@ -337,7 +341,8 @@ export class RunQueryResolver extends ResolverBase {
337
341
  ForceAuditLog: ForceAuditLog,
338
342
  AuditLogDescription: AuditLogDescription,
339
343
  Enrichment: Enrichment,
340
- DataSource: DataSource
344
+ DataSource: DataSource,
345
+ TimeZone: context.userPayload.TimeZone
341
346
  },
342
347
  context.userPayload.userRecord);
343
348
 
@@ -358,6 +363,7 @@ export class RunQueryResolver extends ResolverBase {
358
363
  QueryID: QueryID,
359
364
  QueryName: queryName || 'Unknown Query',
360
365
  Success: result.Success ?? false,
366
+ // fls-guard: saved-query rows. Its SQL is refused before it runs if it reads a field the caller is denied, unless the query has `MJ: Query Permissions` rows, which authorize it by role alone
361
367
  Results: JSON.stringify(result.Results ?? null),
362
368
  RowCount: result.RowCount ?? 0,
363
369
  TotalRowCount: result.TotalRowCount ?? 0,
@@ -398,7 +404,8 @@ export class RunQueryResolver extends ResolverBase {
398
404
  ForceAuditLog: ForceAuditLog,
399
405
  AuditLogDescription: AuditLogDescription,
400
406
  Enrichment: Enrichment,
401
- DataSource: DataSource
407
+ DataSource: DataSource,
408
+ TimeZone: context.userPayload.TimeZone
402
409
  },
403
410
  context.userPayload.userRecord);
404
411
 
@@ -406,6 +413,7 @@ export class RunQueryResolver extends ResolverBase {
406
413
  QueryID: result.QueryID || '',
407
414
  QueryName: QueryName,
408
415
  Success: result.Success ?? false,
416
+ // fls-guard: saved-query rows. Its SQL is refused before it runs if it reads a field the caller is denied, unless the query has `MJ: Query Permissions` rows, which authorize it by role alone
409
417
  Results: JSON.stringify(result.Results ?? null),
410
418
  RowCount: result.RowCount ?? 0,
411
419
  TotalRowCount: result.TotalRowCount ?? 0,
@@ -447,7 +455,8 @@ export class RunQueryResolver extends ResolverBase {
447
455
  ForceAuditLog: i.ForceAuditLog,
448
456
  AuditLogDescription: i.AuditLogDescription,
449
457
  Enrichment: i.Enrichment,
450
- DataSource: i.DataSource
458
+ DataSource: i.DataSource,
459
+ TimeZone: context.userPayload.TimeZone
451
460
  }));
452
461
 
453
462
  // Execute all queries in parallel using the batch method
@@ -460,6 +469,7 @@ export class RunQueryResolver extends ResolverBase {
460
469
  QueryID: result.QueryID || inputItem.QueryID || '',
461
470
  QueryName: result.QueryName || inputItem.QueryName || 'Unknown Query',
462
471
  Success: result.Success ?? false,
472
+ // fls-guard: saved-query rows. Its SQL is refused before it runs if it reads a field the caller is denied, unless the query has `MJ: Query Permissions` rows, which authorize it by role alone
463
473
  Results: JSON.stringify(result.Results ?? null),
464
474
  RowCount: result.RowCount ?? 0,
465
475
  TotalRowCount: result.TotalRowCount ?? 0,
@@ -498,7 +508,8 @@ export class RunQueryResolver extends ResolverBase {
498
508
  ForceAuditLog: i.ForceAuditLog,
499
509
  AuditLogDescription: i.AuditLogDescription,
500
510
  Enrichment: i.Enrichment,
501
- DataSource: i.DataSource
511
+ DataSource: i.DataSource,
512
+ TimeZone: context.userPayload.TimeZone
502
513
  }));
503
514
 
504
515
  // Execute all queries in parallel using the batch method
@@ -511,6 +522,7 @@ export class RunQueryResolver extends ResolverBase {
511
522
  QueryID: result.QueryID || inputItem.QueryID || '',
512
523
  QueryName: result.QueryName || inputItem.QueryName || 'Unknown Query',
513
524
  Success: result.Success ?? false,
525
+ // fls-guard: saved-query rows. Its SQL is refused before it runs if it reads a field the caller is denied, unless the query has `MJ: Query Permissions` rows, which authorize it by role alone
514
526
  Results: JSON.stringify(result.Results ?? null),
515
527
  RowCount: result.RowCount ?? 0,
516
528
  TotalRowCount: result.TotalRowCount ?? 0,
@@ -563,6 +575,7 @@ export class RunQueryResolver extends ResolverBase {
563
575
  AuditLogDescription: item.params.AuditLogDescription,
564
576
  Enrichment: item.params.Enrichment,
565
577
  DataSource: item.params.DataSource,
578
+ TimeZone: context.userPayload.TimeZone,
566
579
  },
567
580
  cacheStatus: item.cacheStatus ? {
568
581
  maxUpdatedAt: item.cacheStatus.maxUpdatedAt,
@@ -578,6 +591,7 @@ export class RunQueryResolver extends ResolverBase {
578
591
  queryIndex: result.queryIndex,
579
592
  queryId: result.queryId,
580
593
  status: result.status,
594
+ // fls-guard: saved-query rows. Its SQL is refused before it runs if it reads a field the caller is denied, unless the query has `MJ: Query Permissions` rows, which authorize it by role alone
581
595
  Results: result.results ? JSON.stringify(result.results) : undefined,
582
596
  maxUpdatedAt: result.maxUpdatedAt,
583
597
  rowCount: result.rowCount,
@@ -1,6 +1,6 @@
1
1
  import { Arg, Ctx, Field, InputType, Mutation, ObjectType, registerEnumType, Resolver, PubSub, PubSubEngine } from 'type-graphql';
2
2
  import { AppContext } from '../types.js';
3
- import { LogError, UserInfo, CompositeKey, DatabaseProviderBase, LogStatus } from '@memberjunction/core';
3
+ import { AssertClientInputWritableForUser, LogError, UserInfo, CompositeKey, DatabaseProviderBase, LogStatus } from '@memberjunction/core';
4
4
  import { MJQueryCategoryEntity, MJQueryPermissionEntity, MJQueryFieldEntity, MJQueryParameterEntity, MJQueryEntityEntity, QueryEngine } from '@memberjunction/core-entities';
5
5
  import { MJQueryResolver, MJQuery_, MJQueryField_, MJQueryParameter_, MJQueryEntity_, MJQueryPermission_ } from '../generated/generated.js';
6
6
  import { GetReadWriteProvider } from '../util.js';
@@ -301,6 +301,8 @@ export class MJQueryResolverExtended extends MJQueryResolver {
301
301
  CacheMaxSize: input.CacheMaxSize || null
302
302
  };
303
303
 
304
+ // Field-level security: refuse a key the caller may not read, or that names no field
305
+ AssertClientInputWritableForUser(record.EntityInfo, fieldsToSet, context.userPayload.userRecord);
304
306
  record.SetMany(fieldsToSet, true);
305
307
 
306
308
  // Pass caller-provided parameter sample values to override LLM guesses
@@ -324,7 +326,7 @@ export class MJQueryResolverExtended extends MJQueryResolver {
324
326
  }
325
327
 
326
328
 
327
- return await this.buildSuccessResult(record);
329
+ return await this.buildSuccessResult(record, context.userPayload.userRecord, provider);
328
330
  }
329
331
  else {
330
332
  // Save failed - check if another request created the same query (race condition)
@@ -337,7 +339,7 @@ export class MJQueryResolverExtended extends MJQueryResolver {
337
339
  LogStatus(`[CreateQuery] Unique constraint detected for query '${input.Name}'. Using existing query (ID: ${existingQuery.ID}) created by concurrent request.`);
338
340
  const existingEntity = await provider.GetEntityObject<MJQueryEntityServer>('MJ: Queries', context.userPayload.userRecord);
339
341
  if (await existingEntity.Load(existingQuery.ID)) {
340
- return await this.buildSuccessResult(existingEntity);
342
+ return await this.buildSuccessResult(existingEntity, context.userPayload.userRecord, provider);
341
343
  }
342
344
  // Entity load failed after confirming the row exists — extremely rare
343
345
  return {
@@ -367,11 +369,16 @@ export class MJQueryResolverExtended extends MJQueryResolver {
367
369
  * Maps an MJQueryEntityServer (with loaded related metadata) to a QueryMutationResultType.
368
370
  * Uses entity.GetAll() for scalar fields so that new CodeGen-generated fields are included
369
371
  * automatically without manual updates. Related entity arrays are mapped explicitly.
372
+ *
373
+ * @param contextUser The caller the result is for. Field security projects the record for them.
370
374
  */
371
- private async buildSuccessResult(entity: MJQueryEntityServer): Promise<QueryMutationResultType> {
375
+ private async buildSuccessResult(entity: MJQueryEntityServer, contextUser: UserInfo, provider: DatabaseProviderBase): Promise<QueryMutationResultType> {
372
376
  return {
373
377
  Success: true,
374
- Query: await this.MapFieldNamesToCodeNames('MJ: Queries', entity.GetAll()) as MJQuery_,
378
+ // Mapped for the caller, so the field-security projection applies: the record was just
379
+ // saved, and a save re-reads the whole row.
380
+ // fls-guard: projected for contextUser by MapFieldNamesToCodeNames
381
+ Query: await this.MapFieldNamesToCodeNames('MJ: Queries', entity.GetAll(), contextUser, provider) as MJQuery_,
375
382
  Fields: this.mapFields(entity.QueryFields),
376
383
  Parameters: this.mapParameters(entity.QueryParameters),
377
384
  Entities: this.mapEntities(entity.QueryEntities),
@@ -523,6 +530,8 @@ export class MJQueryResolverExtended extends MJQueryResolver {
523
530
  if (input.CacheTTLMinutes !== undefined) updateFields.CacheTTLMinutes = input.CacheTTLMinutes;
524
531
  if (input.CacheMaxSize !== undefined) updateFields.CacheMaxSize = input.CacheMaxSize;
525
532
 
533
+ // Field-level security: refuse a key the caller may not read
534
+ AssertClientInputWritableForUser(queryEntity.EntityInfo, updateFields, context.userPayload.userRecord);
526
535
  // Use SetMany to update all fields at once
527
536
  queryEntity.SetMany(updateFields);
528
537
 
@@ -554,7 +563,7 @@ export class MJQueryResolverExtended extends MJQueryResolver {
554
563
  await this.createPermissions(provider, input.Permissions, queryID, context.userPayload.userRecord);
555
564
  }
556
565
 
557
- return await this.buildSuccessResult(queryEntity);
566
+ return await this.buildSuccessResult(queryEntity, context.userPayload.userRecord, provider);
558
567
 
559
568
  } catch (err) {
560
569
  LogError(err);
@@ -318,6 +318,18 @@ export class RSUResolver {
318
318
  SkipRestart: input.SkipRestart ?? undefined,
319
319
  }));
320
320
 
321
+ // MJ-APPLY-6 — deliberately the BARE call, not RunPipelineBatchWithRetry. Everywhere else
322
+ // the MigrationSQL was generated by SchemaBuilder, so the manager knows what a replay would
323
+ // re-execute. Here it is arbitrary SQL handed in by the caller, and `executeMigration` is
324
+ // NOT transactional: it splits on GO/statement boundaries and issues one ExecuteSQL per
325
+ // chunk, so a migration that fails on chunk 3 leaves chunks 1-2 committed while the step is
326
+ // recorded `failed` — which is exactly the state `canSafelyRetry` reads as "nothing was
327
+ // applied, safe to replay". The replay then re-runs the committed chunks and reports THEIR
328
+ // "already exists" error in place of the real one. For generated DDL that risk is accepted
329
+ // upstream (SchemaBuilder.RunSchemaPipeline); for caller-authored DDL it is not ours to
330
+ // take. This is also an attended operator mutation with no in-repo caller — the human on
331
+ // the other end sees the failure and decides. Keep it in step with the singular
332
+ // RunRuntimeSchemaUpdate above, which uses bare RunPipeline for the same reason.
321
333
  const batchResult = await rsm.RunPipelineBatch(pipelineInputs);
322
334
 
323
335
  return {