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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (560) hide show
  1. package/README.md +47 -1
  2. package/dist/.tsbuildinfo +1 -1
  3. package/dist/agentSessions/SessionJanitor.d.ts +91 -30
  4. package/dist/agentSessions/SessionJanitor.d.ts.map +1 -1
  5. package/dist/agentSessions/SessionJanitor.js +311 -81
  6. package/dist/agentSessions/SessionJanitor.js.map +1 -1
  7. package/dist/agentSessions/SessionManager.d.ts +17 -3
  8. package/dist/agentSessions/SessionManager.d.ts.map +1 -1
  9. package/dist/agentSessions/SessionManager.js +44 -10
  10. package/dist/agentSessions/SessionManager.js.map +1 -1
  11. package/dist/apiRoot.d.ts +32 -0
  12. package/dist/apiRoot.d.ts.map +1 -0
  13. package/dist/apiRoot.js +67 -0
  14. package/dist/apiRoot.js.map +1 -0
  15. package/dist/auth/AuthProviderCatalogRouter.d.ts.map +1 -1
  16. package/dist/auth/AuthProviderCatalogRouter.js +27 -0
  17. package/dist/auth/AuthProviderCatalogRouter.js.map +1 -1
  18. package/dist/auth/LoginScreenEngine.d.ts +22 -0
  19. package/dist/auth/LoginScreenEngine.d.ts.map +1 -0
  20. package/dist/auth/LoginScreenEngine.js +57 -0
  21. package/dist/auth/LoginScreenEngine.js.map +1 -0
  22. package/dist/auth/index.d.ts +1 -0
  23. package/dist/auth/index.d.ts.map +1 -1
  24. package/dist/auth/index.js +52 -10
  25. package/dist/auth/index.js.map +1 -1
  26. package/dist/auth/initializeProviders.d.ts +10 -0
  27. package/dist/auth/initializeProviders.d.ts.map +1 -1
  28. package/dist/auth/initializeProviders.js +37 -1
  29. package/dist/auth/initializeProviders.js.map +1 -1
  30. package/dist/auth/localDevAuth.d.ts +79 -0
  31. package/dist/auth/localDevAuth.d.ts.map +1 -0
  32. package/dist/auth/localDevAuth.js +439 -0
  33. package/dist/auth/localDevAuth.js.map +1 -0
  34. package/dist/auth/magicLink/MagicLinkKeys.d.ts +2 -2
  35. package/dist/auth/magicLink/MagicLinkKeys.d.ts.map +1 -1
  36. package/dist/auth/magicLink/MagicLinkKeys.js +1 -1
  37. package/dist/auth/magicLink/MagicLinkKeys.js.map +1 -1
  38. package/dist/auth/magicLink/MagicLinkRouter.d.ts.map +1 -1
  39. package/dist/auth/magicLink/MagicLinkRouter.js +3 -2
  40. package/dist/auth/magicLink/MagicLinkRouter.js.map +1 -1
  41. package/dist/auth/magicLink/types.d.ts +12 -0
  42. package/dist/auth/magicLink/types.d.ts.map +1 -1
  43. package/dist/auth/sessionUserClone.d.ts +1 -1
  44. package/dist/auth/sessionUserClone.d.ts.map +1 -1
  45. package/dist/auth/sessionUserClone.js +3 -2
  46. package/dist/auth/sessionUserClone.js.map +1 -1
  47. package/dist/broadcast/BaseCacheInvalidationBroadcastProvider.d.ts +292 -0
  48. package/dist/broadcast/BaseCacheInvalidationBroadcastProvider.d.ts.map +1 -0
  49. package/dist/broadcast/BaseCacheInvalidationBroadcastProvider.js +442 -0
  50. package/dist/broadcast/BaseCacheInvalidationBroadcastProvider.js.map +1 -0
  51. package/dist/broadcast/index.d.ts +6 -0
  52. package/dist/broadcast/index.d.ts.map +1 -0
  53. package/dist/broadcast/index.js +6 -0
  54. package/dist/broadcast/index.js.map +1 -0
  55. package/dist/broadcast/providers/AWSIoTCoreBroadcastProvider.d.ts +43 -0
  56. package/dist/broadcast/providers/AWSIoTCoreBroadcastProvider.d.ts.map +1 -0
  57. package/dist/broadcast/providers/AWSIoTCoreBroadcastProvider.js +144 -0
  58. package/dist/broadcast/providers/AWSIoTCoreBroadcastProvider.js.map +1 -0
  59. package/dist/broadcast/providers/AzureWebPubSubBroadcastProvider.d.ts +46 -0
  60. package/dist/broadcast/providers/AzureWebPubSubBroadcastProvider.d.ts.map +1 -0
  61. package/dist/broadcast/providers/AzureWebPubSubBroadcastProvider.js +149 -0
  62. package/dist/broadcast/providers/AzureWebPubSubBroadcastProvider.js.map +1 -0
  63. package/dist/broadcast/providers/GCPPubSubBroadcastProvider.d.ts +49 -0
  64. package/dist/broadcast/providers/GCPPubSubBroadcastProvider.d.ts.map +1 -0
  65. package/dist/broadcast/providers/GCPPubSubBroadcastProvider.js +131 -0
  66. package/dist/broadcast/providers/GCPPubSubBroadcastProvider.js.map +1 -0
  67. package/dist/broadcast/providers/SequencedRoleBucketBroadcastProvider.d.ts +24 -0
  68. package/dist/broadcast/providers/SequencedRoleBucketBroadcastProvider.d.ts.map +1 -0
  69. package/dist/broadcast/providers/SequencedRoleBucketBroadcastProvider.js +51 -0
  70. package/dist/broadcast/providers/SequencedRoleBucketBroadcastProvider.js.map +1 -0
  71. package/dist/cache.d.ts +3 -2
  72. package/dist/cache.d.ts.map +1 -1
  73. package/dist/cache.js.map +1 -1
  74. package/dist/config.d.ts +632 -22
  75. package/dist/config.d.ts.map +1 -1
  76. package/dist/config.js +104 -4
  77. package/dist/config.js.map +1 -1
  78. package/dist/context.d.ts +3 -1
  79. package/dist/context.d.ts.map +1 -1
  80. package/dist/context.js +29 -8
  81. package/dist/context.js.map +1 -1
  82. package/dist/coreRoutes.d.ts +29 -0
  83. package/dist/coreRoutes.d.ts.map +1 -0
  84. package/dist/coreRoutes.js +156 -0
  85. package/dist/coreRoutes.js.map +1 -0
  86. package/dist/generated/graphql-schemas/__mj.d.ts +1016 -29
  87. package/dist/generated/graphql-schemas/__mj.d.ts.map +1 -1
  88. package/dist/generated/graphql-schemas/__mj.js +5125 -434
  89. package/dist/generated/graphql-schemas/__mj.js.map +1 -1
  90. package/dist/generic/CacheInvalidationResolver.d.ts +85 -30
  91. package/dist/generic/CacheInvalidationResolver.d.ts.map +1 -1
  92. package/dist/generic/CacheInvalidationResolver.js +175 -60
  93. package/dist/generic/CacheInvalidationResolver.js.map +1 -1
  94. package/dist/generic/OrphanedConversationDetailReconciler.d.ts.map +1 -1
  95. package/dist/generic/OrphanedConversationDetailReconciler.js +2 -0
  96. package/dist/generic/OrphanedConversationDetailReconciler.js.map +1 -1
  97. package/dist/generic/PubSubManager.d.ts +10 -0
  98. package/dist/generic/PubSubManager.d.ts.map +1 -1
  99. package/dist/generic/PubSubManager.js +44 -1
  100. package/dist/generic/PubSubManager.js.map +1 -1
  101. package/dist/generic/PushStatusResolver.d.ts +39 -6
  102. package/dist/generic/PushStatusResolver.d.ts.map +1 -1
  103. package/dist/generic/PushStatusResolver.js +94 -12
  104. package/dist/generic/PushStatusResolver.js.map +1 -1
  105. package/dist/generic/ResolverBase.d.ts +168 -69
  106. package/dist/generic/ResolverBase.d.ts.map +1 -1
  107. package/dist/generic/ResolverBase.js +464 -405
  108. package/dist/generic/ResolverBase.js.map +1 -1
  109. package/dist/generic/RunViewResolver.d.ts +11 -3
  110. package/dist/generic/RunViewResolver.d.ts.map +1 -1
  111. package/dist/generic/RunViewResolver.js +91 -13
  112. package/dist/generic/RunViewResolver.js.map +1 -1
  113. package/dist/generic/actionOutputFieldSecurity.d.ts +26 -0
  114. package/dist/generic/actionOutputFieldSecurity.d.ts.map +1 -0
  115. package/dist/generic/actionOutputFieldSecurity.js +47 -0
  116. package/dist/generic/actionOutputFieldSecurity.js.map +1 -0
  117. package/dist/generic/fieldSecurityTransport.d.ts +30 -0
  118. package/dist/generic/fieldSecurityTransport.d.ts.map +1 -0
  119. package/dist/generic/fieldSecurityTransport.js +30 -0
  120. package/dist/generic/fieldSecurityTransport.js.map +1 -0
  121. package/dist/globalCors.d.ts +33 -0
  122. package/dist/globalCors.d.ts.map +1 -0
  123. package/dist/globalCors.js +58 -0
  124. package/dist/globalCors.js.map +1 -0
  125. package/dist/index.d.ts +14 -0
  126. package/dist/index.d.ts.map +1 -1
  127. package/dist/index.js +230 -222
  128. package/dist/index.js.map +1 -1
  129. package/dist/integration/CustomColumnPromoter.d.ts.map +1 -1
  130. package/dist/integration/CustomColumnPromoter.js +6 -1
  131. package/dist/integration/CustomColumnPromoter.js.map +1 -1
  132. package/dist/logging/variablesLoggingMiddleware.d.ts +12 -0
  133. package/dist/logging/variablesLoggingMiddleware.d.ts.map +1 -1
  134. package/dist/logging/variablesLoggingMiddleware.js +50 -1
  135. package/dist/logging/variablesLoggingMiddleware.js.map +1 -1
  136. package/dist/multiTenancy/index.d.ts.map +1 -1
  137. package/dist/multiTenancy/index.js +22 -2
  138. package/dist/multiTenancy/index.js.map +1 -1
  139. package/dist/realtimeSessions/RealtimeSessionVerificationService.d.ts.map +1 -1
  140. package/dist/realtimeSessions/RealtimeSessionVerificationService.js +2 -1
  141. package/dist/realtimeSessions/RealtimeSessionVerificationService.js.map +1 -1
  142. package/dist/realtimeSessions/RealtimeVerifyRouter.d.ts.map +1 -1
  143. package/dist/realtimeSessions/RealtimeVerifyRouter.js +2 -1
  144. package/dist/realtimeSessions/RealtimeVerifyRouter.js.map +1 -1
  145. package/dist/realtimeSessions/RecordingAssemblyScheduler.d.ts +100 -0
  146. package/dist/realtimeSessions/RecordingAssemblyScheduler.d.ts.map +1 -0
  147. package/dist/realtimeSessions/RecordingAssemblyScheduler.js +173 -0
  148. package/dist/realtimeSessions/RecordingAssemblyScheduler.js.map +1 -0
  149. package/dist/realtimeSessions/SessionVerificationPortsBase.d.ts.map +1 -1
  150. package/dist/realtimeSessions/SessionVerificationPortsBase.js +1 -0
  151. package/dist/realtimeSessions/SessionVerificationPortsBase.js.map +1 -1
  152. package/dist/realtimeSessions/index.d.ts +2 -0
  153. package/dist/realtimeSessions/index.d.ts.map +1 -1
  154. package/dist/realtimeSessions/index.js +2 -0
  155. package/dist/realtimeSessions/index.js.map +1 -1
  156. package/dist/realtimeSessions/ownedRealtimeSession.d.ts +32 -0
  157. package/dist/realtimeSessions/ownedRealtimeSession.d.ts.map +1 -0
  158. package/dist/realtimeSessions/ownedRealtimeSession.js +56 -0
  159. package/dist/realtimeSessions/ownedRealtimeSession.js.map +1 -0
  160. package/dist/realtimeSessions/sessionAccess.d.ts +91 -8
  161. package/dist/realtimeSessions/sessionAccess.d.ts.map +1 -1
  162. package/dist/realtimeSessions/sessionAccess.js +127 -20
  163. package/dist/realtimeSessions/sessionAccess.js.map +1 -1
  164. package/dist/realtimeSessions/sessionConfigStamps.js +1 -1
  165. package/dist/realtimeSessions/sessionConfigStamps.js.map +1 -1
  166. package/dist/realtimeSessions/subscriptionAuthorization.d.ts.map +1 -1
  167. package/dist/realtimeSessions/subscriptionAuthorization.js +5 -2
  168. package/dist/realtimeSessions/subscriptionAuthorization.js.map +1 -1
  169. package/dist/realtimeSessions/verificationWorkflow.d.ts +2 -0
  170. package/dist/realtimeSessions/verificationWorkflow.d.ts.map +1 -1
  171. package/dist/realtimeSessions/verificationWorkflow.js +1 -1
  172. package/dist/realtimeSessions/verificationWorkflow.js.map +1 -1
  173. package/dist/realtimeWidget/WidgetRouter.d.ts.map +1 -1
  174. package/dist/realtimeWidget/WidgetRouter.js +19 -3
  175. package/dist/realtimeWidget/WidgetRouter.js.map +1 -1
  176. package/dist/realtimeWidget/WidgetSessionService.d.ts +67 -7
  177. package/dist/realtimeWidget/WidgetSessionService.d.ts.map +1 -1
  178. package/dist/realtimeWidget/WidgetSessionService.js +201 -19
  179. package/dist/realtimeWidget/WidgetSessionService.js.map +1 -1
  180. package/dist/realtimeWidget/host-identity.d.ts +1 -1
  181. package/dist/realtimeWidget/host-identity.js +1 -1
  182. package/dist/realtimeWidget/widgetCore.d.ts +20 -1
  183. package/dist/realtimeWidget/widgetCore.d.ts.map +1 -1
  184. package/dist/realtimeWidget/widgetCore.js +17 -1
  185. package/dist/realtimeWidget/widgetCore.js.map +1 -1
  186. package/dist/realtimeWidget/widgetGuestElevation.d.ts +1 -1
  187. package/dist/realtimeWidget/widgetGuestElevation.d.ts.map +1 -1
  188. package/dist/realtimeWidget/widgetGuestElevation.js +2 -1
  189. package/dist/realtimeWidget/widgetGuestElevation.js.map +1 -1
  190. package/dist/resolvers/ActionResolver.d.ts +19 -1
  191. package/dist/resolvers/ActionResolver.d.ts.map +1 -1
  192. package/dist/resolvers/ActionResolver.js +63 -12
  193. package/dist/resolvers/ActionResolver.js.map +1 -1
  194. package/dist/resolvers/AdhocQueryResolver.d.ts.map +1 -1
  195. package/dist/resolvers/AdhocQueryResolver.js +1 -0
  196. package/dist/resolvers/AdhocQueryResolver.js.map +1 -1
  197. package/dist/resolvers/AgentRunStatusPublisher.d.ts +18 -4
  198. package/dist/resolvers/AgentRunStatusPublisher.d.ts.map +1 -1
  199. package/dist/resolvers/AgentRunStatusPublisher.js +32 -8
  200. package/dist/resolvers/AgentRunStatusPublisher.js.map +1 -1
  201. package/dist/resolvers/AgentSessionResolver.d.ts +10 -4
  202. package/dist/resolvers/AgentSessionResolver.d.ts.map +1 -1
  203. package/dist/resolvers/AgentSessionResolver.js +40 -17
  204. package/dist/resolvers/AgentSessionResolver.js.map +1 -1
  205. package/dist/resolvers/ArtifactFileResolver.d.ts +5 -1
  206. package/dist/resolvers/ArtifactFileResolver.d.ts.map +1 -1
  207. package/dist/resolvers/ArtifactFileResolver.js +9 -7
  208. package/dist/resolvers/ArtifactFileResolver.js.map +1 -1
  209. package/dist/resolvers/ConversationRunEventsResolver.d.ts +8 -4
  210. package/dist/resolvers/ConversationRunEventsResolver.d.ts.map +1 -1
  211. package/dist/resolvers/ConversationRunEventsResolver.js +6 -6
  212. package/dist/resolvers/ConversationRunEventsResolver.js.map +1 -1
  213. package/dist/resolvers/DatasetResolver.d.ts +24 -3
  214. package/dist/resolvers/DatasetResolver.d.ts.map +1 -1
  215. package/dist/resolvers/DatasetResolver.js +57 -8
  216. package/dist/resolvers/DatasetResolver.js.map +1 -1
  217. package/dist/resolvers/EntityCommunicationsResolver.d.ts +13 -1
  218. package/dist/resolvers/EntityCommunicationsResolver.d.ts.map +1 -1
  219. package/dist/resolvers/EntityCommunicationsResolver.js +50 -14
  220. package/dist/resolvers/EntityCommunicationsResolver.js.map +1 -1
  221. package/dist/resolvers/EntityRecordNameResolver.d.ts +2 -1
  222. package/dist/resolvers/EntityRecordNameResolver.d.ts.map +1 -1
  223. package/dist/resolvers/EntityRecordNameResolver.js +3 -2
  224. package/dist/resolvers/EntityRecordNameResolver.js.map +1 -1
  225. package/dist/resolvers/FetchEntityVectorsResolver.d.ts +46 -3
  226. package/dist/resolvers/FetchEntityVectorsResolver.d.ts.map +1 -1
  227. package/dist/resolvers/FetchEntityVectorsResolver.js +211 -50
  228. package/dist/resolvers/FetchEntityVectorsResolver.js.map +1 -1
  229. package/dist/resolvers/FileCategoryResolver.d.ts.map +1 -1
  230. package/dist/resolvers/FileCategoryResolver.js +1 -0
  231. package/dist/resolvers/FileCategoryResolver.js.map +1 -1
  232. package/dist/resolvers/FileResolver.d.ts +87 -11
  233. package/dist/resolvers/FileResolver.d.ts.map +1 -1
  234. package/dist/resolvers/FileResolver.js +292 -105
  235. package/dist/resolvers/FileResolver.js.map +1 -1
  236. package/dist/resolvers/GetDataContextDataResolver.d.ts +33 -7
  237. package/dist/resolvers/GetDataContextDataResolver.d.ts.map +1 -1
  238. package/dist/resolvers/GetDataContextDataResolver.js +117 -38
  239. package/dist/resolvers/GetDataContextDataResolver.js.map +1 -1
  240. package/dist/resolvers/GetDataResolver.d.ts.map +1 -1
  241. package/dist/resolvers/GetDataResolver.js +6 -3
  242. package/dist/resolvers/GetDataResolver.js.map +1 -1
  243. package/dist/resolvers/IntegrationDiscoveryResolver.d.ts.map +1 -1
  244. package/dist/resolvers/IntegrationDiscoveryResolver.js +49 -9
  245. package/dist/resolvers/IntegrationDiscoveryResolver.js.map +1 -1
  246. package/dist/resolvers/MergeRecordsResolver.d.ts +8 -1
  247. package/dist/resolvers/MergeRecordsResolver.d.ts.map +1 -1
  248. package/dist/resolvers/MergeRecordsResolver.js +31 -3
  249. package/dist/resolvers/MergeRecordsResolver.js.map +1 -1
  250. package/dist/resolvers/PipelineProgressResolver.d.ts +6 -0
  251. package/dist/resolvers/PipelineProgressResolver.d.ts.map +1 -1
  252. package/dist/resolvers/PipelineProgressResolver.js +5 -3
  253. package/dist/resolvers/PipelineProgressResolver.js.map +1 -1
  254. package/dist/resolvers/QueryResolver.d.ts +2 -0
  255. package/dist/resolvers/QueryResolver.d.ts.map +1 -1
  256. package/dist/resolvers/QueryResolver.js +33 -6
  257. package/dist/resolvers/QueryResolver.js.map +1 -1
  258. package/dist/resolvers/QuerySystemUserResolver.d.ts +2 -0
  259. package/dist/resolvers/QuerySystemUserResolver.d.ts.map +1 -1
  260. package/dist/resolvers/QuerySystemUserResolver.js +15 -6
  261. package/dist/resolvers/QuerySystemUserResolver.js.map +1 -1
  262. package/dist/resolvers/RSUResolver.d.ts.map +1 -1
  263. package/dist/resolvers/RSUResolver.js +12 -0
  264. package/dist/resolvers/RSUResolver.js.map +1 -1
  265. package/dist/resolvers/RealtimeClientSessionResolver.d.ts +18 -10
  266. package/dist/resolvers/RealtimeClientSessionResolver.d.ts.map +1 -1
  267. package/dist/resolvers/RealtimeClientSessionResolver.js +92 -54
  268. package/dist/resolvers/RealtimeClientSessionResolver.js.map +1 -1
  269. package/dist/resolvers/RemoteBrowserActionResolver.d.ts.map +1 -1
  270. package/dist/resolvers/RemoteBrowserActionResolver.js +11 -9
  271. package/dist/resolvers/RemoteBrowserActionResolver.js.map +1 -1
  272. package/dist/resolvers/RunAIAgentResolver.d.ts +56 -8
  273. package/dist/resolvers/RunAIAgentResolver.d.ts.map +1 -1
  274. package/dist/resolvers/RunAIAgentResolver.js +241 -67
  275. package/dist/resolvers/RunAIAgentResolver.js.map +1 -1
  276. package/dist/resolvers/RunTemplateResolver.d.ts.map +1 -1
  277. package/dist/resolvers/RunTemplateResolver.js +4 -4
  278. package/dist/resolvers/RunTemplateResolver.js.map +1 -1
  279. package/dist/resolvers/RunTestResolver.d.ts.map +1 -1
  280. package/dist/resolvers/RunTestResolver.js +3 -1
  281. package/dist/resolvers/RunTestResolver.js.map +1 -1
  282. package/dist/resolvers/SearchKnowledgeStreamResolver.d.ts +12 -1
  283. package/dist/resolvers/SearchKnowledgeStreamResolver.d.ts.map +1 -1
  284. package/dist/resolvers/SearchKnowledgeStreamResolver.js +15 -2
  285. package/dist/resolvers/SearchKnowledgeStreamResolver.js.map +1 -1
  286. package/dist/resolvers/SqlLoggingConfigResolver.d.ts.map +1 -1
  287. package/dist/resolvers/SqlLoggingConfigResolver.js +2 -3
  288. package/dist/resolvers/SqlLoggingConfigResolver.js.map +1 -1
  289. package/dist/resolvers/SyncDataResolver.d.ts.map +1 -1
  290. package/dist/resolvers/SyncDataResolver.js +4 -0
  291. package/dist/resolvers/SyncDataResolver.js.map +1 -1
  292. package/dist/resolvers/TaskGraphFrameResolver.d.ts +2 -1
  293. package/dist/resolvers/TaskGraphFrameResolver.d.ts.map +1 -1
  294. package/dist/resolvers/TaskGraphFrameResolver.js +28 -1
  295. package/dist/resolvers/TaskGraphFrameResolver.js.map +1 -1
  296. package/dist/resolvers/TestQuerySQLResolver.d.ts +24 -0
  297. package/dist/resolvers/TestQuerySQLResolver.d.ts.map +1 -1
  298. package/dist/resolvers/TestQuerySQLResolver.js +66 -13
  299. package/dist/resolvers/TestQuerySQLResolver.js.map +1 -1
  300. package/dist/resolvers/TransactionGroupResolver.d.ts +34 -2
  301. package/dist/resolvers/TransactionGroupResolver.d.ts.map +1 -1
  302. package/dist/resolvers/TransactionGroupResolver.js +150 -27
  303. package/dist/resolvers/TransactionGroupResolver.js.map +1 -1
  304. package/dist/resolvers/UserViewResolver.d.ts +1 -1
  305. package/dist/resolvers/UserViewResolver.d.ts.map +1 -1
  306. package/dist/resolvers/UserViewResolver.js +2 -1
  307. package/dist/resolvers/UserViewResolver.js.map +1 -1
  308. package/dist/resolvers/agent-run-data-guard.d.ts +31 -0
  309. package/dist/resolvers/agent-run-data-guard.d.ts.map +1 -0
  310. package/dist/resolvers/agent-run-data-guard.js +72 -0
  311. package/dist/resolvers/agent-run-data-guard.js.map +1 -0
  312. package/dist/rest/EntityCRUDHandler.d.ts +6 -0
  313. package/dist/rest/EntityCRUDHandler.d.ts.map +1 -1
  314. package/dist/rest/EntityCRUDHandler.js +19 -5
  315. package/dist/rest/EntityCRUDHandler.js.map +1 -1
  316. package/dist/rest/RESTEndpointHandler.d.ts.map +1 -1
  317. package/dist/rest/RESTEndpointHandler.js +8 -5
  318. package/dist/rest/RESTEndpointHandler.js.map +1 -1
  319. package/dist/rest/RealtimeRecordingHandler.d.ts +49 -0
  320. package/dist/rest/RealtimeRecordingHandler.d.ts.map +1 -0
  321. package/dist/rest/RealtimeRecordingHandler.js +383 -0
  322. package/dist/rest/RealtimeRecordingHandler.js.map +1 -0
  323. package/dist/rest/ViewOperationsHandler.d.ts +9 -1
  324. package/dist/rest/ViewOperationsHandler.d.ts.map +1 -1
  325. package/dist/rest/ViewOperationsHandler.js +43 -17
  326. package/dist/rest/ViewOperationsHandler.js.map +1 -1
  327. package/dist/rest/setupRESTEndpoints.d.ts +6 -0
  328. package/dist/rest/setupRESTEndpoints.d.ts.map +1 -1
  329. package/dist/rest/setupRESTEndpoints.js +6 -0
  330. package/dist/rest/setupRESTEndpoints.js.map +1 -1
  331. package/dist/serverExtensionReservedRoots.d.ts +12 -10
  332. package/dist/serverExtensionReservedRoots.d.ts.map +1 -1
  333. package/dist/serverExtensionReservedRoots.js +14 -17
  334. package/dist/serverExtensionReservedRoots.js.map +1 -1
  335. package/dist/serverRoleConfigUnits.d.ts +29 -0
  336. package/dist/serverRoleConfigUnits.d.ts.map +1 -0
  337. package/dist/serverRoleConfigUnits.js +33 -0
  338. package/dist/serverRoleConfigUnits.js.map +1 -0
  339. package/dist/services/MJStorageBlobStore.d.ts +8 -1
  340. package/dist/services/MJStorageBlobStore.d.ts.map +1 -1
  341. package/dist/services/MJStorageBlobStore.js +25 -19
  342. package/dist/services/MJStorageBlobStore.js.map +1 -1
  343. package/dist/services/StartTaskGraphDispatcher.js +2 -2
  344. package/dist/services/StartTaskGraphDispatcher.js.map +1 -1
  345. package/dist/services/TaskGraphActionRunner.d.ts +5 -3
  346. package/dist/services/TaskGraphActionRunner.d.ts.map +1 -1
  347. package/dist/services/TaskGraphActionRunner.js +27 -5
  348. package/dist/services/TaskGraphActionRunner.js.map +1 -1
  349. package/dist/services/TaskGraphAgentRunner.d.ts +14 -2
  350. package/dist/services/TaskGraphAgentRunner.d.ts.map +1 -1
  351. package/dist/services/TaskGraphAgentRunner.js +157 -9
  352. package/dist/services/TaskGraphAgentRunner.js.map +1 -1
  353. package/dist/services/TaskGraphContinuationDeliverer.d.ts +59 -2
  354. package/dist/services/TaskGraphContinuationDeliverer.d.ts.map +1 -1
  355. package/dist/services/TaskGraphContinuationDeliverer.js +626 -16
  356. package/dist/services/TaskGraphContinuationDeliverer.js.map +1 -1
  357. package/dist/sharedCache.d.ts.map +1 -1
  358. package/dist/sharedCache.js +3 -0
  359. package/dist/sharedCache.js.map +1 -1
  360. package/dist/types.d.ts +23 -1
  361. package/dist/types.d.ts.map +1 -1
  362. package/dist/types.js.map +1 -1
  363. package/package.json +99 -98
  364. package/src/__tests__/ActionResolver.fieldSecurity.test.ts +237 -0
  365. package/src/__tests__/AdhocQueryResolver.dialect.test.ts +137 -0
  366. package/src/__tests__/AgentRunStatusPublisher.test.ts +49 -0
  367. package/src/__tests__/AgentSessionResolver.ownership.test.ts +137 -0
  368. package/src/__tests__/CustomColumnPromoterOnePass.test.ts +15 -1
  369. package/src/__tests__/DatasetResolver.itemFilters.test.ts +201 -0
  370. package/src/__tests__/EntityCommunicationsResolver.actingUser.test.ts +151 -0
  371. package/src/__tests__/EntityRecordNameResolver.rowSecurity.test.ts +152 -0
  372. package/src/__tests__/EvolutionActsOnWhatItDetects.test.ts +113 -0
  373. package/src/__tests__/FetchEntityVectorsResolver.test.ts +369 -18
  374. package/src/__tests__/FileResolver.fieldSecurity.test.ts +82 -0
  375. package/src/__tests__/FileResolver.storageAccess.test.ts +562 -0
  376. package/src/__tests__/GetDataContextDataResolver.sqlItems.test.ts +416 -0
  377. package/src/__tests__/ListOperationsResolver.adhocScreen.test.ts +137 -0
  378. package/src/__tests__/LoginScreenEngine.test.ts +86 -0
  379. package/src/__tests__/MJStorageBlobStore.storageGate.test.ts +63 -0
  380. package/src/__tests__/PipelineProgressResolver.filter.test.ts +58 -0
  381. package/src/__tests__/QuerySystemUserResolver.fieldSecurity.test.ts +49 -0
  382. package/src/__tests__/RESTEndpointHandler.test.ts +25 -0
  383. package/src/__tests__/RealtimeClientSessionResolver.configTamper.test.ts +23 -3
  384. package/src/__tests__/RealtimeClientSessionResolver.test.ts +324 -46
  385. package/src/__tests__/RecordingAssemblyScheduler.test.ts +178 -0
  386. package/src/__tests__/RemoteBrowserActionResolver.ownership.test.ts +98 -0
  387. package/src/__tests__/ResolverBase.adhocReadScope.test.ts +133 -2
  388. package/src/__tests__/ResolverBase.balancedClause.test.ts +155 -0
  389. package/src/__tests__/ResolverBase.clauseUnscopedRead.test.ts +127 -0
  390. package/src/__tests__/ResolverBase.clientOldValues.test.ts +243 -7
  391. package/src/__tests__/ResolverBase.deleteReturnProjection.test.ts +52 -0
  392. package/src/__tests__/ResolverBase.entityMessageRelay.test.ts +176 -0
  393. package/src/__tests__/ResolverBase.filterEscaping.test.ts +5 -2
  394. package/src/__tests__/ResolverBase.includeBinaryFields.test.ts +23 -1
  395. package/src/__tests__/ResolverBase.publishStatusUpdate.test.ts +68 -0
  396. package/src/__tests__/ResolverBase.userViewCacheAndDedup.test.ts +130 -0
  397. package/src/__tests__/RunAIAgentResolver.branch.test.ts +69 -13
  398. package/src/__tests__/RunAIAgentResolver.conversationWriter.test.ts +210 -0
  399. package/src/__tests__/RunAIAgentResolver.fireAndForgetCompletion.test.ts +209 -0
  400. package/src/__tests__/RunAIAgentResolver.historyFrom.test.ts +20 -28
  401. package/src/__tests__/RunAIAgentResolver.runDataGuard.test.ts +264 -0
  402. package/src/__tests__/RunAIAgentResolver.serverReply.test.ts +358 -0
  403. package/src/__tests__/RunAIAgentResolver.stoppedPredecessor.test.ts +4 -1
  404. package/src/__tests__/RunTemplateResolver.test.ts +35 -2
  405. package/src/__tests__/RunViewResolver.cacheCheckForwarding.test.ts +11 -0
  406. package/src/__tests__/RunViewResolver.cacheCheckScreen.test.ts +165 -0
  407. package/src/__tests__/SearchKnowledgeStreamResolver.test.ts +161 -0
  408. package/src/__tests__/SessionJanitor.test.ts +365 -10
  409. package/src/__tests__/SessionManager.test.ts +122 -0
  410. package/src/__tests__/TaskGraphActionRunner.durablePayload.test.ts +25 -0
  411. package/src/__tests__/TaskGraphAgentRunner.test.ts +266 -0
  412. package/src/__tests__/TaskGraphContinuationDeliverer.test.ts +468 -23
  413. package/src/__tests__/TaskGraphFrameResolver.test.ts +22 -0
  414. package/src/__tests__/TestQuerySQLResolver.test.ts +274 -0
  415. package/src/__tests__/TransactionGroupResolver.fieldSecurity.test.ts +199 -0
  416. package/src/__tests__/TransactionGroupResolver.refusals.test.ts +98 -1
  417. package/src/__tests__/UpdateMJDataContextItem.oldValues.test.ts +220 -0
  418. package/src/__tests__/UserViewResolver.updateWhereClause.test.ts +15 -0
  419. package/src/__tests__/ViewOperationsHandler.clauseScreen.test.ts +157 -0
  420. package/src/__tests__/ViewOperationsHandler.test.ts +7 -2
  421. package/src/__tests__/WidgetSessionService.test.ts +583 -4
  422. package/src/__tests__/agent-run-data-guard.test.ts +130 -0
  423. package/src/__tests__/apiRootBoot.test.ts +202 -0
  424. package/src/__tests__/buildMagicLinkSessionUser.test.ts +96 -0
  425. package/src/__tests__/cacheBroadcastProviders.test.ts +403 -0
  426. package/src/__tests__/cacheInvalidationRecordData.test.ts +109 -0
  427. package/src/__tests__/conversationRunEvents.test.ts +56 -14
  428. package/src/__tests__/fieldSecurityFixtures.ts +91 -0
  429. package/src/__tests__/globalCors.test.ts +217 -0
  430. package/src/__tests__/localDevAuth.test.ts +196 -0
  431. package/src/__tests__/multiTenancy.postgresRewrite.test.ts +181 -0
  432. package/src/__tests__/multiTenancy.security.test.ts +51 -0
  433. package/src/__tests__/multiTenancy.test.ts +1 -1
  434. package/src/__tests__/orphanedConversationDetailReconciler.test.ts +10 -0
  435. package/src/__tests__/ownedRealtimeSession.test.ts +39 -0
  436. package/src/__tests__/pushStatusResolver.filter.test.ts +64 -1
  437. package/src/__tests__/realtimeRecordingHandler.test.ts +669 -0
  438. package/src/__tests__/realtimeSessions/RealtimeSessionEventsResolver.test.ts +29 -1
  439. package/src/__tests__/realtimeSessions/ownedRealtimeSession.anonymous.test.ts +31 -0
  440. package/src/__tests__/realtimeSessions/ownedRealtimeSession.namedScoped.test.ts +37 -0
  441. package/src/__tests__/realtimeSessions/sessionAccess.test.ts +146 -7
  442. package/src/__tests__/realtimeSessions/sessionConfigStamps.test.ts +1 -1
  443. package/src/__tests__/realtimeSessions/verificationWorkflow.test.ts +25 -4
  444. package/src/__tests__/resolverBase.cloudEventChangedFields.test.ts +157 -0
  445. package/src/__tests__/resolverBase.fieldSecurityTransport.test.ts +222 -0
  446. package/src/__tests__/resolverBase.fls.test.ts +146 -97
  447. package/src/__tests__/restTransport.fieldSecurity.test.ts +148 -0
  448. package/src/__tests__/runParamsTimeZone.test.ts +179 -0
  449. package/src/__tests__/runStreams.fieldSecurity.test.ts +100 -0
  450. package/src/__tests__/runViewProperties.test.ts +114 -0
  451. package/src/__tests__/scalabilityBenchmarks.test.ts +765 -0
  452. package/src/__tests__/scalabilityDatabaseIntegration.test.ts +207 -0
  453. package/src/__tests__/serverExtensionHostOverlay.test.ts +66 -0
  454. package/src/__tests__/serverExtensionReservedRoots.test.ts +11 -98
  455. package/src/__tests__/sessionUserClone.test.ts +5 -1
  456. package/src/__tests__/sharedCache.test.ts +5 -3
  457. package/src/__tests__/startupSweepOrder.test.ts +40 -0
  458. package/src/__tests__/unifiedAuth.test.ts +28 -0
  459. package/src/__tests__/widgetGuestElevation.test.ts +2 -0
  460. package/src/agentSessions/SessionJanitor.ts +369 -93
  461. package/src/agentSessions/SessionManager.ts +45 -10
  462. package/src/apiRoot.ts +78 -0
  463. package/src/auth/AuthProviderCatalogRouter.ts +29 -0
  464. package/src/auth/LoginScreenEngine.ts +50 -0
  465. package/src/auth/index.ts +57 -12
  466. package/src/auth/initializeProviders.ts +43 -1
  467. package/src/auth/localDevAuth.ts +528 -0
  468. package/src/auth/magicLink/MagicLinkKeys.ts +2 -2
  469. package/src/auth/magicLink/MagicLinkRouter.ts +3 -2
  470. package/src/auth/magicLink/types.ts +10 -0
  471. package/src/auth/sessionUserClone.ts +3 -2
  472. package/src/broadcast/BaseCacheInvalidationBroadcastProvider.ts +674 -0
  473. package/src/broadcast/index.ts +5 -0
  474. package/src/broadcast/providers/AWSIoTCoreBroadcastProvider.ts +180 -0
  475. package/src/broadcast/providers/AzureWebPubSubBroadcastProvider.ts +182 -0
  476. package/src/broadcast/providers/GCPPubSubBroadcastProvider.ts +169 -0
  477. package/src/broadcast/providers/SequencedRoleBucketBroadcastProvider.ts +57 -0
  478. package/src/cache.ts +2 -1
  479. package/src/config.ts +115 -5
  480. package/src/context.ts +32 -8
  481. package/src/coreRoutes.ts +181 -0
  482. package/src/generated/graphql-schemas/__mj.ts +3684 -284
  483. package/src/generic/CacheInvalidationResolver.ts +172 -60
  484. package/src/generic/OrphanedConversationDetailReconciler.ts +4 -2
  485. package/src/generic/PubSubManager.ts +54 -1
  486. package/src/generic/PushStatusResolver.ts +109 -12
  487. package/src/generic/ResolverBase.ts +540 -431
  488. package/src/generic/RunViewResolver.ts +97 -15
  489. package/src/generic/actionOutputFieldSecurity.ts +75 -0
  490. package/src/generic/fieldSecurityTransport.ts +62 -0
  491. package/src/globalCors.ts +71 -0
  492. package/src/index.ts +239 -238
  493. package/src/integration/CustomColumnPromoter.ts +6 -1
  494. package/src/logging/variablesLoggingMiddleware.ts +59 -3
  495. package/src/multiTenancy/index.ts +24 -2
  496. package/src/realtimeSessions/README.md +7 -3
  497. package/src/realtimeSessions/RealtimeSessionVerificationService.ts +2 -1
  498. package/src/realtimeSessions/RealtimeVerifyRouter.ts +2 -1
  499. package/src/realtimeSessions/RecordingAssemblyScheduler.ts +234 -0
  500. package/src/realtimeSessions/SessionVerificationPortsBase.ts +1 -0
  501. package/src/realtimeSessions/index.ts +2 -0
  502. package/src/realtimeSessions/ownedRealtimeSession.ts +69 -0
  503. package/src/realtimeSessions/sessionAccess.ts +183 -23
  504. package/src/realtimeSessions/sessionConfigStamps.ts +1 -1
  505. package/src/realtimeSessions/subscriptionAuthorization.ts +4 -2
  506. package/src/realtimeSessions/verificationWorkflow.ts +3 -1
  507. package/src/realtimeWidget/WidgetRouter.ts +22 -4
  508. package/src/realtimeWidget/WidgetSessionService.ts +252 -17
  509. package/src/realtimeWidget/host-identity.ts +1 -1
  510. package/src/realtimeWidget/widgetCore.ts +30 -1
  511. package/src/realtimeWidget/widgetGuestElevation.ts +2 -1
  512. package/src/resolvers/ActionResolver.ts +78 -15
  513. package/src/resolvers/AdhocQueryResolver.ts +1 -0
  514. package/src/resolvers/AgentRunStatusPublisher.ts +36 -8
  515. package/src/resolvers/AgentSessionResolver.ts +46 -18
  516. package/src/resolvers/ArtifactFileResolver.ts +9 -8
  517. package/src/resolvers/ConversationRunEventsResolver.ts +15 -11
  518. package/src/resolvers/DatasetResolver.ts +70 -10
  519. package/src/resolvers/EntityCommunicationsResolver.ts +56 -15
  520. package/src/resolvers/EntityRecordNameResolver.ts +3 -2
  521. package/src/resolvers/FetchEntityVectorsResolver.ts +248 -55
  522. package/src/resolvers/FileCategoryResolver.ts +1 -0
  523. package/src/resolvers/FileResolver.ts +334 -109
  524. package/src/resolvers/GetDataContextDataResolver.ts +139 -41
  525. package/src/resolvers/GetDataResolver.ts +6 -3
  526. package/src/resolvers/IntegrationDiscoveryResolver.ts +53 -9
  527. package/src/resolvers/MergeRecordsResolver.ts +23 -2
  528. package/src/resolvers/PipelineProgressResolver.ts +12 -3
  529. package/src/resolvers/QueryResolver.ts +31 -6
  530. package/src/resolvers/QuerySystemUserResolver.ts +15 -6
  531. package/src/resolvers/RSUResolver.ts +12 -0
  532. package/src/resolvers/RealtimeClientSessionResolver.ts +114 -66
  533. package/src/resolvers/RemoteBrowserActionResolver.ts +15 -10
  534. package/src/resolvers/RunAIAgentResolver.ts +296 -85
  535. package/src/resolvers/RunTemplateResolver.ts +6 -4
  536. package/src/resolvers/RunTestResolver.ts +3 -1
  537. package/src/resolvers/SearchKnowledgeStreamResolver.ts +19 -3
  538. package/src/resolvers/SqlLoggingConfigResolver.ts +2 -4
  539. package/src/resolvers/SyncDataResolver.ts +4 -0
  540. package/src/resolvers/TaskGraphFrameResolver.ts +33 -1
  541. package/src/resolvers/TestQuerySQLResolver.ts +69 -13
  542. package/src/resolvers/TransactionGroupResolver.ts +185 -30
  543. package/src/resolvers/UserViewResolver.ts +2 -1
  544. package/src/resolvers/__tests__/GetDataResolver.validation.test.ts +95 -0
  545. package/src/resolvers/__tests__/MergeRecordsResolver.test.ts +43 -0
  546. package/src/resolvers/agent-run-data-guard.ts +89 -0
  547. package/src/rest/EntityCRUDHandler.ts +22 -5
  548. package/src/rest/RESTEndpointHandler.ts +8 -5
  549. package/src/rest/RealtimeRecordingHandler.ts +467 -0
  550. package/src/rest/ViewOperationsHandler.ts +52 -18
  551. package/src/rest/setupRESTEndpoints.ts +6 -0
  552. package/src/serverExtensionReservedRoots.ts +14 -17
  553. package/src/serverRoleConfigUnits.ts +48 -0
  554. package/src/services/MJStorageBlobStore.ts +31 -30
  555. package/src/services/StartTaskGraphDispatcher.ts +2 -2
  556. package/src/services/TaskGraphActionRunner.ts +25 -5
  557. package/src/services/TaskGraphAgentRunner.ts +188 -11
  558. package/src/services/TaskGraphContinuationDeliverer.ts +738 -16
  559. package/src/sharedCache.ts +3 -0
  560. package/src/types.ts +23 -1
