@memberjunction/server 6.1.4 → 6.2.0-edge.1

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 (435) hide show
  1. package/dist/.tsbuildinfo +1 -1
  2. package/dist/agentSessions/ReturningVisitorRecap.d.ts +2 -0
  3. package/dist/agentSessions/ReturningVisitorRecap.d.ts.map +1 -1
  4. package/dist/agentSessions/ReturningVisitorRecap.js +5 -1
  5. package/dist/agentSessions/ReturningVisitorRecap.js.map +1 -1
  6. package/dist/agentSessions/SessionManager.js +2 -2
  7. package/dist/agentSessions/remoteBrowserGoalEngine.d.ts +8 -0
  8. package/dist/agentSessions/remoteBrowserGoalEngine.d.ts.map +1 -1
  9. package/dist/agentSessions/remoteBrowserGoalEngine.js +21 -5
  10. package/dist/agentSessions/remoteBrowserGoalEngine.js.map +1 -1
  11. package/dist/auth/AuthProviderCatalogRouter.d.ts +2 -0
  12. package/dist/auth/AuthProviderCatalogRouter.d.ts.map +1 -1
  13. package/dist/auth/AuthProviderCatalogRouter.js +5 -1
  14. package/dist/auth/AuthProviderCatalogRouter.js.map +1 -1
  15. package/dist/auth/exampleNewUserSubClass.js +2 -1
  16. package/dist/auth/exampleNewUserSubClass.js.map +1 -1
  17. package/dist/auth/index.d.ts +20 -1
  18. package/dist/auth/index.d.ts.map +1 -1
  19. package/dist/auth/index.js +20 -10
  20. package/dist/auth/index.js.map +1 -1
  21. package/dist/auth/initializeProviders.d.ts +8 -0
  22. package/dist/auth/initializeProviders.d.ts.map +1 -1
  23. package/dist/auth/initializeProviders.js +21 -5
  24. package/dist/auth/initializeProviders.js.map +1 -1
  25. package/dist/auth/magicLink/MagicLinkRouter.d.ts +9 -0
  26. package/dist/auth/magicLink/MagicLinkRouter.d.ts.map +1 -1
  27. package/dist/auth/magicLink/MagicLinkRouter.js +22 -10
  28. package/dist/auth/magicLink/MagicLinkRouter.js.map +1 -1
  29. package/dist/auth/magicLink/MagicLinkService.d.ts +17 -15
  30. package/dist/auth/magicLink/MagicLinkService.d.ts.map +1 -1
  31. package/dist/auth/magicLink/MagicLinkService.js +65 -51
  32. package/dist/auth/magicLink/MagicLinkService.js.map +1 -1
  33. package/dist/auth/magicLink/index.d.ts +2 -2
  34. package/dist/auth/magicLink/index.d.ts.map +1 -1
  35. package/dist/auth/magicLink/index.js +2 -2
  36. package/dist/auth/magicLink/index.js.map +1 -1
  37. package/dist/auth/magicLink/magicLinkCore.d.ts +54 -33
  38. package/dist/auth/magicLink/magicLinkCore.d.ts.map +1 -1
  39. package/dist/auth/magicLink/magicLinkCore.js +61 -61
  40. package/dist/auth/magicLink/magicLinkCore.js.map +1 -1
  41. package/dist/auth/magicLink/redeemLanding.d.ts +4 -0
  42. package/dist/auth/magicLink/redeemLanding.d.ts.map +1 -1
  43. package/dist/auth/magicLink/redeemLanding.js +12 -4
  44. package/dist/auth/magicLink/redeemLanding.js.map +1 -1
  45. package/dist/auth/magicLink/types.d.ts +4 -4
  46. package/dist/auth/principals.d.ts +2 -0
  47. package/dist/auth/principals.d.ts.map +1 -1
  48. package/dist/auth/principals.js +6 -2
  49. package/dist/auth/principals.js.map +1 -1
  50. package/dist/cache.d.ts +2 -0
  51. package/dist/cache.d.ts.map +1 -1
  52. package/dist/cache.js +3 -1
  53. package/dist/cache.js.map +1 -1
  54. package/dist/config.d.ts +293 -2
  55. package/dist/config.d.ts.map +1 -1
  56. package/dist/config.js +11 -4
  57. package/dist/config.js.map +1 -1
  58. package/dist/context.d.ts +20 -0
  59. package/dist/context.d.ts.map +1 -1
  60. package/dist/context.js +27 -19
  61. package/dist/context.js.map +1 -1
  62. package/dist/directives/Public.d.ts +2 -0
  63. package/dist/directives/Public.d.ts.map +1 -1
  64. package/dist/directives/Public.js +3 -1
  65. package/dist/directives/Public.js.map +1 -1
  66. package/dist/directives/RequireSystemUser.d.ts +2 -0
  67. package/dist/directives/RequireSystemUser.d.ts.map +1 -1
  68. package/dist/directives/RequireSystemUser.js +3 -1
  69. package/dist/directives/RequireSystemUser.js.map +1 -1
  70. package/dist/generated/graphql-schemas/__mj.d.ts +266 -4
  71. package/dist/generated/graphql-schemas/__mj.d.ts.map +1 -1
  72. package/dist/generated/graphql-schemas/__mj.js +1304 -0
  73. package/dist/generated/graphql-schemas/__mj.js.map +1 -1
  74. package/dist/generic/CacheInvalidationResolver.d.ts +8 -2
  75. package/dist/generic/CacheInvalidationResolver.d.ts.map +1 -1
  76. package/dist/generic/CacheInvalidationResolver.js +7 -3
  77. package/dist/generic/CacheInvalidationResolver.js.map +1 -1
  78. package/dist/generic/FireAndForgetHeartbeat.d.ts +3 -1
  79. package/dist/generic/FireAndForgetHeartbeat.d.ts.map +1 -1
  80. package/dist/generic/FireAndForgetHeartbeat.js +8 -4
  81. package/dist/generic/FireAndForgetHeartbeat.js.map +1 -1
  82. package/dist/generic/PushStatusResolver.d.ts +10 -1
  83. package/dist/generic/PushStatusResolver.d.ts.map +1 -1
  84. package/dist/generic/PushStatusResolver.js +11 -3
  85. package/dist/generic/PushStatusResolver.js.map +1 -1
  86. package/dist/generic/ResolverBase.d.ts +27 -1
  87. package/dist/generic/ResolverBase.d.ts.map +1 -1
  88. package/dist/generic/ResolverBase.js +64 -6
  89. package/dist/generic/ResolverBase.js.map +1 -1
  90. package/dist/index.d.ts +6 -0
  91. package/dist/index.d.ts.map +1 -1
  92. package/dist/index.js +29 -6
  93. package/dist/index.js.map +1 -1
  94. package/dist/integration/CustomColumnPromoter.d.ts +2 -0
  95. package/dist/integration/CustomColumnPromoter.d.ts.map +1 -1
  96. package/dist/integration/CustomColumnPromoter.js +5 -1
  97. package/dist/integration/CustomColumnPromoter.js.map +1 -1
  98. package/dist/integration/EntityMapLifecycle.d.ts +4 -0
  99. package/dist/integration/EntityMapLifecycle.d.ts.map +1 -1
  100. package/dist/integration/EntityMapLifecycle.js +10 -2
  101. package/dist/integration/EntityMapLifecycle.js.map +1 -1
  102. package/dist/logging/NoLog.d.ts +4 -0
  103. package/dist/logging/NoLog.d.ts.map +1 -1
  104. package/dist/logging/NoLog.js +10 -2
  105. package/dist/logging/NoLog.js.map +1 -1
  106. package/dist/logging/StartupLogger.d.ts +2 -0
  107. package/dist/logging/StartupLogger.d.ts.map +1 -1
  108. package/dist/logging/StartupLogger.js +5 -1
  109. package/dist/logging/StartupLogger.js.map +1 -1
  110. package/dist/logging/bootAudit.d.ts +11 -7
  111. package/dist/logging/bootAudit.d.ts.map +1 -1
  112. package/dist/logging/bootAudit.js +15 -7
  113. package/dist/logging/bootAudit.js.map +1 -1
  114. package/dist/logging/boundaryLogPayload.d.ts +4 -0
  115. package/dist/logging/boundaryLogPayload.d.ts.map +1 -1
  116. package/dist/logging/boundaryLogPayload.js +5 -1
  117. package/dist/logging/boundaryLogPayload.js.map +1 -1
  118. package/dist/logging/secretRedactor.d.ts +2 -0
  119. package/dist/logging/secretRedactor.d.ts.map +1 -1
  120. package/dist/logging/secretRedactor.js +8 -4
  121. package/dist/logging/secretRedactor.js.map +1 -1
  122. package/dist/logging/shortenForLog.d.ts +2 -0
  123. package/dist/logging/shortenForLog.d.ts.map +1 -1
  124. package/dist/logging/shortenForLog.js +7 -3
  125. package/dist/logging/shortenForLog.js.map +1 -1
  126. package/dist/logging/variablesLoggingMiddleware.d.ts +2 -0
  127. package/dist/logging/variablesLoggingMiddleware.d.ts.map +1 -1
  128. package/dist/logging/variablesLoggingMiddleware.js +8 -6
  129. package/dist/logging/variablesLoggingMiddleware.js.map +1 -1
  130. package/dist/metadataRefreshSignal.d.ts +19 -0
  131. package/dist/metadataRefreshSignal.d.ts.map +1 -0
  132. package/dist/metadataRefreshSignal.js +45 -0
  133. package/dist/metadataRefreshSignal.js.map +1 -0
  134. package/dist/middleware/MJTenantFilterMiddleware.js +4 -4
  135. package/dist/multiTenancy/index.d.ts +8 -0
  136. package/dist/multiTenancy/index.d.ts.map +1 -1
  137. package/dist/multiTenancy/index.js +21 -5
  138. package/dist/multiTenancy/index.js.map +1 -1
  139. package/dist/realtimeWidget/WidgetRouter.d.ts +5 -0
  140. package/dist/realtimeWidget/WidgetRouter.d.ts.map +1 -1
  141. package/dist/realtimeWidget/WidgetRouter.js +14 -10
  142. package/dist/realtimeWidget/WidgetRouter.js.map +1 -1
  143. package/dist/realtimeWidget/WidgetSessionService.d.ts +6 -6
  144. package/dist/realtimeWidget/WidgetSessionService.js +27 -27
  145. package/dist/realtimeWidget/host-identity.d.ts +5 -1
  146. package/dist/realtimeWidget/host-identity.d.ts.map +1 -1
  147. package/dist/realtimeWidget/host-identity.js +11 -3
  148. package/dist/realtimeWidget/host-identity.js.map +1 -1
  149. package/dist/realtimeWidget/index.d.ts +2 -2
  150. package/dist/realtimeWidget/index.d.ts.map +1 -1
  151. package/dist/realtimeWidget/index.js +2 -2
  152. package/dist/realtimeWidget/index.js.map +1 -1
  153. package/dist/realtimeWidget/visitorIdentity.d.ts +21 -1
  154. package/dist/realtimeWidget/visitorIdentity.d.ts.map +1 -1
  155. package/dist/realtimeWidget/visitorIdentity.js +16 -4
  156. package/dist/realtimeWidget/visitorIdentity.js.map +1 -1
  157. package/dist/realtimeWidget/widgetCore.d.ts +48 -0
  158. package/dist/realtimeWidget/widgetCore.d.ts.map +1 -1
  159. package/dist/realtimeWidget/widgetCore.js +38 -10
  160. package/dist/realtimeWidget/widgetCore.js.map +1 -1
  161. package/dist/realtimeWidget/widgetGuestElevation.d.ts +10 -6
  162. package/dist/realtimeWidget/widgetGuestElevation.d.ts.map +1 -1
  163. package/dist/realtimeWidget/widgetGuestElevation.js +14 -6
  164. package/dist/realtimeWidget/widgetGuestElevation.js.map +1 -1
  165. package/dist/resolvers/AdhocQueryResolver.js +3 -3
  166. package/dist/resolvers/FetchEntityVectorsResolver.js +2 -2
  167. package/dist/resolvers/FetchEntityVectorsResolver.js.map +1 -1
  168. package/dist/resolvers/FileResolver.js +3 -3
  169. package/dist/resolvers/GeoResolver.d.ts +2 -2
  170. package/dist/resolvers/GeoResolver.js +4 -4
  171. package/dist/resolvers/GetDataResolver.d.ts +14 -0
  172. package/dist/resolvers/GetDataResolver.d.ts.map +1 -1
  173. package/dist/resolvers/GetDataResolver.js +43 -15
  174. package/dist/resolvers/GetDataResolver.js.map +1 -1
  175. package/dist/resolvers/IntegrationDiscoveryResolver.js +2 -2
  176. package/dist/resolvers/ListOperationsResolver.d.ts +20 -0
  177. package/dist/resolvers/ListOperationsResolver.d.ts.map +1 -1
  178. package/dist/resolvers/ListOperationsResolver.js +68 -28
  179. package/dist/resolvers/ListOperationsResolver.js.map +1 -1
  180. package/dist/resolvers/MCPResolver.d.ts +11 -1
  181. package/dist/resolvers/MCPResolver.d.ts.map +1 -1
  182. package/dist/resolvers/MCPResolver.js +7 -3
  183. package/dist/resolvers/MCPResolver.js.map +1 -1
  184. package/dist/resolvers/QuerySystemUserResolver.d.ts.map +1 -1
  185. package/dist/resolvers/QuerySystemUserResolver.js +2 -1
  186. package/dist/resolvers/QuerySystemUserResolver.js.map +1 -1
  187. package/dist/resolvers/RealtimeBridgeResolver.js +3 -3
  188. package/dist/resolvers/RealtimeClientSessionResolver.d.ts +37 -0
  189. package/dist/resolvers/RealtimeClientSessionResolver.d.ts.map +1 -1
  190. package/dist/resolvers/RealtimeClientSessionResolver.js +101 -11
  191. package/dist/resolvers/RealtimeClientSessionResolver.js.map +1 -1
  192. package/dist/resolvers/RemoteBrowserActionResolver.js +5 -5
  193. package/dist/resolvers/RunAIAgentResolver.d.ts +10 -4
  194. package/dist/resolvers/RunAIAgentResolver.d.ts.map +1 -1
  195. package/dist/resolvers/RunAIAgentResolver.js +74 -42
  196. package/dist/resolvers/RunAIAgentResolver.js.map +1 -1
  197. package/dist/resolvers/RunAIPromptResolver.d.ts +1 -1
  198. package/dist/resolvers/RunAIPromptResolver.js +2 -2
  199. package/dist/resolvers/RunTestResolver.js +5 -5
  200. package/dist/resolvers/SearchEntitiesResolver.d.ts.map +1 -1
  201. package/dist/resolvers/SearchEntitiesResolver.js +3 -1
  202. package/dist/resolvers/SearchEntitiesResolver.js.map +1 -1
  203. package/dist/resolvers/SearchKnowledgeResolver.d.ts +1 -0
  204. package/dist/resolvers/SearchKnowledgeResolver.d.ts.map +1 -1
  205. package/dist/resolvers/SearchKnowledgeResolver.js +5 -0
  206. package/dist/resolvers/SearchKnowledgeResolver.js.map +1 -1
  207. package/dist/resolvers/SearchKnowledgeSystemUserResolver.d.ts.map +1 -1
  208. package/dist/resolvers/SearchKnowledgeSystemUserResolver.js +1 -0
  209. package/dist/resolvers/SearchKnowledgeSystemUserResolver.js.map +1 -1
  210. package/dist/resolvers/SqlLoggingConfigResolver.d.ts +8 -4
  211. package/dist/resolvers/SqlLoggingConfigResolver.d.ts.map +1 -1
  212. package/dist/resolvers/SqlLoggingConfigResolver.js +5 -5
  213. package/dist/resolvers/TaskGraphFrameResolver.d.ts +8 -1
  214. package/dist/resolvers/TaskGraphFrameResolver.d.ts.map +1 -1
  215. package/dist/resolvers/TaskGraphFrameResolver.js +6 -2
  216. package/dist/resolvers/TaskGraphFrameResolver.js.map +1 -1
  217. package/dist/resolvers/TransactionGroupResolver.d.ts.map +1 -1
  218. package/dist/resolvers/TransactionGroupResolver.js +53 -4
  219. package/dist/resolvers/TransactionGroupResolver.js.map +1 -1
  220. package/dist/resolvers/adhoc-query-helpers.d.ts +6 -0
  221. package/dist/resolvers/adhoc-query-helpers.d.ts.map +1 -1
  222. package/dist/resolvers/adhoc-query-helpers.js +10 -2
  223. package/dist/resolvers/adhoc-query-helpers.js.map +1 -1
  224. package/dist/resolvers/artifact-routing.d.ts +8 -8
  225. package/dist/resolvers/artifact-routing.js +2 -2
  226. package/dist/resolvers/artifact-routing.js.map +1 -1
  227. package/dist/resolvers/meetingRecordingRegistration.d.ts +10 -2
  228. package/dist/resolvers/meetingRecordingRegistration.d.ts.map +1 -1
  229. package/dist/resolvers/meetingRecordingRegistration.js +24 -8
  230. package/dist/resolvers/meetingRecordingRegistration.js.map +1 -1
  231. package/dist/resolvers/peaksSidecar.d.ts +4 -0
  232. package/dist/resolvers/peaksSidecar.d.ts.map +1 -1
  233. package/dist/resolvers/peaksSidecar.js +10 -2
  234. package/dist/resolvers/peaksSidecar.js.map +1 -1
  235. package/dist/rest/EntityCRUDHandler.d.ts +28 -0
  236. package/dist/rest/EntityCRUDHandler.d.ts.map +1 -1
  237. package/dist/rest/EntityCRUDHandler.js +20 -4
  238. package/dist/rest/EntityCRUDHandler.js.map +1 -1
  239. package/dist/rest/MediaStreamHandler.d.ts +2 -0
  240. package/dist/rest/MediaStreamHandler.d.ts.map +1 -1
  241. package/dist/rest/MediaStreamHandler.js +10 -6
  242. package/dist/rest/MediaStreamHandler.js.map +1 -1
  243. package/dist/rest/OAuthCallbackHandler.d.ts +9 -0
  244. package/dist/rest/OAuthCallbackHandler.d.ts.map +1 -1
  245. package/dist/rest/OAuthCallbackHandler.js +15 -3
  246. package/dist/rest/OAuthCallbackHandler.js.map +1 -1
  247. package/dist/rest/RESTEndpointHandler.d.ts +6 -4
  248. package/dist/rest/RESTEndpointHandler.d.ts.map +1 -1
  249. package/dist/rest/RESTEndpointHandler.js +15 -11
  250. package/dist/rest/RESTEndpointHandler.js.map +1 -1
  251. package/dist/rest/RealtimeSdpBrokerHandler.d.ts +5 -1
  252. package/dist/rest/RealtimeSdpBrokerHandler.d.ts.map +1 -1
  253. package/dist/rest/RealtimeSdpBrokerHandler.js +12 -4
  254. package/dist/rest/RealtimeSdpBrokerHandler.js.map +1 -1
  255. package/dist/rest/SignatureWebhookHandler.d.ts +2 -0
  256. package/dist/rest/SignatureWebhookHandler.d.ts.map +1 -1
  257. package/dist/rest/SignatureWebhookHandler.js +5 -1
  258. package/dist/rest/SignatureWebhookHandler.js.map +1 -1
  259. package/dist/rest/UploadTokenManager.d.ts +24 -8
  260. package/dist/rest/UploadTokenManager.d.ts.map +1 -1
  261. package/dist/rest/UploadTokenManager.js +53 -21
  262. package/dist/rest/UploadTokenManager.js.map +1 -1
  263. package/dist/rest/ViewOperationsHandler.d.ts +20 -0
  264. package/dist/rest/ViewOperationsHandler.d.ts.map +1 -1
  265. package/dist/rest/ViewOperationsHandler.js +20 -4
  266. package/dist/rest/ViewOperationsHandler.js.map +1 -1
  267. package/dist/rest/mediaRange.d.ts +4 -0
  268. package/dist/rest/mediaRange.d.ts.map +1 -1
  269. package/dist/rest/mediaRange.js +10 -2
  270. package/dist/rest/mediaRange.js.map +1 -1
  271. package/dist/rest/setupRESTEndpoints.d.ts +4 -0
  272. package/dist/rest/setupRESTEndpoints.d.ts.map +1 -1
  273. package/dist/rest/setupRESTEndpoints.js +13 -7
  274. package/dist/rest/setupRESTEndpoints.js.map +1 -1
  275. package/dist/serverExtensionReservedRoots.d.ts +2 -0
  276. package/dist/serverExtensionReservedRoots.d.ts.map +1 -1
  277. package/dist/serverExtensionReservedRoots.js +5 -1
  278. package/dist/serverExtensionReservedRoots.js.map +1 -1
  279. package/dist/services/MJStorageBlobStore.d.ts +41 -0
  280. package/dist/services/MJStorageBlobStore.d.ts.map +1 -0
  281. package/dist/services/MJStorageBlobStore.js +160 -0
  282. package/dist/services/MJStorageBlobStore.js.map +1 -0
  283. package/dist/services/TaskGraphActionRunner.d.ts +13 -8
  284. package/dist/services/TaskGraphActionRunner.d.ts.map +1 -1
  285. package/dist/services/TaskGraphActionRunner.js +16 -15
  286. package/dist/services/TaskGraphActionRunner.js.map +1 -1
  287. package/dist/types.d.ts +2 -0
  288. package/dist/types.d.ts.map +1 -1
  289. package/dist/types.js +7 -3
  290. package/dist/types.js.map +1 -1
  291. package/dist/util.d.ts +12 -3
  292. package/dist/util.d.ts.map +1 -1
  293. package/dist/util.js +19 -8
  294. package/dist/util.js.map +1 -1
  295. package/package.json +92 -92
  296. package/src/__tests__/FileResolverPeaks.test.ts +13 -13
  297. package/src/__tests__/ListOperationsResolver.test.ts +17 -17
  298. package/src/__tests__/MediaStreamHandler.test.ts +28 -28
  299. package/src/__tests__/NoLog.test.ts +11 -11
  300. package/src/__tests__/PrimaryKeyAlwaysMapped.test.ts +10 -10
  301. package/src/__tests__/RESTEndpointHandler.test.ts +14 -14
  302. package/src/__tests__/RealtimeBridgeResolver.test.ts +4 -2
  303. package/src/__tests__/RealtimeClientSessionResolver.test.ts +229 -0
  304. package/src/__tests__/RealtimeSdpBrokerHandler.test.ts +6 -6
  305. package/src/__tests__/RefreshAdoptsNewColumns.test.ts +8 -8
  306. package/src/__tests__/RemoteBrowserGoalEngine.test.ts +18 -18
  307. package/src/__tests__/ResolverBase.isaPromotionCreate.test.ts +412 -0
  308. package/src/__tests__/RunAIAgentResolver.historyFrom.test.ts +228 -0
  309. package/src/__tests__/SearchEntitiesResolver.readOnlyFallback.test.ts +78 -0
  310. package/src/__tests__/TaskGraphActionRunner.durablePayload.test.ts +129 -0
  311. package/src/__tests__/TaskGraphFrameResolver.test.ts +2 -2
  312. package/src/__tests__/TransactionGroupResolver.refusals.test.ts +290 -0
  313. package/src/__tests__/WidgetSessionService.test.ts +8 -4
  314. package/src/__tests__/adhoc-query-helpers.test.ts +21 -21
  315. package/src/__tests__/bootAudit.test.ts +11 -11
  316. package/src/__tests__/boundaryLogPayload.test.ts +6 -6
  317. package/src/__tests__/databaseAbstraction.test.ts +12 -6
  318. package/src/__tests__/getDataTokenRedaction.test.ts +7 -7
  319. package/src/__tests__/initializeProviders.refresh.test.ts +4 -4
  320. package/src/__tests__/magicLink.test.ts +73 -134
  321. package/src/__tests__/magicLinkService.consume.test.ts +224 -0
  322. package/src/__tests__/meetingRecordingRegistration.test.ts +12 -12
  323. package/src/__tests__/metadataRefreshSignal.test.ts +78 -0
  324. package/src/__tests__/multiTenancy.security.test.ts +40 -40
  325. package/src/__tests__/multiTenancy.test.ts +18 -18
  326. package/src/__tests__/newUsers.test.ts +24 -23
  327. package/src/__tests__/principals.test.ts +22 -22
  328. package/src/__tests__/pushStatusResolver.filter.test.ts +8 -8
  329. package/src/__tests__/secretRedactor.failOpen.test.ts +12 -12
  330. package/src/__tests__/secretRedactor.test.ts +12 -12
  331. package/src/__tests__/serverExtensionReservedRoots.test.ts +7 -7
  332. package/src/__tests__/subscriptionRedaction.test.ts +5 -5
  333. package/src/__tests__/unifiedAuth.test.ts +15 -9
  334. package/src/__tests__/visitorIdentity.resolve.test.ts +6 -6
  335. package/src/__tests__/widget.test.ts +45 -45
  336. package/src/__tests__/widgetGuestElevation.test.ts +7 -7
  337. package/src/__tests__/widgetHostIdentity.test.ts +14 -14
  338. package/src/agentSessions/ReturningVisitorRecap.ts +12 -1
  339. package/src/agentSessions/SessionManager.ts +2 -2
  340. package/src/agentSessions/remoteBrowserGoalEngine.ts +30 -5
  341. package/src/auth/AuthProviderCatalogRouter.ts +6 -1
  342. package/src/auth/__tests__/backward-compatibility.test.ts +3 -3
  343. package/src/auth/exampleNewUserSubClass.ts +2 -2
  344. package/src/auth/index.ts +43 -28
  345. package/src/auth/initializeProviders.ts +25 -5
  346. package/src/auth/magicLink/MagicLinkRouter.ts +28 -10
  347. package/src/auth/magicLink/MagicLinkService.ts +71 -52
  348. package/src/auth/magicLink/index.ts +7 -7
  349. package/src/auth/magicLink/magicLinkCore.ts +96 -64
  350. package/src/auth/magicLink/redeemLanding.ts +14 -4
  351. package/src/auth/magicLink/types.ts +54 -54
  352. package/src/auth/newUsers.ts +1 -1
  353. package/src/auth/principals.ts +11 -2
  354. package/src/cache.ts +4 -1
  355. package/src/config.ts +14 -4
  356. package/src/context.ts +37 -24
  357. package/src/directives/Public.ts +4 -1
  358. package/src/directives/RequireSystemUser.ts +4 -1
  359. package/src/generated/graphql-schemas/__mj.ts +933 -5
  360. package/src/generic/CacheInvalidationResolver.ts +12 -4
  361. package/src/generic/FireAndForgetHeartbeat.ts +19 -14
  362. package/src/generic/PushStatusResolver.ts +21 -7
  363. package/src/generic/ResolverBase.ts +66 -5
  364. package/src/generic/RunViewResolver.ts +22 -22
  365. package/src/generic/__tests__/FireAndForgetHeartbeat.test.ts +7 -7
  366. package/src/index.ts +34 -7
  367. package/src/integration/CustomColumnPromoter.ts +6 -1
  368. package/src/integration/EntityMapLifecycle.ts +20 -2
  369. package/src/logging/NoLog.ts +16 -2
  370. package/src/logging/StartupLogger.ts +6 -1
  371. package/src/logging/bootAudit.ts +26 -16
  372. package/src/logging/boundaryLogPayload.ts +8 -1
  373. package/src/logging/secretRedactor.ts +14 -9
  374. package/src/logging/shortenForLog.ts +8 -3
  375. package/src/logging/variablesLoggingMiddleware.ts +9 -6
  376. package/src/metadataRefreshSignal.ts +45 -0
  377. package/src/middleware/MJTenantFilterMiddleware.ts +4 -4
  378. package/src/multiTenancy/index.ts +29 -5
  379. package/src/realtimeWidget/WidgetRouter.ts +15 -10
  380. package/src/realtimeWidget/WidgetSessionService.ts +76 -76
  381. package/src/realtimeWidget/host-identity.ts +24 -10
  382. package/src/realtimeWidget/index.ts +3 -3
  383. package/src/realtimeWidget/visitorIdentity.ts +39 -8
  384. package/src/realtimeWidget/widgetCore.ts +75 -10
  385. package/src/realtimeWidget/widgetGuestElevation.ts +25 -12
  386. package/src/resolvers/AdhocQueryResolver.ts +3 -3
  387. package/src/resolvers/AgentSessionResolver.ts +3 -3
  388. package/src/resolvers/ColorResolver.ts +6 -6
  389. package/src/resolvers/ComponentRegistryResolver.ts +36 -36
  390. package/src/resolvers/EntityCommunicationsResolver.ts +4 -4
  391. package/src/resolvers/ExecuteRemoteOperationResolver.ts +9 -9
  392. package/src/resolvers/FetchEntityVectorsResolver.ts +2 -2
  393. package/src/resolvers/FileResolver.ts +42 -42
  394. package/src/resolvers/GeoResolver.ts +4 -4
  395. package/src/resolvers/GetDataResolver.ts +50 -15
  396. package/src/resolvers/IntegrationDiscoveryResolver.ts +2 -2
  397. package/src/resolvers/ListOperationsResolver.ts +78 -28
  398. package/src/resolvers/MCPResolver.ts +30 -15
  399. package/src/resolvers/QueryResolver.ts +13 -13
  400. package/src/resolvers/QuerySystemUserResolver.ts +2 -1
  401. package/src/resolvers/RealtimeBridgeResolver.ts +3 -3
  402. package/src/resolvers/RealtimeClientSessionResolver.ts +126 -13
  403. package/src/resolvers/RemoteBrowserActionResolver.ts +5 -5
  404. package/src/resolvers/RunAIAgentResolver.ts +118 -76
  405. package/src/resolvers/RunAIPromptResolver.ts +22 -22
  406. package/src/resolvers/RunTemplateResolver.ts +4 -4
  407. package/src/resolvers/RunTestResolver.ts +25 -25
  408. package/src/resolvers/SearchEntitiesResolver.ts +3 -1
  409. package/src/resolvers/SearchKnowledgeResolver.ts +4 -0
  410. package/src/resolvers/SearchKnowledgeSystemUserResolver.ts +1 -0
  411. package/src/resolvers/SqlLoggingConfigResolver.ts +56 -56
  412. package/src/resolvers/TaskGraphFrameResolver.ts +37 -28
  413. package/src/resolvers/TelemetryResolver.ts +79 -79
  414. package/src/resolvers/TransactionGroupResolver.ts +57 -4
  415. package/src/resolvers/__tests__/GetDataResolver.tokens.test.ts +40 -38
  416. package/src/resolvers/__tests__/artifact-routing.test.ts +23 -23
  417. package/src/resolvers/adhoc-query-helpers.ts +19 -2
  418. package/src/resolvers/artifact-routing.ts +19 -19
  419. package/src/resolvers/meetingRecordingRegistration.ts +44 -10
  420. package/src/resolvers/peaksSidecar.ts +12 -2
  421. package/src/rest/EntityCRUDHandler.ts +24 -4
  422. package/src/rest/MediaStreamHandler.ts +11 -6
  423. package/src/rest/OAuthCallbackHandler.ts +25 -7
  424. package/src/rest/RESTEndpointHandler.ts +20 -15
  425. package/src/rest/RealtimeSdpBrokerHandler.ts +14 -4
  426. package/src/rest/SignatureWebhookHandler.ts +6 -1
  427. package/src/rest/UploadTokenManager.ts +66 -30
  428. package/src/rest/ViewOperationsHandler.ts +24 -4
  429. package/src/rest/mediaRange.ts +16 -6
  430. package/src/rest/setupRESTEndpoints.ts +24 -12
  431. package/src/serverExtensionReservedRoots.ts +6 -1
  432. package/src/services/MJStorageBlobStore.ts +209 -0
  433. package/src/services/TaskGraphActionRunner.ts +16 -15
  434. package/src/types.ts +21 -16
  435. package/src/util.ts +22 -9
