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

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 (463) hide show
  1. package/README.md +69 -0
  2. package/dist/.tsbuildinfo +1 -1
  3. package/dist/agentSessions/BridgeHostInstance.d.ts +8 -3
  4. package/dist/agentSessions/BridgeHostInstance.d.ts.map +1 -1
  5. package/dist/agentSessions/BridgeHostInstance.js +14 -4
  6. package/dist/agentSessions/BridgeHostInstance.js.map +1 -1
  7. package/dist/agentSessions/HandoffOfferExpiry.d.ts +10 -0
  8. package/dist/agentSessions/HandoffOfferExpiry.d.ts.map +1 -0
  9. package/dist/agentSessions/HandoffOfferExpiry.js +18 -0
  10. package/dist/agentSessions/HandoffOfferExpiry.js.map +1 -0
  11. package/dist/agentSessions/ReturningVisitorRecap.js +1 -0
  12. package/dist/agentSessions/ReturningVisitorRecap.js.map +1 -1
  13. package/dist/agentSessions/SessionJanitor.d.ts +31 -8
  14. package/dist/agentSessions/SessionJanitor.d.ts.map +1 -1
  15. package/dist/agentSessions/SessionJanitor.js +85 -41
  16. package/dist/agentSessions/SessionJanitor.js.map +1 -1
  17. package/dist/agentSessions/SessionManager.d.ts +14 -4
  18. package/dist/agentSessions/SessionManager.d.ts.map +1 -1
  19. package/dist/agentSessions/SessionManager.js +28 -4
  20. package/dist/agentSessions/SessionManager.js.map +1 -1
  21. package/dist/agentSessions/conversationBrowsers.d.ts +61 -0
  22. package/dist/agentSessions/conversationBrowsers.d.ts.map +1 -0
  23. package/dist/agentSessions/conversationBrowsers.js +118 -0
  24. package/dist/agentSessions/conversationBrowsers.js.map +1 -0
  25. package/dist/agentSessions/sessionDeadline.d.ts +43 -0
  26. package/dist/agentSessions/sessionDeadline.d.ts.map +1 -0
  27. package/dist/agentSessions/sessionDeadline.js +68 -0
  28. package/dist/agentSessions/sessionDeadline.js.map +1 -0
  29. package/dist/auth/APIKeyScopeAuth.d.ts.map +1 -1
  30. package/dist/auth/APIKeyScopeAuth.js +5 -4
  31. package/dist/auth/APIKeyScopeAuth.js.map +1 -1
  32. package/dist/auth/loginEmailResolution.d.ts +41 -0
  33. package/dist/auth/loginEmailResolution.d.ts.map +1 -0
  34. package/dist/auth/loginEmailResolution.js +47 -0
  35. package/dist/auth/loginEmailResolution.js.map +1 -0
  36. package/dist/auth/magicLink/MagicLinkKeys.d.ts +50 -2
  37. package/dist/auth/magicLink/MagicLinkKeys.d.ts.map +1 -1
  38. package/dist/auth/magicLink/MagicLinkKeys.js +129 -12
  39. package/dist/auth/magicLink/MagicLinkKeys.js.map +1 -1
  40. package/dist/auth/magicLink/MagicLinkRouter.d.ts +3 -0
  41. package/dist/auth/magicLink/MagicLinkRouter.d.ts.map +1 -1
  42. package/dist/auth/magicLink/MagicLinkRouter.js +23 -5
  43. package/dist/auth/magicLink/MagicLinkRouter.js.map +1 -1
  44. package/dist/auth/magicLink/MagicLinkService.d.ts +39 -5
  45. package/dist/auth/magicLink/MagicLinkService.d.ts.map +1 -1
  46. package/dist/auth/magicLink/MagicLinkService.js +221 -9
  47. package/dist/auth/magicLink/MagicLinkService.js.map +1 -1
  48. package/dist/auth/magicLink/index.d.ts +1 -1
  49. package/dist/auth/magicLink/index.d.ts.map +1 -1
  50. package/dist/auth/magicLink/index.js +1 -1
  51. package/dist/auth/magicLink/index.js.map +1 -1
  52. package/dist/auth/magicLink/redeemLanding.d.ts +2 -2
  53. package/dist/auth/magicLink/redeemLanding.d.ts.map +1 -1
  54. package/dist/auth/magicLink/redeemLanding.js +7 -4
  55. package/dist/auth/magicLink/redeemLanding.js.map +1 -1
  56. package/dist/auth/magicLink/types.d.ts +27 -3
  57. package/dist/auth/magicLink/types.d.ts.map +1 -1
  58. package/dist/auth/magicLink/types.js +0 -4
  59. package/dist/auth/magicLink/types.js.map +1 -1
  60. package/dist/auth/newUsers.d.ts.map +1 -1
  61. package/dist/auth/newUsers.js +1 -0
  62. package/dist/auth/newUsers.js.map +1 -1
  63. package/dist/backgroundSweepConfigUnits.d.ts +231 -0
  64. package/dist/backgroundSweepConfigUnits.d.ts.map +1 -0
  65. package/dist/backgroundSweepConfigUnits.js +180 -0
  66. package/dist/backgroundSweepConfigUnits.js.map +1 -0
  67. package/dist/broadcast/BaseCacheInvalidationBroadcastProvider.d.ts.map +1 -1
  68. package/dist/broadcast/BaseCacheInvalidationBroadcastProvider.js +2 -1
  69. package/dist/broadcast/BaseCacheInvalidationBroadcastProvider.js.map +1 -1
  70. package/dist/config.d.ts +906 -8
  71. package/dist/config.d.ts.map +1 -1
  72. package/dist/config.js +59 -0
  73. package/dist/config.js.map +1 -1
  74. package/dist/context.d.ts +2 -0
  75. package/dist/context.d.ts.map +1 -1
  76. package/dist/context.js +41 -12
  77. package/dist/context.js.map +1 -1
  78. package/dist/coreRoutes.d.ts +3 -1
  79. package/dist/coreRoutes.d.ts.map +1 -1
  80. package/dist/coreRoutes.js +6 -4
  81. package/dist/coreRoutes.js.map +1 -1
  82. package/dist/generated/graphql-schemas/__mj.d.ts +1072 -5
  83. package/dist/generated/graphql-schemas/__mj.d.ts.map +1 -1
  84. package/dist/generated/graphql-schemas/__mj.js +10174 -4618
  85. package/dist/generated/graphql-schemas/__mj.js.map +1 -1
  86. package/dist/generic/OrphanedConversationDetailReconciler.d.ts +11 -0
  87. package/dist/generic/OrphanedConversationDetailReconciler.d.ts.map +1 -1
  88. package/dist/generic/OrphanedConversationDetailReconciler.js +27 -1
  89. package/dist/generic/OrphanedConversationDetailReconciler.js.map +1 -1
  90. package/dist/generic/ResolverBase.d.ts +50 -5
  91. package/dist/generic/ResolverBase.d.ts.map +1 -1
  92. package/dist/generic/ResolverBase.js +329 -132
  93. package/dist/generic/ResolverBase.js.map +1 -1
  94. package/dist/generic/RunViewResolver.d.ts +4 -0
  95. package/dist/generic/RunViewResolver.d.ts.map +1 -1
  96. package/dist/generic/RunViewResolver.js +47 -7
  97. package/dist/generic/RunViewResolver.js.map +1 -1
  98. package/dist/globalCors.d.ts.map +1 -1
  99. package/dist/globalCors.js +1 -0
  100. package/dist/globalCors.js.map +1 -1
  101. package/dist/index.d.ts +3 -0
  102. package/dist/index.d.ts.map +1 -1
  103. package/dist/index.js +201 -43
  104. package/dist/index.js.map +1 -1
  105. package/dist/integration/ConnectionPause.d.ts +121 -0
  106. package/dist/integration/ConnectionPause.d.ts.map +1 -0
  107. package/dist/integration/ConnectionPause.js +183 -0
  108. package/dist/integration/ConnectionPause.js.map +1 -0
  109. package/dist/integration/CustomColumnPromoter.d.ts +72 -6
  110. package/dist/integration/CustomColumnPromoter.d.ts.map +1 -1
  111. package/dist/integration/CustomColumnPromoter.js +87 -46
  112. package/dist/integration/CustomColumnPromoter.js.map +1 -1
  113. package/dist/integration/EntityMapLifecycle.d.ts +78 -4
  114. package/dist/integration/EntityMapLifecycle.d.ts.map +1 -1
  115. package/dist/integration/EntityMapLifecycle.js +199 -5
  116. package/dist/integration/EntityMapLifecycle.js.map +1 -1
  117. package/dist/integration/InactiveRowWarnings.d.ts +31 -0
  118. package/dist/integration/InactiveRowWarnings.d.ts.map +1 -1
  119. package/dist/integration/InactiveRowWarnings.js +29 -0
  120. package/dist/integration/InactiveRowWarnings.js.map +1 -1
  121. package/dist/integration/ObjectNamesByID.d.ts +28 -0
  122. package/dist/integration/ObjectNamesByID.d.ts.map +1 -0
  123. package/dist/integration/ObjectNamesByID.js +87 -0
  124. package/dist/integration/ObjectNamesByID.js.map +1 -0
  125. package/dist/integration/PostRestartIntrospect.d.ts +26 -0
  126. package/dist/integration/PostRestartIntrospect.d.ts.map +1 -0
  127. package/dist/integration/PostRestartIntrospect.js +27 -0
  128. package/dist/integration/PostRestartIntrospect.js.map +1 -0
  129. package/dist/integration/RSUPostRestartProgress.d.ts +176 -0
  130. package/dist/integration/RSUPostRestartProgress.d.ts.map +1 -0
  131. package/dist/integration/RSUPostRestartProgress.js +323 -0
  132. package/dist/integration/RSUPostRestartProgress.js.map +1 -0
  133. package/dist/integration/RSUProgressBridge.d.ts +41 -2
  134. package/dist/integration/RSUProgressBridge.d.ts.map +1 -1
  135. package/dist/integration/RSUProgressBridge.js +84 -1
  136. package/dist/integration/RSUProgressBridge.js.map +1 -1
  137. package/dist/integration/RunArtifactAuthorization.d.ts +39 -0
  138. package/dist/integration/RunArtifactAuthorization.d.ts.map +1 -0
  139. package/dist/integration/RunArtifactAuthorization.js +45 -0
  140. package/dist/integration/RunArtifactAuthorization.js.map +1 -0
  141. package/dist/integration/SchemaRefreshLaunch.d.ts +9 -0
  142. package/dist/integration/SchemaRefreshLaunch.d.ts.map +1 -1
  143. package/dist/integration/SchemaRefreshLaunch.js +3 -2
  144. package/dist/integration/SchemaRefreshLaunch.js.map +1 -1
  145. package/dist/integration/ScopedFieldsForBuild.d.ts +46 -0
  146. package/dist/integration/ScopedFieldsForBuild.d.ts.map +1 -0
  147. package/dist/integration/ScopedFieldsForBuild.js +91 -0
  148. package/dist/integration/ScopedFieldsForBuild.js.map +1 -0
  149. package/dist/integration/TargetConfigGuard.d.ts +40 -0
  150. package/dist/integration/TargetConfigGuard.d.ts.map +1 -0
  151. package/dist/integration/TargetConfigGuard.js +40 -0
  152. package/dist/integration/TargetConfigGuard.js.map +1 -0
  153. package/dist/logging/StartupLogger.d.ts +50 -0
  154. package/dist/logging/StartupLogger.d.ts.map +1 -1
  155. package/dist/logging/StartupLogger.js +15 -22
  156. package/dist/logging/StartupLogger.js.map +1 -1
  157. package/dist/middleware/BaseServerMiddleware.d.ts +21 -3
  158. package/dist/middleware/BaseServerMiddleware.d.ts.map +1 -1
  159. package/dist/middleware/BaseServerMiddleware.js +4 -2
  160. package/dist/middleware/BaseServerMiddleware.js.map +1 -1
  161. package/dist/realtimeSessions/ownedRealtimeSession.d.ts.map +1 -1
  162. package/dist/realtimeSessions/ownedRealtimeSession.js +2 -3
  163. package/dist/realtimeSessions/ownedRealtimeSession.js.map +1 -1
  164. package/dist/realtimeSessions/sessionAccess.d.ts +15 -0
  165. package/dist/realtimeSessions/sessionAccess.d.ts.map +1 -1
  166. package/dist/realtimeSessions/sessionAccess.js +22 -0
  167. package/dist/realtimeSessions/sessionAccess.js.map +1 -1
  168. package/dist/realtimeWidget/WidgetSessionService.d.ts.map +1 -1
  169. package/dist/realtimeWidget/WidgetSessionService.js +2 -0
  170. package/dist/realtimeWidget/WidgetSessionService.js.map +1 -1
  171. package/dist/realtimeWidget/visitorIdentity.d.ts.map +1 -1
  172. package/dist/realtimeWidget/visitorIdentity.js +1 -0
  173. package/dist/realtimeWidget/visitorIdentity.js.map +1 -1
  174. package/dist/realtimeWidget/widgetCore.d.ts.map +1 -1
  175. package/dist/realtimeWidget/widgetCore.js +4 -7
  176. package/dist/realtimeWidget/widgetCore.js.map +1 -1
  177. package/dist/realtimeWidget/widgetGuestElevation.js +1 -0
  178. package/dist/realtimeWidget/widgetGuestElevation.js.map +1 -1
  179. package/dist/requestOrigin.d.ts +22 -0
  180. package/dist/requestOrigin.d.ts.map +1 -0
  181. package/dist/requestOrigin.js +60 -0
  182. package/dist/requestOrigin.js.map +1 -0
  183. package/dist/resolvers/AgentRunStatusPublisher.d.ts +10 -0
  184. package/dist/resolvers/AgentRunStatusPublisher.d.ts.map +1 -1
  185. package/dist/resolvers/AgentRunStatusPublisher.js +18 -2
  186. package/dist/resolvers/AgentRunStatusPublisher.js.map +1 -1
  187. package/dist/resolvers/AgentSessionResolver.d.ts.map +1 -1
  188. package/dist/resolvers/AgentSessionResolver.js +6 -2
  189. package/dist/resolvers/AgentSessionResolver.js.map +1 -1
  190. package/dist/resolvers/AutotagPipelineResolver.d.ts +1 -1
  191. package/dist/resolvers/AutotagPipelineResolver.d.ts.map +1 -1
  192. package/dist/resolvers/AutotagPipelineResolver.js +61 -16
  193. package/dist/resolvers/AutotagPipelineResolver.js.map +1 -1
  194. package/dist/resolvers/ConversationRunEventsResolver.d.ts.map +1 -1
  195. package/dist/resolvers/ConversationRunEventsResolver.js +2 -0
  196. package/dist/resolvers/ConversationRunEventsResolver.js.map +1 -1
  197. package/dist/resolvers/ExecuteRemoteOperationResolver.d.ts +6 -0
  198. package/dist/resolvers/ExecuteRemoteOperationResolver.d.ts.map +1 -1
  199. package/dist/resolvers/ExecuteRemoteOperationResolver.js +30 -7
  200. package/dist/resolvers/ExecuteRemoteOperationResolver.js.map +1 -1
  201. package/dist/resolvers/GetDataResolver.d.ts.map +1 -1
  202. package/dist/resolvers/GetDataResolver.js +34 -13
  203. package/dist/resolvers/GetDataResolver.js.map +1 -1
  204. package/dist/resolvers/InfoResolver.js +1 -1
  205. package/dist/resolvers/InfoResolver.js.map +1 -1
  206. package/dist/resolvers/IntegrationDiscoveryResolver.d.ts +401 -11
  207. package/dist/resolvers/IntegrationDiscoveryResolver.d.ts.map +1 -1
  208. package/dist/resolvers/IntegrationDiscoveryResolver.js +2690 -1305
  209. package/dist/resolvers/IntegrationDiscoveryResolver.js.map +1 -1
  210. package/dist/resolvers/MCPResolver.d.ts.map +1 -1
  211. package/dist/resolvers/MCPResolver.js +13 -2
  212. package/dist/resolvers/MCPResolver.js.map +1 -1
  213. package/dist/resolvers/QuerySystemUserResolver.d.ts +38 -1
  214. package/dist/resolvers/QuerySystemUserResolver.d.ts.map +1 -1
  215. package/dist/resolvers/QuerySystemUserResolver.js +181 -88
  216. package/dist/resolvers/QuerySystemUserResolver.js.map +1 -1
  217. package/dist/resolvers/RealtimeBridgeResolver.d.ts.map +1 -1
  218. package/dist/resolvers/RealtimeBridgeResolver.js +1 -0
  219. package/dist/resolvers/RealtimeBridgeResolver.js.map +1 -1
  220. package/dist/resolvers/RealtimeClientSessionResolver.d.ts +6 -0
  221. package/dist/resolvers/RealtimeClientSessionResolver.d.ts.map +1 -1
  222. package/dist/resolvers/RealtimeClientSessionResolver.js +36 -5
  223. package/dist/resolvers/RealtimeClientSessionResolver.js.map +1 -1
  224. package/dist/resolvers/RemoteBrowserActionResolver.d.ts +62 -10
  225. package/dist/resolvers/RemoteBrowserActionResolver.d.ts.map +1 -1
  226. package/dist/resolvers/RemoteBrowserActionResolver.js +217 -34
  227. package/dist/resolvers/RemoteBrowserActionResolver.js.map +1 -1
  228. package/dist/resolvers/RunAIAgentResolver.d.ts +24 -8
  229. package/dist/resolvers/RunAIAgentResolver.d.ts.map +1 -1
  230. package/dist/resolvers/RunAIAgentResolver.js +185 -67
  231. package/dist/resolvers/RunAIAgentResolver.js.map +1 -1
  232. package/dist/resolvers/RunAIPromptResolver.d.ts +5 -3
  233. package/dist/resolvers/RunAIPromptResolver.d.ts.map +1 -1
  234. package/dist/resolvers/RunAIPromptResolver.js +19 -18
  235. package/dist/resolvers/RunAIPromptResolver.js.map +1 -1
  236. package/dist/resolvers/RunTemplateResolver.d.ts.map +1 -1
  237. package/dist/resolvers/RunTemplateResolver.js +3 -1
  238. package/dist/resolvers/RunTemplateResolver.js.map +1 -1
  239. package/dist/resolvers/SqlLoggingConfigResolver.d.ts.map +1 -1
  240. package/dist/resolvers/SqlLoggingConfigResolver.js +7 -8
  241. package/dist/resolvers/SqlLoggingConfigResolver.js.map +1 -1
  242. package/dist/resolvers/TagGovernanceResolver.d.ts +12 -0
  243. package/dist/resolvers/TagGovernanceResolver.d.ts.map +1 -1
  244. package/dist/resolvers/TagGovernanceResolver.js +84 -0
  245. package/dist/resolvers/TagGovernanceResolver.js.map +1 -1
  246. package/dist/resolvers/TelemetryResolver.d.ts +13 -8
  247. package/dist/resolvers/TelemetryResolver.d.ts.map +1 -1
  248. package/dist/resolvers/TelemetryResolver.js +40 -9
  249. package/dist/resolvers/TelemetryResolver.js.map +1 -1
  250. package/dist/resolvers/meetingRecordingRegistration.d.ts.map +1 -1
  251. package/dist/resolvers/meetingRecordingRegistration.js +2 -0
  252. package/dist/resolvers/meetingRecordingRegistration.js.map +1 -1
  253. package/dist/resolvers/meetings/meetingMutations.d.ts.map +1 -1
  254. package/dist/resolvers/meetings/meetingMutations.js +34 -4
  255. package/dist/resolvers/meetings/meetingMutations.js.map +1 -1
  256. package/dist/rest/MediaAccessKeys.d.ts.map +1 -1
  257. package/dist/rest/MediaAccessKeys.js +6 -2
  258. package/dist/rest/MediaAccessKeys.js.map +1 -1
  259. package/dist/rest/OAuthCallbackHandler.d.ts.map +1 -1
  260. package/dist/rest/OAuthCallbackHandler.js +11 -5
  261. package/dist/rest/OAuthCallbackHandler.js.map +1 -1
  262. package/dist/rest/RealtimeRecordingHandler.d.ts +3 -3
  263. package/dist/rest/RealtimeRecordingHandler.d.ts.map +1 -1
  264. package/dist/rest/RealtimeRecordingHandler.js +2 -4
  265. package/dist/rest/RealtimeRecordingHandler.js.map +1 -1
  266. package/dist/rest/ViewOperationsHandler.d.ts.map +1 -1
  267. package/dist/rest/ViewOperationsHandler.js +2 -1
  268. package/dist/rest/ViewOperationsHandler.js.map +1 -1
  269. package/dist/rest/setupRESTEndpoints.d.ts +3 -1
  270. package/dist/rest/setupRESTEndpoints.d.ts.map +1 -1
  271. package/dist/rest/setupRESTEndpoints.js +11 -2
  272. package/dist/rest/setupRESTEndpoints.js.map +1 -1
  273. package/dist/services/DurableEntityActionTaskSubmitter.d.ts.map +1 -1
  274. package/dist/services/DurableEntityActionTaskSubmitter.js +6 -1
  275. package/dist/services/DurableEntityActionTaskSubmitter.js.map +1 -1
  276. package/dist/services/TaskGraphActionRunner.d.ts.map +1 -1
  277. package/dist/services/TaskGraphActionRunner.js +2 -26
  278. package/dist/services/TaskGraphActionRunner.js.map +1 -1
  279. package/dist/services/TaskGraphAgentRunner.d.ts +6 -0
  280. package/dist/services/TaskGraphAgentRunner.d.ts.map +1 -1
  281. package/dist/services/TaskGraphAgentRunner.js +38 -0
  282. package/dist/services/TaskGraphAgentRunner.js.map +1 -1
  283. package/dist/services/TaskGraphContinuationDeliverer.d.ts.map +1 -1
  284. package/dist/services/TaskGraphContinuationDeliverer.js +1 -0
  285. package/dist/services/TaskGraphContinuationDeliverer.js.map +1 -1
  286. package/dist/services/WorkQueueHostService.d.ts +2 -1
  287. package/dist/services/WorkQueueHostService.d.ts.map +1 -1
  288. package/dist/services/WorkQueueHostService.js +19 -4
  289. package/dist/services/WorkQueueHostService.js.map +1 -1
  290. package/dist/services/workQueueConfig.d.ts +2 -2
  291. package/dist/services/workQueueConfig.js +5 -5
  292. package/dist/services/workQueueConfig.js.map +1 -1
  293. package/dist/sharedCache.js +1 -1
  294. package/dist/sharedCache.js.map +1 -1
  295. package/dist/types.d.ts +17 -0
  296. package/dist/types.d.ts.map +1 -1
  297. package/dist/types.js.map +1 -1
  298. package/dist/util.d.ts.map +1 -1
  299. package/dist/util.js +55 -2
  300. package/dist/util.js.map +1 -1
  301. package/package.json +107 -100
  302. package/src/__tests__/APIKeyScopeAuth.test.ts +4 -8
  303. package/src/__tests__/ActiveOperations.test.ts +68 -0
  304. package/src/__tests__/AgentSessionResolver.ownership.test.ts +13 -0
  305. package/src/__tests__/AssertTargetConfigs.test.ts +75 -0
  306. package/src/__tests__/BatchApplyParity.test.ts +109 -0
  307. package/src/__tests__/BridgeHostInstance.test.ts +42 -0
  308. package/src/__tests__/CollectInactiveRowWarnings.test.ts +115 -0
  309. package/src/__tests__/ConnectionPause.test.ts +192 -0
  310. package/src/__tests__/ConnectionPauseResolver.test.ts +166 -0
  311. package/src/__tests__/ConnectionPauseSave.test.ts +143 -0
  312. package/src/__tests__/CustomColumnPromoterCompletion.test.ts +142 -0
  313. package/src/__tests__/DeleteConnectionRemovesPerConnectionCatalog.test.ts +51 -0
  314. package/src/__tests__/DetachedDiscoveryStart.test.ts +65 -0
  315. package/src/__tests__/DurableEntityActionTaskSubmitter.test.ts +73 -0
  316. package/src/__tests__/EntityMapLifecycle.rekey.test.ts +252 -0
  317. package/src/__tests__/ExecuteRemoteOperationResolver.test.ts +213 -0
  318. package/src/__tests__/HandoffOfferExpiry.test.ts +40 -0
  319. package/src/__tests__/IntegrationRunArtifactAuthorization.test.ts +126 -0
  320. package/src/__tests__/MagicLinkKeys.test.ts +102 -0
  321. package/src/__tests__/MediaAccessKeys.test.ts +57 -10
  322. package/src/__tests__/NewArrivalsDefaultDisabled.test.ts +50 -0
  323. package/src/__tests__/ObjectNamesByID.test.ts +176 -0
  324. package/src/__tests__/PostRestartMapsInCatalogScope.test.ts +146 -0
  325. package/src/__tests__/QuerySystemUserResolver.childRows.test.ts +80 -0
  326. package/src/__tests__/QuerySystemUserResolver.collision.test.ts +200 -0
  327. package/src/__tests__/QuerySystemUserResolver.permissions.test.ts +132 -0
  328. package/src/__tests__/RSUPostRestartProgress.test.ts +343 -0
  329. package/src/__tests__/RSUProgressBridge.test.ts +188 -5
  330. package/src/__tests__/RSUStartupSweepWiring.test.ts +30 -0
  331. package/src/__tests__/ReactivateRefreshMessage.test.ts +10 -3
  332. package/src/__tests__/RealtimeClientSessionResolver.test.ts +22 -0
  333. package/src/__tests__/{RefreshAdoptsNewColumns.test.ts → RefreshReportsNewColumns.test.ts} +25 -16
  334. package/src/__tests__/RemoteBrowserActionRecovery.test.ts +287 -0
  335. package/src/__tests__/RemoteBrowserActionResolver.goalRunLink.test.ts +3 -0
  336. package/src/__tests__/RemoteBrowserActionResolver.ownership.test.ts +6 -4
  337. package/src/__tests__/RemoteBrowserAudioStream.test.ts +3 -0
  338. package/src/__tests__/RemoteBrowserSnapshot.test.ts +3 -0
  339. package/src/__tests__/ResolverBase.clientOldValues.test.ts +327 -26
  340. package/src/__tests__/ResolverBase.diagnosticSenderRegistrations.test.ts +26 -0
  341. package/src/__tests__/ResolverBase.encryptedApiZeroReencrypt.test.ts +307 -0
  342. package/src/__tests__/ResolverBase.entityMessageRelay.test.ts +53 -2
  343. package/src/__tests__/ResolverBase.includeBinaryFields.test.ts +60 -0
  344. package/src/__tests__/RunAIAgentResolver.branch.test.ts +4 -0
  345. package/src/__tests__/RunAIAgentResolver.consultPredecessor.test.ts +289 -0
  346. package/src/__tests__/RunAIAgentResolver.handoff.test.ts +649 -0
  347. package/src/__tests__/RunAIAgentResolver.historyFrom.test.ts +2 -0
  348. package/src/__tests__/RunAIAgentResolver.runDataGuard.test.ts +2 -0
  349. package/src/__tests__/RunAIAgentResolver.stoppedPredecessor.test.ts +87 -12
  350. package/src/__tests__/RunAIAgentResolver.streaming.test.ts +28 -4
  351. package/src/__tests__/SchemaEvolutionCustomColumns.test.ts +119 -0
  352. package/src/__tests__/SchemaRefreshLaunch.test.ts +24 -2
  353. package/src/__tests__/ScopedFieldsForBuild.test.ts +192 -0
  354. package/src/__tests__/SessionJanitor.test.ts +195 -4
  355. package/src/__tests__/SessionManager.test.ts +69 -0
  356. package/src/__tests__/TagGovernanceAndAutotagResolvers.test.ts +261 -0
  357. package/src/__tests__/TagGovernanceResolver.test.ts +60 -2
  358. package/src/__tests__/TaskGraphActionRunner.durablePayload.test.ts +2 -2
  359. package/src/__tests__/TaskGraphAgentRunner.test.ts +272 -16
  360. package/src/__tests__/ViewOperationsHandler.test.ts +2 -5
  361. package/src/__tests__/WorkQueueHostService.test.ts +25 -7
  362. package/src/__tests__/backgroundSweepConfigUnits.test.ts +153 -0
  363. package/src/__tests__/contextRequestOrigin.test.ts +105 -0
  364. package/src/__tests__/conversationBrowsers.test.ts +62 -0
  365. package/src/__tests__/globalCors.test.ts +12 -0
  366. package/src/__tests__/magicLinkExtensionCapabilities.test.ts +413 -0
  367. package/src/__tests__/orphanedConversationDetailReconciler.start.test.ts +82 -0
  368. package/src/__tests__/ownedRealtimeSession.test.ts +14 -0
  369. package/src/__tests__/realtimeRecordingHandler.test.ts +7 -2
  370. package/src/__tests__/realtimeSessions/sessionAccess.test.ts +79 -1
  371. package/src/__tests__/requestOrigin.test.ts +104 -0
  372. package/src/__tests__/review-capture-valid-token-log.test.ts +98 -0
  373. package/src/__tests__/runStreams.fieldSecurity.test.ts +5 -5
  374. package/src/__tests__/securityHardeningPr6034.test.ts +120 -0
  375. package/src/__tests__/sessionDeadline.test.ts +51 -0
  376. package/src/__tests__/sharedCache.test.ts +11 -0
  377. package/src/__tests__/unifiedAuth.test.ts +73 -1
  378. package/src/agentSessions/BridgeHostInstance.ts +20 -5
  379. package/src/agentSessions/HandoffOfferExpiry.ts +18 -0
  380. package/src/agentSessions/ReturningVisitorRecap.ts +1 -0
  381. package/src/agentSessions/SessionJanitor.ts +92 -42
  382. package/src/agentSessions/SessionManager.ts +38 -8
  383. package/src/agentSessions/conversationBrowsers.ts +143 -0
  384. package/src/agentSessions/sessionDeadline.ts +81 -0
  385. package/src/auth/APIKeyScopeAuth.ts +5 -4
  386. package/src/auth/__tests__/loginEmailResolution.test.ts +67 -0
  387. package/src/auth/loginEmailResolution.ts +67 -0
  388. package/src/auth/magicLink/MagicLinkKeys.ts +146 -14
  389. package/src/auth/magicLink/MagicLinkRouter.ts +33 -6
  390. package/src/auth/magicLink/MagicLinkService.ts +250 -10
  391. package/src/auth/magicLink/index.ts +1 -1
  392. package/src/auth/magicLink/redeemLanding.ts +7 -4
  393. package/src/auth/magicLink/types.ts +28 -3
  394. package/src/auth/newUsers.ts +1 -0
  395. package/src/backgroundSweepConfigUnits.ts +213 -0
  396. package/src/broadcast/BaseCacheInvalidationBroadcastProvider.ts +2 -1
  397. package/src/config.ts +69 -0
  398. package/src/context.ts +46 -15
  399. package/src/coreRoutes.ts +14 -7
  400. package/src/generated/graphql-schemas/__mj.ts +10360 -6479
  401. package/src/generic/OrphanedConversationDetailReconciler.ts +32 -1
  402. package/src/generic/ResolverBase.ts +422 -143
  403. package/src/generic/RunViewResolver.ts +96 -49
  404. package/src/globalCors.ts +1 -0
  405. package/src/index.ts +246 -44
  406. package/src/integration/ConnectionPause.ts +222 -0
  407. package/src/integration/CustomColumnPromoter.ts +123 -53
  408. package/src/integration/EntityMapLifecycle.ts +229 -6
  409. package/src/integration/InactiveRowWarnings.ts +50 -0
  410. package/src/integration/ObjectNamesByID.ts +97 -0
  411. package/src/integration/PostRestartIntrospect.ts +49 -0
  412. package/src/integration/RSUPostRestartProgress.ts +359 -0
  413. package/src/integration/RSUProgressBridge.ts +85 -2
  414. package/src/integration/RunArtifactAuthorization.ts +65 -0
  415. package/src/integration/SchemaRefreshLaunch.ts +12 -2
  416. package/src/integration/ScopedFieldsForBuild.ts +113 -0
  417. package/src/integration/TargetConfigGuard.ts +71 -0
  418. package/src/logging/StartupLogger.ts +24 -0
  419. package/src/middleware/BaseServerMiddleware.ts +20 -3
  420. package/src/realtimeSessions/ownedRealtimeSession.ts +2 -6
  421. package/src/realtimeSessions/sessionAccess.ts +30 -0
  422. package/src/realtimeWidget/WidgetSessionService.ts +2 -0
  423. package/src/realtimeWidget/visitorIdentity.ts +1 -0
  424. package/src/realtimeWidget/widgetCore.ts +4 -8
  425. package/src/realtimeWidget/widgetGuestElevation.ts +1 -0
  426. package/src/requestOrigin.ts +64 -0
  427. package/src/resolvers/AgentRunStatusPublisher.ts +27 -2
  428. package/src/resolvers/AgentSessionResolver.ts +6 -2
  429. package/src/resolvers/AutotagPipelineResolver.ts +59 -14
  430. package/src/resolvers/ConversationRunEventsResolver.ts +2 -0
  431. package/src/resolvers/ExecuteRemoteOperationResolver.ts +41 -9
  432. package/src/resolvers/GetDataResolver.ts +35 -13
  433. package/src/resolvers/InfoResolver.ts +1 -1
  434. package/src/resolvers/IntegrationDiscoveryResolver.ts +1522 -203
  435. package/src/resolvers/MCPResolver.ts +15 -2
  436. package/src/resolvers/QuerySystemUserResolver.ts +211 -100
  437. package/src/resolvers/RealtimeBridgeResolver.ts +1 -0
  438. package/src/resolvers/RealtimeClientSessionResolver.ts +40 -7
  439. package/src/resolvers/RemoteBrowserActionResolver.ts +242 -32
  440. package/src/resolvers/RunAIAgentResolver.ts +265 -86
  441. package/src/resolvers/RunAIPromptResolver.ts +18 -18
  442. package/src/resolvers/RunTemplateResolver.ts +3 -1
  443. package/src/resolvers/SqlLoggingConfigResolver.ts +7 -9
  444. package/src/resolvers/TagGovernanceResolver.ts +65 -0
  445. package/src/resolvers/TelemetryResolver.ts +44 -9
  446. package/src/resolvers/__tests__/GetDataResolver.tokens.test.ts +52 -0
  447. package/src/resolvers/meetingRecordingRegistration.ts +2 -0
  448. package/src/resolvers/meetings/meetingMutations.ts +37 -7
  449. package/src/rest/MediaAccessKeys.ts +6 -2
  450. package/src/rest/OAuthCallbackHandler.ts +13 -5
  451. package/src/rest/RealtimeRecordingHandler.ts +5 -9
  452. package/src/rest/ViewOperationsHandler.ts +2 -1
  453. package/src/rest/setupRESTEndpoints.ts +11 -2
  454. package/src/services/DurableEntityActionTaskSubmitter.ts +10 -2
  455. package/src/services/TaskGraphActionRunner.ts +2 -23
  456. package/src/services/TaskGraphAgentRunner.ts +43 -0
  457. package/src/services/TaskGraphContinuationDeliverer.ts +1 -0
  458. package/src/services/WorkQueueHostService.ts +26 -4
  459. package/src/services/workQueueConfig.ts +5 -5
  460. package/src/sharedCache.ts +1 -1
  461. package/src/types.ts +18 -0
  462. package/src/util.ts +55 -3
  463. package/src/services/TaskOrchestration-Integration.md +0 -188