@@ -21,16 +21,26 @@ vi.mock('type-graphql', () => {
21
21
  return Object.fromEntries(exportNames.map((name) => [name, decoratorFactory]));
22
22
  });
23
23
 
24
- const runViewCalls: Array<{ params: RunViewParams; contextUser: UserInfo | undefined }> = [];
24
+ const runViewCalls: Array<{ params: RunViewParams; contextUser: UserInfo | undefined; provider: unknown }> = [];
25
+ /** Each RunViews call, as the list of entity names it batched. */
26
+ const runViewsBatches: string[][] = [];
25
27
  let runViewHandler: RunViewHandler = () => ({ Success: true, Results: [] });
26
28
 
27
29
  vi.mock('@memberjunction/core', async (importOriginal) => {
28
30
  const actual = await importOriginal<typeof import('@memberjunction/core')>();
29
31
  class MockRunView {
32
+ constructor(private readonly provider?: unknown) {}
33
+ static FromMetadataProvider(provider: unknown): MockRunView {
34
+ return new MockRunView(provider);
35
+ }
30
36
  async RunView(params: RunViewParams, contextUser?: UserInfo): Promise<ViewResult> {
31
- runViewCalls.push({ params, contextUser });
37
+ runViewCalls.push({ params, contextUser, provider: this.provider });
32
38
  return runViewHandler(params, contextUser);
33
39
  }
40
+ async RunViews(params: RunViewParams[], contextUser?: UserInfo): Promise<ViewResult[]> {
41
+ runViewsBatches.push(params.map((p) => p.EntityName ?? ''));
42
+ return Promise.all(params.map((p) => this.RunView(p, contextUser)));
43
+ }
34
44
  }
35
45
  return { ...actual, RunView: MockRunView, LogStatus: () => undefined, LogError: () => undefined };
36
46
  });
