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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (560) hide show
  1. package/README.md +47 -1
  2. package/dist/.tsbuildinfo +1 -1
  3. package/dist/agentSessions/SessionJanitor.d.ts +91 -30
  4. package/dist/agentSessions/SessionJanitor.d.ts.map +1 -1
  5. package/dist/agentSessions/SessionJanitor.js +311 -81
  6. package/dist/agentSessions/SessionJanitor.js.map +1 -1
  7. package/dist/agentSessions/SessionManager.d.ts +17 -3
  8. package/dist/agentSessions/SessionManager.d.ts.map +1 -1
  9. package/dist/agentSessions/SessionManager.js +44 -10
  10. package/dist/agentSessions/SessionManager.js.map +1 -1
  11. package/dist/apiRoot.d.ts +32 -0
  12. package/dist/apiRoot.d.ts.map +1 -0
  13. package/dist/apiRoot.js +67 -0
  14. package/dist/apiRoot.js.map +1 -0
  15. package/dist/auth/AuthProviderCatalogRouter.d.ts.map +1 -1
  16. package/dist/auth/AuthProviderCatalogRouter.js +27 -0
  17. package/dist/auth/AuthProviderCatalogRouter.js.map +1 -1
  18. package/dist/auth/LoginScreenEngine.d.ts +22 -0
  19. package/dist/auth/LoginScreenEngine.d.ts.map +1 -0
  20. package/dist/auth/LoginScreenEngine.js +57 -0
  21. package/dist/auth/LoginScreenEngine.js.map +1 -0
  22. package/dist/auth/index.d.ts +1 -0
  23. package/dist/auth/index.d.ts.map +1 -1
  24. package/dist/auth/index.js +52 -10
  25. package/dist/auth/index.js.map +1 -1
  26. package/dist/auth/initializeProviders.d.ts +10 -0
  27. package/dist/auth/initializeProviders.d.ts.map +1 -1
  28. package/dist/auth/initializeProviders.js +37 -1
  29. package/dist/auth/initializeProviders.js.map +1 -1
  30. package/dist/auth/localDevAuth.d.ts +79 -0
  31. package/dist/auth/localDevAuth.d.ts.map +1 -0
  32. package/dist/auth/localDevAuth.js +439 -0
  33. package/dist/auth/localDevAuth.js.map +1 -0
  34. package/dist/auth/magicLink/MagicLinkKeys.d.ts +2 -2
  35. package/dist/auth/magicLink/MagicLinkKeys.d.ts.map +1 -1
  36. package/dist/auth/magicLink/MagicLinkKeys.js +1 -1
  37. package/dist/auth/magicLink/MagicLinkKeys.js.map +1 -1
  38. package/dist/auth/magicLink/MagicLinkRouter.d.ts.map +1 -1
  39. package/dist/auth/magicLink/MagicLinkRouter.js +3 -2
  40. package/dist/auth/magicLink/MagicLinkRouter.js.map +1 -1
  41. package/dist/auth/magicLink/types.d.ts +12 -0
  42. package/dist/auth/magicLink/types.d.ts.map +1 -1
  43. package/dist/auth/sessionUserClone.d.ts +1 -1
  44. package/dist/auth/sessionUserClone.d.ts.map +1 -1
  45. package/dist/auth/sessionUserClone.js +3 -2
  46. package/dist/auth/sessionUserClone.js.map +1 -1
  47. package/dist/broadcast/BaseCacheInvalidationBroadcastProvider.d.ts +292 -0
  48. package/dist/broadcast/BaseCacheInvalidationBroadcastProvider.d.ts.map +1 -0
  49. package/dist/broadcast/BaseCacheInvalidationBroadcastProvider.js +442 -0
  50. package/dist/broadcast/BaseCacheInvalidationBroadcastProvider.js.map +1 -0
  51. package/dist/broadcast/index.d.ts +6 -0
  52. package/dist/broadcast/index.d.ts.map +1 -0
  53. package/dist/broadcast/index.js +6 -0
  54. package/dist/broadcast/index.js.map +1 -0
  55. package/dist/broadcast/providers/AWSIoTCoreBroadcastProvider.d.ts +43 -0
  56. package/dist/broadcast/providers/AWSIoTCoreBroadcastProvider.d.ts.map +1 -0
  57. package/dist/broadcast/providers/AWSIoTCoreBroadcastProvider.js +144 -0
  58. package/dist/broadcast/providers/AWSIoTCoreBroadcastProvider.js.map +1 -0
  59. package/dist/broadcast/providers/AzureWebPubSubBroadcastProvider.d.ts +46 -0
  60. package/dist/broadcast/providers/AzureWebPubSubBroadcastProvider.d.ts.map +1 -0
  61. package/dist/broadcast/providers/AzureWebPubSubBroadcastProvider.js +149 -0
  62. package/dist/broadcast/providers/AzureWebPubSubBroadcastProvider.js.map +1 -0
  63. package/dist/broadcast/providers/GCPPubSubBroadcastProvider.d.ts +49 -0
  64. package/dist/broadcast/providers/GCPPubSubBroadcastProvider.d.ts.map +1 -0
  65. package/dist/broadcast/providers/GCPPubSubBroadcastProvider.js +131 -0
  66. package/dist/broadcast/providers/GCPPubSubBroadcastProvider.js.map +1 -0
  67. package/dist/broadcast/providers/SequencedRoleBucketBroadcastProvider.d.ts +24 -0
  68. package/dist/broadcast/providers/SequencedRoleBucketBroadcastProvider.d.ts.map +1 -0
  69. package/dist/broadcast/providers/SequencedRoleBucketBroadcastProvider.js +51 -0
  70. package/dist/broadcast/providers/SequencedRoleBucketBroadcastProvider.js.map +1 -0
  71. package/dist/cache.d.ts +3 -2
  72. package/dist/cache.d.ts.map +1 -1
  73. package/dist/cache.js.map +1 -1
  74. package/dist/config.d.ts +632 -22
  75. package/dist/config.d.ts.map +1 -1
  76. package/dist/config.js +104 -4
  77. package/dist/config.js.map +1 -1
  78. package/dist/context.d.ts +3 -1
  79. package/dist/context.d.ts.map +1 -1
  80. package/dist/context.js +29 -8
  81. package/dist/context.js.map +1 -1
  82. package/dist/coreRoutes.d.ts +29 -0
  83. package/dist/coreRoutes.d.ts.map +1 -0
  84. package/dist/coreRoutes.js +156 -0
  85. package/dist/coreRoutes.js.map +1 -0
  86. package/dist/generated/graphql-schemas/__mj.d.ts +1016 -29
  87. package/dist/generated/graphql-schemas/__mj.d.ts.map +1 -1
  88. package/dist/generated/graphql-schemas/__mj.js +5125 -434
  89. package/dist/generated/graphql-schemas/__mj.js.map +1 -1
  90. package/dist/generic/CacheInvalidationResolver.d.ts +85 -30
  91. package/dist/generic/CacheInvalidationResolver.d.ts.map +1 -1
  92. package/dist/generic/CacheInvalidationResolver.js +175 -60
  93. package/dist/generic/CacheInvalidationResolver.js.map +1 -1
  94. package/dist/generic/OrphanedConversationDetailReconciler.d.ts.map +1 -1
  95. package/dist/generic/OrphanedConversationDetailReconciler.js +2 -0
  96. package/dist/generic/OrphanedConversationDetailReconciler.js.map +1 -1
  97. package/dist/generic/PubSubManager.d.ts +10 -0
  98. package/dist/generic/PubSubManager.d.ts.map +1 -1
  99. package/dist/generic/PubSubManager.js +44 -1
  100. package/dist/generic/PubSubManager.js.map +1 -1
  101. package/dist/generic/PushStatusResolver.d.ts +39 -6
  102. package/dist/generic/PushStatusResolver.d.ts.map +1 -1
  103. package/dist/generic/PushStatusResolver.js +94 -12
  104. package/dist/generic/PushStatusResolver.js.map +1 -1
  105. package/dist/generic/ResolverBase.d.ts +168 -69
  106. package/dist/generic/ResolverBase.d.ts.map +1 -1
  107. package/dist/generic/ResolverBase.js +464 -405
  108. package/dist/generic/ResolverBase.js.map +1 -1
  109. package/dist/generic/RunViewResolver.d.ts +11 -3
  110. package/dist/generic/RunViewResolver.d.ts.map +1 -1
  111. package/dist/generic/RunViewResolver.js +91 -13
  112. package/dist/generic/RunViewResolver.js.map +1 -1
  113. package/dist/generic/actionOutputFieldSecurity.d.ts +26 -0
  114. package/dist/generic/actionOutputFieldSecurity.d.ts.map +1 -0
  115. package/dist/generic/actionOutputFieldSecurity.js +47 -0
  116. package/dist/generic/actionOutputFieldSecurity.js.map +1 -0
  117. package/dist/generic/fieldSecurityTransport.d.ts +30 -0
  118. package/dist/generic/fieldSecurityTransport.d.ts.map +1 -0
  119. package/dist/generic/fieldSecurityTransport.js +30 -0
  120. package/dist/generic/fieldSecurityTransport.js.map +1 -0
  121. package/dist/globalCors.d.ts +33 -0
  122. package/dist/globalCors.d.ts.map +1 -0
  123. package/dist/globalCors.js +58 -0
  124. package/dist/globalCors.js.map +1 -0
  125. package/dist/index.d.ts +14 -0
  126. package/dist/index.d.ts.map +1 -1
  127. package/dist/index.js +230 -222
  128. package/dist/index.js.map +1 -1
  129. package/dist/integration/CustomColumnPromoter.d.ts.map +1 -1
  130. package/dist/integration/CustomColumnPromoter.js +6 -1
  131. package/dist/integration/CustomColumnPromoter.js.map +1 -1
  132. package/dist/logging/variablesLoggingMiddleware.d.ts +12 -0
  133. package/dist/logging/variablesLoggingMiddleware.d.ts.map +1 -1
  134. package/dist/logging/variablesLoggingMiddleware.js +50 -1
  135. package/dist/logging/variablesLoggingMiddleware.js.map +1 -1
  136. package/dist/multiTenancy/index.d.ts.map +1 -1
  137. package/dist/multiTenancy/index.js +22 -2
  138. package/dist/multiTenancy/index.js.map +1 -1
  139. package/dist/realtimeSessions/RealtimeSessionVerificationService.d.ts.map +1 -1
  140. package/dist/realtimeSessions/RealtimeSessionVerificationService.js +2 -1
  141. package/dist/realtimeSessions/RealtimeSessionVerificationService.js.map +1 -1
  142. package/dist/realtimeSessions/RealtimeVerifyRouter.d.ts.map +1 -1
  143. package/dist/realtimeSessions/RealtimeVerifyRouter.js +2 -1
  144. package/dist/realtimeSessions/RealtimeVerifyRouter.js.map +1 -1
  145. package/dist/realtimeSessions/RecordingAssemblyScheduler.d.ts +100 -0
  146. package/dist/realtimeSessions/RecordingAssemblyScheduler.d.ts.map +1 -0
  147. package/dist/realtimeSessions/RecordingAssemblyScheduler.js +173 -0
  148. package/dist/realtimeSessions/RecordingAssemblyScheduler.js.map +1 -0
  149. package/dist/realtimeSessions/SessionVerificationPortsBase.d.ts.map +1 -1
  150. package/dist/realtimeSessions/SessionVerificationPortsBase.js +1 -0
  151. package/dist/realtimeSessions/SessionVerificationPortsBase.js.map +1 -1
  152. package/dist/realtimeSessions/index.d.ts +2 -0
  153. package/dist/realtimeSessions/index.d.ts.map +1 -1
  154. package/dist/realtimeSessions/index.js +2 -0
  155. package/dist/realtimeSessions/index.js.map +1 -1
  156. package/dist/realtimeSessions/ownedRealtimeSession.d.ts +32 -0
  157. package/dist/realtimeSessions/ownedRealtimeSession.d.ts.map +1 -0
  158. package/dist/realtimeSessions/ownedRealtimeSession.js +56 -0
  159. package/dist/realtimeSessions/ownedRealtimeSession.js.map +1 -0
  160. package/dist/realtimeSessions/sessionAccess.d.ts +91 -8
  161. package/dist/realtimeSessions/sessionAccess.d.ts.map +1 -1
  162. package/dist/realtimeSessions/sessionAccess.js +127 -20
  163. package/dist/realtimeSessions/sessionAccess.js.map +1 -1
  164. package/dist/realtimeSessions/sessionConfigStamps.js +1 -1
  165. package/dist/realtimeSessions/sessionConfigStamps.js.map +1 -1
  166. package/dist/realtimeSessions/subscriptionAuthorization.d.ts.map +1 -1
  167. package/dist/realtimeSessions/subscriptionAuthorization.js +5 -2
  168. package/dist/realtimeSessions/subscriptionAuthorization.js.map +1 -1
  169. package/dist/realtimeSessions/verificationWorkflow.d.ts +2 -0
  170. package/dist/realtimeSessions/verificationWorkflow.d.ts.map +1 -1
  171. package/dist/realtimeSessions/verificationWorkflow.js +1 -1
  172. package/dist/realtimeSessions/verificationWorkflow.js.map +1 -1
  173. package/dist/realtimeWidget/WidgetRouter.d.ts.map +1 -1
  174. package/dist/realtimeWidget/WidgetRouter.js +19 -3
  175. package/dist/realtimeWidget/WidgetRouter.js.map +1 -1
  176. package/dist/realtimeWidget/WidgetSessionService.d.ts +67 -7
  177. package/dist/realtimeWidget/WidgetSessionService.d.ts.map +1 -1
  178. package/dist/realtimeWidget/WidgetSessionService.js +201 -19
  179. package/dist/realtimeWidget/WidgetSessionService.js.map +1 -1
  180. package/dist/realtimeWidget/host-identity.d.ts +1 -1
  181. package/dist/realtimeWidget/host-identity.js +1 -1
  182. package/dist/realtimeWidget/widgetCore.d.ts +20 -1
  183. package/dist/realtimeWidget/widgetCore.d.ts.map +1 -1
  184. package/dist/realtimeWidget/widgetCore.js +17 -1
  185. package/dist/realtimeWidget/widgetCore.js.map +1 -1
  186. package/dist/realtimeWidget/widgetGuestElevation.d.ts +1 -1
  187. package/dist/realtimeWidget/widgetGuestElevation.d.ts.map +1 -1
  188. package/dist/realtimeWidget/widgetGuestElevation.js +2 -1
  189. package/dist/realtimeWidget/widgetGuestElevation.js.map +1 -1
  190. package/dist/resolvers/ActionResolver.d.ts +19 -1
  191. package/dist/resolvers/ActionResolver.d.ts.map +1 -1
  192. package/dist/resolvers/ActionResolver.js +63 -12
  193. package/dist/resolvers/ActionResolver.js.map +1 -1
  194. package/dist/resolvers/AdhocQueryResolver.d.ts.map +1 -1
  195. package/dist/resolvers/AdhocQueryResolver.js +1 -0
  196. package/dist/resolvers/AdhocQueryResolver.js.map +1 -1
  197. package/dist/resolvers/AgentRunStatusPublisher.d.ts +18 -4
  198. package/dist/resolvers/AgentRunStatusPublisher.d.ts.map +1 -1
  199. package/dist/resolvers/AgentRunStatusPublisher.js +32 -8
  200. package/dist/resolvers/AgentRunStatusPublisher.js.map +1 -1
  201. package/dist/resolvers/AgentSessionResolver.d.ts +10 -4
  202. package/dist/resolvers/AgentSessionResolver.d.ts.map +1 -1
  203. package/dist/resolvers/AgentSessionResolver.js +40 -17
  204. package/dist/resolvers/AgentSessionResolver.js.map +1 -1
  205. package/dist/resolvers/ArtifactFileResolver.d.ts +5 -1
  206. package/dist/resolvers/ArtifactFileResolver.d.ts.map +1 -1
  207. package/dist/resolvers/ArtifactFileResolver.js +9 -7
  208. package/dist/resolvers/ArtifactFileResolver.js.map +1 -1
  209. package/dist/resolvers/ConversationRunEventsResolver.d.ts +8 -4
  210. package/dist/resolvers/ConversationRunEventsResolver.d.ts.map +1 -1
  211. package/dist/resolvers/ConversationRunEventsResolver.js +6 -6
  212. package/dist/resolvers/ConversationRunEventsResolver.js.map +1 -1
  213. package/dist/resolvers/DatasetResolver.d.ts +24 -3
  214. package/dist/resolvers/DatasetResolver.d.ts.map +1 -1
  215. package/dist/resolvers/DatasetResolver.js +57 -8
  216. package/dist/resolvers/DatasetResolver.js.map +1 -1
  217. package/dist/resolvers/EntityCommunicationsResolver.d.ts +13 -1
  218. package/dist/resolvers/EntityCommunicationsResolver.d.ts.map +1 -1
  219. package/dist/resolvers/EntityCommunicationsResolver.js +50 -14
  220. package/dist/resolvers/EntityCommunicationsResolver.js.map +1 -1
  221. package/dist/resolvers/EntityRecordNameResolver.d.ts +2 -1
  222. package/dist/resolvers/EntityRecordNameResolver.d.ts.map +1 -1
  223. package/dist/resolvers/EntityRecordNameResolver.js +3 -2
  224. package/dist/resolvers/EntityRecordNameResolver.js.map +1 -1
  225. package/dist/resolvers/FetchEntityVectorsResolver.d.ts +46 -3
  226. package/dist/resolvers/FetchEntityVectorsResolver.d.ts.map +1 -1
  227. package/dist/resolvers/FetchEntityVectorsResolver.js +211 -50
  228. package/dist/resolvers/FetchEntityVectorsResolver.js.map +1 -1
  229. package/dist/resolvers/FileCategoryResolver.d.ts.map +1 -1
  230. package/dist/resolvers/FileCategoryResolver.js +1 -0
  231. package/dist/resolvers/FileCategoryResolver.js.map +1 -1
  232. package/dist/resolvers/FileResolver.d.ts +87 -11
  233. package/dist/resolvers/FileResolver.d.ts.map +1 -1
  234. package/dist/resolvers/FileResolver.js +292 -105
  235. package/dist/resolvers/FileResolver.js.map +1 -1
  236. package/dist/resolvers/GetDataContextDataResolver.d.ts +33 -7
  237. package/dist/resolvers/GetDataContextDataResolver.d.ts.map +1 -1
  238. package/dist/resolvers/GetDataContextDataResolver.js +117 -38
  239. package/dist/resolvers/GetDataContextDataResolver.js.map +1 -1
  240. package/dist/resolvers/GetDataResolver.d.ts.map +1 -1
  241. package/dist/resolvers/GetDataResolver.js +6 -3
  242. package/dist/resolvers/GetDataResolver.js.map +1 -1
  243. package/dist/resolvers/IntegrationDiscoveryResolver.d.ts.map +1 -1
  244. package/dist/resolvers/IntegrationDiscoveryResolver.js +49 -9
  245. package/dist/resolvers/IntegrationDiscoveryResolver.js.map +1 -1
  246. package/dist/resolvers/MergeRecordsResolver.d.ts +8 -1
  247. package/dist/resolvers/MergeRecordsResolver.d.ts.map +1 -1
  248. package/dist/resolvers/MergeRecordsResolver.js +31 -3
  249. package/dist/resolvers/MergeRecordsResolver.js.map +1 -1
  250. package/dist/resolvers/PipelineProgressResolver.d.ts +6 -0
  251. package/dist/resolvers/PipelineProgressResolver.d.ts.map +1 -1
  252. package/dist/resolvers/PipelineProgressResolver.js +5 -3
  253. package/dist/resolvers/PipelineProgressResolver.js.map +1 -1
  254. package/dist/resolvers/QueryResolver.d.ts +2 -0
  255. package/dist/resolvers/QueryResolver.d.ts.map +1 -1
  256. package/dist/resolvers/QueryResolver.js +33 -6
  257. package/dist/resolvers/QueryResolver.js.map +1 -1
  258. package/dist/resolvers/QuerySystemUserResolver.d.ts +2 -0
  259. package/dist/resolvers/QuerySystemUserResolver.d.ts.map +1 -1
  260. package/dist/resolvers/QuerySystemUserResolver.js +15 -6
  261. package/dist/resolvers/QuerySystemUserResolver.js.map +1 -1
  262. package/dist/resolvers/RSUResolver.d.ts.map +1 -1
  263. package/dist/resolvers/RSUResolver.js +12 -0
  264. package/dist/resolvers/RSUResolver.js.map +1 -1
  265. package/dist/resolvers/RealtimeClientSessionResolver.d.ts +18 -10
  266. package/dist/resolvers/RealtimeClientSessionResolver.d.ts.map +1 -1
  267. package/dist/resolvers/RealtimeClientSessionResolver.js +92 -54
  268. package/dist/resolvers/RealtimeClientSessionResolver.js.map +1 -1
  269. package/dist/resolvers/RemoteBrowserActionResolver.d.ts.map +1 -1
  270. package/dist/resolvers/RemoteBrowserActionResolver.js +11 -9
  271. package/dist/resolvers/RemoteBrowserActionResolver.js.map +1 -1
  272. package/dist/resolvers/RunAIAgentResolver.d.ts +56 -8
  273. package/dist/resolvers/RunAIAgentResolver.d.ts.map +1 -1
  274. package/dist/resolvers/RunAIAgentResolver.js +241 -67
  275. package/dist/resolvers/RunAIAgentResolver.js.map +1 -1
  276. package/dist/resolvers/RunTemplateResolver.d.ts.map +1 -1
  277. package/dist/resolvers/RunTemplateResolver.js +4 -4
  278. package/dist/resolvers/RunTemplateResolver.js.map +1 -1
  279. package/dist/resolvers/RunTestResolver.d.ts.map +1 -1
  280. package/dist/resolvers/RunTestResolver.js +3 -1
  281. package/dist/resolvers/RunTestResolver.js.map +1 -1
  282. package/dist/resolvers/SearchKnowledgeStreamResolver.d.ts +12 -1
  283. package/dist/resolvers/SearchKnowledgeStreamResolver.d.ts.map +1 -1
  284. package/dist/resolvers/SearchKnowledgeStreamResolver.js +15 -2
  285. package/dist/resolvers/SearchKnowledgeStreamResolver.js.map +1 -1
  286. package/dist/resolvers/SqlLoggingConfigResolver.d.ts.map +1 -1
  287. package/dist/resolvers/SqlLoggingConfigResolver.js +2 -3
  288. package/dist/resolvers/SqlLoggingConfigResolver.js.map +1 -1
  289. package/dist/resolvers/SyncDataResolver.d.ts.map +1 -1
  290. package/dist/resolvers/SyncDataResolver.js +4 -0
  291. package/dist/resolvers/SyncDataResolver.js.map +1 -1
  292. package/dist/resolvers/TaskGraphFrameResolver.d.ts +2 -1
  293. package/dist/resolvers/TaskGraphFrameResolver.d.ts.map +1 -1
  294. package/dist/resolvers/TaskGraphFrameResolver.js +28 -1
  295. package/dist/resolvers/TaskGraphFrameResolver.js.map +1 -1
  296. package/dist/resolvers/TestQuerySQLResolver.d.ts +24 -0
  297. package/dist/resolvers/TestQuerySQLResolver.d.ts.map +1 -1
  298. package/dist/resolvers/TestQuerySQLResolver.js +66 -13
  299. package/dist/resolvers/TestQuerySQLResolver.js.map +1 -1
  300. package/dist/resolvers/TransactionGroupResolver.d.ts +34 -2
  301. package/dist/resolvers/TransactionGroupResolver.d.ts.map +1 -1
  302. package/dist/resolvers/TransactionGroupResolver.js +150 -27
  303. package/dist/resolvers/TransactionGroupResolver.js.map +1 -1
  304. package/dist/resolvers/UserViewResolver.d.ts +1 -1
  305. package/dist/resolvers/UserViewResolver.d.ts.map +1 -1
  306. package/dist/resolvers/UserViewResolver.js +2 -1
  307. package/dist/resolvers/UserViewResolver.js.map +1 -1
  308. package/dist/resolvers/agent-run-data-guard.d.ts +31 -0
  309. package/dist/resolvers/agent-run-data-guard.d.ts.map +1 -0
  310. package/dist/resolvers/agent-run-data-guard.js +72 -0
  311. package/dist/resolvers/agent-run-data-guard.js.map +1 -0
  312. package/dist/rest/EntityCRUDHandler.d.ts +6 -0
  313. package/dist/rest/EntityCRUDHandler.d.ts.map +1 -1
  314. package/dist/rest/EntityCRUDHandler.js +19 -5
  315. package/dist/rest/EntityCRUDHandler.js.map +1 -1
  316. package/dist/rest/RESTEndpointHandler.d.ts.map +1 -1
  317. package/dist/rest/RESTEndpointHandler.js +8 -5
  318. package/dist/rest/RESTEndpointHandler.js.map +1 -1
  319. package/dist/rest/RealtimeRecordingHandler.d.ts +49 -0
  320. package/dist/rest/RealtimeRecordingHandler.d.ts.map +1 -0
  321. package/dist/rest/RealtimeRecordingHandler.js +383 -0
  322. package/dist/rest/RealtimeRecordingHandler.js.map +1 -0
  323. package/dist/rest/ViewOperationsHandler.d.ts +9 -1
  324. package/dist/rest/ViewOperationsHandler.d.ts.map +1 -1
  325. package/dist/rest/ViewOperationsHandler.js +43 -17
  326. package/dist/rest/ViewOperationsHandler.js.map +1 -1
  327. package/dist/rest/setupRESTEndpoints.d.ts +6 -0
  328. package/dist/rest/setupRESTEndpoints.d.ts.map +1 -1
  329. package/dist/rest/setupRESTEndpoints.js +6 -0
  330. package/dist/rest/setupRESTEndpoints.js.map +1 -1
  331. package/dist/serverExtensionReservedRoots.d.ts +12 -10
  332. package/dist/serverExtensionReservedRoots.d.ts.map +1 -1
  333. package/dist/serverExtensionReservedRoots.js +14 -17
  334. package/dist/serverExtensionReservedRoots.js.map +1 -1
  335. package/dist/serverRoleConfigUnits.d.ts +29 -0
  336. package/dist/serverRoleConfigUnits.d.ts.map +1 -0
  337. package/dist/serverRoleConfigUnits.js +33 -0
  338. package/dist/serverRoleConfigUnits.js.map +1 -0
  339. package/dist/services/MJStorageBlobStore.d.ts +8 -1
  340. package/dist/services/MJStorageBlobStore.d.ts.map +1 -1
  341. package/dist/services/MJStorageBlobStore.js +25 -19
  342. package/dist/services/MJStorageBlobStore.js.map +1 -1
  343. package/dist/services/StartTaskGraphDispatcher.js +2 -2
  344. package/dist/services/StartTaskGraphDispatcher.js.map +1 -1
  345. package/dist/services/TaskGraphActionRunner.d.ts +5 -3
  346. package/dist/services/TaskGraphActionRunner.d.ts.map +1 -1
  347. package/dist/services/TaskGraphActionRunner.js +27 -5
  348. package/dist/services/TaskGraphActionRunner.js.map +1 -1
  349. package/dist/services/TaskGraphAgentRunner.d.ts +14 -2
  350. package/dist/services/TaskGraphAgentRunner.d.ts.map +1 -1
  351. package/dist/services/TaskGraphAgentRunner.js +157 -9
  352. package/dist/services/TaskGraphAgentRunner.js.map +1 -1
  353. package/dist/services/TaskGraphContinuationDeliverer.d.ts +59 -2
  354. package/dist/services/TaskGraphContinuationDeliverer.d.ts.map +1 -1
  355. package/dist/services/TaskGraphContinuationDeliverer.js +626 -16
  356. package/dist/services/TaskGraphContinuationDeliverer.js.map +1 -1
  357. package/dist/sharedCache.d.ts.map +1 -1
  358. package/dist/sharedCache.js +3 -0
  359. package/dist/sharedCache.js.map +1 -1
  360. package/dist/types.d.ts +23 -1
  361. package/dist/types.d.ts.map +1 -1
  362. package/dist/types.js.map +1 -1
  363. package/package.json +99 -98
  364. package/src/__tests__/ActionResolver.fieldSecurity.test.ts +237 -0
  365. package/src/__tests__/AdhocQueryResolver.dialect.test.ts +137 -0
  366. package/src/__tests__/AgentRunStatusPublisher.test.ts +49 -0
  367. package/src/__tests__/AgentSessionResolver.ownership.test.ts +137 -0
  368. package/src/__tests__/CustomColumnPromoterOnePass.test.ts +15 -1
  369. package/src/__tests__/DatasetResolver.itemFilters.test.ts +201 -0
  370. package/src/__tests__/EntityCommunicationsResolver.actingUser.test.ts +151 -0
  371. package/src/__tests__/EntityRecordNameResolver.rowSecurity.test.ts +152 -0
  372. package/src/__tests__/EvolutionActsOnWhatItDetects.test.ts +113 -0
  373. package/src/__tests__/FetchEntityVectorsResolver.test.ts +369 -18
  374. package/src/__tests__/FileResolver.fieldSecurity.test.ts +82 -0
  375. package/src/__tests__/FileResolver.storageAccess.test.ts +562 -0
  376. package/src/__tests__/GetDataContextDataResolver.sqlItems.test.ts +416 -0
  377. package/src/__tests__/ListOperationsResolver.adhocScreen.test.ts +137 -0
  378. package/src/__tests__/LoginScreenEngine.test.ts +86 -0
  379. package/src/__tests__/MJStorageBlobStore.storageGate.test.ts +63 -0
  380. package/src/__tests__/PipelineProgressResolver.filter.test.ts +58 -0
  381. package/src/__tests__/QuerySystemUserResolver.fieldSecurity.test.ts +49 -0
  382. package/src/__tests__/RESTEndpointHandler.test.ts +25 -0
  383. package/src/__tests__/RealtimeClientSessionResolver.configTamper.test.ts +23 -3
  384. package/src/__tests__/RealtimeClientSessionResolver.test.ts +324 -46
  385. package/src/__tests__/RecordingAssemblyScheduler.test.ts +178 -0
  386. package/src/__tests__/RemoteBrowserActionResolver.ownership.test.ts +98 -0
  387. package/src/__tests__/ResolverBase.adhocReadScope.test.ts +133 -2
  388. package/src/__tests__/ResolverBase.balancedClause.test.ts +155 -0
  389. package/src/__tests__/ResolverBase.clauseUnscopedRead.test.ts +127 -0
  390. package/src/__tests__/ResolverBase.clientOldValues.test.ts +243 -7
  391. package/src/__tests__/ResolverBase.deleteReturnProjection.test.ts +52 -0
  392. package/src/__tests__/ResolverBase.entityMessageRelay.test.ts +176 -0
  393. package/src/__tests__/ResolverBase.filterEscaping.test.ts +5 -2
  394. package/src/__tests__/ResolverBase.includeBinaryFields.test.ts +23 -1
  395. package/src/__tests__/ResolverBase.publishStatusUpdate.test.ts +68 -0
  396. package/src/__tests__/ResolverBase.userViewCacheAndDedup.test.ts +130 -0
  397. package/src/__tests__/RunAIAgentResolver.branch.test.ts +69 -13
  398. package/src/__tests__/RunAIAgentResolver.conversationWriter.test.ts +210 -0
  399. package/src/__tests__/RunAIAgentResolver.fireAndForgetCompletion.test.ts +209 -0
  400. package/src/__tests__/RunAIAgentResolver.historyFrom.test.ts +20 -28
  401. package/src/__tests__/RunAIAgentResolver.runDataGuard.test.ts +264 -0
  402. package/src/__tests__/RunAIAgentResolver.serverReply.test.ts +358 -0
  403. package/src/__tests__/RunAIAgentResolver.stoppedPredecessor.test.ts +4 -1
  404. package/src/__tests__/RunTemplateResolver.test.ts +35 -2
  405. package/src/__tests__/RunViewResolver.cacheCheckForwarding.test.ts +11 -0
  406. package/src/__tests__/RunViewResolver.cacheCheckScreen.test.ts +165 -0
  407. package/src/__tests__/SearchKnowledgeStreamResolver.test.ts +161 -0
  408. package/src/__tests__/SessionJanitor.test.ts +365 -10
  409. package/src/__tests__/SessionManager.test.ts +122 -0
  410. package/src/__tests__/TaskGraphActionRunner.durablePayload.test.ts +25 -0
  411. package/src/__tests__/TaskGraphAgentRunner.test.ts +266 -0
  412. package/src/__tests__/TaskGraphContinuationDeliverer.test.ts +468 -23
  413. package/src/__tests__/TaskGraphFrameResolver.test.ts +22 -0
  414. package/src/__tests__/TestQuerySQLResolver.test.ts +274 -0
  415. package/src/__tests__/TransactionGroupResolver.fieldSecurity.test.ts +199 -0
  416. package/src/__tests__/TransactionGroupResolver.refusals.test.ts +98 -1
  417. package/src/__tests__/UpdateMJDataContextItem.oldValues.test.ts +220 -0
  418. package/src/__tests__/UserViewResolver.updateWhereClause.test.ts +15 -0
  419. package/src/__tests__/ViewOperationsHandler.clauseScreen.test.ts +157 -0
  420. package/src/__tests__/ViewOperationsHandler.test.ts +7 -2
  421. package/src/__tests__/WidgetSessionService.test.ts +583 -4
  422. package/src/__tests__/agent-run-data-guard.test.ts +130 -0
  423. package/src/__tests__/apiRootBoot.test.ts +202 -0
  424. package/src/__tests__/buildMagicLinkSessionUser.test.ts +96 -0
  425. package/src/__tests__/cacheBroadcastProviders.test.ts +403 -0
  426. package/src/__tests__/cacheInvalidationRecordData.test.ts +109 -0
  427. package/src/__tests__/conversationRunEvents.test.ts +56 -14
  428. package/src/__tests__/fieldSecurityFixtures.ts +91 -0
  429. package/src/__tests__/globalCors.test.ts +217 -0
  430. package/src/__tests__/localDevAuth.test.ts +196 -0
  431. package/src/__tests__/multiTenancy.postgresRewrite.test.ts +181 -0
  432. package/src/__tests__/multiTenancy.security.test.ts +51 -0
  433. package/src/__tests__/multiTenancy.test.ts +1 -1
  434. package/src/__tests__/orphanedConversationDetailReconciler.test.ts +10 -0
  435. package/src/__tests__/ownedRealtimeSession.test.ts +39 -0
  436. package/src/__tests__/pushStatusResolver.filter.test.ts +64 -1
  437. package/src/__tests__/realtimeRecordingHandler.test.ts +669 -0
  438. package/src/__tests__/realtimeSessions/RealtimeSessionEventsResolver.test.ts +29 -1
  439. package/src/__tests__/realtimeSessions/ownedRealtimeSession.anonymous.test.ts +31 -0
  440. package/src/__tests__/realtimeSessions/ownedRealtimeSession.namedScoped.test.ts +37 -0
  441. package/src/__tests__/realtimeSessions/sessionAccess.test.ts +146 -7
  442. package/src/__tests__/realtimeSessions/sessionConfigStamps.test.ts +1 -1
  443. package/src/__tests__/realtimeSessions/verificationWorkflow.test.ts +25 -4
  444. package/src/__tests__/resolverBase.cloudEventChangedFields.test.ts +157 -0
  445. package/src/__tests__/resolverBase.fieldSecurityTransport.test.ts +222 -0
  446. package/src/__tests__/resolverBase.fls.test.ts +146 -97
  447. package/src/__tests__/restTransport.fieldSecurity.test.ts +148 -0
  448. package/src/__tests__/runParamsTimeZone.test.ts +179 -0
  449. package/src/__tests__/runStreams.fieldSecurity.test.ts +100 -0
  450. package/src/__tests__/runViewProperties.test.ts +114 -0
  451. package/src/__tests__/scalabilityBenchmarks.test.ts +765 -0
  452. package/src/__tests__/scalabilityDatabaseIntegration.test.ts +207 -0
  453. package/src/__tests__/serverExtensionHostOverlay.test.ts +66 -0
  454. package/src/__tests__/serverExtensionReservedRoots.test.ts +11 -98
  455. package/src/__tests__/sessionUserClone.test.ts +5 -1
  456. package/src/__tests__/sharedCache.test.ts +5 -3
  457. package/src/__tests__/startupSweepOrder.test.ts +40 -0
  458. package/src/__tests__/unifiedAuth.test.ts +28 -0
  459. package/src/__tests__/widgetGuestElevation.test.ts +2 -0
  460. package/src/agentSessions/SessionJanitor.ts +369 -93
  461. package/src/agentSessions/SessionManager.ts +45 -10
  462. package/src/apiRoot.ts +78 -0
  463. package/src/auth/AuthProviderCatalogRouter.ts +29 -0
  464. package/src/auth/LoginScreenEngine.ts +50 -0
  465. package/src/auth/index.ts +57 -12
  466. package/src/auth/initializeProviders.ts +43 -1
  467. package/src/auth/localDevAuth.ts +528 -0
  468. package/src/auth/magicLink/MagicLinkKeys.ts +2 -2
  469. package/src/auth/magicLink/MagicLinkRouter.ts +3 -2
  470. package/src/auth/magicLink/types.ts +10 -0
  471. package/src/auth/sessionUserClone.ts +3 -2
  472. package/src/broadcast/BaseCacheInvalidationBroadcastProvider.ts +674 -0
  473. package/src/broadcast/index.ts +5 -0
  474. package/src/broadcast/providers/AWSIoTCoreBroadcastProvider.ts +180 -0
  475. package/src/broadcast/providers/AzureWebPubSubBroadcastProvider.ts +182 -0
  476. package/src/broadcast/providers/GCPPubSubBroadcastProvider.ts +169 -0
  477. package/src/broadcast/providers/SequencedRoleBucketBroadcastProvider.ts +57 -0
  478. package/src/cache.ts +2 -1
  479. package/src/config.ts +115 -5
  480. package/src/context.ts +32 -8
  481. package/src/coreRoutes.ts +181 -0
  482. package/src/generated/graphql-schemas/__mj.ts +3684 -284
  483. package/src/generic/CacheInvalidationResolver.ts +172 -60
  484. package/src/generic/OrphanedConversationDetailReconciler.ts +4 -2
  485. package/src/generic/PubSubManager.ts +54 -1
  486. package/src/generic/PushStatusResolver.ts +109 -12
  487. package/src/generic/ResolverBase.ts +540 -431
  488. package/src/generic/RunViewResolver.ts +97 -15
  489. package/src/generic/actionOutputFieldSecurity.ts +75 -0
  490. package/src/generic/fieldSecurityTransport.ts +62 -0
  491. package/src/globalCors.ts +71 -0
  492. package/src/index.ts +239 -238
  493. package/src/integration/CustomColumnPromoter.ts +6 -1
  494. package/src/logging/variablesLoggingMiddleware.ts +59 -3
  495. package/src/multiTenancy/index.ts +24 -2
  496. package/src/realtimeSessions/README.md +7 -3
  497. package/src/realtimeSessions/RealtimeSessionVerificationService.ts +2 -1
  498. package/src/realtimeSessions/RealtimeVerifyRouter.ts +2 -1
  499. package/src/realtimeSessions/RecordingAssemblyScheduler.ts +234 -0
  500. package/src/realtimeSessions/SessionVerificationPortsBase.ts +1 -0
  501. package/src/realtimeSessions/index.ts +2 -0
  502. package/src/realtimeSessions/ownedRealtimeSession.ts +69 -0
  503. package/src/realtimeSessions/sessionAccess.ts +183 -23
  504. package/src/realtimeSessions/sessionConfigStamps.ts +1 -1
  505. package/src/realtimeSessions/subscriptionAuthorization.ts +4 -2
  506. package/src/realtimeSessions/verificationWorkflow.ts +3 -1
  507. package/src/realtimeWidget/WidgetRouter.ts +22 -4
  508. package/src/realtimeWidget/WidgetSessionService.ts +252 -17
  509. package/src/realtimeWidget/host-identity.ts +1 -1
  510. package/src/realtimeWidget/widgetCore.ts +30 -1
  511. package/src/realtimeWidget/widgetGuestElevation.ts +2 -1
  512. package/src/resolvers/ActionResolver.ts +78 -15
  513. package/src/resolvers/AdhocQueryResolver.ts +1 -0
  514. package/src/resolvers/AgentRunStatusPublisher.ts +36 -8
  515. package/src/resolvers/AgentSessionResolver.ts +46 -18
  516. package/src/resolvers/ArtifactFileResolver.ts +9 -8
  517. package/src/resolvers/ConversationRunEventsResolver.ts +15 -11
  518. package/src/resolvers/DatasetResolver.ts +70 -10
  519. package/src/resolvers/EntityCommunicationsResolver.ts +56 -15
  520. package/src/resolvers/EntityRecordNameResolver.ts +3 -2
  521. package/src/resolvers/FetchEntityVectorsResolver.ts +248 -55
  522. package/src/resolvers/FileCategoryResolver.ts +1 -0
  523. package/src/resolvers/FileResolver.ts +334 -109
  524. package/src/resolvers/GetDataContextDataResolver.ts +139 -41
  525. package/src/resolvers/GetDataResolver.ts +6 -3
  526. package/src/resolvers/IntegrationDiscoveryResolver.ts +53 -9
  527. package/src/resolvers/MergeRecordsResolver.ts +23 -2
  528. package/src/resolvers/PipelineProgressResolver.ts +12 -3
  529. package/src/resolvers/QueryResolver.ts +31 -6
  530. package/src/resolvers/QuerySystemUserResolver.ts +15 -6
  531. package/src/resolvers/RSUResolver.ts +12 -0
  532. package/src/resolvers/RealtimeClientSessionResolver.ts +114 -66
  533. package/src/resolvers/RemoteBrowserActionResolver.ts +15 -10
  534. package/src/resolvers/RunAIAgentResolver.ts +296 -85
  535. package/src/resolvers/RunTemplateResolver.ts +6 -4
  536. package/src/resolvers/RunTestResolver.ts +3 -1
  537. package/src/resolvers/SearchKnowledgeStreamResolver.ts +19 -3
  538. package/src/resolvers/SqlLoggingConfigResolver.ts +2 -4
  539. package/src/resolvers/SyncDataResolver.ts +4 -0
  540. package/src/resolvers/TaskGraphFrameResolver.ts +33 -1
  541. package/src/resolvers/TestQuerySQLResolver.ts +69 -13
  542. package/src/resolvers/TransactionGroupResolver.ts +185 -30
  543. package/src/resolvers/UserViewResolver.ts +2 -1
  544. package/src/resolvers/__tests__/GetDataResolver.validation.test.ts +95 -0
  545. package/src/resolvers/__tests__/MergeRecordsResolver.test.ts +43 -0
  546. package/src/resolvers/agent-run-data-guard.ts +89 -0
  547. package/src/rest/EntityCRUDHandler.ts +22 -5
  548. package/src/rest/RESTEndpointHandler.ts +8 -5
  549. package/src/rest/RealtimeRecordingHandler.ts +467 -0
  550. package/src/rest/ViewOperationsHandler.ts +52 -18
  551. package/src/rest/setupRESTEndpoints.ts +6 -0
  552. package/src/serverExtensionReservedRoots.ts +14 -17
  553. package/src/serverRoleConfigUnits.ts +48 -0
  554. package/src/services/MJStorageBlobStore.ts +31 -30
  555. package/src/services/StartTaskGraphDispatcher.ts +2 -2
  556. package/src/services/TaskGraphActionRunner.ts +25 -5
  557. package/src/services/TaskGraphAgentRunner.ts +188 -11
  558. package/src/services/TaskGraphContinuationDeliverer.ts +738 -16
  559. package/src/sharedCache.ts +3 -0
  560. package/src/types.ts +23 -1