@@ -0,0 +1,359 @@
1
+ /**
2
+ * @fileoverview The second half of an RSU run's progress stream — the half that happens in a
3
+ * different process from the first.
4
+ *
5
+ * The RSU pipeline restarts the API as one of its own steps. Everything after that restart —
6
+ * creating the entity maps and field maps, then starting the first sync — runs in the NEW process,
7
+ * from the durable pending-work queue. Until now none of it was observable: the pre-restart process
8
+ * died mid-run, so its emitter never wrote a terminal result and the run stayed flagged in-flight
9
+ * forever, while the post-restart work reported nothing at all. A client watching the setup journey
10
+ * saw the stream stop at "restart instance" and had no way to learn whether the connector ever
11
+ * became live.
12
+ *
13
+ * This module re-attaches to that same run and finishes it. Re-attaching (rather than opening a new
14
+ * run) is the entire point: the tail API returns strictly `seq > cursor`, and a fresh emitter
15
+ * restarts its sequence at 1 — so a brand-new run's events would sort BELOW the cursor of every
16
+ * client already tailing, permanently invisible to them, while the resolver echoed their unchanged
17
+ * cursor back and they polled forever receiving nothing.
18
+ *
19
+ * Correlation is by MANIFEST, not by checkpoint. The manifest is written when the run opens, minutes
20
+ * before the restart; the checkpoint is written in the last moments before the kill signal. Keying
21
+ * off the durable artifact and treating the checkpoint as enrichment means a lost checkpoint costs
22
+ * step numbers, not the whole re-attachment.
23
+ */
24
+ import {
25
+ IntegrationProgressEmitter,
26
+ IntegrationProgressReader,
27
+ IntegrationRunResumeError,
28
+ SweepInterruptedRuns,
29
+ } from '@memberjunction/integration-progress-artifacts';
30
+ import type { EmitterOptions, IntegrationProgressEvent, IntegrationRunSnapshot } from '@memberjunction/integration-progress-artifacts';
31
+ import { RuntimeSchemaManager } from '@memberjunction/schema-engine';
32
+ import { LogError, LogStatus } from '@memberjunction/core';
33
+ import { RESTART_STAGE } from './RSUProgressBridge.js';
34
+
35
+ /** The two stages this module owns, in order. Named by the pipeline so the halves cannot drift. */
36
+ const [ENTITY_MAPS_STAGE, START_SYNC_STAGE] = RuntimeSchemaManager.EXPECTED_STEPS_POST_RESTART;
37
+
38
+ /**
39
+ * How many in-flight RSU runs to consider for a single connection before giving up.
40
+ *
41
+ * More than one can exist only when an earlier run's post-restart work never completed (its process
42
+ * died again, or its pending-work rows were consumed on a boot that then crashed). The newest is the
43
+ * one this pending-work item belongs to; the window is small so a pathological backlog cannot turn
44
+ * one item's re-attachment into a directory scan of the whole run history.
45
+ */
46
+ const CANDIDATE_RUN_WINDOW = 25;
47
+
48
+ /** Step position carried across the restart, when the checkpoint survived. */
49
+ interface RestartPosition {
50
+ /** 1-based index of `RestartMJAPI`; the post-restart steps follow it. */
51
+ stepIndex?: number;
52
+ stepTotal?: number;
53
+ }
54
+
55
+ /**
56
+ * One re-attached RSU run. Emits the two post-restart stages into the run the pre-restart process
57
+ * opened, and terminates it exactly once.
58
+ *
59
+ * Obtained from {@link RSUPostRestartProgressSession}, never constructed directly — the session is
60
+ * what guarantees a run is attached (and terminated) once even when several pending-work items in
61
+ * one sweep belong to the same batch.
62
+ */
63
+ export class RSUPostRestartProgress {
64
+ private entityMapsOpen = false;
65
+ private syncOpen = false;
66
+ private failed = false;
67
+ private readonly failures: string[] = [];
68
+
69
+ /** @internal — use {@link RSUPostRestartProgressSession.For}. */
70
+ public constructor(
71
+ private readonly emitter: IntegrationProgressEmitter,
72
+ private readonly position: RestartPosition
73
+ ) {}
74
+
75
+ /** The run being continued, so a caller can log or hand it to a client. */
76
+ public get RunID(): string {
77
+ return this.emitter.RunID;
78
+ }
79
+
80
+ /**
81
+ * "step 12 of 14" for a post-restart step, or undefined when the checkpoint did not survive.
82
+ *
83
+ * `offset` is the step's position AFTER `RestartMJAPI` (1 for CreateEntityMaps, 2 for StartSync).
84
+ * Derived from the checkpoint rather than recomputed, because the pipeline skips steps
85
+ * conditionally (`SkipGitCommit`) and a recomputed index would disagree with the one the client
86
+ * has already been shown.
87
+ */
88
+ private positionLabel(offset: number): string | undefined {
89
+ const { stepIndex, stepTotal } = this.position;
90
+ if (stepIndex == null || stepTotal == null) return undefined;
91
+ return `step ${stepIndex + offset} of ${stepTotal}`;
92
+ }
93
+
94
+ /** Opens the `CreateEntityMaps` stage for one pending-work item. */
95
+ public BeginEntityMaps(objectCount: number): void {
96
+ this.emitter.stageStart(ENTITY_MAPS_STAGE, this.positionLabel(1));
97
+ this.entityMapsOpen = true;
98
+ this.emitter.heartbeat(ENTITY_MAPS_STAGE, `Mapping ${objectCount} object(s)`, { processed: 0, totalKnown: objectCount });
99
+ }
100
+
101
+ /**
102
+ * Reports one object's entity map + field maps as done.
103
+ *
104
+ * A heartbeat rather than a `records.batch.complete`: heartbeat counts are explicitly excluded
105
+ * from the run's applied aggregate, so per-object liveness cannot double-count against the
106
+ * stage rollup that follows.
107
+ */
108
+ public ObjectMapped(objectName: string, processed: number, totalKnown: number): void {
109
+ this.emitter.heartbeat(ENTITY_MAPS_STAGE, `Mapped ${objectName} (${processed}/${totalKnown})`, { processed, totalKnown });
110
+ }
111
+
112
+ /**
113
+ * Closes `CreateEntityMaps` with the applied quartet, in OBJECTS.
114
+ *
115
+ * The run's aggregate is in objects, not migrations, for a resumed run: the migration quartet is
116
+ * emitted by `run.end`, which a restarting run by definition never reaches. Emitting both here
117
+ * would sum two different units into one number (2 migrations + 12 objects reported as
118
+ * "processed: 14"), so the migration counts stay in the terminal message instead.
119
+ */
120
+ public CompleteEntityMaps(processed: number, succeeded: number, failed: number): void {
121
+ if (!this.entityMapsOpen) return;
122
+ this.entityMapsOpen = false;
123
+ this.emitter.stageComplete(ENTITY_MAPS_STAGE, { processed, succeeded, failed, totalKnown: processed });
124
+ }
125
+
126
+ /**
127
+ * Closes `CreateEntityMaps` as an ERROR, naming the objects that were never mapped.
128
+ *
129
+ * Naming them is the requirement, not a nicety: when this half fails, those objects have tables
130
+ * in the database and no way to sync into them, and the operator's only signal that something
131
+ * needs re-applying is this message.
132
+ */
133
+ public FailEntityMaps(message: string, unmappedObjects: string[]): void {
134
+ this.entityMapsOpen = false;
135
+ this.failed = true;
136
+ const named = unmappedObjects.length > 0
137
+ ? ` Objects never mapped: ${unmappedObjects.slice(0, 20).join(', ')}${unmappedObjects.length > 20 ? ` (+${unmappedObjects.length - 20} more)` : ''}.`
138
+ : '';
139
+ const full = `${message}${named}`;
140
+ this.failures.push(full);
141
+ this.emitter.stageError(ENTITY_MAPS_STAGE, full, {
142
+ code: 'RSU_POST_RESTART_ENTITY_MAPS_FAILED',
143
+ unmappedObjects,
144
+ });
145
+ }
146
+
147
+ /** Opens the `StartSync` stage. */
148
+ public BeginStartSync(): void {
149
+ this.emitter.stageStart(START_SYNC_STAGE, this.positionLabel(2));
150
+ this.syncOpen = true;
151
+ }
152
+
153
+ /**
154
+ * Closes `StartSync`, carrying the sync run's ID so a client can hop from this stream to the
155
+ * sync's own.
156
+ *
157
+ * Deliberately carries NO `counts`: the records belong to the sync run, and counting them here
158
+ * would add another unit to this run's aggregate. `syncRunID` may be null when the sync was
159
+ * launched but its run row was not yet readable — the stage still completes, because the sync
160
+ * did start; the client simply has to find the run itself.
161
+ */
162
+ public CompleteStartSync(syncRunID: string | null): void {
163
+ if (!this.syncOpen) return;
164
+ this.syncOpen = false;
165
+ this.emitter.emit('stage.complete', {
166
+ stage: START_SYNC_STAGE,
167
+ level: 'info',
168
+ message: syncRunID ? `Sync started — run ${syncRunID}` : 'Sync started',
169
+ data: { syncRunID },
170
+ });
171
+ }
172
+
173
+ /** Closes `StartSync` as skipped — the caller asked for no initial sync. */
174
+ public SkipStartSync(reason: string): void {
175
+ if (!this.syncOpen) return;
176
+ this.syncOpen = false;
177
+ this.emitter.emit('stage.complete', {
178
+ stage: START_SYNC_STAGE,
179
+ level: 'info',
180
+ message: `Sync not started — ${reason}`,
181
+ data: { syncRunID: null, skipped: true, reason },
182
+ });
183
+ }
184
+
185
+ /** Closes `StartSync` as an error — the sync could not be launched. */
186
+ public FailStartSync(message: string): void {
187
+ this.syncOpen = false;
188
+ this.failed = true;
189
+ this.failures.push(message);
190
+ this.emitter.stageError(START_SYNC_STAGE, message, { code: 'RSU_POST_RESTART_SYNC_START_FAILED' });
191
+ }
192
+
193
+ /** Records a failure that happened outside either stage (connector resolution, for example). */
194
+ public RecordFailure(stage: string, message: string): void {
195
+ this.failed = true;
196
+ this.failures.push(message);
197
+ this.emitter.stageError(stage, message, { code: 'RSU_POST_RESTART_FAILED' });
198
+ }
199
+
200
+ /**
201
+ * Terminates the run. Idempotent — the emitter refuses a second terminal write, and the result
202
+ * file is created-not-truncated, so a duplicate call cannot rewrite an outcome a client has
203
+ * already read.
204
+ */
205
+ public async Finish(): Promise<void> {
206
+ // An unclosed stage would leave a client's stepper stuck on it forever; a run must never end
207
+ // with a stage still open.
208
+ if (this.entityMapsOpen) this.FailEntityMaps('Post-restart processing ended with entity-map creation still open', []);
209
+ if (this.syncOpen) this.FailStartSync('Post-restart processing ended with sync start still open');
210
+ if (this.failed) {
211
+ await this.emitter.fail(
212
+ `RSU post-restart work failed — ${this.failures.join(' | ')}`,
213
+ 'rsu-post-restart-failed'
214
+ );
215
+ } else {
216
+ await this.emitter.complete('RSU pipeline complete — entity maps created and sync started');
217
+ }
218
+ }
219
+ }
220
+
221
+ /**
222
+ * Owns the re-attachments for ONE pass of the post-restart pending-work queue.
223
+ *
224
+ * A single RSU batch registers one pending-work row per input, so several rows in one sweep can
225
+ * belong to the same run. Attaching per row would try to resume — and terminate — the same run
226
+ * repeatedly; the second attempt would be refused as already-terminal and its work would go
227
+ * unreported. Memoizing by run ID makes the run's stream a single coherent continuation, with one
228
+ * `run.resumed` at the top and one terminal event at the bottom.
229
+ */
230
+ export class RSUPostRestartProgressSession {
231
+ private readonly byRunID = new Map<string, RSUPostRestartProgress>();
232
+ private readonly byCompanyIntegrationID = new Map<string, RSUPostRestartProgress | null>();
233
+ /**
234
+ * Every run any session in this process re-attached to. Process-wide on purpose: the startup
235
+ * sweep that left restart-killed runs in flight and the pass that resumes them live in
236
+ * different places, and the one question that joins them — "did anything pick this run up?" —
237
+ * must survive the resuming pass throwing halfway.
238
+ */
239
+ private static readonly attachedRunIDs = new Set<string>();
240
+ public static get AttachedRunIDs(): ReadonlySet<string> {
241
+ return RSUPostRestartProgressSession.attachedRunIDs;
242
+ }
243
+
244
+ constructor(private readonly emitterOptions: EmitterOptions = {}) {}
245
+
246
+ /**
247
+ * The continuation handle for a connection's RSU run, or null when there is nothing to continue
248
+ * (no in-flight run for that connection, or it was already finished).
249
+ *
250
+ * Null is a normal outcome, not an error: pending work can legitimately be processed on a boot
251
+ * whose run artifact has been pruned, or after a retry that already closed the run. The caller
252
+ * does its work either way — progress reporting never gates the work itself.
253
+ */
254
+ public async For(companyIntegrationID: string): Promise<RSUPostRestartProgress | null> {
255
+ const cached = this.byCompanyIntegrationID.get(companyIntegrationID);
256
+ if (cached !== undefined) return cached;
257
+
258
+ const attached = await this.attach(companyIntegrationID);
259
+ this.byCompanyIntegrationID.set(companyIntegrationID, attached);
260
+ return attached;
261
+ }
262
+
263
+ /** Terminates every run this session re-attached to. Safe to call more than once. */
264
+ public async FinishAll(): Promise<void> {
265
+ for (const progress of this.byRunID.values()) {
266
+ await progress.Finish().catch(err => LogError(`[RSU] post-restart terminal write failed for run ${progress.RunID}: ${err}`));
267
+ }
268
+ }
269
+
270
+ private async attach(companyIntegrationID: string): Promise<RSUPostRestartProgress | null> {
271
+ try {
272
+ const reader = new IntegrationProgressReader(this.emitterOptions.rootDir);
273
+ const candidates = await reader.ListRuns(
274
+ { runKind: 'RSU', companyIntegrationID, inFlightOnly: true },
275
+ CANDIDATE_RUN_WINDOW
276
+ );
277
+ // A run this session already picked up no longer LOOKS killed-at-the-restart — its latest
278
+ // event is now the `run.resumed` this session wrote. So an already-attached run must be
279
+ // matched first, or the second pending-work item of the same batch would find nothing to
280
+ // report into and its work would go unobserved.
281
+ const snapshot = candidates.find(s => this.byRunID.has(s.manifest.runID))
282
+ ?? candidates.find(RSUPostRestartProgressSession.WasKilledAtRestart);
283
+ if (!snapshot) return null;
284
+
285
+ const runID = snapshot.manifest.runID;
286
+ const existing = this.byRunID.get(runID);
287
+ if (existing) return existing;
288
+
289
+ const checkpoint = await reader.LatestCheckpoint(runID);
290
+ const emitter = await IntegrationProgressEmitter.Resume(runID, this.emitterOptions);
291
+ emitter.RunResumed(
292
+ `Resumed after the RSU restart — continuing with ${RuntimeSchemaManager.EXPECTED_STEPS_POST_RESTART.join(' then ')}`,
293
+ { companyIntegrationID }
294
+ );
295
+ const progress = new RSUPostRestartProgress(emitter, RSUPostRestartProgressSession.PositionFrom(checkpoint));
296
+ this.byRunID.set(runID, progress);
297
+ RSUPostRestartProgressSession.attachedRunIDs.add(runID);
298
+ LogStatus(`[RSU] Re-attached to run ${runID} for connection ${companyIntegrationID}`);
299
+ return progress;
300
+ } catch (err: unknown) {
301
+ // A run that is already terminal, or has no manifest, is not an error condition worth
302
+ // failing the actual work over — the work still has to happen, unobserved.
303
+ if (err instanceof IntegrationRunResumeError) {
304
+ LogStatus(`[RSU] Not resuming progress for ${companyIntegrationID}: ${err.message}`);
305
+ return null;
306
+ }
307
+ LogError(`[RSU] Could not re-attach progress for ${companyIntegrationID}: ${err instanceof Error ? err.message : String(err)}`);
308
+ return null;
309
+ }
310
+ }
311
+
312
+ /**
313
+ * Whether a run's stream stops at the restart — which is the signature of a run this process is
314
+ * the continuation of.
315
+ *
316
+ * Checking the LAST stage, not the presence of the checkpoint, on purpose. The checkpoint is
317
+ * written in the moments before the kill signal and can be lost; the `stage.start RestartMJAPI`
318
+ * that precedes it cannot, because the pipeline waits on nothing between emitting it and dying.
319
+ * Requiring the checkpoint would trade a recoverable loss of step numbers for an unrecoverable
320
+ * loss of the whole continuation.
321
+ */
322
+ public static WasKilledAtRestart(snapshot: IntegrationRunSnapshot): boolean {
323
+ return snapshot.latestEvent?.stage === RESTART_STAGE;
324
+ }
325
+ /**
326
+ * Closes out the restart-killed runs the startup sweep left in flight that NO session then
327
+ * re-attached to — because there was no system user, or the pending work was stale and
328
+ * dropped, or it simply failed before reaching the run. Without this they would read as in
329
+ * flight forever, which is the state the sweep exists to end.
330
+ *
331
+ * Only the runs in `leftInFlight` (the sweep's `Skipped` list) are candidates: anything else
332
+ * in flight by now may be a run this process started, and is not ours to judge. A candidate
333
+ * a session attached to is live — its session terminates it — and is left alone.
334
+ */
335
+ public static async SweepUnattached(
336
+ leftInFlight: readonly string[],
337
+ rootDir?: string,
338
+ log: (message: string) => void = () => {},
339
+ ): Promise<{ Swept: string[]; Failed: string[]; Skipped: string[] }> {
340
+ if (leftInFlight.length === 0) return { Swept: [], Failed: [], Skipped: [] };
341
+ const candidates = new Set(leftInFlight);
342
+ return SweepInterruptedRuns(rootDir, log, snapshot => {
343
+ const runID = snapshot.manifest.runID;
344
+ return !candidates.has(runID) || RSUPostRestartProgressSession.attachedRunIDs.has(runID);
345
+ });
346
+ }
347
+
348
+ /** Reads the step position out of a restart checkpoint, tolerating a missing or partial one. */
349
+ public static PositionFrom(checkpoint: IntegrationProgressEvent | undefined): RestartPosition {
350
+ const state = checkpoint?.stage === RESTART_STAGE ? checkpoint.resumableState : undefined;
351
+ if (!state) return {};
352
+ const stepIndex = state.stepIndex;
353
+ const stepTotal = state.stepTotal;
354
+ return {
355
+ stepIndex: typeof stepIndex === 'number' ? stepIndex : undefined,
356
+ stepTotal: typeof stepTotal === 'number' ? stepTotal : undefined,
357
+ };
358
+ }
359
+ }
@@ -24,6 +24,13 @@ import { BaseSingleton } from '@memberjunction/global';
24
24
  /** The emitter stage name used for run-level (non-step) events. */