@@ -54,9 +64,13 @@ import type { AppContext } from '../types.js';
54
64
  const DOC_ID = 'DOC-1';
55
65
  const contextUser = new UserInfo();
56
66
 
67
+ /** Entity read and field-level security the external-index path checks, per test. */
68
+ const access = { canRead: true, deniedReadFields: new Set<string>() };
69
+
57
70
  const membersEntity = {
58
71
  Name: 'Members',
59
72
  PrimaryKeys: [{ Name: 'ID' }],
73
+ FirstPrimaryKey: { Name: 'ID' },
60
74
  Fields: [
61
75
  { Name: 'ID', IsNameField: false },
62
76
  { Name: 'FirstName', IsNameField: true },
@@ -64,11 +78,16 @@ const membersEntity = {
64
78
  { Name: 'Email', IsNameField: false },
65
79
  ],
66
80
  NameField: undefined,
81
+ GetUserPermisions: (user: UserInfo) => ({ CanRead: user === contextUser && access.canRead }),
82
+ GetDeniedReadFields: (user: UserInfo) => (user === contextUser ? access.deniedReadFields : new Set<string>()),
67
83
  } as unknown as EntityInfo;
68
84
 
69
85
  const orderLinesEntity = {
70
86
  Name: 'Order Lines',
71
87
  PrimaryKeys: [{ Name: 'OrderID' }, { Name: 'LineNo' }],
88
+ FirstPrimaryKey: { Name: 'OrderID' },
89
+ GetUserPermisions: (user: UserInfo) => ({ CanRead: user === contextUser && access.canRead }),
90
+ GetDeniedReadFields: () => new Set<string>(),
72
91
  Fields: [
73
92
  { Name: 'OrderID', IsNameField: false },
74
93
  { Name: 'LineNo', IsNameField: false },
@@ -123,6 +142,38 @@ const memberRows = [
123
142
  { ID: 'ccc', FirstName: 'Alan', LastName: null },
124
143
  ];
125
144
 
145
+ /**
146
+ * A fake `MJ: Entity Record Documents` view over `rows`: returns only the requested `Fields`, and
147
+ * only the rows named by an `ID IN ('a','b')` filter when one is given.
148
+ */
149
+ function erdView(rows: Array<Record<string, string>>) {
150
+ return (params: RunViewParams): ViewResult => {
151
+ const idList = /^ID IN \((.*)\)$/.exec(params.ExtraFilter ?? '');
152
+ const ids = idList ? new Set(idList[1].split(',').map((v) => v.trim().replace(/^'|'$/g, ''))) : undefined;
153
+ const picked = ids ? rows.filter((r) => ids.has(r.ID)) : rows;
154
+ const fields = params.Fields ?? Object.keys(rows[0] ?? {});
155
+ return { Success: true, Results: picked.map((r) => Object.fromEntries(fields.map((f) => [f, r[f]]))) };
156
+ };
157
+ }
158
+
159
+ /**
160
+ * A fake `Members` view of the rows the caller may read (row-level security already applied):
161
+ * returns the requested `Fields` of the rows named by an `ID IN ('a','b')` filter.
162
+ */
163
+ function readableMembersView(readableIDs: string[]) {
164
+ return (params: RunViewParams): ViewResult => {
165
+ const idList = /^ID IN \((.*)\)$/.exec(params.ExtraFilter ?? '');
166
+ const requested = idList ? idList[1].split(',').map((v) => v.trim().replace(/^'|'$/g, '')) : [];
167
+ const rows = requested.filter((id) => readableIDs.includes(id)).map((id) => ({ ID: id }));
168
+ return { Success: true, Results: rows.map((r) => Object.fromEntries((params.Fields ?? ['ID']).map((f) => [f, r[f as 'ID']]))) };
169
+ };
170
+ }
171
+
172
+ /** The VectorJSON fetches: Entity Record Documents views filtered by ID. */
173
+ function vectorFetches() {
174
+ return callsFor('MJ: Entity Record Documents').filter((c) => c.params.ExtraFilter?.startsWith('ID IN ('));
175
+ }
176
+
126
177
  function callsFor(entityName: string) {
127
178
  return runViewCalls.filter((c) => c.params.EntityName === entityName);
128
179
  }
@@ -134,6 +185,9 @@ describe('FetchEntityVectorsResolver', () => {
134
185
 
135
186
  beforeEach(() => {
136
187
  runViewCalls.length = 0;
188
+ runViewsBatches.length = 0;
189
+ access.canRead = true;
190
+ access.deniedReadFields = new Set<string>();
137
191
  });
138
192
 
139
193
  afterEach(() => {
@@ -150,7 +204,7 @@ describe('FetchEntityVectorsResolver', () => {
150
204
  it('reads VectorJSON for the document with the context user instead of calling QueryIndex', async () => {
151
205
  const { queryIndex } = useVectorDB(true);
152
206
  routeViews('Members', {
153
- 'MJ: Entity Record Documents': () => ({ Success: true, Results: storedRows }),
207
+ 'MJ: Entity Record Documents': erdView(storedRows),
154
208
  Members: () => ({ Success: true, Results: memberRows }),
155
209
  });
156
210
 
@@ -163,18 +217,53 @@ describe('FetchEntityVectorsResolver', () => {
163
217
  expect(result.Results.map((r) => r.ID)).toEqual(['ERD-1', 'ERD-2', 'ERD-3']);
164
218
  expect(result.Results.map((r) => r.Values)).toEqual([[0.1, 0.2], [0.3, 0.4], [0.5, 0.6]]);
165
219
 
166
- const [erdCall] = callsFor('MJ: Entity Record Documents');
167
- expect(erdCall.contextUser).toBe(contextUser);
168
- expect(erdCall.params.ExtraFilter).toBe(`EntityDocumentID='${DOC_ID}' AND VectorJSON IS NOT NULL`);
169
- expect(erdCall.params.Fields).toEqual(['ID', 'RecordID', 'VectorJSON']);
170
- expect(erdCall.params.ResultType).toBe('simple');
171
- expect(erdCall.params.IgnoreMaxRows).toBe(true);
220
+ const [listCall, vectorCall] = callsFor('MJ: Entity Record Documents');
221
+ expect(listCall.contextUser).toBe(contextUser);
222
+ expect(listCall.params.ExtraFilter).toBe(`EntityDocumentID='${DOC_ID}' AND VectorJSON IS NOT NULL`);
223
+ expect(listCall.params.Fields).toEqual(['ID', 'RecordID']);
224
+ expect(listCall.params.ResultType).toBe('simple');
225
+ expect(listCall.params.IgnoreMaxRows).toBe(true);
226
+ expect(vectorCall.contextUser).toBe(contextUser);
227
+ expect(vectorCall.params.ExtraFilter).toBe("ID IN ('ERD-1','ERD-2','ERD-3')");
228
+ expect(vectorCall.params.Fields).toEqual(['ID', 'VectorJSON']);
229
+ expect(vectorCall.params.ResultType).toBe('simple');
230
+ expect(vectorCall.params.IgnoreMaxRows).toBe(true);
231
+ });
232
+
233
+ it("REGRESSION: runs every view on the request's provider", async () => {
234
+ useVectorDB(true);
235
+ routeViews('Members', {
236
+ 'MJ: Entity Record Documents': erdView(storedRows),
237
+ Members: () => ({ Success: true, Results: memberRows }),
238
+ });
239
+ const context = appContext(membersEntity);
240
+
241
+ await new FetchEntityVectorsResolver().FetchEntityVectors(DOC_ID, undefined, undefined, context);
242
+
243
+ const requestProvider = context.providers[0].provider;
244
+ expect(callsFor('MJ: Entity Record Documents')).toHaveLength(2);
245
+ expect(runViewCalls.every((c) => c.provider === requestProvider)).toBe(true);
246
+ });
247
+
248
+ it('bypasses the server RunView cache for both Entity Record Documents views, so Fields is honoured', async () => {
249
+ useVectorDB(true);
250
+ routeViews('Members', {
251
+ 'MJ: Entity Record Documents': erdView(storedRows),
252
+ Members: () => ({ Success: true, Results: memberRows }),
253
+ });
254
+
255
+ await new FetchEntityVectorsResolver().FetchEntityVectors(DOC_ID, undefined, undefined, appContext(membersEntity));
256
+
257
+ const erdCalls = callsFor('MJ: Entity Record Documents');
258
+ expect(erdCalls).toHaveLength(2);
259
+ expect(erdCalls.map((c) => c.params.BypassCache)).toEqual([true, true]);
260
+ expect(callsFor('Members')[0].params.BypassCache).toBeUndefined();
172
261
  });
173
262
 
174
263
  it('labels each item with Entity, RecordID and the record name fields (RecordID matched case-insensitively)', async () => {
175
264
  useVectorDB(true);
176
265
  routeViews('Members', {
177
- 'MJ: Entity Record Documents': () => ({ Success: true, Results: storedRows }),
266
+ 'MJ: Entity Record Documents': erdView(storedRows),
178
267
  Members: () => ({ Success: true, Results: memberRows }),
179
268
  });
180
269
 
@@ -196,7 +285,7 @@ describe('FetchEntityVectorsResolver', () => {
196
285
  useVectorDB(true);
197
286
  const filter = "LastName = 'Hopper'";
198
287
  routeViews('Members', {
199
- 'MJ: Entity Record Documents': () => ({ Success: true, Results: storedRows }),
288
+ 'MJ: Entity Record Documents': erdView(storedRows),
200
289
  Members: (params) => ({
201
290
  Success: true,
202
291
  Results: params.ExtraFilter === filter ? memberRows.filter((r) => r.LastName === 'Hopper') : memberRows,
@@ -215,7 +304,7 @@ describe('FetchEntityVectorsResolver', () => {
215
304
  it('stops at maxRecords', async () => {
216
305
  useVectorDB(true);
217
306
  routeViews('Members', {
218
- 'MJ: Entity Record Documents': () => ({ Success: true, Results: storedRows }),
307
+ 'MJ: Entity Record Documents': erdView(storedRows),
219
308
  Members: () => ({ Success: true, Results: memberRows }),
220
309
  });
221
310
 
@@ -223,15 +312,74 @@ describe('FetchEntityVectorsResolver', () => {
223
312
 
224
313
  expect(result.Results.map((r) => r.ID)).toEqual(['ERD-1', 'ERD-2']);
225
314
  expect(result.TotalCount).toBe(2);
315
+ expect(vectorFetches().map((c) => c.params.ExtraFilter)).toEqual(["ID IN ('ERD-1','ERD-2')"]);
316
+ });
317
+
318
+ it('fetches VectorJSON only for vectors whose record passed the filter', async () => {
319
+ useVectorDB(true);
320
+ const filter = "LastName = 'Hopper'";
321
+ routeViews('Members', {
322
+ 'MJ: Entity Record Documents': erdView(storedRows),
323
+ Members: () => ({ Success: true, Results: memberRows.filter((r) => r.LastName === 'Hopper') }),
324
+ });
325
+
326
+ const result = await new FetchEntityVectorsResolver().FetchEntityVectors(DOC_ID, undefined, filter, appContext(membersEntity));
327
+
328
+ expect(result.Results.map((r) => r.Values)).toEqual([[0.3, 0.4]]);
329
+ expect(vectorFetches().map((c) => c.params.ExtraFilter)).toEqual(["ID IN ('ERD-2')"]);
330
+ });
331
+
332
+ it('runs no VectorJSON fetch when no stored vector belongs to a readable record', async () => {
333
+ useVectorDB(true);
334
+ routeViews('Members', {
335
+ 'MJ: Entity Record Documents': erdView(storedRows),
336
+ Members: () => ({ Success: true, Results: [] }),
337
+ });
338
+
339
+ const result = await new FetchEntityVectorsResolver().FetchEntityVectors(DOC_ID, undefined, undefined, appContext(membersEntity));
340
+
341
+ expect(result.Success).toBe(true);
342
+ expect(result.Results).toEqual([]);
343
+ expect(vectorFetches()).toHaveLength(0);
344
+ });
345
+
346
+ it('splits a large VectorJSON fetch into ID lists of at most 1000, sent in one RunViews batch', async () => {
347
+ useVectorDB(true);
348
+ const ids = Array.from({ length: 1500 }, (_, i) => `id-${i}`);
349
+ routeViews('Members', {
350
+ 'MJ: Entity Record Documents': erdView(ids.map((id, i) => ({ ID: `ERD-${i}`, RecordID: id, VectorJSON: `[${i}]` }))),
351
+ Members: () => ({ Success: true, Results: ids.map((id) => ({ ID: id, FirstName: id, LastName: null })) }),
352
+ });
353
+
354
+ const result = await new FetchEntityVectorsResolver().FetchEntityVectors(DOC_ID, 1500, undefined, appContext(membersEntity));
355
+
356
+ expect(result.TotalCount).toBe(1500);
357
+ expect(result.Results[1499]).toMatchObject({ ID: 'ERD-1499', Values: [1499] });
358
+ const fetchSizes = vectorFetches().map((c) => (c.params.ExtraFilter ?? '').split(',').length);
359
+ expect(fetchSizes).toEqual([1000, 500]);
360
+ expect(runViewsBatches[1]).toEqual(['MJ: Entity Record Documents', 'MJ: Entity Record Documents']);
361
+ });
362
+
363
+ it('returns an error result when the VectorJSON fetch fails', async () => {
364
+ useVectorDB(true);
365
+ const listing = erdView(storedRows);
366
+ routeViews('Members', {
367
+ 'MJ: Entity Record Documents': (params) =>
368
+ params.ExtraFilter?.startsWith('ID IN (') ? { Success: false, Results: [], ErrorMessage: 'deadlock' } : listing(params),
369
+ Members: () => ({ Success: true, Results: memberRows }),
370
+ });
371
+
372
+ const result = await new FetchEntityVectorsResolver().FetchEntityVectors(DOC_ID, undefined, undefined, appContext(membersEntity));
373
+
374
+ expect(result.Success).toBe(false);
375
+ expect(result.Results).toEqual([]);
376
+ expect(result.ErrorMessage).toBe('Failed to load vector values: deadlock');
226
377
  });
227
378
 
228
379
  it('matches composite-key records by the compact RecordID segment and falls back to NameField for labels', async () => {
229
380
  useVectorDB(true);
230
381
  routeViews('Order Lines', {
231
- 'MJ: Entity Record Documents': () => ({
232
- Success: true,
233
- Results: [{ ID: 'ERD-9', RecordID: 'OrderID|o-1||LineNo|2', VectorJSON: '[1,0]' }],
234
- }),
382
+ 'MJ: Entity Record Documents': erdView([{ ID: 'ERD-9', RecordID: 'OrderID|o-1||LineNo|2', VectorJSON: '[1,0]' }]),
235
383
  'Order Lines': () => ({ Success: true, Results: [{ OrderID: 'o-1', LineNo: 2, Description: 'Brie wheel' }] }),
236
384
  });
237
385
 
@@ -250,7 +398,7 @@ describe('FetchEntityVectorsResolver', () => {
250
398
  it('returns an error result when the entity records cannot be loaded (e.g. an invalid filter)', async () => {
251
399
  useVectorDB(true);
252
400
  routeViews('Members', {
253
- 'MJ: Entity Record Documents': () => ({ Success: true, Results: storedRows }),
401
+ 'MJ: Entity Record Documents': erdView(storedRows),
254
402
  Members: () => ({ Success: false, Results: [], ErrorMessage: 'bad filter' }),
255
403
  });
256
404
 
@@ -260,12 +408,55 @@ describe('FetchEntityVectorsResolver', () => {
260
408
  expect(result.Results).toEqual([]);
261
409
  expect(result.ErrorMessage).toBe('Failed to load "Members" records: bad filter');
262
410
  });
411
+
412
+ it('returns an error result when the stored vectors cannot be loaded', async () => {
413
+ useVectorDB(true);
414
+ routeViews('Members', {
415
+ 'MJ: Entity Record Documents': () => ({ Success: false, Results: [], ErrorMessage: 'timeout' }),
416
+ Members: () => ({ Success: true, Results: memberRows }),
417
+ });
418
+
419
+ const result = await new FetchEntityVectorsResolver().FetchEntityVectors(DOC_ID, undefined, undefined, appContext(membersEntity));
420
+
421
+ expect(result.Success).toBe(false);
422
+ expect(result.Results).toEqual([]);
423
+ expect(result.TotalCount).toBe(0);
424
+ expect(result.ErrorMessage).toBe('Failed to load stored vectors: timeout');
425
+ });
426
+
427
+ it('returns an error result, and runs neither view, when the document names an entity missing from metadata', async () => {
428
+ const { queryIndex } = useVectorDB(true);
429
+ routeViews('Retired Entity', {
430
+ 'MJ: Entity Record Documents': erdView(storedRows),
431
+ });
432
+
433
+ const result = await new FetchEntityVectorsResolver().FetchEntityVectors(DOC_ID, undefined, undefined, appContext(membersEntity));
434
+
435
+ expect(result.Success).toBe(false);
436
+ expect(result.Results).toEqual([]);
437
+ expect(result.ErrorMessage).toBe('Entity "Retired Entity" not found in metadata');
438
+ expect(queryIndex).not.toHaveBeenCalled();
439
+ expect(callsFor('MJ: Entity Record Documents')).toHaveLength(0);
440
+ expect(runViewsBatches).toHaveLength(0);
441
+ });
442
+
443
+ it('lists the entity records and the stored vectors in one RunViews batch, then fetches the vector values', async () => {
444
+ useVectorDB(true);
445
+ routeViews('Members', {
446
+ 'MJ: Entity Record Documents': erdView(storedRows),
447
+ Members: () => ({ Success: true, Results: memberRows }),
448
+ });
449
+
450
+ await new FetchEntityVectorsResolver().FetchEntityVectors(DOC_ID, undefined, undefined, appContext(membersEntity));
451
+
452
+ expect(runViewsBatches).toEqual([['Members', 'MJ: Entity Record Documents'], ['MJ: Entity Record Documents']]);
453
+ });
263
454
  });
264
455
 
265
456
  describe('index-keyed providers (Pinecone path, unchanged)', () => {
266
457
  it('queries the provider index by name and never reads Entity Record Documents', async () => {
267
458
  const { queryIndex } = useVectorDB(false);
268
- routeViews('Members', {});
459
+ routeViews('Members', { Members: readableMembersView(['a']) });
269
460
 
270
461
  const result = await new FetchEntityVectorsResolver().FetchEntityVectors(DOC_ID, 50, undefined, appContext(membersEntity));
271
462
 
@@ -279,7 +470,167 @@ describe('FetchEntityVectorsResolver', () => {
279
470
  expect(query.vector).toHaveLength(2);
280
471
  expect(result.Results).toEqual([{ ID: 'pc-1', Values: [0.5, 0.5], Metadata: JSON.stringify({ Entity: 'Members', RecordID: 'a' }) }]);
281
472
  expect(callsFor('MJ: Entity Record Documents')).toHaveLength(0);
473
+ });
474
+
475
+ it('removes metadata keys naming fields the caller may not read', async () => {
476
+ const { queryIndex } = useVectorDB(false);
477
+ queryIndex.mockResolvedValueOnce({
478
+ success: true,
479
+ data: { matches: [{ id: 'pc-1', values: [0.5, 0.5], metadata: { Entity: 'Members', RecordID: 'a', FirstName: 'Ada', Email: 'ada@secret.org' } }] },
480
+ });
481
+ access.deniedReadFields = new Set(['email']);
482
+ routeViews('Members', { Members: readableMembersView(['a']) });
483
+
484
+ const result = await new FetchEntityVectorsResolver().FetchEntityVectors(DOC_ID, 50, undefined, appContext(membersEntity));
485
+
486
+ expect(result.Success).toBe(true);
487
+ expect(JSON.parse(result.Results[0].Metadata)).toEqual({ Entity: 'Members', RecordID: 'a', FirstName: 'Ada' });
488
+ });
489
+
490
+ it('keeps every metadata key for a caller denied nothing', async () => {
491
+ const { queryIndex } = useVectorDB(false);
492
+ const metadata = { Entity: 'Members', RecordID: 'a', FirstName: 'Ada', Email: 'ada@secret.org' };
493
+ queryIndex.mockResolvedValueOnce({ success: true, data: { matches: [{ id: 'pc-1', values: [0.5, 0.5], metadata }] } });
494
+ routeViews('Members', { Members: readableMembersView(['a']) });
495
+
496
+ const result = await new FetchEntityVectorsResolver().FetchEntityVectors(DOC_ID, 50, undefined, appContext(membersEntity));
497
+
498
+ expect(JSON.parse(result.Results[0].Metadata)).toEqual(metadata);
499
+ });
500
+
501
+ it('refuses, without querying the index, a caller who may not read the source entity', async () => {
502
+ const { queryIndex } = useVectorDB(false);
503
+ access.canRead = false;
504
+ routeViews('Members', {});
505
+
506
+ const result = await new FetchEntityVectorsResolver().FetchEntityVectors(DOC_ID, 50, undefined, appContext(membersEntity));
507
+
508
+ expect(result.Success).toBe(false);
509
+ expect(result.Results).toEqual([]);
510
+ expect(result.ErrorMessage).toBe('Entity "Members" does not exist or you do not have permission to read it');
511
+ expect(queryIndex).not.toHaveBeenCalled();
512
+ });
513
+ });
514
+
515
+ describe('index-keyed providers: rows the caller cannot read', () => {
516
+ function matchesFor(recordIDs: Array<string | undefined>) {
517
+ return recordIDs.map((recordID, i) => ({
518
+ id: `pc-${i}`,
519
+ values: [i, i],
520
+ score: 1 - i / 10,
521
+ metadata: recordID === undefined ? { Entity: 'Members' } : { Entity: 'Members', RecordID: recordID },
522
+ }));
523
+ }
524
+
525
+ it('drops matches whose record the caller cannot read, keeping the order of the rest', async () => {
526
+ const { queryIndex } = useVectorDB(false);
527
+ queryIndex.mockResolvedValueOnce({ success: true, data: { matches: matchesFor(['ID|aaa', 'ID|bbb', 'ID|ccc']) } });
528
+ routeViews('Members', { Members: readableMembersView(['ccc', 'aaa']) });
529
+
530
+ const result = await new FetchEntityVectorsResolver().FetchEntityVectors(DOC_ID, 50, undefined, appContext(membersEntity));
531
+
532
+ expect(result.Success).toBe(true);
533
+ expect(result.Results.map((r) => r.ID)).toEqual(['pc-0', 'pc-2']);
534
+ expect(result.Results.map((r) => r.Values)).toEqual([[0, 0], [2, 2]]);
535
+ expect(result.TotalCount).toBe(2);
536
+ });
537
+
538
+ it("REGRESSION: runs every view, the key check included, on the request's provider", async () => {
539
+ const { queryIndex } = useVectorDB(false);
540
+ queryIndex.mockResolvedValueOnce({ success: true, data: { matches: matchesFor(['ID|aaa']) } });
541
+ routeViews('Members', { Members: readableMembersView(['aaa']) });
542
+ const context = appContext(membersEntity);
543
+
544
+ await new FetchEntityVectorsResolver().FetchEntityVectors(DOC_ID, 50, undefined, context);
545
+
546
+ const requestProvider = context.providers[0].provider;
547
+ expect(callsFor('Members')).toHaveLength(1);
548
+ expect(runViewCalls.length).toBeGreaterThan(1);
549
+ expect(runViewCalls.every((c) => c.provider === requestProvider)).toBe(true);
550
+ });
551
+
552
+ it('checks the matched keys with a simple view of the primary key, as the caller', async () => {
553
+ const { queryIndex } = useVectorDB(false);
554
+ queryIndex.mockResolvedValueOnce({ success: true, data: { matches: matchesFor(['ID|aaa', 'bbb']) } });
555
+ routeViews('Members', { Members: readableMembersView(['aaa', 'bbb']) });
556
+
557
+ const result = await new FetchEntityVectorsResolver().FetchEntityVectors(DOC_ID, 50, undefined, appContext(membersEntity));
558
+
559
+ expect(result.Results.map((r) => r.ID)).toEqual(['pc-0', 'pc-1']);
560
+ const [check] = callsFor('Members');
561
+ expect(check.contextUser).toBe(contextUser);
562
+ expect(check.params.ExtraFilter).toBe("ID IN ('aaa','bbb')");
563
+ expect(check.params.Fields).toEqual(['ID']);
564
+ expect(check.params.ResultType).toBe('simple');
565
+ expect(check.params.IgnoreMaxRows).toBe(true);
566
+ });
567
+
568
+ it('drops a match with no record ID, or one naming a field that is not the primary key, without querying for it', async () => {
569
+ const { queryIndex } = useVectorDB(false);
570
+ queryIndex.mockResolvedValueOnce({ success: true, data: { matches: matchesFor([undefined, "Salary|1' OR 1=1 --", 'ID|aaa']) } });
571
+ routeViews('Members', { Members: readableMembersView(['aaa']) });
572
+
573
+ const result = await new FetchEntityVectorsResolver().FetchEntityVectors(DOC_ID, 50, undefined, appContext(membersEntity));
574
+
575
+ expect(result.Results.map((r) => r.ID)).toEqual(['pc-2']);
576
+ expect(callsFor('Members').map((c) => c.params.ExtraFilter)).toEqual(["ID IN ('aaa')"]);
577
+ });
578
+
579
+ it('returns no matches and runs no check when none carries a usable record ID', async () => {
580
+ const { queryIndex } = useVectorDB(false);
581
+ queryIndex.mockResolvedValueOnce({ success: true, data: { matches: matchesFor([undefined]) } });
582
+ routeViews('Members', { Members: readableMembersView([]) });
583
+
584
+ const result = await new FetchEntityVectorsResolver().FetchEntityVectors(DOC_ID, 50, undefined, appContext(membersEntity));
585
+
586
+ expect(result.Success).toBe(true);
587
+ expect(result.Results).toEqual([]);
282
588
  expect(callsFor('Members')).toHaveLength(0);
283
589
  });
590
+
591
+ it('checks composite keys with one clause per record, using the real key columns', async () => {
592
+ const { queryIndex } = useVectorDB(false);
593
+ queryIndex.mockResolvedValueOnce({
594
+ success: true,
595
+ data: { matches: [
596
+ { id: 'pc-a', values: [1], metadata: { Entity: 'Order Lines', RecordID: 'OrderID|o-1||LineNo|2' } },
597
+ { id: 'pc-b', values: [2], metadata: { Entity: 'Order Lines', RecordID: 'OrderID|o-2||LineNo|1' } },
598
+ ] },
599
+ });
600
+ routeViews('Order Lines', {
601
+ 'Order Lines': () => ({ Success: true, Results: [{ OrderID: 'o-2', LineNo: 1 }] }),
602
+ });
603
+
604
+ const result = await new FetchEntityVectorsResolver().FetchEntityVectors(DOC_ID, 50, undefined, appContext(orderLinesEntity));
605
+
606
+ expect(result.Results.map((r) => r.ID)).toEqual(['pc-b']);
607
+ const [check] = callsFor('Order Lines');
608
+ expect(check.params.ExtraFilter).toBe("(OrderID='o-1' AND LineNo='2') OR (OrderID='o-2' AND LineNo='1')");
609
+ expect(check.params.Fields).toEqual(['OrderID', 'LineNo']);
610
+ });
611
+
612
+ it('splits a large key check into lists of at most 1000, sent in one RunViews batch', async () => {
613
+ const { queryIndex } = useVectorDB(false);
614
+ const ids = Array.from({ length: 1500 }, (_, i) => `id-${i}`);
615
+ queryIndex.mockResolvedValueOnce({ success: true, data: { matches: matchesFor(ids.map((id) => `ID|${id}`)) } });
616
+ routeViews('Members', { Members: readableMembersView(ids) });
617
+
618
+ const result = await new FetchEntityVectorsResolver().FetchEntityVectors(DOC_ID, 1500, undefined, appContext(membersEntity));
619
+
620
+ expect(result.TotalCount).toBe(1500);
621
+ expect(callsFor('Members').map((c) => (c.params.ExtraFilter ?? '').split(',').length)).toEqual([1000, 500]);
622
+ expect(runViewsBatches).toEqual([['Members', 'Members']]);
623
+ });
624
+
625
+ it('returns an error result when the key check fails', async () => {
626
+ const { queryIndex } = useVectorDB(false);
627
+ queryIndex.mockResolvedValueOnce({ success: true, data: { matches: matchesFor(['ID|aaa']) } });
628
+ routeViews('Members', { Members: () => ({ Success: false, Results: [], ErrorMessage: 'timeout' }) });
629
+
630
+ const result = await new FetchEntityVectorsResolver().FetchEntityVectors(DOC_ID, 50, undefined, appContext(membersEntity));
631
+
632
+ expect(result.Success).toBe(false);
633
+ expect(result.Results).toEqual([]);
634
+ });
284
635
  });
285
636
  });
@@ -0,0 +1,82 @@
1
+ /**
2
+ * Field-level security on FileResolver's own create path and responses.
3
+ *
4
+ * `CreateFile` applies its input with `NewRecord` rather than through the generated create
5
+ * resolver, so it gates the input itself: a restricted caller's key for a field it may not read,
6
+ * or a key that names no field, is refused with the same message, before any storage work. The
7
+ * `File` it returns (and `UploadStorageFile`'s) is the record just saved or reloaded, which is
8
+ * projected for the caller. The file entity here stands in with the fixture's restricted fields.
9
+ */
10
+ import 'reflect-metadata';
11
+ import { describe, it, expect, vi } from 'vitest';
12
+
13
+ const mocks = vi.hoisted(() => ({ createUploadUrl: vi.fn() }));
14
+
15
+ vi.mock('@memberjunction/server', () => {
16
+ const noopDecorator = () => () => undefined;
17
+ return {
18
+ Arg: noopDecorator, Ctx: noopDecorator, Field: noopDecorator, FieldResolver: noopDecorator, InputType: noopDecorator,
19
+ Mutation: noopDecorator, ObjectType: noopDecorator, PubSub: noopDecorator, Query: noopDecorator, Resolver: noopDecorator,
20
+ Root: noopDecorator, Int: {}, Float: {}, DeleteOptionsInput: class {},
21
+ };
22
+ });
23
+ vi.mock('../generated/generated.js', () => ({
24
+ CreateMJFileInput: class {},
25
+ UpdateMJFileInput: class {},
26
+ MJFile_: class {},
27
+ MJFileResolver: class {
28
+ protected GetUserFromPayload(userPayload: { userRecord?: object } | undefined) {
29
+ return userPayload?.userRecord;
30
+ }
31
+ },
32
+ }));
33
+ vi.mock('@memberjunction/ai-agents', () => ({ readRealtimeRecordingFile: vi.fn() }));
34
+ vi.mock('@memberjunction/graphql-dataprovider', () => ({ FieldMapper: class { MapFields(o: object) { return o; } } }));
35
+ vi.mock('../config.js', () => ({ configInfo: {} }));
36
+ vi.mock('../rest/MediaAccessKeys.js', () => ({ MediaAccessKeyManager: { Instance: {} } }));
37
+ vi.mock('../rest/UploadTokenManager.js', () => ({ UploadTokenManager: { Instance: { Consume: vi.fn() } } }));
38
+ vi.mock('@memberjunction/storage', async () => {
39
+ const actual = await vi.importActual<typeof import('@memberjunction/storage')>('@memberjunction/storage');
40
+ return { ...actual, createUploadUrl: mocks.createUploadUrl };
41
+ });
42
+
43
+ import type { PubSubEngine } from '@memberjunction/server';
44
+ import { FileResolver } from '../resolvers/FileResolver.js';
45
+ import type { AppContext } from '../types.js';
46
+ import { EmployeeEntity, FullWidthEmployeeRow, Intern, NoRecordChangesMetadata, RefusalFor, SECRET_SALARY } from './fieldSecurityFixtures.js';
47
+
48
+ function fileEntity() {
49
+ return { EntityInfo: EmployeeEntity(), CheckPermissions: vi.fn(), NewRecord: vi.fn(() => true), GetAll: () => FullWidthEmployeeRow() };
50
+ }
51
+
52
+ function contextFor(entity: ReturnType<typeof fileEntity>): AppContext {
53
+ const provider = { GetEntityObject: vi.fn(async () => entity), RunView: vi.fn(), RunViews: vi.fn() };
54
+ return { providers: [{ provider, type: 'Read-Write' }], userPayload: { userRecord: Intern() } } as unknown as AppContext;
55
+ }
56
+
57
+ describe('FileResolver.CreateFile: client input is gated for a restricted caller', () => {
58
+ it.each([
59
+ ['a denied field', 'Salary'],
60
+ ['a key that names no field', 'Bogus'],
61
+ ])('REGRESSION: refuses %s with the same message, before any storage work', async (_label, key) => {
62
+ const entity = fileEntity();
63
+ const input = { Name: 'new.pdf', [key]: 1 }; // keys of the stand-in entity
64
+ await expect(new FileResolver().CreateFile(input as never, contextFor(entity), {} as PubSubEngine))
65
+ .rejects.toThrow(RefusalFor(key));
66
+ expect(entity.NewRecord).not.toHaveBeenCalled();
67
+ expect(mocks.createUploadUrl).not.toHaveBeenCalled();
68
+ });
69
+ });
70
+
71
+ describe('FileResolver responses are projected for the caller', () => {
72
+ it('REGRESSION: the File returned never carries a denied value of a full-width record', () => {
73
+ const respond = (FileResolver.prototype as unknown as {
74
+ fileResponse(entity: ReturnType<typeof fileEntity>, user: unknown, provider: unknown): Record<string, unknown>;
75
+ }).fileResponse;
76
+
77
+ const file = respond.call(new FileResolver(), fileEntity(), Intern(), NoRecordChangesMetadata);
78
+
79
+ expect(JSON.stringify(file)).not.toContain(String(SECRET_SALARY));
80
+ expect(file).toMatchObject({ ID: 'emp-1', Name: 'Ada' });
81
+ });
82
+ });