@@ -0,0 +1,207 @@
1
+ import 'reflect-metadata';
2
+ import { describe, it, expect, beforeAll, afterAll, vi } from 'vitest';
3
+ import sql from 'mssql';
4
+ import { RunViewParams, RunViewResult, SetProvider, UserInfo } from '@memberjunction/core';
5
+ import { GenericDatabaseProvider, UserCache } from '@memberjunction/generic-database-provider';
6
+ import { SQLServerDataProvider, SQLServerProviderConfigData } from '@memberjunction/sqlserver-dataprovider';
7
+ import {
8
+ CacheInvalidationBroadcastManager,
9
+ SequencedRoleBucketBroadcastProvider,
10
+ } from '../broadcast/index.js';
11
+
12
+ const IT_DB_NAME = process.env.MJ_IT_DB;
13
+
14
+ describe.skipIf(!IT_DB_NAME)('MJServer Scalability Architecture — Live SQL Server Integration', () => {
15
+ let pool: sql.ConnectionPool;
16
+ let sharedProvider: SQLServerDataProvider;
17
+ let sysUser: UserInfo;
18
+
19
+ beforeAll(async () => {
20
+ pool = new sql.ConnectionPool({
21
+ server: process.env.MJ_IT_HOST || 'localhost',
22
+ port: parseInt(process.env.MJ_IT_PORT || '1433', 10),
23
+ database: IT_DB_NAME,
24
+ user: process.env.MJ_IT_USER || 'sa',
25
+ password: process.env.MJ_IT_PASSWORD || 'GHWUc7QKZNT8GQiqnJHdEPonPcukY2WA',
26
+ options: {
27
+ encrypt: false,
28
+ trustServerCertificate: true,
29
+ },
30
+ });
31
+ await pool.connect();
32
+
33
+ sharedProvider = new SQLServerDataProvider();
34
+ const config = new SQLServerProviderConfigData(pool, '__mj', 0);
35
+ await sharedProvider.Config(config);
36
+ SetProvider(sharedProvider);
37
+ await UserCache.Instance.Refresh(sharedProvider, 0);
38
+
39
+ const foundUser =
40
+ UserCache.Instance.GetSystemUser() ||
41
+ UserCache.Instance.Users.find((u) => u.IsActive && u.Type === 'Owner') ||
42
+ UserCache.Instance.Users[0];
43
+ if (!foundUser) {
44
+ throw new Error(`No user found in ${IT_DB_NAME}`);
45
+ }
46
+ sysUser = foundUser;
47
+ }, 60_000);
48
+
49
+ afterAll(async () => {
50
+ if (pool?.connected) {
51
+ await pool.close();
52
+ }
53
+ });
54
+
55
+ it('creates per-request SQLServerDataProvider with zero-copy prototype-delegated metadata shell and lazy transaction queues', async () => {
56
+ const reqProvider = new SQLServerDataProvider();
57
+ const reqConfig = new SQLServerProviderConfigData(
58
+ pool,
59
+ '__mj',
60
+ 0,
61
+ undefined,
62
+ undefined,
63
+ false // ignoreExistingMetadata = false -> reuses shared metadata shell
64
+ );
65
+ await reqProvider.Config(reqConfig);
66
+
67
+ // Verify lazy RxJS Subjects are NOT allocated on a read-only request provider
68
+ const internal = reqProvider as unknown as {
69
+ _sqlQueue$: unknown;
70
+ _transactionState$: unknown;
71
+ };
72
+
73
+ expect(internal._sqlQueue$).toBeNull();
74
+ expect(internal._transactionState$).toBeNull();
75
+
76
+ // Verify zero-copy prototype delegation to shared metadata
77
+ expect(Object.getPrototypeOf(reqProvider.AllMetadata)).toBe(sharedProvider.AllMetadata);
78
+ expect(reqProvider.Entities.length).toBeGreaterThan(50);
79
+ expect(reqProvider.Entities).toBe(sharedProvider.Entities);
80
+ });
81
+
82
+ it('coalesces 10 concurrent identical InternalRunView calls against SQL Server into a single SQL execution and isolates result mutations', async () => {
83
+ // Warm up the view column order cache for vwEntities (must return >=1 row so sortViewRowKeys caches sys.columns)
84
+ await sharedProvider.RunView(
85
+ {
86
+ EntityName: 'MJ: Entities',
87
+ ExtraFilter: "Name = 'MJ: Entities'",
88
+ ResultType: 'simple',
89
+ IgnoreMaxRows: true,
90
+ },
91
+ sysUser
92
+ );
93
+
94
+ const executeSpy = vi.spyOn(sharedProvider, 'ExecuteSQL');
95
+ const internalRunner = sharedProvider as unknown as {
96
+ InternalRunView<T>(params: RunViewParams, contextUser?: UserInfo): Promise<RunViewResult<T>>;
97
+ };
98
+
99
+ try {
100
+ const concurrentCount = 10;
101
+ const promises = Array.from({ length: concurrentCount }, () =>
102
+ internalRunner.InternalRunView<Record<string, unknown>>(
103
+ {
104
+ EntityName: 'MJ: Entities',
105
+ ExtraFilter: "SchemaName = '__mj' AND Name LIKE 'MJ: %'",
106
+ OrderBy: 'Name ASC',
107
+ IgnoreMaxRows: true,
108
+ ResultType: 'simple',
109
+ },
110
+ sysUser
111
+ )
112
+ );
113
+
114
+ const results = await Promise.all(promises);
115
+
116
+ // Only 1 physical SQL execution should have been issued for the 10 identical concurrent views
117
+ expect(executeSpy).toHaveBeenCalledTimes(1);
118
+
119
+ for (const res of results) {
120
+ expect(res.Success).toBe(true);
121
+ expect(res.Results.length).toBeGreaterThan(0);
122
+ }
123
+
124
+ // Mutating caller 0's result array or row object must not affect caller 1's result
125
+ const originalName = results[1].Results[0].Name;
126
+ const originalLength = results[1].Results.length;
127
+ results[0].Results[0].Name = 'MUTATED_IN_CALLER_0';
128
+ results[0].Results.pop();
129
+
130
+ expect(results[1].Results[0].Name).toBe(originalName);
131
+ expect(results[0].Results.length).toBe(originalLength - 1);
132
+ expect(results[1].Results.length).toBe(originalLength);
133
+ } finally {
134
+ executeSpy.mockRestore();
135
+ }
136
+ });
137
+
138
+ it('bounds concurrent SQL executions in RunViews to MaxConcurrentViewsPerBatch', async () => {
139
+ let activeCount = 0;
140
+ let maxObservedConcurrency = 0;
141
+
142
+ const originalExecuteSQL = sharedProvider.ExecuteSQL.bind(sharedProvider);
143
+ const executeSpy = vi.spyOn(sharedProvider, 'ExecuteSQL').mockImplementation(async (query, params, options, contextUser) => {
144
+ activeCount++;
145
+ if (activeCount > maxObservedConcurrency) {
146
+ maxObservedConcurrency = activeCount;
147
+ }
148
+ try {
149
+ return await originalExecuteSQL(query, params, options, contextUser);
150
+ } finally {
151
+ activeCount--;
152
+ }
153
+ });
154
+
155
+ try {
156
+ const batchParams = Array.from({ length: 16 }, (_, idx) => ({
157
+ EntityName: 'MJ: Entities',
158
+ ExtraFilter: `SchemaName = '__mj' AND UserViewMaxRows IS NULL AND ${idx} = ${idx}`,
159
+ IgnoreMaxRows: true,
160
+ ResultType: 'simple' as const,
161
+ }));
162
+
163
+ const batchResults = await sharedProvider.RunViews(batchParams, sysUser);
164
+ expect(batchResults).toHaveLength(16);
165
+ for (const r of batchResults) {
166
+ expect(r.Success).toBe(true);
167
+ }
168
+ expect(maxObservedConcurrency).toBeLessThanOrEqual(GenericDatabaseProvider.MaxConcurrentViewsPerBatch);
169
+ expect(maxObservedConcurrency).toBeGreaterThan(1);
170
+ } finally {
171
+ executeSpy.mockRestore();
172
+ }
173
+ });
174
+
175
+ it('sequences and shapes cache invalidation broadcasts against live entity metadata and replays delta catch-up', async () => {
176
+ const provider = CacheInvalidationBroadcastManager.Instance.Configure({
177
+ provider: 'SequencedRoleBucket',
178
+ ringBufferSize: 500,
179
+ }) as SequencedRoleBucketBroadcastProvider;
180
+ expect(provider).toBeInstanceOf(SequencedRoleBucketBroadcastProvider);
181
+
182
+ const baseSeq = provider.CurrentSequence;
183
+ const entityInfo = sharedProvider.Entities.find((e) => e.Name === 'MJ: Entities');
184
+ expect(entityInfo).toBeDefined();
185
+
186
+ const published = provider.RecordInvalidation({
187
+ entityName: 'MJ: Entities',
188
+ primaryKeyValues: JSON.stringify([{ FieldName: 'ID', Value: entityInfo!.ID }]),
189
+ action: 'save',
190
+ sourceServerId: 'it-server-node-1',
191
+ timestamp: new Date(),
192
+ recordData: JSON.stringify({ ID: entityInfo!.ID, Name: entityInfo!.Name }),
193
+ });
194
+
195
+ expect(published.sequence).toBe(baseSeq + 1);
196
+ expect(provider.CurrentSequence).toBe(baseSeq + 1);
197
+
198
+ // Verify delta catch-up shapes payload for sysUser against real MJ: Entities metadata
199
+ const catchUp = provider.GetChangesSince(baseSeq, sysUser);
200
+ expect(catchUp.RequiresFullRefresh).toBe(false);
201
+ expect(catchUp.CurrentSequence).toBe(baseSeq + 1);
202
+ expect(catchUp.Events).toHaveLength(1);
203
+ expect(catchUp.Events[0].EntityName).toBe('MJ: Entities');
204
+ expect(catchUp.Events[0].Sequence).toBe(baseSeq + 1);
205
+ expect(catchUp.Events[0].RecordData).toContain(entityInfo!.Name);
206
+ });
207
+ });
@@ -0,0 +1,66 @@
1
+ /**
2
+ * A host mj.config.cjs serverExtensions[] entry may be a partial OVERLAY of an extension an Open App
3
+ * package publishes: only DriverClass is required. The schema used to require RootPath and default
4
+ * Enabled to true, so `{ DriverClass, OwnsCORS: false }` failed config parsing at startup, and an
5
+ * overlay that omitted Enabled re-enabled an extension its package ships disabled. Completeness is
6
+ * checked after the merge instead.
7
+ */
8
+ import { describe, it, expect, vi } from 'vitest';
9
+
10
+ // config.ts evaluates `configInfo = LoadConfig()` at module load, and it rejects a configuration
11
+ // carrying no database credentials. Stubbing only the cosmiconfig search lets the module import so the
12
+ // REAL ServerExtensionSchema is under test (same pattern as newUserRolesDefault.test.ts).
13
+ vi.mock('cosmiconfig', () => ({
14
+ cosmiconfigSync: () => ({
15
+ search: () => ({
16
+ filepath: '/test/mj.config.cjs',
17
+ isEmpty: false,
18
+ config: { dbUsername: 'test-user', dbPassword: 'test-password', dbDatabase: 'test-db' },
19
+ }),
20
+ }),
21
+ }));
22
+
23
+ import { MergeServerExtensionConfigs, PrepareServerExtensionConfigs } from '@memberjunction/server-extensions-core';
24
+ import type { ServerExtensionConfig } from '@memberjunction/server-extensions-core';
25
+ import { ServerExtensionSchema } from '../config.js';
26
+
27
+ const published: ServerExtensionConfig = {
28
+ Enabled: true,
29
+ DriverClass: 'BallotEdge',
30
+ RootPath: '/vote',
31
+ OwnsCORS: true,
32
+ Settings: { RateLimitMax: 20 },
33
+ };
34
+
35
+ describe('host serverExtensions[] overlay schema', () => {
36
+ it('accepts an overlay that names only DriverClass and the fields it overrides', () => {
37
+ const parsed = ServerExtensionSchema.safeParse({ DriverClass: 'BallotEdge', OwnsCORS: false });
38
+ expect(parsed.success).toBe(true);
39
+ expect(parsed.success && parsed.data.RootPath).toBeUndefined();
40
+ expect(parsed.success && parsed.data.Enabled).toBeUndefined();
41
+ });
42
+
43
+ it('merges the overlay onto the published entry without losing RootPath or Enabled', () => {
44
+ const overlay = ServerExtensionSchema.parse({ DriverClass: 'BallotEdge', OwnsCORS: false, Settings: { RateLimitMax: 2000 } });
45
+ const [merged] = MergeServerExtensionConfigs([published], [overlay as ServerExtensionConfig]);
46
+ expect(merged).toMatchObject({ DriverClass: 'BallotEdge', RootPath: '/vote', Enabled: true, OwnsCORS: false });
47
+ expect(merged.Settings).toEqual({ RateLimitMax: 2000 });
48
+ });
49
+
50
+ it('does not re-enable an extension the package ships disabled', () => {
51
+ const overlay = ServerExtensionSchema.parse({ DriverClass: 'BallotEdge', Settings: { RateLimitMax: 5 } });
52
+ const [merged] = MergeServerExtensionConfigs([{ ...published, Enabled: false }], [overlay as ServerExtensionConfig]);
53
+ expect(merged.Enabled).toBe(false);
54
+ });
55
+
56
+ it('still rejects a host-only entry without a RootPath, after the merge', () => {
57
+ const overlay = ServerExtensionSchema.parse({ DriverClass: 'NotPublishedAnywhere' });
58
+ const problems: string[] = [];
59
+ const prepared = PrepareServerExtensionConfigs(
60
+ MergeServerExtensionConfigs([published], [overlay as ServerExtensionConfig]),
61
+ { onInvalid: (message: string) => problems.push(message) },
62
+ );
63
+ expect(prepared.map(c => c.DriverClass)).toEqual(['BallotEdge']);
64
+ expect(problems.some(p => p.includes('NotPublishedAnywhere') && p.includes('RootPath is empty'))).toBe(true);
65
+ });
66
+ });
@@ -1,8 +1,8 @@
1
1
  /**
2
2
  * Pins the reserved-root set passed into prepareServerExtensionConfigs.
3
- * A new public mount in index.ts / a drifted `*_MOUNT_PATH` constant is a
4
- * contract failure — this file reads those sources as text so it never loads
5
- * `config.ts`.
3
+ * A drifted `*_MOUNT_PATH` constant is a contract failure — this file reads those
4
+ * sources as text so it never loads `config.ts`. That every core mount is reserved
5
+ * is checked against the real mounts in apiRootBoot.test.ts.
6
6
  */