25
25
  const RUN_STAGE = 'RSUPipeline';
26
26
 
27
+ /**
28
+ * The pipeline step that kills this process. Named here because BOTH sides of the restart use it:
29
+ * this bridge closes the stage and checkpoints under it, and the post-restart consumer recognises a
30
+ * run as resumable by finding it as the run's last stage.
31
+ */
32
+ export const RESTART_STAGE = 'RestartMJAPI';
33
+
27
34
  /**
28
35
  * How often an in-flight step reports that it is still alive.
29
36
  *
@@ -53,6 +60,15 @@ export class RSUProgressBridge extends BaseSingleton<RSUProgressBridge> {
53
60
  private currentRunID: string | null = null;
54
61
  /** Liveness timer for the step currently in flight — see {@link HEARTBEAT_INTERVAL_MS}. */
55
62
  private heartbeat: ReturnType<typeof setInterval> | null = null;
63
+ /**
64
+ * The stage the live heartbeat belongs to, held on the INSTANCE rather than only captured in
65
+ * the timer closure. A closure-only stage name is a leak waiting to happen: a timer that
66
+ * outlives its step (or its whole run) keeps firing with the name it was created with, and
67
+ * writes that stale name into whatever emitter the bridge is pointing at by then. Comparing the
68
+ * tick's captured name against this field makes that structurally impossible — a stopped or
69
+ * superseded heartbeat writes nothing.
70
+ */
71
+ private heartbeatStage: string | null = null;
56
72
  private emitterOptions: EmitterOptions = {};
