@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
@@ -1,5 +1,9 @@
1
- import { Field, ObjectType, Resolver, Root, Subscription } from 'type-graphql';
2
- import { Metadata, UserInfo } from '@memberjunction/core';
1
+ import 'reflect-metadata';
2
+ import { Arg, Ctx, Field, Int, ObjectType, Query, Resolver, Root, Subscription } from 'type-graphql';
3
+ import { EntityInfo, Metadata, RecordChangeFieldSecurityProjector, RecordChangesEntityName, UserInfo } from '@memberjunction/core';
4
+ import {
5
+ CacheInvalidationBroadcastManager,
6
+ } from '../broadcast/index.js';
3
7
 
4
8
  export const CACHE_INVALIDATION_TOPIC = 'CACHE_INVALIDATION';
5
9
 
@@ -36,22 +40,55 @@ export function ConfigureRecordDataBroadcast(entities: readonly string[] | undef
36
40
  *
37
41
  * The subscription below is filtered only by ENTITY-level read permission, so anything included
38
42
  * here is disclosed to every signed-in session that may read the entity at all — regardless of
39
- * row-level security or any tenant scoping a consumer applies on the read path, neither of which
40
- * this push traverses. The answer is therefore NO unless a deployment has explicitly opted the
41
- * entity in via `cacheSettings.recordDataBroadcastEntities`.
43
+ * row-level security, field-level security, or any tenant scoping a consumer applies on the read
44
+ * path, none of which this push traverses. The answer is therefore NO unless a deployment has
45
+ * explicitly opted the entity in via `cacheSettings.recordDataBroadcastEntities`, and always NO
46
+ * for an entity under field-level security (its own flag or an IS-A ancestor's), and for
47
+ * `MJ: Record Changes`, whatever the allowlist says: the row would carry columns (or, for Record
48
+ * Changes, other entities' values) some of those sessions may not read.
42
49
  *
43
50
  * Callers that get `false` simply omit `recordData`; the event still carries the entity name and
44
51
  * primary key, so clients evict and re-fetch through the normal access-controlled read path.
45
52
  *
46
53
  * Matching is exact (trimmed, case-insensitive) rather than substring: a loose match would let an
47
54
  * allowlisted name drag in every entity whose name contains it.
55
+ *
56
+ * @param entityInfo The entity's metadata, when the caller holds it. Otherwise it is looked up by
57
+ * name, so the field-security refusal applies either way.
48
58
  */
49
- export function MayBroadcastRecordData(entityName: string): boolean {
59
+ export function MayBroadcastRecordData(entityName: string, entityInfo?: EntityInfo | null): boolean {
50
60
  if (allowedRecordDataEntities.length === 0) return false;
61
+ if (fieldSecurityApplies(entityName, entityInfo)) return false;
62
+ if (isRecordChanges(entityName, entityInfo)) return false;
51
63
  if (allowedRecordDataEntities.includes('*')) return true;
52
64
  return allowedRecordDataEntities.includes(entityName.trim().toLowerCase());
53
65
  }
54
66
 
67
+ /** Whether field-level security applies to the entity, resolving it by name when not supplied. */
68
+ function fieldSecurityApplies(entityName: string, entityInfo: EntityInfo | null | undefined): boolean {
69
+ const info = entityInfo ?? lookUpEntity(entityName);
70
+ return Boolean(info?.FieldLevelSecurityApplies);
71
+ }
72
+
73
+ /**
74
+ * Whether the entity is the Record Changes audit trail. Its rows carry other entities' values in
75
+ * `ChangesJSON` and `FullRecordJSON`, which are projected per reader, so a row is never broadcast
76
+ * even though field-level security is not switched on for the audit entity itself.
77
+ */
78
+ function isRecordChanges(entityName: string, entityInfo: EntityInfo | null | undefined): boolean {
79
+ if (entityInfo) return RecordChangeFieldSecurityProjector.IsRecordChangesEntity(entityInfo);
80
+ return entityName.trim().toLowerCase() === RecordChangesEntityName.toLowerCase();
81
+ }
82
+
83
+ /** The entity by name from the process's metadata, or undefined when none is loaded. */
84
+ function lookUpEntity(entityName: string): EntityInfo | undefined {
85
+ try {
86
+ return new Metadata().EntityByName(entityName) ?? undefined; // global-provider-ok: metadata lookup for a process-wide broadcast gate
87
+ } catch {
88
+ return undefined;
89
+ }
90
+ }
91
+
55
92
  @ObjectType()
56
93
  export class CacheInvalidationNotification {
57
94
  @Field(() => String)
@@ -74,6 +111,48 @@ export class CacheInvalidationNotification {
74
111
 
75
112
  @Field(() => String, { nullable: true })
76
113
  RecordData?: string;
114
+
115
+ @Field(() => Int, { nullable: true })
116
+ Sequence?: number;
117
+ }
118
+
119
+ @ObjectType()
120
+ export class CacheInvalidationCatchUpResult {
121
+ @Field(() => Boolean)
122
+ RequiresFullRefresh!: boolean;
123
+
124
+ @Field(() => Int)
125
+ CurrentSequence!: number;
126
+
127
+ @Field(() => String)
128
+ ServerProcessID!: string;
129
+
130
+ @Field(() => [CacheInvalidationNotification])
131
+ Events!: CacheInvalidationNotification[];
132
+ }
133
+
134
+ @ObjectType()
135
+ export class CacheBroadcastConnectionToken {
136
+ @Field(() => String)
137
+ Provider!: string;
138
+
139
+ @Field(() => String)
140
+ Url!: string;
141
+
142
+ @Field(() => String, { nullable: true })
143
+ Token?: string;
144
+
145
+ @Field(() => [String])
146
+ Groups!: string[];
147
+
148
+ @Field(() => String)
149
+ ExpiresAt!: string;
150
+
151
+ @Field(() => String)
152
+ ServerProcessID!: string;
153
+
154
+ @Field(() => Int)
155
+ CurrentSequence!: number;
77
156
  }
78
157
 
79
158
  /**
@@ -88,6 +167,7 @@ export interface CacheInvalidationPayload {
88
167
  timestamp: Date;
89
168
  originSessionId?: string;
90
169
  recordData?: string;
170
+ sequence?: number;
91
171
  }
92
172
 
93
173
  /**
@@ -116,39 +196,65 @@ export interface CacheInvalidationFilterContext {
116
196
  * see the class docblock below.
117
197
  *
118
198
  * `GetUserPermisions` is an in-memory match of the entity's permission rows against the user's
119
- * roles, so this stays cheap enough to run per subscriber per event. It is not free: it runs for
120
- * every connected socket on every save, so keep it free of I/O.
199
+ * roles, memoized per `(payload, entityName, roleFingerprint)` by the active
200
+ * {@link BaseCacheInvalidationBroadcastProvider} so $N$ connected subscribers sharing $R$ role
201
+ * combinations only run `GetUserPermisions` $R$ times per event.
121
202
  */
122
203
  export function CacheInvalidationFilter(data: {
123
204
  payload: CacheInvalidationPayload;
124
205
  context: CacheInvalidationFilterContext | undefined;
125
206
  }): boolean {
126
207
  const user = data.context?.userPayload?.userRecord as UserInfo | undefined;
127
- // FAIL CLOSED on identity, matching `statusUpdatesFilter`. `onConnect` rejects a socket whose
128
- // token does not validate, so a subscriber with no user record should not exist; if one does,
129
- // it is the case least deserving of an unfiltered firehose.
130
208
  if (!user) {
131
209
  return false;
132
210
  }
211
+ return CacheInvalidationBroadcastManager.Instance.Provider.EvaluateEntityReadAccess(
212
+ data.payload,
213
+ user,
214
+ (entityName) => new Metadata().EntityByName(entityName) // global-provider-ok: subscription filter — server-wide metadata, no request-scoped provider exists here
215
+ );
216
+ }
133
217
 
134
- const entityName = data.payload?.entityName;
135
- if (!entityName) {
136
- return false;
137
- }
218
+ /** The part of a permission row and of a user this module reads, so the rule can be tested on plain objects. */
219
+ export interface ReadPermissionRow {
220
+ RoleID: string;
221
+ CanRead: boolean | null;
222
+ /** `Deny` rows subtract; they never carry the read the rule looks at. */
223
+ IsDeny?: boolean;
224
+ Type?: string | null;
225
+ ReadRLSFilterID: string | null;
226
+ }
227
+ export interface RoleHolder {
228
+ UserRoles?: ReadonlyArray<{ RoleID: string }> | null;
229
+ }
138
230
 
139
- // A pub/sub subscription filter has no per-request provider to thread: it runs on the server's
140
- // own metadata, which is the one place the entity's permission rows can be answered from.
141
- const entity = new Metadata().EntityByName(entityName); // global-provider-ok: subscription filter — server-wide metadata, no request-scoped provider exists here
142
- // DELIVER when the entity is unknown here, deliberately — the opposite of the choice above.
143
- // A name absent from metadata cannot be permission-checked, and withholding would silently
144
- // stop invalidating a legitimately cacheable entity, which is a correctness bug rather than a
145
- // disclosure one. All it discloses is the name and key of something this server cannot
146
- // describe.
147
- if (!entity) {
148
- return true;
149
- }
231
+ /**
232
+ * Is this user's read of the entity row-filtered? True when every read the user holds on the entity comes through a permission
233
+ * row that carries a `ReadRLSFilterID`: there is a row-level security filter between them and the rows, so a record's key, its
234
+ * existence and when it changed are not theirs to learn unless the row passes the filter — which a subscription filter cannot
235
+ * evaluate per event. False when any of their read rows is unfiltered (they could read the row anyway), and false when they hold
236
+ * no read at all (the entity-level filter has already withheld the event).
237
+ *
238
+ * The same in-memory match `GetUserPermisions` does, with no I/O, so it can run per subscriber per event.
239
+ */
240
+ export function ReadIsRowFiltered(entity: { Permissions: ReadonlyArray<ReadPermissionRow> }, user: RoleHolder): boolean {
241
+ return CacheInvalidationBroadcastManager.Instance.Provider.ReadIsRowFiltered(entity, user);
242
+ }
150
243
 
151
- return entity.GetUserPermisions(user).CanRead;
244
+ /**
245
+ * The event as one subscriber may see it. A subscriber whose read of the entity is row-filtered gets the entity, the action and
246
+ * the time, and neither the key nor the row: "something in this entity changed", which the client already takes as a
247
+ * whole-entity invalidation (a null key on `remote-invalidate`). Everyone else gets the event as published.
248
+ *
249
+ * Memoized per `(payload, entity, roleFingerprint)` via the active broadcast provider so $N$ subscribers
250
+ * sharing $R$ role buckets only allocate and evaluate `ReadIsRowFiltered` $R$ times per event.
251
+ */
252
+ export function ShapeCacheInvalidationEvent(
253
+ payload: CacheInvalidationPayload,
254
+ user: RoleHolder | undefined,
255
+ entity: { Permissions: ReadonlyArray<ReadPermissionRow> } | null | undefined,
256
+ ): CacheInvalidationNotification {
257
+ return CacheInvalidationBroadcastManager.Instance.Provider.ShapeEventForSubscriber(payload, user, entity);
152
258
  }
153
259
 
154
260
  /** @deprecated Use {@link CacheInvalidationFilter}. */
@@ -165,29 +271,8 @@ export class CacheInvalidationResolver {
165
271
  * Subscription that broadcasts cache invalidation events to connected clients, so cross-server
166
272
  * cache invalidation reaches browsers.
167
273
  *
168
- * Delivery is filtered only by ENTITY-level read permission (see {@link CacheInvalidationFilter}).
169
- * Every session that may read an entity at all still receives every event for it, from every
170
- * other session and tenant.
171
- *
172
- * Because that narrowing stops well short of row level, `RecordData` is only populated for entities a
173
- * deployment has opted in through `cacheSettings.recordDataBroadcastEntities` — see
174
- * {@link MayBroadcastRecordData}, applied at both publish sites.
175
- *
176
- * `EntityName`, `PrimaryKeyValues`, `Action` and `Timestamp` ARE still broadcast to every
177
- * session, and that is a disclosure, not a safe residue. An earlier version of this comment
178
- * called them "safe to broadcast" on the grounds that reading the record still goes through
179
- * the access-controlled path — true of the row's CONTENTS, and beside the point for the
180
- * metadata itself. A session with no relationship to a record still learns that it exists,
181
- * its stable key, and every time it changes; across entities it learns the cadence and volume
182
- * of another tenant's activity.
183
- *
184
- * The key is kept deliberately, because consumers need it to re-read the record they were
185
- * told about (see `ResolveEntityEventKey` in @memberjunction/core) and because the alternative
186
- * — a coarse "something in this entity changed" — forces whole-entity invalidation on the
187
- * hottest write paths. That is a trade, not an absence of cost. Narrowing it means filtering
188
- * delivery per subscriber, which needs an authorization predicate cheap enough to run per
189
- * subscriber per event; entity-level permissions (`EntityInfo.GetUserPermisions`, an in-memory
190
- * role match) would cover part of it, row-level tenancy would not.
274
+ * Delivery is filtered by ENTITY-level read permission (see {@link CacheInvalidationFilter})
275
+ * and shaped per role-fingerprint bucket (see {@link ShapeCacheInvalidationEvent}).
191
276
  */
192
277
  @Subscription(() => CacheInvalidationNotification, {
193
278
  topics: CACHE_INVALIDATION_TOPIC,
@@ -195,16 +280,43 @@ export class CacheInvalidationResolver {
195
280
  CacheInvalidationFilter(data),
196
281
  })
197
282
  cacheInvalidation( // case-violation-ok-legacy-back-compat: the property name is the GraphQL schema field name — renaming it breaks every client query
198
- @Root() payload: CacheInvalidationPayload
283
+ @Root() payload: CacheInvalidationPayload,
284
+ @Ctx() context: CacheInvalidationFilterContext | undefined
199
285
  ): CacheInvalidationNotification {
200
- return {
201
- EntityName: payload.entityName,
202
- PrimaryKeyValues: payload.primaryKeyValues ?? undefined,
203
- Action: payload.action,
204
- SourceServerID: payload.sourceServerId,
205
- Timestamp: payload.timestamp,
206
- OriginSessionID: payload.originSessionId ?? undefined,
207
- RecordData: payload.recordData ?? undefined,
208
- };
286
+ const user = context?.userPayload?.userRecord as UserInfo | undefined;
287
+ const provider = CacheInvalidationBroadcastManager.Instance.Provider;
288
+ const entity = payload?.entityName ? provider.ResolveEntityForPayload(payload) : null;
289
+ return provider.ShapeEventForSubscriber(payload, user, entity);
290
+ }
291
+
292
+ /**
293
+ * Returns all permission-allowed, RLS-shaped cache invalidation events strictly after
294
+ * `sinceSequence` from the server's bounded circular ring buffer.
295
+ *
296
+ * Invoked by `GraphQLDataProvider` when an HTTP response's `X-MJ-Cache-Seq` header or a
297
+ * WebSocket reconnect reveals missed invalidation sequence numbers.
298
+ */
299
+ @Query(() => CacheInvalidationCatchUpResult)
300
+ GetCacheInvalidationsSince(
301
+ @Arg('sinceSequence', () => Int) sinceSequence: number,
302
+ @Ctx() context: CacheInvalidationFilterContext | undefined
303
+ ): CacheInvalidationCatchUpResult {
304
+ const user = context?.userPayload?.userRecord as UserInfo | undefined;
305
+ return CacheInvalidationBroadcastManager.Instance.Provider.GetChangesSince(sinceSequence, user);
306
+ }
307
+
308
+ /**
309
+ * Negotiates a client broadcast connection (URL, signed token, and authorized role-bucket groups)
310
+ * for the active {@link BaseCacheInvalidationBroadcastProvider}.
311
+ */
312
+ @Query(() => CacheBroadcastConnectionToken)
313
+ async NegotiateCacheBroadcastConnection(
314
+ @Ctx() context: CacheInvalidationFilterContext | undefined
315
+ ): Promise<CacheBroadcastConnectionToken> {
316
+ const user = context?.userPayload?.userRecord as UserInfo | undefined;
317
+ if (!user) {
318
+ throw new Error('Authentication required to negotiate cache broadcast connection.');
319
+ }
320
+ return CacheInvalidationBroadcastManager.Instance.Provider.NegotiateClientConnection(user);
209
321
  }
210
322
  }
@@ -2,7 +2,7 @@ import { LogError, LogStatus, RunView } from '@memberjunction/core';
2
2
  import { IndexByUUID } from '@memberjunction/global';
3
3
  import { UserCache } from '@memberjunction/sqlserver-dataprovider';
4
4
  import type { IMetadataProvider, UserInfo } from '@memberjunction/core';
5
- import type { MJAIAgentRunEntity, MJConversationDetailEntity } from '@memberjunction/core-entities';
5
+ import type { MJAIAgentRunEntity, MJConversationDetailEntity, MJConversationDetailEntityExtended } from '@memberjunction/core-entities';
6
6
 
7
7
  /**
8
8
  * Grace period after a run reaches a terminal state before its conversation detail is considered
@@ -110,7 +110,9 @@ export async function ReconcileOrphanedConversationDetails(
110
110
  LogError(`[OrphanDetailReconciler] No owner resolved for conversation ${detail.ConversationID}; leaving detail ${detail.ID}`);
111
111
  continue;
112
112
  }
113
- const writable = await provider.GetEntityObject<MJConversationDetailEntity>('MJ: Conversation Details', owner);
113
+ const writable = await provider.GetEntityObject<MJConversationDetailEntityExtended>('MJ: Conversation Details', owner);
114
+ // Closing an agent's reply is the server's write: unmarked, the gate refuses any change to what a reply says.
115
+ writable.MarkServerAuthoredReply();
114
116
  if (!(await writable.Load(detail.ID))) {
115
117
  LogError(`[OrphanDetailReconciler] Could not load detail ${detail.ID} for write`);
116
118
  continue;
@@ -1,5 +1,45 @@
1
1
  import { PubSubEngine } from 'type-graphql';
2
2
  import { BaseSingleton } from '@memberjunction/global';
3
+ import {
4
+ CACHE_INVALIDATION_TOPIC,
5
+ type CacheInvalidationPayload,
6
+ } from './CacheInvalidationResolver.js';
7
+ import {
8
+ GetSessionStatusTopic,
9
+ PUSH_STATUS_UPDATES_TOPIC,
10
+ } from './PushStatusResolver.js';
11
+ import { CacheInvalidationBroadcastManager } from '../broadcast/index.js';
12
+
13
+ const _wrappedPubSubEngines = new WeakSet<PubSubEngine>();
14
+
15
+ /**
16
+ * Wraps a TypeGraphQL `PubSubEngine` so that publishes to {@link PUSH_STATUS_UPDATES_TOPIC}
17
+ * carrying a specific `sessionId` (`sessionId !== '*'`) are dynamically routed in $O(1)$ to
18
+ * `PUSH_STATUS_UPDATES:session:<sessionId>` instead of waking every connected browser tab.
19
+ * Wildcard (`sessionId === '*'`) updates continue to publish on {@link PUSH_STATUS_UPDATES_TOPIC}.
20
+ */
21
+ export function CreateSessionRoutingPubSub(pubSub: PubSubEngine): PubSubEngine {
22
+ if (!pubSub || typeof pubSub.publish !== 'function' || _wrappedPubSubEngines.has(pubSub)) {
23
+ return pubSub;
24
+ }
25
+ const rawPublish = pubSub.publish.bind(pubSub);
26
+ pubSub.publish = async (routingKey: string, payload: unknown): Promise<void> => {
27
+ if (
28
+ routingKey === PUSH_STATUS_UPDATES_TOPIC &&
29
+ typeof payload === 'object' &&
30
+ payload !== null
31
+ ) {
32
+ const sessionId = (payload as { sessionId?: unknown }).sessionId;
33
+ if (typeof sessionId === 'string' && sessionId.trim().length > 0 && sessionId.trim() !== '*') {
34
+ await rawPublish(GetSessionStatusTopic(sessionId), payload);
35
+ return;
36
+ }
37
+ }
38
+ await rawPublish(routingKey, payload);
39
+ };
40
+ _wrappedPubSubEngines.add(pubSub);
41
+ return pubSub;
42
+ }
3
43
 
4
44
  /**
5
45
  * Singleton manager that holds a reference to the type-graphql PubSubEngine,
@@ -20,9 +60,10 @@ export class PubSubManager extends BaseSingleton<PubSubManager> {
20
60
  /**
21
61
  * Sets the PubSubEngine instance. Should be called once during server startup
22
62
  * after buildSchemaSync creates the PubSub instance.
63
+ * Automatically installs $O(1)$ dynamic session routing for `PUSH_STATUS_UPDATES`.
23
64
  */
24
65
  public SetPubSubEngine(pubSub: PubSubEngine): void {
25
- this._pubSub = pubSub;
66
+ this._pubSub = CreateSessionRoutingPubSub(pubSub);
26
67
  }
27
68
 
28
69
  /**
@@ -34,9 +75,21 @@ export class PubSubManager extends BaseSingleton<PubSubManager> {
34
75
 
35
76
  /**
36
77
  * Publishes a payload to the specified topic via the PubSubEngine.
78
+ * For `CACHE_INVALIDATION`, automatically stamps a monotonic sequence number and
79
+ * records the event in the active {@link BaseCacheInvalidationBroadcastProvider}'s ring buffer.
37
80
  * No-op if the PubSubEngine has not been configured yet.
38
81
  */
39
82
  public Publish(topic: string, payload: Record<string, unknown>): void {
83
+ if (topic === CACHE_INVALIDATION_TOPIC) {
84
+ const provider = CacheInvalidationBroadcastManager.Instance.Provider;
85
+ const entityName = typeof payload.entityName === 'string' ? payload.entityName : undefined;
86
+ if (!provider.ShouldBroadcastEntity(entityName)) {
87
+ return;
88
+ }
89
+ if (payload.sequence === undefined) {
90
+ provider.RecordInvalidation(payload as unknown as CacheInvalidationPayload);
91
+ }
92
+ }
40
93
  if (this._pubSub) {
41
94
  this._pubSub.publish(topic, payload);
42
95
  } else {
@@ -1,6 +1,8 @@
1
+ import 'reflect-metadata';
1
2
  import { Arg, Field, ID, ObjectType, PubSubEngine, Resolver, ResolverFilterData, Root, Subscription } from 'type-graphql';
2
3
  import { MJGlobal, UUIDsEqual } from '@memberjunction/global';
3
4
  import { UserPayload } from '../types.js';
5
+ import { ResolveSessionScopeIdentity, type SessionAccessPrincipal } from '../realtimeSessions/sessionAccess.js';
4
6
 
5
7
  export const PUSH_STATUS_UPDATES_TOPIC = 'PUSH_STATUS_UPDATES';
6
8
 
@@ -94,6 +96,57 @@ export function ParseReplicatedStatusUpdate(raw: string, localServerId: string):
94
96
  return { sessionId, ownerUserId, message: body, SourceServerId: source };
95
97
  }
96
98
 
99
+ /**
100
+ * Returns the $O(1)$ session-scoped pub/sub topic name for a given `sessionId`.
101
+ * Wildcard (`'*'`) or empty session IDs fall back to the broadcast {@link PUSH_STATUS_UPDATES_TOPIC}.
102
+ */
103
+ export function GetSessionStatusTopic(sessionId: string): string {
104
+ const normalized = sessionId?.trim();
105
+ if (!normalized || normalized === '*') {
106
+ return PUSH_STATUS_UPDATES_TOPIC;
107
+ }
108
+ return `${PUSH_STATUS_UPDATES_TOPIC}:session:${normalized}`;
109
+ }
110
+
111
+ interface ParsedMessageCacheEntry {
112
+ parsed: Record<string, unknown> | null;
113
+ isJson: boolean;
114
+ }
115
+
116
+ const _parsedMessageMemo = new WeakMap<PushStatusNotificationPayload, ParsedMessageCacheEntry>();
117
+
118
+ /**
119
+ * Parses and memoizes the JSON `message` envelope on a {@link PushStatusNotificationPayload}
120
+ * in a `WeakMap` so multiple consumers (`ShouldReplicateStatusUpdate`, filters, hooks) never
121
+ * call `JSON.parse` more than once per payload object.
122
+ */
123
+ export function ParseStatusUpdateMessage(
124
+ payload: PushStatusNotificationPayload
125
+ ): ParsedMessageCacheEntry {
126
+ const cached = _parsedMessageMemo.get(payload);
127
+ if (cached) {
128
+ return cached;
129
+ }
130
+ if (!payload.message) {
131
+ const emptyEntry: ParsedMessageCacheEntry = { parsed: null, isJson: false };
132
+ _parsedMessageMemo.set(payload, emptyEntry);
133
+ return emptyEntry;
134
+ }
135
+ try {
136
+ const raw: unknown = JSON.parse(payload.message);
137
+ const entry: ParsedMessageCacheEntry = {
138
+ parsed: typeof raw === 'object' && raw !== null ? (raw as Record<string, unknown>) : null,
139
+ isJson: true,
140
+ };
141
+ _parsedMessageMemo.set(payload, entry);
142
+ return entry;
143
+ } catch {
144
+ const plainEntry: ParsedMessageCacheEntry = { parsed: null, isJson: false };
145
+ _parsedMessageMemo.set(payload, plainEntry);
146
+ return plainEntry;
147
+ }
148
+ }
149
+
97
150
  /**
98
151
  * Whether an update is worth sending to other server instances.
99
152
  *
@@ -103,10 +156,20 @@ export function ParseReplicatedStatusUpdate(raw: string, localServerId: string):
103
156
  * Progress and completion are low-rate and each is individually meaningful, which is exactly the
104
157
  * kind of message that must survive landing on the wrong replica.
105
158
  */
106
- export function ShouldReplicateStatusUpdate(message?: string): boolean {
159
+ export function ShouldReplicateStatusUpdate(
160
+ message?: string,
161
+ payload?: PushStatusNotificationPayload
162
+ ): boolean {
107
163
  if (!message) {
108
164
  return false;
109
165
  }
166
+ if (payload && payload.message === message) {
167
+ const entry = ParseStatusUpdateMessage(payload);
168
+ if (!entry.isJson) {
169
+ return true;
170
+ }
171
+ return entry.parsed?.type !== 'StreamingContent';
172
+ }
110
173
  try {
111
174
  const parsed: unknown = JSON.parse(message);
112
175
  const type = (parsed as { type?: unknown })?.type;
@@ -150,7 +213,7 @@ export function PublishStatusUpdate(pubSub: PubSubEngine, params: StatusUpdatePa
150
213
  // Fan out to other instances. Only reached by updates originating HERE: a message arriving from
151
214
  // the bus is republished straight onto the local topic and never comes back through this
152
215
  // function, so there is no loop to break beyond the SourceServerId check on the receiving side.
153
- if (_publishHook && ShouldReplicateStatusUpdate(params.message)) {
216
+ if (_publishHook && ShouldReplicateStatusUpdate(params.message, payload)) {
154
217
  try {
155
218
  _publishHook(payload);
156
219
  } catch {
@@ -170,18 +233,47 @@ export interface StatusUpdatesFilterContext {
170
233
  userPayload?: UserPayload;
171
234
  }
172
235
 
236
+ /**
237
+ * The owner key of a status update for `user`: its user ID, or, for an anonymous visitor, its user ID plus its scope
238
+ * identity (`ResolveSessionScopeIdentity`). Every anonymous visitor shares one Anonymous user and the session id is
239
+ * chosen by the client (`x-session-id`), so the user ID alone would let one visitor receive another's run events.
240
+ * An anonymous visitor with no scope identity gets no key and receives nothing.
241
+ */
242
+ export function StatusUpdateOwnerKey(user: SessionAccessPrincipal | null | undefined): string {
243
+ if (!user?.ID) {
244
+ return '';
245
+ }
246
+ if (!user.IsMagicLinkAnonymous) {
247
+ return user.ID;
248
+ }
249
+ const scope = ResolveSessionScopeIdentity(user);
250
+ return scope ? `${user.ID.toLowerCase()}|${scope}` : '';
251
+ }
252
+
253
+ /**
254
+ * The owner key a status update is published with: that of the request's user, or, on a payload elevated by
255
+ * `ElevateUserPayload`, of the user the elevated principal stands in for (a public widget guest). The guest's socket
256
+ * is authenticated as the guest, so an update stamped with the elevated system user would never pass
257
+ * {@link StatusUpdatesFilter} and the guest would wait forever for its run to finish.
258
+ */
259
+ export function StatusUpdateOwnerID(userPayload: UserPayload | undefined): string {
260
+ return StatusUpdateOwnerKey(userPayload?.ElevatedFrom ?? userPayload?.userRecord);
261
+ }
262
+
173
263
  /**
174
264
  * Filter predicate for the `statusUpdates` subscription. Exported so it can be unit-tested in
175
265
  * isolation (it is the security-critical gate). A push reaches a subscriber only when BOTH hold:
176
- * 1. the push's `sessionId` matches the subscriber's requested `sessionId` (tab routing), AND
177
- * 2. the push's `ownerUserId` matches the subscriber CONNECTION's authenticated identity
178
- * (`context.userPayload.userRecord.ID`, established once at WS connect).
266
+ * 1. the push's `sessionId` matches the subscriber's requested `sessionId` (tab routing), or is `'*'`, AND
267
+ * 2. the push's `ownerUserId` matches the owner key ({@link StatusUpdateOwnerKey}) of the subscriber
268
+ * CONNECTION's authenticated identity (`context.userPayload.userRecord`, established once at WS
269
+ * connect): the user ID for a named user, compared as a UUID; for an anonymous visitor, the user ID
270
+ * plus its scope identity, compared exactly because the scope part is case-significant.
179
271
  *
180
272
  * Condition (2) is the fix for the session-hijack class (B49): `sessionId` is a client-chosen
181
273
  * correlation value, so a subscriber who lifts another user's `sessionId` would previously receive
182
274
  * their pushes. Binding delivery to the connection's server-authenticated identity means knowing a
183
- * `sessionId` is no longer sufficient. Fails CLOSED — a missing owner or connection identity never
184
- * matches.
275
+ * `sessionId` is no longer sufficient; the scope part does the same between anonymous visitors, who all
276
+ * share one Anonymous user. Fails CLOSED — a missing owner or connection key never matches.
185
277
  */
186
278
  export function StatusUpdatesFilter(data: {
187
279
  payload: PushStatusNotificationPayload;
@@ -189,14 +281,16 @@ export function StatusUpdatesFilter(data: {
189
281
  context: StatusUpdatesFilterContext | undefined;
190
282
  }): boolean {
191
283
  const { payload, args, context } = data;
192
- if (payload.sessionId !== args.sessionId) {
284
+ if (payload.sessionId !== '*' && payload.sessionId !== args.sessionId) {
193
285
  return false;
194
286
  }
195
- const connectionUserId: string | undefined = context?.userPayload?.userRecord?.ID;
196
- if (!connectionUserId || !payload.ownerUserId) {
287
+ const connectionUser: SessionAccessPrincipal | undefined = context?.userPayload?.userRecord;
288
+ const connectionKey = StatusUpdateOwnerKey(connectionUser);
289
+ if (!connectionKey || !payload.ownerUserId) {
197
290
  return false; // fail closed — no identity on either side means no delivery
198
291
  }
199
- return UUIDsEqual(payload.ownerUserId, connectionUserId);
292
+ // An anonymous visitor's key carries its scope identity (case-significant), so it must match exactly.
293
+ return connectionUser?.IsMagicLinkAnonymous ? payload.ownerUserId === connectionKey : UUIDsEqual(payload.ownerUserId, connectionKey);
200
294
  }
201
295
 
202
296
  /** @deprecated Use {@link StatusUpdatesFilter}. */
@@ -211,7 +305,10 @@ export function statusUpdatesFilter(data: {
211
305
  @Resolver()
212
306
  export class PushStatusResolver {
213
307
  @Subscription(() => PushStatusNotification, {
214
- topics: PUSH_STATUS_UPDATES_TOPIC,
308
+ topics: ({ args }: { args: PushStatusNotificationArgs }) =>
309
+ args?.sessionId && args.sessionId !== '*'
310
+ ? [GetSessionStatusTopic(args.sessionId), PUSH_STATUS_UPDATES_TOPIC]
311
+ : PUSH_STATUS_UPDATES_TOPIC,
215
312
  filter: (data: ResolverFilterData<PushStatusNotificationPayload, PushStatusNotificationArgs, StatusUpdatesFilterContext>) =>
216
313
  StatusUpdatesFilter(data),
217
314
  })