@@ -119,7 +119,7 @@ export interface CacheInvalidationFilterContext {
119
119
  * roles, so this stays cheap enough to run per subscriber per event. It is not free: it runs for
120
120
  * every connected socket on every save, so keep it free of I/O.
121
121
  */
122
- export function cacheInvalidationFilter(data: {
122
+ export function CacheInvalidationFilter(data: {
123
123
  payload: CacheInvalidationPayload;
124
124
  context: CacheInvalidationFilterContext | undefined;
125
125
  }): boolean {
@@ -151,13 +151,21 @@ export function cacheInvalidationFilter(data: {
151
151
  return entity.GetUserPermisions(user).CanRead;
152
152
  }
153
153
 
154
+ /** @deprecated Use {@link CacheInvalidationFilter}. */
155
+ export function cacheInvalidationFilter(data: {
156
+ payload: CacheInvalidationPayload;
157
+ context: CacheInvalidationFilterContext | undefined;
158
+ }): boolean {
159
+ return CacheInvalidationFilter(data);
160
+ }
161
+
154
162
  @Resolver()
155
163
  export class CacheInvalidationResolver {
156
164
  /**
157
165
  * Subscription that broadcasts cache invalidation events to connected clients, so cross-server
158
166
  * cache invalidation reaches browsers.
159
167
  *
160
- * Delivery is filtered only by ENTITY-level read permission (see {@link cacheInvalidationFilter}).
168
+ * Delivery is filtered only by ENTITY-level read permission (see {@link CacheInvalidationFilter}).
161
169
  * Every session that may read an entity at all still receives every event for it, from every
162
170
  * other session and tenant.
163
171
  *
@@ -184,9 +192,9 @@ export class CacheInvalidationResolver {
184
192
  @Subscription(() => CacheInvalidationNotification, {
185
193
  topics: CACHE_INVALIDATION_TOPIC,
186
194
  filter: (data: { payload: CacheInvalidationPayload; context: CacheInvalidationFilterContext | undefined }) =>
187
- cacheInvalidationFilter(data),
195
+ CacheInvalidationFilter(data),
188
196
  })
189
- cacheInvalidation(
197
+ cacheInvalidation( // case-violation-ok-legacy-back-compat: the property name is the GraphQL schema field name — renaming it breaks every client query
190
198
  @Root() payload: CacheInvalidationPayload
191
199
  ): CacheInvalidationNotification {
192
200
  return {
@@ -1,6 +1,6 @@
1
1
  import { PubSubEngine } from 'type-graphql';
2
2
  import { LogError } from '@memberjunction/core';
3
- import { publishStatusUpdate } from './PushStatusResolver.js';
3
+ import { PublishStatusUpdate } from './PushStatusResolver.js';
4
4
 
5
5
  /** Default cadence for fire-and-forget liveness pulses (5 minutes). */
6
6
  export const DEFAULT_PULSE_INTERVAL_MS = 5 * 60 * 1000;
@@ -15,38 +15,38 @@ export const HEARTBEAT_MESSAGE_TYPE = 'Heartbeat';
15
15
  */
16
16
  export interface PulseStatus {
17
17
  /** Primary key of the persisted run record (AIAgentRun, TestRun, TestSuiteRun). */
18
- runId?: string;
18
+ runId?: string; // case-violation-ok-legacy-back-compat: optional, and the old name is also read off a value the checker cannot type; renaming it stays assignable and silently yields undefined
19
19
  /** Current run status, e.g. 'Running'. */
20
- status?: string;
20
+ status?: string; // case-violation-ok-legacy-back-compat: optional, and the old name is also read off a value the checker cannot type; renaming it stays assignable and silently yields undefined
21
21
  /** Optional human-readable current step for UI display. */
22
- currentStep?: string;
22
+ currentStep?: string; // case-violation-ok-legacy-back-compat: optional, and the old name is also read off a value the checker cannot type; renaming it stays assignable and silently yields undefined
23
23
  }
24
24
 
25
25
  /** Handle returned by {@link startLivenessPulse}; call `stop()` when the work settles. */
26
26
  export interface LivenessPulseHandle {
27
- stop(): void;
27
+ Stop(): void;
28
28
  }
29
29
 
30
30
  export interface LivenessPulseOptions {
31
31
  /** PubSub engine used to publish on the shared push-status topic. */
32
- pubSub: PubSubEngine;
32
+ pubSub: PubSubEngine; // case-violation-ok-legacy-back-compat: renaming it broke a use the checker could not see from the declaration — the compile proved it
33
33
  /** Session the client is subscribed on (used by the subscription filter). */
34
- sessionId: string;
34
+ sessionId: string; // case-violation-ok-legacy-back-compat: the old name is also read off a value typed `any`, where a rename would compile and silently return undefined
35
35
  /**
36
36
  * Authenticated user the operation belongs to (B49). Stamped on every pulse so the
37
37
  * subscription filter binds delivery to identity, not just the client-chosen sessionId.
38
38
  */
39
- ownerUserId: string;
39
+ ownerUserId: string; // case-violation-ok-legacy-back-compat: renaming it broke a use the checker could not see from the declaration — the compile proved it
40
40
  /** Resolver label echoed in the message envelope (e.g. 'RunAIAgentResolver'). */
41
- resolver: string;
41
+ resolver: string; // case-violation-ok-legacy-back-compat: the old name is also read off a value typed `any`, where a rename would compile and silently return undefined
42
42
  /** Pulse cadence in ms. Defaults to {@link DEFAULT_PULSE_INTERVAL_MS}. */
43
- intervalMs?: number;
43
+ intervalMs?: number; // case-violation-ok-legacy-back-compat: renaming it broke a use the checker could not see from the declaration — the compile proved it
44
44
  /**
45
45
  * Optional cheap status reader invoked on each tick. Should read from an
46
46
  * in-memory ref, not the database. Errors are swallowed so a transient read
47
47
  * never kills the pulse loop.
48
48
  */
49
- readStatus?: () => PulseStatus | undefined;
49
+ readStatus?: () => PulseStatus | undefined; // case-violation-ok-legacy-back-compat: renaming it broke a use the checker could not see from the declaration — the compile proved it
50
50
  }
51
51
 
52
52
  /**
@@ -60,7 +60,7 @@ export interface LivenessPulseOptions {
60
60
  * (`{ message: JSON.stringify({ resolver, type, status, data }), sessionId }`),
61
61
  * so the client receives it through the same subscription with no special parsing.
62
62
  */
63
- export function startLivenessPulse(options: LivenessPulseOptions): LivenessPulseHandle {
63
+ export function StartLivenessPulse(options: LivenessPulseOptions): LivenessPulseHandle {
64
64
  const { pubSub, sessionId, ownerUserId, resolver, readStatus } = options;
65
65
  const intervalMs = options.intervalMs ?? DEFAULT_PULSE_INTERVAL_MS;
66
66
 
@@ -73,7 +73,7 @@ export function startLivenessPulse(options: LivenessPulseOptions): LivenessPulse
73
73
  LogError(`[LivenessPulse:${resolver}] readStatus failed: ${(e as Error).message}`);
74
74
  }
75
75
 
76
- publishStatusUpdate(pubSub, {
76
+ PublishStatusUpdate(pubSub, {
77
77
  sessionId,
78
78
  ownerUserId,
79
79
  message: JSON.stringify({
@@ -86,6 +86,11 @@ export function startLivenessPulse(options: LivenessPulseOptions): LivenessPulse
86
86
  }, intervalMs);
87
87
 
88
88
  return {
89
- stop: () => clearInterval(timer),
89
+ Stop: () => clearInterval(timer),
90
90
  };
91
91
  }
92
+
93
+ /** @deprecated Use {@link StartLivenessPulse}. */
94
+ export function startLivenessPulse(options: LivenessPulseOptions): LivenessPulseHandle {
95
+ return StartLivenessPulse(options);
96
+ }
@@ -7,13 +7,13 @@ export const PUSH_STATUS_UPDATES_TOPIC = 'PUSH_STATUS_UPDATES';
7
7
  @ObjectType()
8
8
  export class PushStatusNotification {
9
9
  @Field(() => String, { nullable: true })
10
- message?: string;
10
+ message?: string; // case-violation-ok-legacy-back-compat: the property name is the GraphQL schema field name — renaming it breaks every client query
11
11
 
12
12
  @Field((_type) => Date)
13
- date!: Date;
13
+ date!: Date; // case-violation-ok-legacy-back-compat: the property name is the GraphQL schema field name — renaming it breaks every client query
14
14
 
15
15
  @Field((_type) => ID)
16
- sessionId!: string;
16
+ sessionId!: string; // case-violation-ok-legacy-back-compat: the property name is the GraphQL schema field name — renaming it breaks every client query
17
17
  }
18
18
 
19
19
  /**
@@ -55,7 +55,7 @@ export interface StatusUpdateParams {
55
55
  * payload shape and the required-identity guarantee live in exactly one place. Adding a field to
56
56
  * the push is a one-line change here; a new publisher physically cannot omit `ownerUserId`.
57
57
  */
58
- export function publishStatusUpdate(pubSub: PubSubEngine, params: StatusUpdateParams): void {
58
+ export function PublishStatusUpdate(pubSub: PubSubEngine, params: StatusUpdateParams): void {
59
59
  const payload: PushStatusNotificationPayload = {
60
60
  sessionId: params.sessionId,
61
61
  ownerUserId: params.ownerUserId,
@@ -64,6 +64,11 @@ export function publishStatusUpdate(pubSub: PubSubEngine, params: StatusUpdatePa
64
64
  pubSub.publish(PUSH_STATUS_UPDATES_TOPIC, payload);
65
65
  }
66
66
 
67
+ /** @deprecated Use {@link PublishStatusUpdate}. */
68
+ export function publishStatusUpdate(pubSub: PubSubEngine, params: StatusUpdateParams): void {
69
+ return PublishStatusUpdate(pubSub, params);
70
+ }
71
+
67
72
  /** Minimal shape of the subscription's connection context needed by the filter. */
68
73
  export interface StatusUpdatesFilterContext {
69
74
  userPayload?: UserPayload;
@@ -82,7 +87,7 @@ export interface StatusUpdatesFilterContext {
82
87
  * `sessionId` is no longer sufficient. Fails CLOSED — a missing owner or connection identity never
83
88
  * matches.
84
89
  */
85
- export function statusUpdatesFilter(data: {
90
+ export function StatusUpdatesFilter(data: {
86
91
  payload: PushStatusNotificationPayload;
87
92
  args: PushStatusNotificationArgs;
88
93
  context: StatusUpdatesFilterContext | undefined;
@@ -98,14 +103,23 @@ export function statusUpdatesFilter(data: {
98
103
  return UUIDsEqual(payload.ownerUserId, connectionUserId);
99
104
  }
100
105
 
106
+ /** @deprecated Use {@link StatusUpdatesFilter}. */
107
+ export function statusUpdatesFilter(data: {
108
+ payload: PushStatusNotificationPayload;
109
+ args: PushStatusNotificationArgs;
110
+ context: StatusUpdatesFilterContext | undefined;
111
+ }): boolean {
112
+ return StatusUpdatesFilter(data);
113
+ }
114
+
101
115
  @Resolver()
102
116
  export class PushStatusResolver {
103
117
  @Subscription(() => PushStatusNotification, {
104
118
  topics: PUSH_STATUS_UPDATES_TOPIC,
105
119
  filter: (data: ResolverFilterData<PushStatusNotificationPayload, PushStatusNotificationArgs, StatusUpdatesFilterContext>) =>
106
- statusUpdatesFilter(data),
120
+ StatusUpdatesFilter(data),
107
121
  })
108
- statusUpdates(
122
+ statusUpdates( // case-violation-ok-legacy-back-compat: the property name is the GraphQL schema field name — renaming it breaks every client query
109
123
  @Root() { message }: PushStatusNotificationPayload,
110
124
  @Arg('sessionId', () => String) sessionId: string
111
125
  ): PushStatusNotification {
@@ -10,6 +10,7 @@ import {
10
10
  EntitySaveOptions,
11
11
  IMetadataProvider,
12
12
  IRunViewProvider,
13
+ KeyValuePair,
13
14
  LogDebug,
14
15
  LogError,
15
16
  LogStatus,
@@ -39,7 +40,7 @@ import { MJEvent, MJEventType, MJGlobal, ENCRYPTED_SENTINEL, EscapeSQLString, Is
39
40
  import { SQLParser } from '@memberjunction/sql-parser';
40
41
  import { PostgreSQLDialect, SQLServerDialect, type SQLParserDialect } from '@memberjunction/sql-dialect';
41
42
  import { EncryptionEngine } from '@memberjunction/encryption';
42
- import { PUSH_STATUS_UPDATES_TOPIC, publishStatusUpdate } from './PushStatusResolver.js';
43
+ import { PUSH_STATUS_UPDATES_TOPIC, PublishStatusUpdate } from './PushStatusResolver.js';
43
44
  import { CACHE_INVALIDATION_TOPIC, MayBroadcastRecordData } from './CacheInvalidationResolver.js';
44
45
  import { PubSubManager } from './PubSubManager.js';
45
46
  import { FieldMapper } from '@memberjunction/graphql-dataprovider';
@@ -50,7 +51,7 @@ export class ResolverBase {
50
51
  private static _cloudeventsHeaders = process.env.CLOUDEVENTS_HTTP_HEADERS ? JSON.parse(process.env.CLOUDEVENTS_HTTP_HEADERS) : {};
51
52
 
52
53
  private static _eventSubscriptionKey: string = '___MJServer___ResolverBase___EventSubscriptions';
53
- private get EventSubscriptions(): Map<string, Subscription> {
54
+ private get eventSubscriptions(): Map<string, Subscription> {
54
55
  // here we use the global object store instead of a static member becuase in some cases based on import code paths/bundling/etc, the static member
55
56
  // could actually be duplicated and we'd end up with multiple instances of the same map, which would be bad.
56
57
  const g = MJGlobal.Instance.GetGlobalObjectStore();
@@ -1479,7 +1480,7 @@ export class ResolverBase {
1479
1480
  * `publishStatusUpdate()` function directly with an explicit `ownerUserId`.
1480
1481
  */
1481
1482
  protected PublishStatusUpdate(pubSub: PubSubEngine, sessionId: string, message: string | undefined, userPayload: UserPayload): void {
1482
- publishStatusUpdate(pubSub, {
1483
+ PublishStatusUpdate(pubSub, {
1483
1484
  sessionId,
1484
1485
  ownerUserId: userPayload?.userRecord?.ID ?? '',
1485
1486
  message,
@@ -1492,7 +1493,7 @@ export class ResolverBase {
1492
1493
  // cause issues with multiple messages for the same event.
1493
1494
  const uniqueKey = entityObject.EntityInfo.Name;
1494
1495
 
1495
- if (!this.EventSubscriptions.has(uniqueKey)) {
1496
+ if (!this.eventSubscriptions.has(uniqueKey)) {
1496
1497
  // listen for events from the entityObject in case it is a long running task and we can push messages back to the client via pubSub
1497
1498
  LogDebug(`ResolverBase.ListenForEntityMessages: About to call MJGlobal.Instance.GetEventListener() to get the event listener subscription for ${uniqueKey}`);
1498
1499
  const theSub = MJGlobal.Instance.GetEventListener(false).subscribe(async (event: MJEvent) => {
@@ -1525,10 +1526,66 @@ export class ResolverBase {
1525
1526
  }
1526
1527
  }
1527
1528
  });
1528
- this.EventSubscriptions.set(uniqueKey, theSub);
1529
+ this.eventSubscriptions.set(uniqueKey, theSub);
1529
1530
  }
1530
1531
  }
1531
1532
 
1533
+ /**
1534
+ * IS-A PROMOTION over the wire: an EXISTING parent record gaining a subtype — an Animal that is
1535
+ * now also a Dog. It arrives as the CHILD's create mutation carrying the existing parent's
1536
+ * primary key, because in IS-A the shared key is the relationship and the child has no key of
1537
+ * its own to mint. `NewRecord()` has just reset the whole chain to "new", so left alone the
1538
+ * parent would save as a CREATE: a second copy of a row that already exists (a unique-constraint
1539
+ * violation at best, a silently duplicated parent at worst).
1540
+ *
1541
+ * When a child create carries a complete primary key, bind the new child to the existing parent
1542
+ * row with `AttachToParent` (#3825): it loads the parent chain, so the parent saves as an UPDATE
1543
+ * and only the child is INSERTed. A key that matches no row leaves the fresh chain exactly as
1544
+ * `NewRecord()` built it — the ordinary whole-chain create. A non-IS-A entity, or a create with
1545
+ * no key, never gets past the guards.
1546
+ *
1547
+ * @returns true when the child was attached to an existing parent row.
1548
+ */
1549
+ protected async attachToExistingParentIfPromotion(entityObject: BaseEntity, input: Record<string, unknown>): Promise<boolean> {
1550
+ if (!entityObject.EntityInfo.IsChildType) {
1551
+ return false;
1552
+ }
1553
+ const key = this.primaryKeyFromInput(entityObject.EntityInfo, input);
1554
+ if (!key) {
1555
+ return false;
1556
+ }
1557
+ const attached = await entityObject.AttachToParent(key);
1558
+ if (!attached) {
1559
+ // No parent row under that key: this is a whole-chain create on the CALLER's key (the
1560
+ // client mints the shared key at the root and sends it, so honoring it keeps the client's
1561
+ // in-memory chain and the stored rows on one key). AttachToParent restored the fresh chain,
1562
+ // but the root's ReadOnly key slot is no longer writable — a later SetMany would silently
1563
+ // keep the server-minted value — so re-seed the chain with the supplied key explicitly.
1564
+ entityObject.NewRecord(key);
1565
+ }
1566
+ return attached;
1567
+ }
1568
+
1569
+ /**
1570
+ * The entity's primary key as supplied on a mutation input, or null when any part of it is
1571
+ * missing. Composite keys are all-or-nothing: a half-specified key identifies nothing. An empty
1572
+ * string counts as missing, the same as null — it is never a valid key value, and a GUID or
1573
+ * string key that is "" identifies no row, so the create proceeds as a whole-chain create rather
1574
+ * than failing the lookup. A malformed non-empty key (e.g. a non-GUID for a uniqueidentifier)
1575
+ * is NOT swallowed: the load throws and the mutation fails loudly.
1576
+ */
1577
+ protected primaryKeyFromInput(entityInfo: EntityInfo, input: Record<string, unknown>): CompositeKey | null {
1578
+ const pairs: KeyValuePair[] = [];
1579
+ for (const pk of entityInfo.PrimaryKeys) {
1580
+ const value = input[pk.Name] ?? input[pk.CodeName];
1581
+ if (value === null || value === undefined || value === '') {
1582
+ return null;
1583
+ }
1584
+ pairs.push(new KeyValuePair(pk.Name, value));
1585
+ }
1586
+ return pairs.length > 0 ? CompositeKey.FromKeyValuePairs(pairs) : null;
1587
+ }
1588
+
1532
1589
  protected async CreateRecord(entityName: string, input: any, provider: DatabaseProviderBase, userPayload: UserPayload, pubSub: PubSubEngine) {
1533
1590
  // Check API key scope authorization for entity create operations
1534
1591
  await this.CheckAPIKeyScopeAuthorization('entity:create', entityName, userPayload);
@@ -1546,6 +1603,10 @@ export class ResolverBase {
1546
1603
  for (const key of Object.keys(input)) {
1547
1604
  if (key !== 'RestoreContext___') fieldsForSet[key] = input[key];
1548
1605
  }
1606
+ // IS-A promotion: bind the new child to its EXISTING parent row BEFORE the field
1607
+ // assignments, so values the client sent for parent fields land on the loaded parent
1608
+ // (as an update) instead of being wiped by the load.
1609
+ await this.attachToExistingParentIfPromotion(entityObject, fieldsForSet);
1549
1610
  entityObject.SetMany(fieldsForSet);
1550
1611
 
1551
1612
  // Reconstruct the client-side restore context, if any, on this server
@@ -45,13 +45,13 @@ export class AggregateExpressionInput {
45
45
  @Field(() => String, {
46
46
  description: 'SQL expression for the aggregate (e.g., "SUM(OrderTotal)", "COUNT(*)", "AVG(Price)")'
47
47
  })
48
- expression: string;
48
+ expression: string; // case-violation-ok-legacy-back-compat: the property name is the GraphQL schema field name — renaming it breaks every client query
49
49
 
50
50
  @Field(() => String, {
51
51
  nullable: true,
52
52
  description: 'Optional alias for the result (used in error messages and debugging)'
53
53
  })
54
- alias?: string;
54
+ alias?: string; // case-violation-ok-legacy-back-compat: the property name is the GraphQL schema field name — renaming it breaks every client query
55
55
  }
56
56
 
57
57
  /**
@@ -60,22 +60,22 @@ export class AggregateExpressionInput {
60
60
  @ObjectType()
61
61
  export class AggregateResultOutput {
62
62
  @Field(() => String, { description: 'The expression that was calculated' })
63
- expression: string;
63
+ expression: string; // case-violation-ok-legacy-back-compat: the property name is the GraphQL schema field name — renaming it breaks every client query
64
64
 
65
65
  @Field(() => String, { description: 'The alias (or expression if no alias provided)' })
66
- alias: string;
66
+ alias: string; // case-violation-ok-legacy-back-compat: the property name is the GraphQL schema field name — renaming it breaks every client query
67
67
 
68
68
  @Field(() => String, {
69
69
  nullable: true,
70
70
  description: 'The calculated value as a JSON string (preserves type information)'
71
71
  })
72
- value?: string;
72
+ value?: string; // case-violation-ok-legacy-back-compat: the property name is the GraphQL schema field name — renaming it breaks every client query
73
73
 
74
74
  @Field(() => String, {
75
75
  nullable: true,
76
76
  description: 'Error message if calculation failed'
77
77
  })
78
- error?: string;
78
+ error?: string; // case-violation-ok-legacy-back-compat: the property name is the GraphQL schema field name — renaming it breaks every client query
79
79
  }
80
80
 
81
81
  //****************************************************************************
@@ -573,22 +573,22 @@ export class RunViewGenericInput {
573
573
  @InputType()
574
574
  export class RunViewCacheStatusInput {
575
575
  @Field(() => String, { description: 'The maximum __mj_UpdatedAt value from cached results' })
576
- maxUpdatedAt: string;
576
+ maxUpdatedAt: string; // case-violation-ok-legacy-back-compat: the property name is the GraphQL schema field name — renaming it breaks every client query
577
577
 
578
578
  @Field(() => Int, { description: 'The number of rows in cached results' })
579
- rowCount: number;
579
+ rowCount: number; // case-violation-ok-legacy-back-compat: the property name is the GraphQL schema field name — renaming it breaks every client query
580
580
  }
581
581
 
582
582
  @InputType()
583
583
  export class RunViewWithCacheCheckInput {
584
584
  @Field(() => RunDynamicViewInput, { description: 'The RunView parameters' })
585
- params: RunDynamicViewInput;
585
+ params: RunDynamicViewInput; // case-violation-ok-legacy-back-compat: the property name is the GraphQL schema field name — renaming it breaks every client query
586
586
 
587
587
  @Field(() => RunViewCacheStatusInput, {
588
588
  nullable: true,
589
589
  description: 'Optional cache status - if provided, server will check if cache is current'
590
590
  })
591
- cacheStatus?: RunViewCacheStatusInput;
591
+ cacheStatus?: RunViewCacheStatusInput; // case-violation-ok-legacy-back-compat: the property name is the GraphQL schema field name — renaming it breaks every client query
592
592
  }
593
593
 
594
594
  @ObjectType()
@@ -596,21 +596,21 @@ export class DifferentialDataOutput {
596
596
  @Field(() => [RunViewGenericResultRow], {
597
597
  description: 'Records that have been created or updated since the client\'s maxUpdatedAt'
598
598
  })
599
- updatedRows: RunViewGenericResultRow[];
599
+ updatedRows: RunViewGenericResultRow[]; // case-violation-ok-legacy-back-compat: the property name is the GraphQL schema field name — renaming it breaks every client query
600
600
 
601
601
  @Field(() => [String], {
602
602
  description: 'Primary key values (as concatenated strings) of records that have been deleted'
603
603
  })
604
- deletedRecordIDs: string[];
604
+ deletedRecordIDs: string[]; // case-violation-ok-legacy-back-compat: the property name is the GraphQL schema field name — renaming it breaks every client query
605
605
  }
606
606
 
607
607
  @ObjectType()
608
608
  export class RunViewWithCacheCheckResultOutput {
609
609
  @Field(() => Int, { description: 'The index of this view in the batch request' })
610
- viewIndex: number;
610
+ viewIndex: number; // case-violation-ok-legacy-back-compat: the property name is the GraphQL schema field name — renaming it breaks every client query
611
611
 
612
612
  @Field(() => String, { description: "'current', 'differential', 'stale', or 'error'" })
613
- status: string;
613
+ status: string; // case-violation-ok-legacy-back-compat: the property name is the GraphQL schema field name — renaming it breaks every client query
614
614
 
615
615
  @Field(() => [RunViewGenericResultRow], {
616
616
  nullable: true,
@@ -622,34 +622,34 @@ export class RunViewWithCacheCheckResultOutput {
622
622
  nullable: true,
623
623
  description: 'Differential update data - only populated when status is differential'
624
624
  })
625
- differentialData?: DifferentialDataOutput;
625
+ differentialData?: DifferentialDataOutput; // case-violation-ok-legacy-back-compat: the property name is the GraphQL schema field name — renaming it breaks every client query
626
626
 
627
627
  @Field(() => String, { nullable: true, description: 'Max __mj_UpdatedAt from results when stale or differential' })
628
- maxUpdatedAt?: string;
628
+ maxUpdatedAt?: string; // case-violation-ok-legacy-back-compat: the property name is the GraphQL schema field name — renaming it breaks every client query
629
629
 
630
630
  @Field(() => Int, { nullable: true, description: 'Row count of results when stale or differential (total after applying delta)' })
631
- rowCount?: number;
631
+ rowCount?: number; // case-violation-ok-legacy-back-compat: the property name is the GraphQL schema field name — renaming it breaks every client query
632
632
 
633
633
  @Field(() => String, { nullable: true, description: 'Error message if status is error' })
634
- errorMessage?: string;
634
+ errorMessage?: string; // case-violation-ok-legacy-back-compat: the property name is the GraphQL schema field name — renaming it breaks every client query
635
635
 
636
636
  @Field(() => [AggregateResultOutput], {
637
637
  nullable: true,
638
638
  description: 'Aggregate results when status is stale and aggregates were requested (B40 — previously never marshalled, so CacheLocal callers got no aggregates at all)'
639
639
  })
640
- aggregateResults?: AggregateResultOutput[];
640
+ aggregateResults?: AggregateResultOutput[]; // case-violation-ok-legacy-back-compat: the property name is the GraphQL schema field name — renaming it breaks every client query
641
641
  }
642
642
 
643
643
  @ObjectType()
644
644
  export class RunViewsWithCacheCheckOutput {
645
645
  @Field(() => Boolean, { description: 'Whether the overall operation succeeded' })
646
- success: boolean;
646
+ success: boolean; // case-violation-ok-legacy-back-compat: the property name is the GraphQL schema field name — renaming it breaks every client query
647
647
 
648
648
  @Field(() => [RunViewWithCacheCheckResultOutput], { description: 'Results for each view in the batch' })
649
- results: RunViewWithCacheCheckResultOutput[];
649
+ results: RunViewWithCacheCheckResultOutput[]; // case-violation-ok-legacy-back-compat: the property name is the GraphQL schema field name — renaming it breaks every client query
650
650
 
651
651
  @Field(() => String, { nullable: true, description: 'Overall error message if success is false' })
652
- errorMessage?: string;
652
+ errorMessage?: string; // case-violation-ok-legacy-back-compat: the property name is the GraphQL schema field name — renaming it breaks every client query
653
653
  }
654
654
 
655
655
  //****************************************************************************
@@ -2,7 +2,7 @@ import 'reflect-metadata';
2
2
  import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
3
3
  import { PubSubEngine } from 'type-graphql';
4
4
  import {
5
- startLivenessPulse,
5
+ StartLivenessPulse,
6
6
  DEFAULT_PULSE_INTERVAL_MS,
7
7
  HEARTBEAT_MESSAGE_TYPE,
8
8
  } from '../FireAndForgetHeartbeat';
@@ -31,14 +31,14 @@ describe('startLivenessPulse', () => {
31
31
 
32
32
  it('does not publish before the first interval elapses', () => {
33
33
  const pubSub = makePubSub();
34
- startLivenessPulse({ pubSub, sessionId: 's1', resolver: 'TestResolver' });
34
+ StartLivenessPulse({ pubSub, sessionId: 's1', resolver: 'TestResolver' });
35
35
  vi.advanceTimersByTime(DEFAULT_PULSE_INTERVAL_MS - 1);
36
36
  expect(pubSub.publish).not.toHaveBeenCalled();
37
37
  });
38
38
 
39
39
  it('publishes a heartbeat on the push-status topic each interval', () => {
40
40
  const pubSub = makePubSub();
41
- startLivenessPulse({ pubSub, sessionId: 's1', resolver: 'TestResolver', intervalMs: 1000 });
41
+ StartLivenessPulse({ pubSub, sessionId: 's1', resolver: 'TestResolver', intervalMs: 1000 });
42
42
 
43
43
  vi.advanceTimersByTime(3000);
44
44
 
@@ -52,7 +52,7 @@ describe('startLivenessPulse', () => {
52
52
 
53
53
  it('includes the enriched status snapshot from readStatus', () => {
54
54
  const pubSub = makePubSub();
55
- startLivenessPulse({
55
+ StartLivenessPulse({
56
56
  pubSub,
57
57
  sessionId: 's1',
58
58
  resolver: 'RunAIAgentResolver',
@@ -68,7 +68,7 @@ describe('startLivenessPulse', () => {
68
68
 
69
69
  it('publishes an empty data object when readStatus throws (loop survives)', () => {
70
70
  const pubSub = makePubSub();
71
- startLivenessPulse({
71
+ StartLivenessPulse({
72
72
  pubSub,
73
73
  sessionId: 's1',
74
74
  resolver: 'RunAIAgentResolver',
@@ -87,12 +87,12 @@ describe('startLivenessPulse', () => {
87
87
 
88
88
  it('stops publishing after stop() is called', () => {
89
89
  const pubSub = makePubSub();
90
- const handle = startLivenessPulse({ pubSub, sessionId: 's1', resolver: 'TestResolver', intervalMs: 1000 });
90
+ const handle = StartLivenessPulse({ pubSub, sessionId: 's1', resolver: 'TestResolver', intervalMs: 1000 });
91
91
 
92
92
  vi.advanceTimersByTime(2000);
93
93
  expect(pubSub.publish).toHaveBeenCalledTimes(2);
94
94
 
95
- handle.stop();
95
+ handle.Stop();
96
96
  vi.advanceTimersByTime(5000);
97
97
  expect(pubSub.publish).toHaveBeenCalledTimes(2);
98
98
  });
package/src/index.ts CHANGED
@@ -32,7 +32,7 @@ import { RealtimeProxyServer } from './realtimeProxy/RealtimeProxyServer.js';
32
32
  import buildApolloServer from './apolloServer/index.js';
33
33
  import { configInfo, configFilePath, dbDatabase, dbHost, dbPort, dbUsername, graphqlPort, graphqlRootPath, mj_core_schema, websiteRunFromPackage, RESTApiOptions } from './config.js';
34
34
  import { default as jwt } from 'jsonwebtoken';
35
- import { contextFunction, createUnifiedAuthMiddleware, getUserPayload } from './context.js';
35
+ import { contextFunction, CreateUnifiedAuthMiddleware, getUserPayload } from './context.js';
36
36
  import { UserPayload } from './types.js';
37
37
  import { requireSystemUserDirective, publicDirective } from './directives/index.js';
38
38
  import { variablesLoggingMiddleware } from './logging/variablesLoggingMiddleware.js';
@@ -67,6 +67,8 @@ import { RegisterRSUProgressBridge } from './integration/RSUProgressBridge.js';
67
67
  import { ClientToolRequestManager, AgentRunWatchdog } from '@memberjunction/ai-agents';
68
68
  import { SessionJanitor } from './agentSessions/index.js';
69
69
  import { StartTaskGraphDispatcher } from './services/StartTaskGraphDispatcher.js';
70
+ import { GetAttachmentService } from '@memberjunction/aiengine';
71
+ import { MJStorageBlobStore } from './services/MJStorageBlobStore.js';
70
72
  import { CACHE_INVALIDATION_TOPIC, MayBroadcastRecordData, ConfigureRecordDataBroadcast } from './generic/CacheInvalidationResolver.js';
71
73
  import { ConnectorFactory, IntegrationEngine, IntegrationSyncOptions } from '@memberjunction/integration-engine';
72
74
  import { CronExpressionHelper } from '@memberjunction/scheduling-engine';
@@ -80,6 +82,7 @@ import {
80
82
  import { ServerExtensionLoader, ServerExtensionConfig, mergeServerExtensionConfigs, prepareServerExtensionConfigs, describeServerExtensionMount, InstallMediaUpgradeDispatcher, IsGraphQLWsPath } from '@memberjunction/server-extensions-core';
81
83
  import { coreReservedServerExtensionRoots } from './serverExtensionReservedRoots.js';
82
84
  import { MetadataCacheRefreshIntervalSeconds } from './providerConfigUnits.js';
85
+ import { CreateMetadataRefreshSignalHandler, METADATA_REFRESH_SIGNAL } from './metadataRefreshSignal.js';
83
86
 
84
87
  const cacheRefreshInterval = configInfo.databaseSettings.metadataCacheRefreshInterval;
85
88
 
@@ -97,10 +100,15 @@ export { MetadataCacheRefreshIntervalSeconds } from './providerConfigUnits.js';
97
100
  * CodeGenLib). This wrapper keeps the public `getDbType()` symbol that
98
101
  * MJServer consumers (and the broader stack) already import.
99
102
  */
100
- export function getDbType(): DatabasePlatform {
103
+ export function GetDbType(): DatabasePlatform {
101
104
  return resolveDbPlatformFromEnv() ?? 'sqlserver';
102
105
  }
103
106
 
107
+ /** @deprecated Use {@link GetDbType}. */
108
+ export function getDbType(): DatabasePlatform {
109
+ return GetDbType();
110
+ }
111
+
104
112
  export { MaxLength } from 'class-validator';
105
113
  export * from 'type-graphql';
106
114
  // Named re-export so Open App generated resolvers get a live ESM binding for
@@ -261,7 +269,10 @@ const localPath = (p: string) => {
261
269
  return resolvedPath;
262
270
  };
263
271
 
264
- export const createApp = (): Application => express();
272
+ export const CreateApp = (): Application => express();
273
+
274
+ /** @deprecated Use {@link CreateApp}. */
275
+ export const createApp = CreateApp;
265
276
 
266
277
  /**
267
278
  * Resolves the MJServer package version for the startup summary header.
@@ -281,7 +292,18 @@ function resolveServerVersion(): string | undefined {
281
292
  }
282
293
  }
283
294
 
284
- export const serve = async (resolverPaths: Array<string>, app: Application = createApp(), options?: MJServerOptions): Promise<void> => {
295
+ // Bind MJStorage as the conversation-attachment blob store. The attachment service itself no longer
296
+ // imports `@memberjunction/storage` — that dependency made it unusable from any browser or React
297
+ // Native client, which is why the same attachment rules had been reimplemented three times.
298
+ //
299
+ // This runs at module load, not inside `serve()`, so that merely importing MJServer is enough: any
300
+ // entry point that reaches the attachment service — a resolver under test, a script, a worker that
301
+ // never calls `serve()` — finds storage already bound rather than degrading to "storage is not
302
+ // available on this host". The store is stateless and configures `FileStorageEngine` on use, so
303
+ // there is no ordering hazard in binding this early.
304
+ GetAttachmentService().BlobStore = new MJStorageBlobStore();
305
+
306
+ export const Serve = async (resolverPaths: Array<string>, app: Application = CreateApp(), options?: MJServerOptions): Promise<void> => {
285
307
  const t0 = performance.now();
286
308
  // Level-gated startup logger. Resolves verbosity from telemetry.level (single
287
309
  // operator knob). At `standard` (default), per-phase timings are collapsed into
@@ -307,8 +329,8 @@ export const serve = async (resolverPaths: Array<string>, app: Application = cre
307
329
  console.log({ combinedResolverPaths, paths, cwd: process.cwd() });
308
330
  }
309
331
 
310
- const setupComplete$ = new ReplaySubject(1);
311
- const dbType = getDbType();
332
+ const setupComplete$ = new ReplaySubject(1);
333
+ const dbType = GetDbType();
312
334
  const dataSources: DataSourceInfo[] = [];
313
335
 
314
336
  if (dbType === 'postgresql') {
@@ -1377,7 +1399,7 @@ export const serve = async (resolverPaths: Array<string>, app: Application = cre
1377
1399
  startupLog.LogIf('verbose', `[Auth] Public provider catalog registered at ${AUTH_CATALOG_MOUNT_PATH}/providers`);
1378
1400
 
1379
1401
  // ─── Unified auth middleware (replaces both REST authMiddleware and contextFunction auth) ─────
1380
- app.use(createUnifiedAuthMiddleware(dataSources));
1402
+ app.use(CreateUnifiedAuthMiddleware(dataSources));
1381
1403
 
1382
1404
  // ─── Post-auth middleware from BaseServerMiddleware plugins ─────
1383
1405
  // Middleware here has access to the authenticated user via req.userPayload.
@@ -1666,6 +1688,8 @@ export const serve = async (resolverPaths: Array<string>, app: Application = cre
1666
1688
 
1667
1689
  process.on('SIGTERM', () => gracefulShutdown('SIGTERM'));
1668
1690
  process.on('SIGINT', () => gracefulShutdown('SIGINT'));
1691
+ // Operator control: `kill -HUP <pid>` hard-reloads metadata from the DB — see metadataRefreshSignal.ts
1692
+ process.on(METADATA_REFRESH_SIGNAL, CreateMetadataRefreshSignalHandler(() => Metadata.Provider.Refresh())); // global-provider-ok: operator-triggered refresh of the global cache that per-request providers adopt from
1669
1693
 
1670
1694
  // Handle unhandled promise rejections to prevent server crashes
1671
1695
  process.on('unhandledRejection', (reason, promise) => {
@@ -1676,6 +1700,9 @@ export const serve = async (resolverPaths: Array<string>, app: Application = cre
1676
1700
  });
1677
1701
  };
1678
1702
 
1703
+ /** @deprecated Use {@link Serve}. */
1704
+ export const serve = Serve;
1705
+
1679
1706
  /**
1680
1707
  * Age at which an unprocessed `MJ: RSU Pending Works` row is reported as stranded.
1681
1708
  * A row older than this survived at least one full restart cycle without being completed.
@@ -133,7 +133,7 @@ interface WorkItem {
133
133
  }
134
134
 
135
135
  /** Registers the post-sync custom-column promotion hook on the IntegrationEngine singleton. */
136
- export function registerIntegrationCustomColumnPromoter(): void {
136
+ export function RegisterIntegrationCustomColumnPromoter(): void {
137
137
  IntegrationEngine.Instance.SetPostSyncSchemaPromotionCallback(async (ctx) => {
138
138
  const user = ctx.ContextUser as UserInfo;
139
139
  const provider = ctx.Provider as IMetadataProvider | undefined;
@@ -164,6 +164,11 @@ export function registerIntegrationCustomColumnPromoter(): void {
164
164
  LogStatusEx({ message: '[CustomColumnPromoter] Registered post-sync custom-column promotion hook (auto-promote opt-in, default OFF).', verboseOnly: true });
165
165
  }
166
166
 
167
+ /** @deprecated Use {@link RegisterIntegrationCustomColumnPromoter}. */
168
+ export function registerIntegrationCustomColumnPromoter(): void {
169
+ return RegisterIntegrationCustomColumnPromoter();
170
+ }
171
+
167
172
  /** Reads the per-connection `autoPromoteCustomColumns` flag (default false = capture-only, on-demand promotion). */
168
173
  async function readAutoPromoteFlag(companyIntegrationID: string, user: UserInfo, provider?: IMetadataProvider): Promise<boolean> {
169
174
  try {