57
73
 
58
74
  /** The one bridge for this process. */
@@ -96,8 +112,14 @@ export class RSUProgressBridge extends BaseSingleton<RSUProgressBridge> {
96
112
  this.currentRunID = null;
97
113
  }
98
114
 
99
- /** The observer to hand to {@link RuntimeSchemaManager.PipelineObserver}. */
100
- public readonly Observe = (event: RSUObserverEvent): void => {
115
+ /**
116
+ * The observer to hand to {@link RuntimeSchemaManager.PipelineObserver}.
117
+ *
118
+ * Returns a promise ONLY for `restart.pending` — the pipeline awaits that one (bounded) because
119
+ * the process is about to be killed and an unflushed write would be lost. Every other event
120
+ * stays fire-and-forget.
121
+ */
122
+ public readonly Observe = (event: RSUObserverEvent): void | Promise<void> => {
101
123
  switch (event.Kind) {
102
124
  case 'run.start':
103
125
  return this.onRunStart(event);
@@ -105,24 +127,76 @@ export class RSUProgressBridge extends BaseSingleton<RSUProgressBridge> {
105
127
  return this.onStepStart(event);
106
128
  case 'step.end':
107
129
  return this.onStepEnd(event);
130
+ case 'restart.pending':
131
+ return this.onRestartPending(event);
108
132
  case 'run.end':
109
133
  return this.onRunEnd(event);
110
134
  }
111
135
  };
112
136
 
137
+ /**
138
+ * Closes the `RestartMJAPI` stage and writes the checkpoint the next process re-attaches from,
139
+ * then FLUSHES — the caller awaits this, and pm2 kills the process moments later.
140
+ *
141
+ * Two separate things are written here, and they matter for different reasons:
142
+ *
143
+ * - the `stage.complete` for `RestartMJAPI`, because `step.end` for that step is unreachable
144
+ * (the process dies inside the step), so without it the stream ends on an open stage and a
145
+ * determinate stepper stalls one short of the restart;
146
+ * - the `checkpoint`, carrying the step position and the pending-work correlation, so the
147
+ * consumer on the other side can resume the stepper where this process left it.
148
+ *
149
+ * The checkpoint is deliberately NOT the consumer's only way back to this run. Re-attachment
150
+ * keys off the MANIFEST — written minutes earlier, when the run opened — precisely because this
151
+ * write is the one racing a kill signal. If it is lost the run is still found and still
152
+ * resumed; only the step indices degrade to defaults.
153
+ */
154
+ private async onRestartPending(event: Extract<RSUObserverEvent, { Kind: 'restart.pending' }>): Promise<void> {
155
+ this.stopHeartbeat();
156
+ const emitter = this.emitter;
157
+ if (!emitter) return;
158
+ emitter.stageComplete(RESTART_STAGE);
159
+ emitter.checkpoint(RESTART_STAGE, {
160
+ runID: emitter.RunID,
161
+ remainingSteps: event.RemainingSteps,
162
+ stepIndex: event.StepIndex,
163
+ stepTotal: event.StepTotal,
164
+ companyIntegrationIDs: event.CompanyIntegrationIDs,
165
+ pendingWorkIDs: event.PendingWorkIDs,
166
+ migrationTotalCount: event.TotalCount,
167
+ migrationSuccessCount: event.SuccessCount,
168
+ });
169
+ // A failed flush must not stop the restart — the pipeline swallows this, but say so.
170
+ await emitter.flush().catch(err => LogError(`RSUProgressBridge: pre-restart flush failed — ${err}`));
171
+ }
172
+
113
173
  private onRunStart(event: Extract<RSUObserverEvent, { Kind: 'run.start' }>): void {
174
+ // Unconditional, and BEFORE the emitter check below. Stopping the previous run's liveness
175
+ // timer must not be reachable only via `closeCurrent`, whose call is gated on there being
176
+ // an emitter — that gate is the one path on which a live heartbeat could survive into the
177
+ // next run and stamp the previous run's stage name onto it.
178
+ this.stopHeartbeat();
114
179
  // A retry re-enters RunPipelineBatch, so it legitimately starts a NEW run. If a previous
115
180
  // emitter is still open (a throw escaped before run.end), close it rather than leak it.
116
181
  if (this.emitter) this.closeCurrent('Superseded by a new RSU run');
117
182
 
118
183
  const runID = IntegrationProgressEmitter.newRunID('rsu');
119
184
  this.currentRunID = runID;
185
+ const companyIntegrationIDs = event.CompanyIntegrationIDs ?? [];
120
186
  this.emitter = new IntegrationProgressEmitter(
121
187
  {
122
188
  runID,
123
189
  runKind: 'RSU',
124
190
  triggerType: 'Pipeline',
125
191
  startedAt: new Date().toISOString(),
192
+ // The run's IDENTITY, and the reason an RSU run is readable over the API at all: the
193
+ // run-artifact authorization check tests the caller against the connections on the
194
+ // manifest, and an RSU manifest never carried any. The plural field is the whole set
195
+ // (a batch may span connections); the singular one is populated ONLY for a
196
+ // single-connection run, so everything that reports "the" connection of a run keeps
197
+ // reporting one unambiguous answer rather than an arbitrary member of a set.
198
+ companyIntegrationIDs,
199
+ companyIntegrationID: companyIntegrationIDs.length === 1 ? companyIntegrationIDs[0] : undefined,
126
200
  context: {
127
201
  itemCount: event.ItemCount,
128
202
  descriptions: event.Descriptions,
@@ -195,7 +269,12 @@ export class RSUProgressBridge extends BaseSingleton<RSUProgressBridge> {
195
269
  private startHeartbeat(stage: string): void {
196
270
  this.stopHeartbeat();
197
271
  const startedAt = Date.now();
272
+ this.heartbeatStage = stage;
198
273
  this.heartbeat = setInterval(() => {
274
+ // Guard on the INSTANCE's current stage, not just the captured one. A tick that fires
275
+ // after the heartbeat was stopped, or after a different step/run took over, must not
276
+ // write this step's name into someone else's stream.
277
+ if (this.heartbeatStage !== stage) return;
199
278
  const elapsedSec = Math.round((Date.now() - startedAt) / 1000);
200
279
  this.emitter?.heartbeat(stage, `${stage} still running — ${elapsedSec}s elapsed`);
201
280
  }, HEARTBEAT_INTERVAL_MS);
@@ -205,6 +284,10 @@ export class RSUProgressBridge extends BaseSingleton<RSUProgressBridge> {
205
284
 
206
285
  /** Stops the liveness timer. Safe to call when none is running. */
207
286
  private stopHeartbeat(): void {
287
+ // Deliberately unconditional on the stage: clearing the name is what disarms a tick that is
288
+ // already queued on the event loop, so it must happen even when there is no timer handle
289
+ // left to clear.
290
+ this.heartbeatStage = null;
208
291
  if (!this.heartbeat) return;
209
292
  clearInterval(this.heartbeat);
210
293
  this.heartbeat = null;
@@ -0,0 +1,65 @@
1
+ /**
2
+ * @fileoverview Who may read an integration run artifact.
3
+ *
4
+ * A security boundary, kept in its own module so the rule is stated once, testable on its own, and
5
+ * not buried among a resolver's several thousand lines.
6
+ */
7
+ import { IntegrationProgressReader } from '@memberjunction/integration-progress-artifacts';
8
+ import type { IntegrationRunManifest } from '@memberjunction/integration-progress-artifacts';
9
+
10
+ /**
11
+ * Answers "may the calling user read this one connection?".
12
+ *
13
+ * Supplied by the resolver, which implements it as a `RunView` on `MJ: Company Integrations` under
14
+ * the CALLER's context — so the data provider applies the same row-level security it applies to
15
+ * every other user-context read, and no row coming back means unauthorized.
16
+ */
17
+ export type CompanyIntegrationReadCheck = (companyIntegrationID: string) => Promise<boolean>;
18
+
19
+ /** The verdict, carrying the connection that blocked it so a denial can name it. */
20
+ export interface RunArtifactAuthorizationResult {
21
+ Authorized: boolean;
22
+ /** The first connection the caller could not read. Absent when the run has no identity at all. */
23
+ DeniedCompanyIntegrationID?: string;
24
+ }
25
+
26
+ /**
27
+ * Authorizes a caller for a run artifact against EVERY connection the run touched.
28
+ *
29
+ * ── The rule ───────────────────────────────────────────────────────────────────────────────────
30
+ * A run is readable only if the caller is authorized for ALL of its connections. Not any — all.
31
+ *
32
+ * Most runs touch exactly one connection and the rule collapses to the obvious check. But a schema
33
+ * update (RSU) run is a BATCH: the pipeline takes N inputs, and those inputs may legitimately
34
+ * belong to different connectors. A run artifact is one undivided event stream — its migration
35
+ * descriptions, affected table names and error messages interleave every connection in the batch,
36
+ * and there is no mechanism to serve a caller a partial view of it. "Authorized for any one of
37
+ * them" would therefore hand connection A's schema-change descriptions to a user who only holds
38
+ * rights on connection B. AND is the only rule that cannot leak; a cross-tenant batch simply
39
+ * becomes readable to nobody but a caller holding rights to both, which is the correct conservative
40
+ * answer rather than a silent disclosure.
41
+ *
42
+ * A run with NO connection identity is denied: non-tenant-scoped artifacts are not exposed through
43
+ * the per-company endpoints, and that must not change as a side effect of runs gaining a set.
44
+ *
45
+ * Short-circuits on the first denial, so an unauthorized caller cannot use the number of permission
46
+ * lookups to probe which connections a run covers.
47
+ */
48
+ export async function AuthorizeRunArtifact(
49
+ manifest: IntegrationRunManifest,
50
+ canReadCompanyIntegration: CompanyIntegrationReadCheck
51
+ ): Promise<RunArtifactAuthorizationResult> {
52
+ // Reads the SET, not the singular field: a batch spanning connections leaves the singular field
53
+ // deliberately empty, and testing only that would make such a run look connection-less — and so
54
+ // unreadable to everyone, including its rightful owner.
55
+ const companyIntegrationIDs = IntegrationProgressReader.CompanyIntegrationIDsFor(manifest);
56
+ if (companyIntegrationIDs.length === 0) {
57
+ return { Authorized: false };
58
+ }
59
+ for (const companyIntegrationID of companyIntegrationIDs) {
60
+ if (!await canReadCompanyIntegration(companyIntegrationID)) {
61
+ return { Authorized: false, DeniedCompanyIntegrationID: companyIntegrationID };
62
+ }
63
+ }
64
+ return { Authorized: true };
65
+ }
@@ -30,6 +30,15 @@ export interface SchemaRefreshSummaryLike {
30
30
  FailureMessage?: string;
31
31
  ObjectsCreated: number;
32
32
  ObjectsUpdated: number;
33
+ /**
34
+ * FIELD counts, not an optional nicety: a connector whose objects are PRESUPPOSED (declared in
35
+ * its own metadata) creates no objects on any run, so the object counts of a refresh that
36
+ * created 69 fields and updated 487 are "0 and 0" — which reads as "the refresh found nothing".
37
+ * Both counts were already carried on the pipeline result; only this type stopped them reaching
38
+ * the sentence.
39
+ */
40
+ FieldsCreated: number;
41
+ FieldsUpdated: number;
33
42
  UnresolvedObjects: string[];
34
43
  }
35
44
 
@@ -38,8 +47,9 @@ function describeFinishedRefresh(summary: SchemaRefreshSummaryLike): string {
38
47
  if (!summary.Succeeded) {
39
48
  return `schema refresh FAILED (${summary.FailureMessage ?? 'no reason reported'}) — see run ${summary.RunID}`;
40
49
  }
41
- return `schema refresh: ${summary.ObjectsCreated} created, ` +
42
- `${summary.ObjectsUpdated} updated, ${summary.UnresolvedObjects.length} PK-unresolved`;
50
+ return `schema refresh: ${summary.ObjectsCreated} objects created, ` +
51
+ `${summary.ObjectsUpdated} updated, ${summary.FieldsCreated} fields created, ` +
52
+ `${summary.FieldsUpdated} updated, ${summary.UnresolvedObjects.length} PK-unresolved`;
43
53
  }
44
54
 
45
55
  /**