7
7
  import { describe, it, expect } from 'vitest';
8
8
  import { readFileSync } from 'node:fs';
@@ -26,11 +26,6 @@ function exportedStringConst(relativeFile: string, name: string): string {
26
26
  return match[1];
27
27
  }
28
28
 
29
- function indexContainsLiteralPath(path: string): boolean {
30
- const src = readFileSync(join(SRC, 'index.ts'), 'utf8');
31
- return src.includes(`'${path}'`) || src.includes(`\`${path}\``) || src.includes(path.replace(/^\//, ''));
32
- }
33
-
34
29
  describe('coreReservedServerExtensionRoots', () => {
35
30
  it('stays in lockstep with exported *_MOUNT_PATH constants', () => {
36
31
  expect(CORE_CONSTANT_RESERVED_SERVER_EXTENSION_ROOTS).toEqual([
@@ -40,92 +35,11 @@ describe('coreReservedServerExtensionRoots', () => {
40
35
  ]);
41
36
  });
42
37
 
43
- it('includes the literal pre-auth paths serve() registers without a constant', () => {
44
- const indexSrc = readFileSync(join(SRC, 'index.ts'), 'utf8');
45
- for (const path of CORE_STATIC_RESERVED_SERVER_EXTENSION_ROOTS) {
46
- if (path === '/health') {
47
- expect(indexSrc, `${path} missing from serve()`).toContain("'/health/extensions'");
48
- } else if (path === '/realtime') {
49
- expect(indexSrc, `${path} missing from serve()`).toMatch(/REALTIME_SDP_EXCHANGE_PATH|'\/realtime'/);
50
- } else if (path === '/realtime-proxy') {
51
- expect(indexSrc, `${path} missing from serve()`).toContain('/realtime-proxy');
52
- } else {
53
- expect(indexSrc, `${path} missing from serve()`).toContain(`'${path}'`);
54
- }
55
- }
56
- expect(indexContainsLiteralPath('/healthcheck')).toBe(true);
57
- });
58
-
59
- it('structurally guarantees every pre-auth app.use / app.get / app.post mount in index.ts is reserved', () => {
60
- const indexSrc = readFileSync(join(SRC, 'index.ts'), 'utf8');
61
- const authMiddlewareIndex = indexSrc.indexOf('app.use(CreateUnifiedAuthMiddleware');
62
- expect(authMiddlewareIndex).toBeGreaterThan(0);
63
- const preAuthSrc = indexSrc.substring(0, authMiddlewareIndex);
64
-
65
- const mountRegex = /app\.(?:use|get|post)\s*\(\s*([^,\s)]+)/g;
66
- const reservedRoots = CoreReservedServerExtensionRoots('/');
67
-
68
- // Explicit allowlist of known non-path middleware mounts (e.g. app.use(mw), app.use(cors()), app.use(compression(...)))
69
- const knownNonPathTokens = [
70
- 'cors',
71
- 'express.',
72
- 'compression',
73
- 'cookieParser',
74
- 'CreateUnifiedAuthMiddleware',
75
- 'mw',
76
- ];
77
- const isKnownNonPath = (t: string): boolean =>
78
- knownNonPathTokens.some(k => t === k || t.startsWith(k));
79
-
80
- // Strip single-line and multi-line comments so comments like `app.use(...)` are not treated as code
81
- const cleanPreAuthSrc = preAuthSrc.replace(/\/\*[\s\S]*?\*\/|\/\/.*/g, '');
82
-
83
- let match: RegExpExecArray | null;
84
- const mountedTokens: string[] = [];
85
- while ((match = mountRegex.exec(cleanPreAuthSrc)) !== null) {
86
- mountedTokens.push(match[1].trim());
87
- }
88
-
89
- expect(mountedTokens.length).toBeGreaterThan(0);
90
-
91
- const knownConstants: Record<string, string> = {
92
- REALTIME_SDP_EXCHANGE_PATH: '/realtime',
93
- // Public verify-link routes for realtime identity verification; under the reserved '/realtime' root.
94
- REALTIME_VERIFY_MOUNT_PATH: '/realtime/verify',
95
- MAGIC_LINK_MOUNT_PATH: '/magic-link',
96
- WIDGET_MOUNT_PATH: '/widget',
97
- AUTH_CATALOG_MOUNT_PATH: '/auth',
98
- };
99
-
100
- for (const token of mountedTokens) {
101
- const isLiteralPath = token.startsWith("'") || token.startsWith('"') || token.startsWith('`');
102
- const isPathConstant = /^[A-Z0-9_]+_PATH$/.test(token) || token in knownConstants;
103
- if (!isLiteralPath && !isPathConstant) {
104
- if (isKnownNonPath(token)) {
105
- continue;
106
- }
107
- throw new Error(`Unrecognized pre-auth mount argument '${token}' in index.ts. If this is a non-path middleware, add it to knownNonPathTokens; if it is a route, reserve its prefix or map its constant.`);
108
- }
109
-
110
- let pathPrefix: string;
111
- if (isLiteralPath) {
112
- pathPrefix = token.slice(1, -1);
113
- } else if (token in knownConstants) {
114
- pathPrefix = knownConstants[token];
115
- } else {
116
- throw new Error(`Unrecognized pre-auth mount constant '${token}' in index.ts. If this is a new pre-auth route, map its constant or reserve its prefix.`);
117
- }
118
-
119
- const isCovered = reservedRoots.some(root => pathPrefix === root || pathPrefix.startsWith(`${root}/`));
120
- expect(isCovered, `Pre-auth mount '${pathPrefix}' (from token '${token}') must be covered by reserved roots`).toBe(true);
121
- }
122
- });
123
-
124
- it('includes graphqlRootPath and every core mount', () => {
125
- const roots = CoreReservedServerExtensionRoots('/api');
38
+ it('includes every core mount, and no longer graphqlRootPath', () => {
39
+ const roots = CoreReservedServerExtensionRoots();
40
+ expect(roots).not.toContain('/api');
126
41
  expect(roots).toEqual(
127
42
  expect.arrayContaining([
128
- '/api',
129
43
  '/auth',
130
44
  '/magic-link',
131
45
  '/widget',
@@ -141,7 +55,7 @@ describe('coreReservedServerExtensionRoots', () => {
141
55
  });
142
56
 
143
57
  it('closes the F-M2 bypasses: cased /auth and sibling /healthcheck', () => {
144
- const extra = CoreReservedServerExtensionRoots('/');
58
+ const extra = CoreReservedServerExtensionRoots();
145
59
  expect(validateServerExtensionRootPath('/Auth', extra)).toMatch(/reserved prefix/);
146
60
  expect(validateServerExtensionRootPath('/healthcheck', extra)).toMatch(/reserved prefix/);
147
61
  expect(validateServerExtensionRootPath('/Healthcheck', extra)).toMatch(/reserved prefix/);
@@ -157,10 +71,9 @@ describe('coreReservedServerExtensionRoots', () => {
157
71
  expect(validateServerExtensionRootPath('/meetings/teams', extra)).toBeNull();
158
72
  });
159
73
 
160
- it('reserves a non-default graphqlRootPath and its nested paths', () => {
161
- const extra = CoreReservedServerExtensionRoots('/api');
162
- expect(validateServerExtensionRootPath('/api', extra)).toMatch(/reserved prefix/);
163
- expect(validateServerExtensionRootPath('/api/graphql', extra)).toMatch(/reserved prefix/);
164
- expect(validateServerExtensionRootPath('/apiv2', extra)).toBeNull();
74
+ it('lets extensions sit under the GraphQL path (#5674): RootPaths are relative to MJAPI_BASE_PATH', () => {
75
+ const extra = CoreReservedServerExtensionRoots();
76
+ expect(validateServerExtensionRootPath('/api', extra)).toBeNull();
77
+ expect(validateServerExtensionRootPath('/forms', extra)).toBeNull();
165
78
  });
166
79
  });
@@ -43,6 +43,7 @@ function buildSourceUser(): UserInfo {
43
43
  user.ReturningVisitorContext = { VisitorKey: 'vk-1' };
44
44
  user.WidgetGuestContext = { WidgetID: 'W0000000-0000-0000-0000-000000000001' };
45
45
  user.IsMagicLinkAnonymous = true;
46
+ user.AnonymousVisitorID = 'visitor-sid-1';
46
47
  return user;
47
48
  }
48
49
 
@@ -99,13 +100,14 @@ describe('CloneUserForSessionContext', () => {
99
100
  expect(clone.APIKeyRowFilters).toHaveLength(1);
100
101
  });
101
102
 
102
- it('preserves ReturningVisitorContext, WidgetGuestContext, and IsMagicLinkAnonymous', () => {
103
+ it('preserves ReturningVisitorContext, WidgetGuestContext, IsMagicLinkAnonymous, and AnonymousVisitorID', () => {
103
104
  const source = buildSourceUser();
104
105
  const clone = CloneUserForSessionContext(source);
105
106
 
106
107
  expect(clone.ReturningVisitorContext).toEqual({ VisitorKey: 'vk-1' });
107
108
  expect(clone.WidgetGuestContext).toEqual({ WidgetID: 'W0000000-0000-0000-0000-000000000001' });
108
109
  expect(clone.IsMagicLinkAnonymous).toBe(true);
110
+ expect(clone.AnonymousVisitorID).toBe('visitor-sid-1');
109
111
  });
110
112
 
111
113
  it('stamping new contexts on the clone does NOT write through to the source', () => {
@@ -120,6 +122,7 @@ describe('CloneUserForSessionContext', () => {
120
122
  clone.APIKeyActingContext = { ActingOrganizationID: 'ORG-OTHER' };
121
123
  clone.APIKeyRowFilters = newBindings;
122
124
  clone.IsMagicLinkAnonymous = false;
125
+ clone.AnonymousVisitorID = 'visitor-sid-OTHER';
123
126
 
124
127
  // The source (potentially the shared UserCache instance) must be untouched
125
128
  expect(source.TenantContext).toEqual({ TenantID: 'tenant-1', Source: 'header' });
@@ -128,6 +131,7 @@ describe('CloneUserForSessionContext', () => {
128
131
  expect(source.APIKeyRowFilters).toHaveLength(1);
129
132
  expect(source.APIKeyRowFilters?.[0].FilterID).toBe('F0000000-0000-0000-0000-000000000001');
130
133
  expect(source.IsMagicLinkAnonymous).toBe(true);
134
+ expect(source.AnonymousVisitorID).toBe('visitor-sid-1');
131
135
  });
132
136
 
133
137
  it('clearing contexts on the clone does NOT clear them on the source', () => {
@@ -45,7 +45,7 @@ import { RedisLocalStorageProvider } from '@memberjunction/redis-provider';
45
45
  import type { CacheSettingsConfig } from '../config.js';
46
46
 
47
47
  function settings(overrides: Partial<CacheSettingsConfig>): CacheSettingsConfig {
48
- return { maxMemoryMB: 150, maxPercentOfCachePerEntity: 50, defaultTTLSeconds: 0, evictionSweepIntervalSeconds: 300, verboseLogging: false, engineSweepIntervalSeconds: 300, ...overrides };
48
+ return { maxMemoryMB: 150, maxPercentOfCachePerEntity: 50, maxEntryPercentOfCache: 'auto', defaultTTLSeconds: 0, evictionSweepIntervalSeconds: 300, verboseLogging: false, engineSweepIntervalSeconds: 300, ...overrides };
49
49
  }
50
50
 
51
51
  function event(overrides: Partial<CacheChangedEvent>): CacheChangedEvent {
@@ -117,9 +117,11 @@ describe('shared cache configuration', () => {
117
117
  });
118
118
 
119
119
  it('maps cacheSettings onto LocalCacheManager settings', () => {
120
- expect(CacheManagerConfigFromSettings(settings({ maxMemoryMB: 10, maxPercentOfCachePerEntity: 20, defaultTTLSeconds: 5, evictionSweepIntervalSeconds: 7, verboseLogging: true })))
121
- .toEqual({ maxSizeBytes: 10 * 1024 * 1024, maxPercentOfCachePerEntity: 20, defaultTTLMs: 5000, evictionSweepIntervalMs: 7000, verboseLogging: true });
120
+ expect(CacheManagerConfigFromSettings(settings({ maxMemoryMB: 10, maxPercentOfCachePerEntity: 20, maxEntryPercentOfCache: 40, defaultTTLSeconds: 5, evictionSweepIntervalSeconds: 7, verboseLogging: true })))
121
+ .toEqual({ maxSizeBytes: 10 * 1024 * 1024, maxPercentOfCachePerEntity: 20, maxEntryPercentOfCache: 40, defaultTTLMs: 5000, evictionSweepIntervalMs: 7000, verboseLogging: true });
122
122
  expect(CacheManagerConfigFromSettings(undefined).maxSizeBytes).toBe(150 * 1024 * 1024);
123
+ // The per-entry ceiling reaches the cache, and an unset one is derived rather than a fixed fraction.
124
+ expect(CacheManagerConfigFromSettings(undefined).maxEntryPercentOfCache).toBe('auto');
123
125
  });
124
126
 
125
127
  it('starts the engine sweep at the configured interval, off when 0', () => {
@@ -0,0 +1,40 @@
1
+ /**
2
+ * `SweepInterruptedRuns` closes out run artifacts a dead process left open. It is only sound
3
+ * BEFORE anything in this process can start a run: a run started first is still in flight when
4
+ * the sweep looks, so the sweep marks a live run "killed" and the wizard and active-operations
5
+ * panel report a failure for work that is still going.
6
+ *
7
+ * Booting the server is far too heavy for a unit test, so this pins the order in the source:
8
+ * the sweep is awaited before the scheduler, the integration sync worker (whose Start() polls
9
+ * immediately), the HTTP listener, and pending RSU work.
10
+ */
11
+ import { readFileSync } from 'node:fs';
12
+ import { join } from 'node:path';
13
+ import { describe, expect, it } from 'vitest';
14
+
15
+ const source = readFileSync(join(__dirname, '..', 'index.ts'), 'utf8')
16
+ .replace(/\/\*[\s\S]*?\*\//g, '')
17
+ .replace(/^\s*\/\/.*$/gm, '');
18
+
19
+ function at(needle: string): number {
20
+ const i = source.indexOf(needle);
21
+ expect(i, `not found in index.ts: ${needle}`).toBeGreaterThan(-1);
22
+ return i;
23
+ }
24
+
25
+ describe('startup: the interrupted-run sweep runs before anything can start a run', () => {
26
+ const sweep = () => at('await SweepInterruptedRuns(');
27
+
28
+ it.each([
29
+ ['the scheduled jobs service', 'scheduledJobsService.Start()'],
30
+ ['the integration sync worker', 'integrationSyncWorker.Start()'],
31
+ ['the HTTP listener', 'httpServer.listen('],
32
+ ['pending RSU work', 'processRSUPendingWork().catch('],
33
+ ])('precedes %s', (_label, needle) => {
34
+ expect(sweep()).toBeLessThan(at(needle));
35
+ });
36
+
37
+ it('is called exactly once', () => {
38
+ expect(source.split('await SweepInterruptedRuns(').length - 1).toBe(1);
39
+ });
40
+ });
@@ -263,6 +263,34 @@ describe('createUnifiedAuthMiddleware', () => {
263
263
  });
264
264
  });
265
265
 
266
+ describe('x-mj-timezone header', () => {
267
+ beforeEach(() => {
268
+ setupSuccessfulAuth();
269
+ });
270
+
271
+ async function payloadFor(headers: Record<string, string>) {
272
+ const req = makeMockReq({ headers: { authorization: 'Bearer valid-token', ...headers } });
273
+ await middleware(req, makeMockRes(), vi.fn());
274
+ return req.userPayload!;
275
+ }
276
+
277
+ it('carries a valid IANA zone on the user payload', async () => {
278
+ expect((await payloadFor({ 'x-mj-timezone': 'Pacific/Auckland' })).TimeZone).toBe('Pacific/Auckland');
279
+ });
280
+
281
+ it('drops an invalid zone, so callers fall back to UTC', async () => {
282
+ expect((await payloadFor({ 'x-mj-timezone': 'Mars/Olympus_Mons' })).TimeZone).toBeUndefined();
283
+ expect((await payloadFor({ 'x-mj-timezone': "UTC'; DROP TABLE x;--" })).TimeZone).toBeUndefined();
284
+ expect((await payloadFor({ 'x-mj-timezone': '' })).TimeZone).toBeUndefined();
285
+ });
286
+
287
+ it('leaves the payload exactly as before when the header is absent', async () => {
288
+ const payload = await payloadFor({});
289
+ expect(payload.TimeZone).toBeUndefined();
290
+ expect(Object.keys(payload)).not.toContain('TimeZone');
291
+ });
292
+ });
293
+
266
294
  describe('Authentication failures', () => {
267
295
  it('should return 401 when no authorization header is provided, quietly (no stack-trace log)', async () => {
268
296
  mockGetReadOnlyDS.mockReturnValue(MOCK_POOL);
@@ -155,6 +155,8 @@ describe('widgetGuestElevation — elevateUserPayload', () => {
155
155
  expect(elevated.isSystemUser).toBe(true);
156
156
  // The session id MUST be preserved so progress/streaming still routes to the guest's websocket.
157
157
  expect(elevated.sessionId).toBe('sess-123');
158
+ // The guest it stands in for, so conversation writes can stay the guest's (MJ#4791).
159
+ expect(elevated.ElevatedFrom).toBe(guestPayload.userRecord);
158
160
  });
159
161
 
160
162
  it('does not mutate the original guest payload', () => {