@memberjunction/server 6.2.0-edge.3 → 6.2.0-edge.5

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 (543) hide show
  1. package/README.md +20 -0
  2. package/dist/.tsbuildinfo +1 -1
  3. package/dist/agentSessions/BridgeHostInstance.d.ts +25 -0
  4. package/dist/agentSessions/BridgeHostInstance.d.ts.map +1 -0
  5. package/dist/agentSessions/BridgeHostInstance.js +30 -0
  6. package/dist/agentSessions/BridgeHostInstance.js.map +1 -0
  7. package/dist/agentSessions/HostInstance.d.ts +75 -11
  8. package/dist/agentSessions/HostInstance.d.ts.map +1 -1
  9. package/dist/agentSessions/HostInstance.js +99 -28
  10. package/dist/agentSessions/HostInstance.js.map +1 -1
  11. package/dist/agentSessions/ReturningVisitorRecap.d.ts.map +1 -1
  12. package/dist/agentSessions/ReturningVisitorRecap.js +7 -3
  13. package/dist/agentSessions/ReturningVisitorRecap.js.map +1 -1
  14. package/dist/agentSessions/SessionJanitor.d.ts +236 -27
  15. package/dist/agentSessions/SessionJanitor.d.ts.map +1 -1
  16. package/dist/agentSessions/SessionJanitor.js +682 -54
  17. package/dist/agentSessions/SessionJanitor.js.map +1 -1
  18. package/dist/agentSessions/SessionManager.d.ts +48 -3
  19. package/dist/agentSessions/SessionManager.d.ts.map +1 -1
  20. package/dist/agentSessions/SessionManager.js +112 -9
  21. package/dist/agentSessions/SessionManager.js.map +1 -1
  22. package/dist/agentSessions/SessionRunIDVerifier.d.ts +63 -0
  23. package/dist/agentSessions/SessionRunIDVerifier.d.ts.map +1 -0
  24. package/dist/agentSessions/SessionRunIDVerifier.js +129 -0
  25. package/dist/agentSessions/SessionRunIDVerifier.js.map +1 -0
  26. package/dist/agentSessions/index.d.ts +2 -0
  27. package/dist/agentSessions/index.d.ts.map +1 -1
  28. package/dist/agentSessions/index.js +10 -3
  29. package/dist/agentSessions/index.js.map +1 -1
  30. package/dist/apiRoot.d.ts +32 -0
  31. package/dist/apiRoot.d.ts.map +1 -0
  32. package/dist/apiRoot.js +67 -0
  33. package/dist/apiRoot.js.map +1 -0
  34. package/dist/auth/AuthProviderCatalogRouter.d.ts.map +1 -1
  35. package/dist/auth/AuthProviderCatalogRouter.js +25 -0
  36. package/dist/auth/AuthProviderCatalogRouter.js.map +1 -1
  37. package/dist/auth/LoginScreenEngine.d.ts +22 -0
  38. package/dist/auth/LoginScreenEngine.d.ts.map +1 -0
  39. package/dist/auth/LoginScreenEngine.js +57 -0
  40. package/dist/auth/LoginScreenEngine.js.map +1 -0
  41. package/dist/auth/actionAuthorization.d.ts +40 -0
  42. package/dist/auth/actionAuthorization.d.ts.map +1 -0
  43. package/dist/auth/actionAuthorization.js +94 -0
  44. package/dist/auth/actionAuthorization.js.map +1 -0
  45. package/dist/auth/index.d.ts +1 -0
  46. package/dist/auth/index.d.ts.map +1 -1
  47. package/dist/auth/index.js +4 -10
  48. package/dist/auth/index.js.map +1 -1
  49. package/dist/auth/magicLink/MagicLinkRouter.d.ts.map +1 -1
  50. package/dist/auth/magicLink/MagicLinkRouter.js +3 -2
  51. package/dist/auth/magicLink/MagicLinkRouter.js.map +1 -1
  52. package/dist/auth/magicLink/MagicLinkService.d.ts.map +1 -1
  53. package/dist/auth/magicLink/MagicLinkService.js +6 -3
  54. package/dist/auth/magicLink/MagicLinkService.js.map +1 -1
  55. package/dist/auth/newUsers.d.ts +10 -0
  56. package/dist/auth/newUsers.d.ts.map +1 -1
  57. package/dist/auth/newUsers.js +80 -11
  58. package/dist/auth/newUsers.js.map +1 -1
  59. package/dist/config.d.ts +2514 -22
  60. package/dist/config.d.ts.map +1 -1
  61. package/dist/config.js +166 -4
  62. package/dist/config.js.map +1 -1
  63. package/dist/context.d.ts +8 -0
  64. package/dist/context.d.ts.map +1 -1
  65. package/dist/context.js +14 -11
  66. package/dist/context.js.map +1 -1
  67. package/dist/coreRoutes.d.ts +29 -0
  68. package/dist/coreRoutes.d.ts.map +1 -0
  69. package/dist/coreRoutes.js +142 -0
  70. package/dist/coreRoutes.js.map +1 -0
  71. package/dist/generated/graphql-schemas/__mj.d.ts +504 -3
  72. package/dist/generated/graphql-schemas/__mj.d.ts.map +1 -1
  73. package/dist/generated/graphql-schemas/__mj.js +2456 -7
  74. package/dist/generated/graphql-schemas/__mj.js.map +1 -1
  75. package/dist/generic/CacheInvalidationResolver.d.ts +43 -8
  76. package/dist/generic/CacheInvalidationResolver.d.ts.map +1 -1
  77. package/dist/generic/CacheInvalidationResolver.js +59 -19
  78. package/dist/generic/CacheInvalidationResolver.js.map +1 -1
  79. package/dist/generic/OrphanedConversationDetailReconciler.js +3 -3
  80. package/dist/generic/OrphanedConversationDetailReconciler.js.map +1 -1
  81. package/dist/generic/PushStatusResolver.js +1 -1
  82. package/dist/generic/PushStatusResolver.js.map +1 -1
  83. package/dist/generic/ResolverBase.d.ts +123 -48
  84. package/dist/generic/ResolverBase.d.ts.map +1 -1
  85. package/dist/generic/ResolverBase.js +293 -305
  86. package/dist/generic/ResolverBase.js.map +1 -1
  87. package/dist/generic/RunViewResolver.d.ts.map +1 -1
  88. package/dist/generic/RunViewResolver.js +16 -0
  89. package/dist/generic/RunViewResolver.js.map +1 -1
  90. package/dist/globalCors.d.ts +33 -0
  91. package/dist/globalCors.d.ts.map +1 -0
  92. package/dist/globalCors.js +58 -0
  93. package/dist/globalCors.js.map +1 -0
  94. package/dist/index.d.ts +17 -0
  95. package/dist/index.d.ts.map +1 -1
  96. package/dist/index.js +219 -278
  97. package/dist/index.js.map +1 -1
  98. package/dist/isolatedProvider.d.ts +20 -0
  99. package/dist/isolatedProvider.d.ts.map +1 -0
  100. package/dist/isolatedProvider.js +22 -0
  101. package/dist/isolatedProvider.js.map +1 -0
  102. package/dist/multiTenancy/index.d.ts.map +1 -1
  103. package/dist/multiTenancy/index.js +22 -2
  104. package/dist/multiTenancy/index.js.map +1 -1
  105. package/dist/realtimeProxy/RealtimeProxyServer.d.ts +44 -4
  106. package/dist/realtimeProxy/RealtimeProxyServer.d.ts.map +1 -1
  107. package/dist/realtimeProxy/RealtimeProxyServer.js +121 -16
  108. package/dist/realtimeProxy/RealtimeProxyServer.js.map +1 -1
  109. package/dist/realtimeProxy/RealtimeRelayTunnel.d.ts +171 -0
  110. package/dist/realtimeProxy/RealtimeRelayTunnel.d.ts.map +1 -0
  111. package/dist/realtimeProxy/RealtimeRelayTunnel.js +532 -0
  112. package/dist/realtimeProxy/RealtimeRelayTunnel.js.map +1 -0
  113. package/dist/realtimeSessions/RealtimeSessionEventService.d.ts +148 -0
  114. package/dist/realtimeSessions/RealtimeSessionEventService.d.ts.map +1 -0
  115. package/dist/realtimeSessions/RealtimeSessionEventService.js +191 -0
  116. package/dist/realtimeSessions/RealtimeSessionEventService.js.map +1 -0
  117. package/dist/realtimeSessions/RealtimeSessionVerificationService.d.ts +66 -0
  118. package/dist/realtimeSessions/RealtimeSessionVerificationService.d.ts.map +1 -0
  119. package/dist/realtimeSessions/RealtimeSessionVerificationService.js +139 -0
  120. package/dist/realtimeSessions/RealtimeSessionVerificationService.js.map +1 -0
  121. package/dist/realtimeSessions/RealtimeVerifyRouter.d.ts +38 -0
  122. package/dist/realtimeSessions/RealtimeVerifyRouter.d.ts.map +1 -0
  123. package/dist/realtimeSessions/RealtimeVerifyRouter.js +99 -0
  124. package/dist/realtimeSessions/RealtimeVerifyRouter.js.map +1 -0
  125. package/dist/realtimeSessions/RecordingAssemblyScheduler.d.ts +100 -0
  126. package/dist/realtimeSessions/RecordingAssemblyScheduler.d.ts.map +1 -0
  127. package/dist/realtimeSessions/RecordingAssemblyScheduler.js +173 -0
  128. package/dist/realtimeSessions/RecordingAssemblyScheduler.js.map +1 -0
  129. package/dist/realtimeSessions/SessionVerificationPortsBase.d.ts +61 -0
  130. package/dist/realtimeSessions/SessionVerificationPortsBase.d.ts.map +1 -0
  131. package/dist/realtimeSessions/SessionVerificationPortsBase.js +149 -0
  132. package/dist/realtimeSessions/SessionVerificationPortsBase.js.map +1 -0
  133. package/dist/realtimeSessions/SlidingWindowRateLimiter.d.ts +57 -0
  134. package/dist/realtimeSessions/SlidingWindowRateLimiter.d.ts.map +1 -0
  135. package/dist/realtimeSessions/SlidingWindowRateLimiter.js +69 -0
  136. package/dist/realtimeSessions/SlidingWindowRateLimiter.js.map +1 -0
  137. package/dist/realtimeSessions/index.d.ts +19 -0
  138. package/dist/realtimeSessions/index.d.ts.map +1 -0
  139. package/dist/realtimeSessions/index.js +19 -0
  140. package/dist/realtimeSessions/index.js.map +1 -0
  141. package/dist/realtimeSessions/ownedRealtimeSession.d.ts +30 -0
  142. package/dist/realtimeSessions/ownedRealtimeSession.d.ts.map +1 -0
  143. package/dist/realtimeSessions/ownedRealtimeSession.js +47 -0
  144. package/dist/realtimeSessions/ownedRealtimeSession.js.map +1 -0
  145. package/dist/realtimeSessions/sessionAccess.d.ts +92 -0
  146. package/dist/realtimeSessions/sessionAccess.d.ts.map +1 -0
  147. package/dist/realtimeSessions/sessionAccess.js +94 -0
  148. package/dist/realtimeSessions/sessionAccess.js.map +1 -0
  149. package/dist/realtimeSessions/sessionConfigStamps.d.ts +70 -0
  150. package/dist/realtimeSessions/sessionConfigStamps.d.ts.map +1 -0
  151. package/dist/realtimeSessions/sessionConfigStamps.js +93 -0
  152. package/dist/realtimeSessions/sessionConfigStamps.js.map +1 -0
  153. package/dist/realtimeSessions/subscriptionAuthorization.d.ts +65 -0
  154. package/dist/realtimeSessions/subscriptionAuthorization.d.ts.map +1 -0
  155. package/dist/realtimeSessions/subscriptionAuthorization.js +71 -0
  156. package/dist/realtimeSessions/subscriptionAuthorization.js.map +1 -0
  157. package/dist/realtimeSessions/verificationCore.d.ts +243 -0
  158. package/dist/realtimeSessions/verificationCore.d.ts.map +1 -0
  159. package/dist/realtimeSessions/verificationCore.js +356 -0
  160. package/dist/realtimeSessions/verificationCore.js.map +1 -0
  161. package/dist/realtimeSessions/verificationPages.d.ts +66 -0
  162. package/dist/realtimeSessions/verificationPages.d.ts.map +1 -0
  163. package/dist/realtimeSessions/verificationPages.js +110 -0
  164. package/dist/realtimeSessions/verificationPages.js.map +1 -0
  165. package/dist/realtimeSessions/verificationPolicy.d.ts +147 -0
  166. package/dist/realtimeSessions/verificationPolicy.d.ts.map +1 -0
  167. package/dist/realtimeSessions/verificationPolicy.js +249 -0
  168. package/dist/realtimeSessions/verificationPolicy.js.map +1 -0
  169. package/dist/realtimeSessions/verificationWorkflow.d.ts +263 -0
  170. package/dist/realtimeSessions/verificationWorkflow.d.ts.map +1 -0
  171. package/dist/realtimeSessions/verificationWorkflow.js +435 -0
  172. package/dist/realtimeSessions/verificationWorkflow.js.map +1 -0
  173. package/dist/realtimeWidget/widgetGuestElevation.d.ts +20 -2
  174. package/dist/realtimeWidget/widgetGuestElevation.d.ts.map +1 -1
  175. package/dist/realtimeWidget/widgetGuestElevation.js +31 -2
  176. package/dist/realtimeWidget/widgetGuestElevation.js.map +1 -1
  177. package/dist/resolvers/ActionResolver.d.ts +31 -5
  178. package/dist/resolvers/ActionResolver.d.ts.map +1 -1
  179. package/dist/resolvers/ActionResolver.js +75 -14
  180. package/dist/resolvers/ActionResolver.js.map +1 -1
  181. package/dist/resolvers/AdhocQueryResolver.d.ts +14 -1
  182. package/dist/resolvers/AdhocQueryResolver.d.ts.map +1 -1
  183. package/dist/resolvers/AdhocQueryResolver.js +21 -5
  184. package/dist/resolvers/AdhocQueryResolver.js.map +1 -1
  185. package/dist/resolvers/ArtifactFileResolver.d.ts +5 -1
  186. package/dist/resolvers/ArtifactFileResolver.d.ts.map +1 -1
  187. package/dist/resolvers/ArtifactFileResolver.js +9 -7
  188. package/dist/resolvers/ArtifactFileResolver.js.map +1 -1
  189. package/dist/resolvers/CacheStatsResolver.d.ts +27 -0
  190. package/dist/resolvers/CacheStatsResolver.d.ts.map +1 -1
  191. package/dist/resolvers/CacheStatsResolver.js +113 -2
  192. package/dist/resolvers/CacheStatsResolver.js.map +1 -1
  193. package/dist/resolvers/ClientToolRequestResolver.d.ts +4 -2
  194. package/dist/resolvers/ClientToolRequestResolver.d.ts.map +1 -1
  195. package/dist/resolvers/ClientToolRequestResolver.js +28 -10
  196. package/dist/resolvers/ClientToolRequestResolver.js.map +1 -1
  197. package/dist/resolvers/ConversationRunEventsResolver.d.ts +8 -4
  198. package/dist/resolvers/ConversationRunEventsResolver.d.ts.map +1 -1
  199. package/dist/resolvers/ConversationRunEventsResolver.js +6 -6
  200. package/dist/resolvers/ConversationRunEventsResolver.js.map +1 -1
  201. package/dist/resolvers/DatasetResolver.d.ts +24 -3
  202. package/dist/resolvers/DatasetResolver.d.ts.map +1 -1
  203. package/dist/resolvers/DatasetResolver.js +57 -8
  204. package/dist/resolvers/DatasetResolver.js.map +1 -1
  205. package/dist/resolvers/EntityRecordNameResolver.d.ts +2 -1
  206. package/dist/resolvers/EntityRecordNameResolver.d.ts.map +1 -1
  207. package/dist/resolvers/EntityRecordNameResolver.js +3 -2
  208. package/dist/resolvers/EntityRecordNameResolver.js.map +1 -1
  209. package/dist/resolvers/FetchEntityVectorsResolver.d.ts +27 -1
  210. package/dist/resolvers/FetchEntityVectorsResolver.d.ts.map +1 -1
  211. package/dist/resolvers/FetchEntityVectorsResolver.js +148 -2
  212. package/dist/resolvers/FetchEntityVectorsResolver.js.map +1 -1
  213. package/dist/resolvers/FileResolver.d.ts +82 -11
  214. package/dist/resolvers/FileResolver.d.ts.map +1 -1
  215. package/dist/resolvers/FileResolver.js +279 -101
  216. package/dist/resolvers/FileResolver.js.map +1 -1
  217. package/dist/resolvers/GetDataContextDataResolver.d.ts +33 -7
  218. package/dist/resolvers/GetDataContextDataResolver.d.ts.map +1 -1
  219. package/dist/resolvers/GetDataContextDataResolver.js +117 -38
  220. package/dist/resolvers/GetDataContextDataResolver.js.map +1 -1
  221. package/dist/resolvers/GetDataResolver.d.ts.map +1 -1
  222. package/dist/resolvers/GetDataResolver.js +6 -3
  223. package/dist/resolvers/GetDataResolver.js.map +1 -1
  224. package/dist/resolvers/HumanHandoffResolver.d.ts.map +1 -1
  225. package/dist/resolvers/HumanHandoffResolver.js +7 -1
  226. package/dist/resolvers/HumanHandoffResolver.js.map +1 -1
  227. package/dist/resolvers/IntegrationDiscoveryResolver.d.ts.map +1 -1
  228. package/dist/resolvers/IntegrationDiscoveryResolver.js +4 -2
  229. package/dist/resolvers/IntegrationDiscoveryResolver.js.map +1 -1
  230. package/dist/resolvers/MCPResolver.d.ts.map +1 -1
  231. package/dist/resolvers/MCPResolver.js +9 -4
  232. package/dist/resolvers/MCPResolver.js.map +1 -1
  233. package/dist/resolvers/QueryResolver.d.ts +2 -0
  234. package/dist/resolvers/QueryResolver.d.ts.map +1 -1
  235. package/dist/resolvers/QueryResolver.js +13 -0
  236. package/dist/resolvers/QueryResolver.js.map +1 -1
  237. package/dist/resolvers/RealtimeBridgeResolver.d.ts +54 -2
  238. package/dist/resolvers/RealtimeBridgeResolver.d.ts.map +1 -1
  239. package/dist/resolvers/RealtimeBridgeResolver.js +155 -10
  240. package/dist/resolvers/RealtimeBridgeResolver.js.map +1 -1
  241. package/dist/resolvers/RealtimeClientSessionResolver.d.ts +157 -33
  242. package/dist/resolvers/RealtimeClientSessionResolver.d.ts.map +1 -1
  243. package/dist/resolvers/RealtimeClientSessionResolver.js +404 -116
  244. package/dist/resolvers/RealtimeClientSessionResolver.js.map +1 -1
  245. package/dist/resolvers/RealtimeSessionEventsResolver.d.ts +61 -0
  246. package/dist/resolvers/RealtimeSessionEventsResolver.d.ts.map +1 -0
  247. package/dist/resolvers/RealtimeSessionEventsResolver.js +130 -0
  248. package/dist/resolvers/RealtimeSessionEventsResolver.js.map +1 -0
  249. package/dist/resolvers/RealtimeSessionVerificationResolver.d.ts +85 -0
  250. package/dist/resolvers/RealtimeSessionVerificationResolver.d.ts.map +1 -0
  251. package/dist/resolvers/RealtimeSessionVerificationResolver.js +188 -0
  252. package/dist/resolvers/RealtimeSessionVerificationResolver.js.map +1 -0
  253. package/dist/resolvers/RemoteBrowserActionResolver.d.ts +2 -0
  254. package/dist/resolvers/RemoteBrowserActionResolver.d.ts.map +1 -1
  255. package/dist/resolvers/RemoteBrowserActionResolver.js +5 -1
  256. package/dist/resolvers/RemoteBrowserActionResolver.js.map +1 -1
  257. package/dist/resolvers/RunAIAgentResolver.d.ts +10 -0
  258. package/dist/resolvers/RunAIAgentResolver.d.ts.map +1 -1
  259. package/dist/resolvers/RunAIAgentResolver.js +100 -14
  260. package/dist/resolvers/RunAIAgentResolver.js.map +1 -1
  261. package/dist/resolvers/RunTemplateResolver.d.ts.map +1 -1
  262. package/dist/resolvers/RunTemplateResolver.js +12 -11
  263. package/dist/resolvers/RunTemplateResolver.js.map +1 -1
  264. package/dist/resolvers/SearchKnowledgeStreamResolver.d.ts +12 -1
  265. package/dist/resolvers/SearchKnowledgeStreamResolver.d.ts.map +1 -1
  266. package/dist/resolvers/SearchKnowledgeStreamResolver.js +15 -2
  267. package/dist/resolvers/SearchKnowledgeStreamResolver.js.map +1 -1
  268. package/dist/resolvers/SqlLoggingConfigResolver.d.ts.map +1 -1
  269. package/dist/resolvers/SqlLoggingConfigResolver.js +2 -3
  270. package/dist/resolvers/SqlLoggingConfigResolver.js.map +1 -1
  271. package/dist/resolvers/SyncDataResolver.d.ts.map +1 -1
  272. package/dist/resolvers/SyncDataResolver.js +6 -2
  273. package/dist/resolvers/SyncDataResolver.js.map +1 -1
  274. package/dist/resolvers/SyncRolesUsersResolver.d.ts +2 -0
  275. package/dist/resolvers/SyncRolesUsersResolver.d.ts.map +1 -1
  276. package/dist/resolvers/SyncRolesUsersResolver.js +23 -6
  277. package/dist/resolvers/SyncRolesUsersResolver.js.map +1 -1
  278. package/dist/resolvers/TaskGraphFrameResolver.d.ts +2 -1
  279. package/dist/resolvers/TaskGraphFrameResolver.d.ts.map +1 -1
  280. package/dist/resolvers/TaskGraphFrameResolver.js +28 -1
  281. package/dist/resolvers/TaskGraphFrameResolver.js.map +1 -1
  282. package/dist/resolvers/TestQuerySQLResolver.d.ts +24 -0
  283. package/dist/resolvers/TestQuerySQLResolver.d.ts.map +1 -1
  284. package/dist/resolvers/TestQuerySQLResolver.js +65 -13
  285. package/dist/resolvers/TestQuerySQLResolver.js.map +1 -1
  286. package/dist/resolvers/TransactionGroupResolver.d.ts +19 -2
  287. package/dist/resolvers/TransactionGroupResolver.d.ts.map +1 -1
  288. package/dist/resolvers/TransactionGroupResolver.js +102 -24
  289. package/dist/resolvers/TransactionGroupResolver.js.map +1 -1
  290. package/dist/resolvers/UserAvatarResolver.d.ts +16 -0
  291. package/dist/resolvers/UserAvatarResolver.d.ts.map +1 -0
  292. package/dist/resolvers/UserAvatarResolver.js +124 -0
  293. package/dist/resolvers/UserAvatarResolver.js.map +1 -0
  294. package/dist/resolvers/UserResolver.d.ts.map +1 -1
  295. package/dist/resolvers/UserResolver.js +2 -17
  296. package/dist/resolvers/UserResolver.js.map +1 -1
  297. package/dist/resolvers/agent-run-data-guard.d.ts +31 -0
  298. package/dist/resolvers/agent-run-data-guard.d.ts.map +1 -0
  299. package/dist/resolvers/agent-run-data-guard.js +72 -0
  300. package/dist/resolvers/agent-run-data-guard.js.map +1 -0
  301. package/dist/resolvers/agentVisionConsentAudit.d.ts +37 -0
  302. package/dist/resolvers/agentVisionConsentAudit.d.ts.map +1 -0
  303. package/dist/resolvers/agentVisionConsentAudit.js +63 -0
  304. package/dist/resolvers/agentVisionConsentAudit.js.map +1 -0
  305. package/dist/resolvers/avatarInputValidation.d.ts +40 -0
  306. package/dist/resolvers/avatarInputValidation.d.ts.map +1 -0
  307. package/dist/resolvers/avatarInputValidation.js +97 -0
  308. package/dist/resolvers/avatarInputValidation.js.map +1 -0
  309. package/dist/resolvers/currentUserRoles.d.ts +29 -0
  310. package/dist/resolvers/currentUserRoles.d.ts.map +1 -1
  311. package/dist/resolvers/currentUserRoles.js +30 -0
  312. package/dist/resolvers/currentUserRoles.js.map +1 -1
  313. package/dist/resolvers/meetings/meetingMutations.d.ts +6 -0
  314. package/dist/resolvers/meetings/meetingMutations.d.ts.map +1 -1
  315. package/dist/resolvers/meetings/meetingMutations.js +21 -0
  316. package/dist/resolvers/meetings/meetingMutations.js.map +1 -1
  317. package/dist/rest/RealtimeRecordingHandler.d.ts +49 -0
  318. package/dist/rest/RealtimeRecordingHandler.d.ts.map +1 -0
  319. package/dist/rest/RealtimeRecordingHandler.js +383 -0
  320. package/dist/rest/RealtimeRecordingHandler.js.map +1 -0
  321. package/dist/rest/ViewOperationsHandler.d.ts +3 -1
  322. package/dist/rest/ViewOperationsHandler.d.ts.map +1 -1
  323. package/dist/rest/ViewOperationsHandler.js +21 -12
  324. package/dist/rest/ViewOperationsHandler.js.map +1 -1
  325. package/dist/rest/setupRESTEndpoints.d.ts +6 -0
  326. package/dist/rest/setupRESTEndpoints.d.ts.map +1 -1
  327. package/dist/rest/setupRESTEndpoints.js +6 -0
  328. package/dist/rest/setupRESTEndpoints.js.map +1 -1
  329. package/dist/serverExtensionReservedRoots.d.ts +12 -10
  330. package/dist/serverExtensionReservedRoots.d.ts.map +1 -1
  331. package/dist/serverExtensionReservedRoots.js +14 -17
  332. package/dist/serverExtensionReservedRoots.js.map +1 -1
  333. package/dist/services/MJStorageBlobStore.d.ts +8 -1
  334. package/dist/services/MJStorageBlobStore.d.ts.map +1 -1
  335. package/dist/services/MJStorageBlobStore.js +25 -19
  336. package/dist/services/MJStorageBlobStore.js.map +1 -1
  337. package/dist/services/StartTaskGraphDispatcher.js +2 -2
  338. package/dist/services/StartTaskGraphDispatcher.js.map +1 -1
  339. package/dist/services/TaskGraphActionRunner.d.ts +5 -3
  340. package/dist/services/TaskGraphActionRunner.d.ts.map +1 -1
  341. package/dist/services/TaskGraphActionRunner.js +27 -5
  342. package/dist/services/TaskGraphActionRunner.js.map +1 -1
  343. package/dist/services/TaskGraphAgentRunner.d.ts +14 -2
  344. package/dist/services/TaskGraphAgentRunner.d.ts.map +1 -1
  345. package/dist/services/TaskGraphAgentRunner.js +157 -9
  346. package/dist/services/TaskGraphAgentRunner.js.map +1 -1
  347. package/dist/services/TaskGraphContinuationDeliverer.d.ts +57 -4
  348. package/dist/services/TaskGraphContinuationDeliverer.d.ts.map +1 -1
  349. package/dist/services/TaskGraphContinuationDeliverer.js +607 -18
  350. package/dist/services/TaskGraphContinuationDeliverer.js.map +1 -1
  351. package/dist/sharedCache.d.ts +105 -0
  352. package/dist/sharedCache.d.ts.map +1 -0
  353. package/dist/sharedCache.js +279 -0
  354. package/dist/sharedCache.js.map +1 -0
  355. package/dist/types.d.ts +6 -0
  356. package/dist/types.d.ts.map +1 -1
  357. package/dist/types.js.map +1 -1
  358. package/package.json +99 -96
  359. package/src/__tests__/ActionResolver.authorization.test.ts +353 -0
  360. package/src/__tests__/AdhocQueryResolver.sqlAuthorizer.test.ts +104 -0
  361. package/src/__tests__/BridgeHostInstance.test.ts +232 -0
  362. package/src/__tests__/ClientToolRequestResolver.test.ts +116 -0
  363. package/src/__tests__/DatasetResolver.itemFilters.test.ts +201 -0
  364. package/src/__tests__/EntityRecordNameResolver.rowSecurity.test.ts +152 -0
  365. package/src/__tests__/FetchEntityVectorsResolver.test.ts +433 -0
  366. package/src/__tests__/FileResolver.storageAccess.test.ts +562 -0
  367. package/src/__tests__/GetDataContextDataResolver.sqlItems.test.ts +416 -0
  368. package/src/__tests__/HostInstance.test.ts +84 -0
  369. package/src/__tests__/HumanHandoffResolver.test.ts +17 -0
  370. package/src/__tests__/ListOperationsResolver.adhocScreen.test.ts +137 -0
  371. package/src/__tests__/LoginScreenEngine.test.ts +86 -0
  372. package/src/__tests__/MJStorageBlobStore.storageGate.test.ts +63 -0
  373. package/src/__tests__/MeetingResolver.test.ts +75 -1
  374. package/src/__tests__/RealtimeBridgeResolver.test.ts +415 -6
  375. package/src/__tests__/RealtimeClientSessionResolver.configTamper.test.ts +383 -0
  376. package/src/__tests__/RealtimeClientSessionResolver.test.ts +682 -58
  377. package/src/__tests__/RealtimeProxyServer.test.ts +16 -1
  378. package/src/__tests__/RealtimeRelay.test.ts +876 -0
  379. package/src/__tests__/RecordingAssemblyScheduler.test.ts +178 -0
  380. package/src/__tests__/RemoteBrowserActionResolver.goalRunLink.test.ts +129 -0
  381. package/src/__tests__/ResolverBase.adhocReadScope.test.ts +133 -2
  382. package/src/__tests__/ResolverBase.aggregateScreen.test.ts +174 -0
  383. package/src/__tests__/ResolverBase.balancedClause.test.ts +155 -0
  384. package/src/__tests__/ResolverBase.clauseUnscopedRead.test.ts +127 -0
  385. package/src/__tests__/ResolverBase.clientOldValues.test.ts +212 -6
  386. package/src/__tests__/ResolverBase.filterEscaping.test.ts +5 -2
  387. package/src/__tests__/ReturningVisitorRecap.test.ts +115 -0
  388. package/src/__tests__/RunAIAgentResolver.branch.test.ts +170 -0
  389. package/src/__tests__/RunAIAgentResolver.historyFrom.test.ts +6 -4
  390. package/src/__tests__/RunAIAgentResolver.runDataGuard.test.ts +258 -0
  391. package/src/__tests__/RunAIAgentResolver.stoppedPredecessor.test.ts +166 -0
  392. package/src/__tests__/RunTemplateResolver.test.ts +132 -0
  393. package/src/__tests__/RunViewResolver.cacheCheckForwarding.test.ts +63 -0
  394. package/src/__tests__/RunViewResolver.cacheCheckScreen.test.ts +165 -0
  395. package/src/__tests__/SearchKnowledgeStreamResolver.test.ts +161 -0
  396. package/src/__tests__/SessionJanitor.test.ts +1203 -21
  397. package/src/__tests__/SessionManager.test.ts +240 -17
  398. package/src/__tests__/SessionRunIDVerifier.test.ts +251 -0
  399. package/src/__tests__/SyncRolesUsersResolver.userRoles.test.ts +97 -0
  400. package/src/__tests__/TaskGraphActionRunner.durablePayload.test.ts +25 -0
  401. package/src/__tests__/TaskGraphAgentRunner.test.ts +266 -0
  402. package/src/__tests__/TaskGraphContinuationDeliverer.test.ts +403 -22
  403. package/src/__tests__/TaskGraphFrameResolver.test.ts +22 -0
  404. package/src/__tests__/TestQuerySQLResolver.test.ts +274 -0
  405. package/src/__tests__/TransactionGroupResolver.refusals.test.ts +98 -1
  406. package/src/__tests__/UpdateMJDataContextItem.oldValues.test.ts +220 -0
  407. package/src/__tests__/UserAvatarResolver.test.ts +255 -0
  408. package/src/__tests__/UserResolver.currentUser.test.ts +16 -1
  409. package/src/__tests__/ViewOperationsHandler.clauseScreen.test.ts +157 -0
  410. package/src/__tests__/actionAuthorization.test.ts +130 -0
  411. package/src/__tests__/agent-run-data-guard.test.ts +130 -0
  412. package/src/__tests__/agentVisionConsentAudit.test.ts +118 -0
  413. package/src/__tests__/apiRootBoot.test.ts +202 -0
  414. package/src/__tests__/avatarInputValidation.test.ts +160 -0
  415. package/src/__tests__/cacheInvalidationRecordData.test.ts +64 -0
  416. package/src/__tests__/conversationRunEvents.test.ts +56 -14
  417. package/src/__tests__/globalCors.test.ts +217 -0
  418. package/src/__tests__/isolatedProviderTransactions.test.ts +169 -0
  419. package/src/__tests__/magicLinkService.consume.test.ts +6 -0
  420. package/src/__tests__/magicLinkService.provisionLog.test.ts +5 -0
  421. package/src/__tests__/multiTenancy.postgresRewrite.test.ts +181 -0
  422. package/src/__tests__/multiTenancy.security.test.ts +51 -0
  423. package/src/__tests__/multiTenancy.test.ts +1 -1
  424. package/src/__tests__/newUsers.template.test.ts +151 -0
  425. package/src/__tests__/newUsers.test.ts +74 -5
  426. package/src/__tests__/orphanedConversationDetailReconciler.test.ts +17 -2
  427. package/src/__tests__/ownedRealtimeSession.test.ts +39 -0
  428. package/src/__tests__/pushStatusResolver.filter.test.ts +7 -0
  429. package/src/__tests__/realtimeRecordingHandler.test.ts +652 -0
  430. package/src/__tests__/realtimeSessions/RealtimeSessionEventService.test.ts +201 -0
  431. package/src/__tests__/realtimeSessions/RealtimeSessionEventsResolver.test.ts +215 -0
  432. package/src/__tests__/realtimeSessions/RealtimeSessionVerificationResolver.test.ts +102 -0
  433. package/src/__tests__/realtimeSessions/RealtimeSessionVerificationService.test.ts +349 -0
  434. package/src/__tests__/realtimeSessions/RealtimeVerifyRouter.test.ts +131 -0
  435. package/src/__tests__/realtimeSessions/SlidingWindowRateLimiter.test.ts +56 -0
  436. package/src/__tests__/realtimeSessions/helpers.ts +4 -0
  437. package/src/__tests__/realtimeSessions/sessionAccess.test.ts +129 -0
  438. package/src/__tests__/realtimeSessions/sessionConfigStamps.test.ts +124 -0
  439. package/src/__tests__/realtimeSessions/verificationCore.test.ts +355 -0
  440. package/src/__tests__/realtimeSessions/verificationPages.test.ts +84 -0
  441. package/src/__tests__/realtimeSessions/verificationPolicy.test.ts +226 -0
  442. package/src/__tests__/realtimeSessions/verificationWorkflow.test.ts +633 -0
  443. package/src/__tests__/resolverBase.fls.test.ts +87 -4
  444. package/src/__tests__/serverExtensionHostOverlay.test.ts +66 -0
  445. package/src/__tests__/serverExtensionReservedRoots.test.ts +11 -96
  446. package/src/__tests__/sharedCache.test.ts +312 -0
  447. package/src/__tests__/widgetGuestElevation.test.ts +44 -0
  448. package/src/agentSessions/BridgeHostInstance.ts +32 -0
  449. package/src/agentSessions/HostInstance.ts +124 -28
  450. package/src/agentSessions/ReturningVisitorRecap.ts +7 -3
  451. package/src/agentSessions/SessionJanitor.ts +821 -59
  452. package/src/agentSessions/SessionManager.ts +138 -26
  453. package/src/agentSessions/SessionRunIDVerifier.ts +171 -0
  454. package/src/agentSessions/index.ts +10 -3
  455. package/src/apiRoot.ts +78 -0
  456. package/src/auth/AuthProviderCatalogRouter.ts +26 -0
  457. package/src/auth/LoginScreenEngine.ts +50 -0
  458. package/src/auth/actionAuthorization.ts +116 -0
  459. package/src/auth/index.ts +4 -8
  460. package/src/auth/magicLink/MagicLinkRouter.ts +3 -2
  461. package/src/auth/magicLink/MagicLinkService.ts +6 -3
  462. package/src/auth/newUsers.ts +99 -11
  463. package/src/config.ts +171 -5
  464. package/src/context.ts +15 -10
  465. package/src/coreRoutes.ts +166 -0
  466. package/src/generated/graphql-schemas/__mj.ts +1815 -57
  467. package/src/generic/CacheInvalidationResolver.ts +73 -18
  468. package/src/generic/OrphanedConversationDetailReconciler.ts +3 -3
  469. package/src/generic/PushStatusResolver.ts +1 -1
  470. package/src/generic/ResolverBase.ts +335 -326
  471. package/src/generic/RunViewResolver.ts +21 -0
  472. package/src/globalCors.ts +71 -0
  473. package/src/index.ts +231 -300
  474. package/src/isolatedProvider.ts +22 -0
  475. package/src/multiTenancy/index.ts +24 -2
  476. package/src/realtimeProxy/RealtimeProxyServer.ts +148 -16
  477. package/src/realtimeProxy/RealtimeRelayTunnel.ts +645 -0
  478. package/src/realtimeSessions/README.md +179 -0
  479. package/src/realtimeSessions/RealtimeSessionEventService.ts +274 -0
  480. package/src/realtimeSessions/RealtimeSessionVerificationService.ts +168 -0
  481. package/src/realtimeSessions/RealtimeVerifyRouter.ts +109 -0
  482. package/src/realtimeSessions/RecordingAssemblyScheduler.ts +234 -0
  483. package/src/realtimeSessions/SessionVerificationPortsBase.ts +189 -0
  484. package/src/realtimeSessions/SlidingWindowRateLimiter.ts +92 -0
  485. package/src/realtimeSessions/index.ts +18 -0
  486. package/src/realtimeSessions/ownedRealtimeSession.ts +55 -0
  487. package/src/realtimeSessions/sessionAccess.ts +145 -0
  488. package/src/realtimeSessions/sessionConfigStamps.ts +123 -0
  489. package/src/realtimeSessions/subscriptionAuthorization.ts +116 -0
  490. package/src/realtimeSessions/verificationCore.ts +502 -0
  491. package/src/realtimeSessions/verificationPages.ts +149 -0
  492. package/src/realtimeSessions/verificationPolicy.ts +321 -0
  493. package/src/realtimeSessions/verificationWorkflow.ts +702 -0
  494. package/src/realtimeWidget/widgetGuestElevation.ts +34 -2
  495. package/src/resolvers/ActionResolver.ts +84 -21
  496. package/src/resolvers/AdhocQueryResolver.ts +24 -6
  497. package/src/resolvers/ArtifactFileResolver.ts +9 -8
  498. package/src/resolvers/CacheStatsResolver.ts +89 -2
  499. package/src/resolvers/ClientToolRequestResolver.ts +25 -7
  500. package/src/resolvers/ConversationRunEventsResolver.ts +15 -11
  501. package/src/resolvers/DatasetResolver.ts +70 -10
  502. package/src/resolvers/EntityRecordNameResolver.ts +3 -2
  503. package/src/resolvers/FetchEntityVectorsResolver.ts +179 -2
  504. package/src/resolvers/FileResolver.ts +321 -106
  505. package/src/resolvers/GetDataContextDataResolver.ts +139 -41
  506. package/src/resolvers/GetDataResolver.ts +6 -3
  507. package/src/resolvers/HumanHandoffResolver.ts +8 -0
  508. package/src/resolvers/IntegrationDiscoveryResolver.ts +4 -2
  509. package/src/resolvers/MCPResolver.ts +8 -5
  510. package/src/resolvers/QueryResolver.ts +11 -0
  511. package/src/resolvers/RealtimeBridgeResolver.ts +163 -11
  512. package/src/resolvers/RealtimeClientSessionResolver.ts +511 -129
  513. package/src/resolvers/RealtimeSessionEventsResolver.ts +133 -0
  514. package/src/resolvers/RealtimeSessionVerificationResolver.ts +174 -0
  515. package/src/resolvers/RemoteBrowserActionResolver.ts +11 -1
  516. package/src/resolvers/RunAIAgentResolver.ts +116 -14
  517. package/src/resolvers/RunTemplateResolver.ts +13 -12
  518. package/src/resolvers/SearchKnowledgeStreamResolver.ts +19 -3
  519. package/src/resolvers/SqlLoggingConfigResolver.ts +2 -4
  520. package/src/resolvers/SyncDataResolver.ts +6 -2
  521. package/src/resolvers/SyncRolesUsersResolver.ts +24 -6
  522. package/src/resolvers/TaskGraphFrameResolver.ts +33 -1
  523. package/src/resolvers/TestQuerySQLResolver.ts +68 -13
  524. package/src/resolvers/TransactionGroupResolver.ts +131 -27
  525. package/src/resolvers/UserAvatarResolver.ts +111 -0
  526. package/src/resolvers/UserResolver.ts +2 -16
  527. package/src/resolvers/__tests__/GetDataResolver.validation.test.ts +95 -0
  528. package/src/resolvers/agent-run-data-guard.ts +89 -0
  529. package/src/resolvers/agentVisionConsentAudit.ts +82 -0
  530. package/src/resolvers/avatarInputValidation.ts +123 -0
  531. package/src/resolvers/currentUserRoles.ts +32 -1
  532. package/src/resolvers/meetings/meetingMutations.ts +21 -0
  533. package/src/rest/RealtimeRecordingHandler.ts +464 -0
  534. package/src/rest/ViewOperationsHandler.ts +29 -13
  535. package/src/rest/setupRESTEndpoints.ts +6 -0
  536. package/src/serverExtensionReservedRoots.ts +14 -17
  537. package/src/services/MJStorageBlobStore.ts +31 -30
  538. package/src/services/StartTaskGraphDispatcher.ts +2 -2
  539. package/src/services/TaskGraphActionRunner.ts +25 -5
  540. package/src/services/TaskGraphAgentRunner.ts +188 -11
  541. package/src/services/TaskGraphContinuationDeliverer.ts +713 -17
  542. package/src/sharedCache.ts +305 -0
  543. package/src/types.ts +7 -0
@@ -12,16 +12,15 @@ var __param = (this && this.__param) || function (paramIndex, decorator) {
12
12
  };
13
13
  import { EntityPermissionType, FieldValueCollection, LogError } from '@memberjunction/core';
14
14
  import { NormalizeUUID } from '@memberjunction/global';
15
- import { readRealtimeRecordingFile } from '@memberjunction/ai-agents';
16
15
  import { Arg, Ctx, DeleteOptionsInput, Field, FieldResolver, Float, InputType, Int, Mutation, ObjectType, PubSub, PubSubEngine, Query, Resolver, Root, } from '@memberjunction/server';
17
- import { createDownloadUrl, createUploadUrl, deleteObject, moveObject, copyObject, listObjects, copyObjectBetweenProviders, searchAcrossAccounts, FileStorageEngine, } from '@memberjunction/storage';
16
+ import { createDownloadUrl, createUploadUrl, deleteObject, moveObject, copyObject, listObjects, copyObjectBetweenProviders, searchAcrossAccounts, FileStorageEngine, StorageAccessEvaluator, StorageAccountAccessDeniedError, StorageObjectKeyOf, } from '@memberjunction/storage';
18
17
  import { CreateMJFileInput, MJFileResolver as FileResolverBase, MJFile_, UpdateMJFileInput } from '../generated/generated.js';
19
18
  import { FieldMapper } from '@memberjunction/graphql-dataprovider';
20
19
  import { GetReadOnlyProvider } from '../util.js';
21
- import { configInfo } from '../config.js';
22
20
  import { MediaAccessKeyManager } from '../rest/MediaAccessKeys.js';
23
21
  import { UploadTokenManager } from '../rest/UploadTokenManager.js';
24
22
  import { DeriveSidecarPath, ParsePeaksSidecar } from './peaksSidecar.js';
23
+ import { ApiRoute } from '../apiRoot.js';
25
24
  let CreateUploadURLInput = class CreateUploadURLInput {
26
25
  };
27
26
  __decorate([
@@ -576,16 +575,9 @@ MediaAccessTokenResult = __decorate([
576
575
  ObjectType()
577
576
  ], MediaAccessTokenResult);
578
577
  export { MediaAccessTokenResult };
578
+ /** The failure reported for an account `SearchAcrossAccounts` refuses — the same whether it is restricted or does not exist. */
579
+ const SEARCH_ACCOUNT_REFUSED_MESSAGE = 'Access denied: you do not have access to this storage account or it does not exist.';
579
580
  let FileResolver = class FileResolver extends FileResolverBase {
580
- /**
581
- * Resolves the server's public base URL the same way callbacks/magic-link do:
582
- * `publicUrl` if configured (e.g. an ngrok URL), else `baseUrl:graphqlPort`. Trailing
583
- * slash stripped so callers can append `/media/...`.
584
- */
585
- resolvePublicBaseUrl() {
586
- const base = configInfo.publicUrl || `${configInfo.baseUrl}:${configInfo.graphqlPort}${configInfo.graphqlRootPath || ''}`;
587
- return base.replace(/\/+$/, '');
588
- }
589
581
  /**
590
582
  * Mints a short-lived signed media-access token for an `MJ: Files` record and returns the
591
583
  * authenticated streaming URL (`<publicBase>/media/<fileId>?token=<token>`). Permission-gated
@@ -607,14 +599,17 @@ let FileResolver = class FileResolver extends FileResolverBase {
607
599
  if (!loaded) {
608
600
  return { Success: false, ErrorMessage: 'You do not have access to this file or it does not exist.' };
609
601
  }
602
+ // Storage gate: the token is the capability the /media route honours, so it is minted only when the caller may read
603
+ // the account the row's provider resolves to and every row tracking the object (throws — caught below).
604
+ const resolved = await this.assertFileObjectAccess(fileEntity, 'Read', contextUser, provider);
610
605
  // Access authorized — mint the capability token.
611
606
  const { Token, ExpiresAt } = MediaAccessKeyManager.Instance.Sign(fileId, contextUser.ID);
612
607
  const fileNameSuffix = fileEntity.Name ? `/${encodeURIComponent(fileEntity.Name)}` : '';
613
- const url = `${this.resolvePublicBaseUrl()}/media/${encodeURIComponent(fileId)}${fileNameSuffix}?token=${encodeURIComponent(Token)}`;
608
+ const url = ApiRoute(`/media/${encodeURIComponent(fileId)}${fileNameSuffix}?token=${encodeURIComponent(Token)}`, { absolute: true });
614
609
  // Best-effort: surface precomputed waveform peaks from a peaks.json sidecar beside the file, so
615
610
  // the player renders the real waveform instantly without fetching/decoding the audio. Never
616
611
  // blocks token minting — any failure just omits Peaks.
617
- const peaks = await this.tryReadPeaksSidecar(fileEntity, contextUser);
612
+ const peaks = await this.tryReadPeaksSidecar(fileEntity, resolved?.Driver);
618
613
  return { Success: true, Token, Url: url, ExpiresAt, MimeType: fileEntity.ContentType ?? undefined, Peaks: peaks };
619
614
  }
620
615
  catch (error) {
@@ -627,33 +622,22 @@ let FileResolver = class FileResolver extends FileResolverBase {
627
622
  * Best-effort read of a `peaks.json` waveform sidecar that sits in the SAME storage folder as the
628
623
  * given file (a JSON array of normalized `0..1` numbers, written at capture time). Derives the
629
624
  * folder from the file's `ProviderKey` (strips the final path segment), reads `<folder>/peaks.json`
630
- * via the file's own storage driver, parses + validates it, and returns sanitized peaks. Returns
631
- * `undefined` on ANY failure (no ProviderKey, no sidecar, parse error, garbage) — the caller treats
632
- * peaks as a pure optimization and must never let a sidecar problem affect token minting.
625
+ * through the file's own storage driver — the one {@link assertFileObjectAccess} resolved, so the account is already
626
+ * gated — parses + validates it, and returns sanitized peaks. Returns `undefined` on ANY failure (no ProviderKey, no
627
+ * driver, no sidecar, parse error, garbage) — the caller treats peaks as a pure optimization and must never let a
628
+ * sidecar problem affect token minting.
633
629
  *
634
630
  * @param file The already-loaded (under the user's context) `MJ: Files` row.
635
- * @param contextUser The calling user — used to resolve the storage driver.
631
+ * @param driver The gated driver for the file's account, or undefined when the provider has none.
636
632
  * @returns Sanitized `0..1` peaks (length-capped), or `undefined`.
637
633
  */
638
- async tryReadPeaksSidecar(file, contextUser) {
634
+ async tryReadPeaksSidecar(file, driver) {
639
635
  try {
640
636
  // The sidecar lives next to the recording: replace the final path segment with peaks.json.
641
637
  const sidecarPath = DeriveSidecarPath(file.ProviderKey);
642
- if (!sidecarPath) {
643
- return undefined;
644
- }
645
- // Resolve the file's storage account → driver (mirror of readRealtimeRecordingFile's pattern).
646
- await FileStorageEngine.Instance.Config(false, contextUser);
647
- let accounts = FileStorageEngine.Instance.GetAccountsByProviderID(file.ProviderID);
648
- if (accounts.length === 0) {
649
- await FileStorageEngine.Instance.Config(true, contextUser);
650
- accounts = FileStorageEngine.Instance.GetAccountsByProviderID(file.ProviderID);
651
- }
652
- const account = accounts[0];
653
- if (!account) {
638
+ if (!sidecarPath || !driver) {
654
639
  return undefined;
655
640
  }
656
- const driver = await FileStorageEngine.Instance.GetDriver(account.ID, contextUser);
657
641
  const bytes = await driver.GetObject({ fullPath: sidecarPath });
658
642
  if (!bytes || bytes.length === 0) {
659
643
  return undefined;
@@ -689,6 +673,65 @@ let FileResolver = class FileResolver extends FileResolverBase {
689
673
  accountEntity,
690
674
  };
691
675
  }
676
+ /**
677
+ * The storage-account gate. Every route keyed by a client-supplied account ID runs it FIRST — before the account is
678
+ * loaded and before any driver call. Throws `StorageAccountAccessDeniedError` ("You do not have access to this storage
679
+ * account or it does not exist.") unless the caller may use the account for `access`, evaluated per call against
680
+ * `MJ: File Storage Account Permissions` by `StorageAccessEvaluator` (`@memberjunction/storage`). An unknown account
681
+ * gets the same refusal, so the error never reveals whether an account ID is real.
682
+ */
683
+ async assertAccountAccess(accountId, access, context) {
684
+ const md = GetReadOnlyProvider(context.providers, { allowFallbackToReadWrite: true });
685
+ const user = this.GetUserFromPayload(context.userPayload);
686
+ await StorageAccessEvaluator.Instance.AssertAccountAccess(accountId, user, access, md);
687
+ }
688
+ /**
689
+ * The tracked-file rule for an account-keyed route: when any of `objectNames` in the account backs an `MJ: Files` row,
690
+ * the caller must be able to read that row — the rule {@link GetFileContents} applies to a file ID — else this throws
691
+ * `TrackedFileAccessDeniedError` ("You do not have access to this file or it does not exist."). Keys are compared the
692
+ * way the account's driver addresses them (`FileStorageBase.NormalizeObjectKey`) and case-insensitively; a key whose
693
+ * meaning depends on the provider (`..`, `\`, `%2F`, ...) is refused outright. Applied to every object a route reads
694
+ * or signs, and to every object it overwrites, moves or deletes — the minimum for a write is that the caller may read
695
+ * what it replaces. An object no row tracks is governed by the account gate, which the caller has already passed.
696
+ */
697
+ async assertTrackedObjectReadable(accountId, objectNames, user, md) {
698
+ const providerID = await this.accountProviderID(accountId, user, md);
699
+ const driver = await FileStorageEngine.Instance.GetDriver(accountId, user);
700
+ await StorageAccessEvaluator.Instance.AssertTrackedObjectsReadable(providerID, objectNames, user, md, driver);
701
+ }
702
+ /**
703
+ * The storage gate for a route that goes from an `MJ: Files` row (already loaded as the caller) to its object: the
704
+ * account the row's provider resolves to must allow `access`, and every row tracking the object must be readable by
705
+ * the caller (`FileStorageEngine.ResolveFileObject`) — so a row whose client-written `ProviderID`/`ProviderKey`/`Name`
706
+ * aliases someone else's object is refused. `extraKeys` (a rename target) get the tracked-file rule too. When the
707
+ * provider has no storage account the legacy env-configured driver serves the row; there is no account to gate, so the
708
+ * tracked-file rule alone applies. Returns the resolved object, or `null` in that legacy case.
709
+ */
710
+ async assertFileObjectAccess(file, access, user, md, extraKeys = []) {
711
+ const location = { ProviderID: file.ProviderID, ProviderKey: file.ProviderKey ?? null, Name: file.Name };
712
+ const resolved = await FileStorageEngine.Instance.ResolveFileObject(location, user, access, md);
713
+ const keys = resolved ? extraKeys : [StorageObjectKeyOf(location), ...extraKeys];
714
+ if (keys.length > 0) {
715
+ await StorageAccessEvaluator.Instance.AssertTrackedObjectsReadable(file.ProviderID, keys, user, md, resolved?.Driver);
716
+ }
717
+ return resolved;
718
+ }
719
+ /**
720
+ * The storage provider ID of an account the caller has already passed the gate for, from the engine's cache (refreshed
721
+ * once on a miss). Refuses like the gate when the account still cannot be found.
722
+ */
723
+ async accountProviderID(accountId, user, md) {
724
+ await FileStorageEngine.Instance.Config(false, user, md);
725
+ let account = FileStorageEngine.Instance.GetAccountById(accountId);
726
+ if (!account) {
727
+ await FileStorageEngine.Instance.RefreshAfterMiss(md);
728
+ account = FileStorageEngine.Instance.GetAccountById(accountId);
729
+ }
730
+ if (!account) {
731
+ throw new StorageAccountAccessDeniedError();
732
+ }
733
+ return account.ProviderID;
734
+ }
692
735
  /**
693
736
  * Loads a FileStorageAccount and its associated FileStorageProvider.
694
737
  * This is the standard way to get provider information in the enterprise model.
@@ -729,6 +772,10 @@ let FileResolver = class FileResolver extends FileResolverBase {
729
772
  const fileEntity = await provider.GetEntityObject('MJ: Files', user);
730
773
  const providerEntity = await provider.GetEntityObject('MJ: File Storage Providers', user);
731
774
  fileEntity.CheckPermissions(EntityPermissionType.Create, true);
775
+ // The upload URL signs an overwrite of `Name` (and the row will point at `ProviderKey`): Write on the account the
776
+ // provider resolves to, and the tracked-file rule on both keys, as for every other route that writes an object.
777
+ const location = { ProviderID: input.ProviderID ?? '', ProviderKey: null, Name: input.Name ?? '' };
778
+ await this.assertFileObjectAccess(location, 'Write', user, provider, input.ProviderKey ? [input.ProviderKey] : []);
732
779
  const [sameName] = await this.findBy(provider, 'MJ: Files', { Name: input.Name, ProviderID: input.ProviderID }, context.userPayload.userRecord);
733
780
  const NameExists = Boolean(sameName);
734
781
  const success = fileEntity.NewRecord(FieldValueCollection.FromObject({ ...input, Status: 'Pending' }));
@@ -761,6 +808,12 @@ let FileResolver = class FileResolver extends FileResolverBase {
761
808
  // Permission check on MJ: Files
762
809
  const fileEntity = await provider.GetEntityObject('MJ: Files', user);
763
810
  fileEntity.CheckPermissions(EntityPermissionType.Create, true);
811
+ // Write gate on the account the upload lands in — before the staged upload is consumed or any driver is touched.
812
+ // With no account at all there is nothing to gate; UploadFile then refuses with its documented error.
813
+ const targetAccountId = await this.resolveUploadAccountId(input.AccountID, user, provider);
814
+ if (targetAccountId) {
815
+ await this.assertAccountAccess(targetAccountId, 'Write', context);
816
+ }
764
817
  let buffer;
765
818
  let fileName = input.FileName || 'file';
766
819
  let mimeType = input.MimeType || 'application/octet-stream';
@@ -785,17 +838,12 @@ let FileResolver = class FileResolver extends FileResolverBase {
785
838
  ErrorMessage: 'Either UploadToken or Base64Data must be provided.',
786
839
  };
787
840
  }
788
- // Ensure FileStorageEngine is configured under the user context
789
- await FileStorageEngine.Instance.Config(false, user, provider);
790
- if (input.AccountID && !FileStorageEngine.Instance.GetAccountById(input.AccountID)) {
791
- await FileStorageEngine.Instance.Config(true, user, provider);
792
- }
793
841
  const uploadResult = await FileStorageEngine.Instance.UploadFile({
794
842
  content: buffer,
795
843
  fileName,
796
844
  mimeType,
797
845
  contextUser: user,
798
- storageAccountId: input.AccountID,
846
+ storageAccountId: targetAccountId,
799
847
  categoryID: input.CategoryID,
800
848
  description: input.Description,
801
849
  pathPrefix: input.PathPrefix,
@@ -819,32 +867,42 @@ let FileResolver = class FileResolver extends FileResolverBase {
819
867
  };
820
868
  }
821
869
  }
870
+ /**
871
+ * The account an upload will land in: the requested one, else the engine's default (the first active account) —
872
+ * returned as an explicit ID so the account the gate checks is the account `UploadFile` uses. A requested ID is
873
+ * returned as-is: the gate refuses an unknown one without revealing that it does not exist, and `UploadFile` refreshes
874
+ * the engine's cache itself when a permitted account is missing from it.
875
+ */
876
+ async resolveUploadAccountId(accountId, user, md) {
877
+ if (accountId) {
878
+ return accountId;
879
+ }
880
+ await FileStorageEngine.Instance.Config(false, user, md);
881
+ return FileStorageEngine.Instance.ResolveStorageAccount()?.account.ID;
882
+ }
822
883
  async DownloadUrl(file, context) {
823
884
  const md = GetReadOnlyProvider(context.providers, { allowFallbackToReadWrite: true });
824
885
  const user = this.GetUserFromPayload(context.userPayload);
825
886
  const fileEntity = await md.GetEntityObject('MJ: Files', user);
826
887
  fileEntity.CheckPermissions(EntityPermissionType.Read, true);
827
- await FileStorageEngine.Instance.Config(false, user, md);
828
- let accounts = FileStorageEngine.Instance.GetAccountsByProviderID(file.ProviderID);
829
- if (accounts.length === 0) {
830
- await FileStorageEngine.Instance.Config(true, user, md);
831
- accounts = FileStorageEngine.Instance.GetAccountsByProviderID(file.ProviderID);
832
- }
833
- if (accounts.length > 0) {
834
- const account = accounts[0];
835
- const driver = await FileStorageEngine.Instance.GetDriver(account.ID, user);
836
- return await driver.CreatePreAuthDownloadUrl(file.ProviderKey ?? file.Name);
888
+ // Storage gate: Read on the account the row's provider resolves to, and the tracked-file rule on its object
889
+ const location = { ProviderID: file.ProviderID, ProviderKey: file.ProviderKey ?? null, Name: file.Name ?? '' };
890
+ const resolved = await this.assertFileObjectAccess(location, 'Read', user, md);
891
+ if (resolved) {
892
+ return await resolved.Driver.CreatePreAuthDownloadUrl(resolved.ObjectKey);
837
893
  }
838
894
  const providerEntity = await md.GetEntityObject('MJ: File Storage Providers', user);
839
895
  await providerEntity.Load(file.ProviderID);
840
896
  const userContext = this.buildUserContext(context);
841
- const url = await createDownloadUrl(providerEntity, file.ProviderKey ?? file.Name, userContext);
897
+ const url = await createDownloadUrl(providerEntity, StorageObjectKeyOf(file), userContext);
842
898
  return url;
843
899
  }
844
900
  /**
845
901
  * Returns an `MJ: Files` record's bytes as base64, read server-side through authenticated MJStorage
846
902
  * (`GetObject`) — NOT a public pre-signed link. Permission-gated: the file is first loaded under the
847
- * calling user's context, so MJ row-level security determines access. Never throws to the client.
903
+ * calling user's context, so MJ row-level security determines access; then the storage gate
904
+ * ({@link assertFileObjectAccess}) requires Read on the account the row's provider resolves to and refuses an object
905
+ * another row tracks when the caller cannot read that row. Never throws to the client.
848
906
  *
849
907
  * @param fileId The `MJ: Files` id whose bytes to return.
850
908
  * @returns `{ Success, Base64?, MimeType?, ErrorMessage? }`.
@@ -860,15 +918,17 @@ let FileResolver = class FileResolver extends FileResolverBase {
860
918
  if (!loaded) {
861
919
  return { Success: false, ErrorMessage: 'You do not have access to this file or it does not exist.' };
862
920
  }
863
- // Read the bytes via authenticated MJStorage (server-side GetObject on the file's own account).
864
- const result = await readRealtimeRecordingFile(fileId, contextUser, provider);
865
- if (!result) {
921
+ // Storage gate, then read the bytes via authenticated MJStorage (server-side GetObject on the file's own account).
922
+ // A refusal throws and is reported below; a row whose provider has no account has no bytes to read.
923
+ const resolved = await this.assertFileObjectAccess(fileEntity, 'Read', contextUser, provider);
924
+ const bytes = resolved ? await resolved.Driver.GetObject({ fullPath: resolved.ObjectKey }) : null;
925
+ if (!bytes || bytes.length === 0) {
866
926
  return { Success: false, ErrorMessage: 'The file could not be read from storage.' };
867
927
  }
868
928
  return {
869
929
  Success: true,
870
- Base64: result.Bytes.toString('base64'),
871
- MimeType: result.MimeType ?? fileEntity.ContentType ?? undefined,
930
+ Base64: bytes.toString('base64'),
931
+ MimeType: fileEntity.ContentType ?? undefined,
872
932
  };
873
933
  }
874
934
  catch (error) {
@@ -885,6 +945,8 @@ let FileResolver = class FileResolver extends FileResolverBase {
885
945
  fileEntity.CheckPermissions(EntityPermissionType.Update, true);
886
946
  await fileEntity.Load(input.ID);
887
947
  if (fileEntity.Name !== input.Name) {
948
+ // Renaming moves the object: Write on its account, and the tracked-file rule on the object and the new name
949
+ await this.assertFileObjectAccess(fileEntity, 'Write', user, md, [fileEntity.Name, input.Name]);
888
950
  const providerEntity = await md.GetEntityObject('MJ: File Storage Providers', user);
889
951
  await providerEntity.Load(fileEntity.ProviderID);
890
952
  const userContext = this.buildUserContext(context);
@@ -905,12 +967,14 @@ let FileResolver = class FileResolver extends FileResolverBase {
905
967
  return null;
906
968
  }
907
969
  fileEntity.CheckPermissions(EntityPermissionType.Delete, true);
908
- // Only delete the object from the provider if it's actually been uploaded
970
+ // Only delete the object from the provider if it's actually been uploaded — after the storage gate: Write on its
971
+ // account, and no other row the caller cannot read may track the object (a row can alias another row's object)
909
972
  if (fileEntity.Status === 'Uploaded') {
973
+ await this.assertFileObjectAccess(fileEntity, 'Write', userInfo, md);
910
974
  const providerEntity = await md.GetEntityObject('MJ: File Storage Providers', userInfo);
911
975
  await providerEntity.Load(fileEntity.ProviderID);
912
976
  const userContext = this.buildUserContext(context);
913
- await deleteObject(providerEntity, fileEntity.ProviderKey ?? fileEntity.Name, userContext);
977
+ await deleteObject(providerEntity, StorageObjectKeyOf(fileEntity), userContext);
914
978
  }
915
979
  return super.DeleteMJFile(ID, options, context, pubSub);
916
980
  }
@@ -922,12 +986,14 @@ let FileResolver = class FileResolver extends FileResolverBase {
922
986
  });
923
987
  const md = GetReadOnlyProvider(context.providers, { allowFallbackToReadWrite: true });
924
988
  const user = this.GetUserFromPayload(context.userPayload);
989
+ // Account gate first — before the account is loaded or any driver is touched
990
+ await this.assertAccountAccess(input.AccountID, 'Read', context);
925
991
  // Load the account and its provider
926
992
  const { account, provider: providerEntity } = await this.loadAccountAndProvider(input.AccountID, context);
927
993
  console.log('[FileResolver] Provider loaded:', {
928
994
  Name: providerEntity.Name,
929
995
  ServerDriverKey: providerEntity.ServerDriverKey,
930
- HasConfiguration: !!providerEntity.Get('Configuration'),
996
+ HasConfiguration: !!providerEntity.Configuration,
931
997
  });
932
998
  // Check permissions - user must have read access to Files entity
933
999
  const fileEntity = await md.GetEntityObject('MJ: Files', user);
@@ -941,8 +1007,10 @@ let FileResolver = class FileResolver extends FileResolverBase {
941
1007
  objects: result.objects.map((o) => ({ name: o.name, isDirectory: o.isDirectory })),
942
1008
  prefixes: result.prefixes,
943
1009
  });
944
- // Convert Date objects to ISO strings for GraphQL
945
- const objects = result.objects.map((obj) => ({
1010
+ // Drop objects behind an MJ: Files row the caller cannot read — a listing never names a tracked object the caller
1011
+ // is refused — then convert Date objects to ISO strings for GraphQL
1012
+ const visible = await this.dropUnreadableListedObjects(input.AccountID, result.objects, user, md);
1013
+ const objects = visible.map((obj) => ({
946
1014
  ...obj,
947
1015
  lastModified: obj.lastModified.toISOString(),
948
1016
  }));
@@ -957,6 +1025,9 @@ let FileResolver = class FileResolver extends FileResolverBase {
957
1025
  // Check permissions
958
1026
  const fileEntity = await md.GetEntityObject('MJ: Files', user);
959
1027
  fileEntity.CheckPermissions(EntityPermissionType.Read, true);
1028
+ // Account gate, then — when the object backs an MJ: Files row — that row must be readable by the caller
1029
+ await this.assertAccountAccess(input.AccountID, 'Read', context);
1030
+ await this.assertTrackedObjectReadable(input.AccountID, [input.ObjectName], user, md);
960
1031
  await FileStorageEngine.Instance.Config(false, user, md);
961
1032
  const driver = await FileStorageEngine.Instance.GetDriver(input.AccountID, user);
962
1033
  const downloadUrl = await driver.CreatePreAuthDownloadUrl(input.ObjectName);
@@ -982,6 +1053,9 @@ let FileResolver = class FileResolver extends FileResolverBase {
982
1053
  // Check permissions
983
1054
  const fileEntity = await md.GetEntityObject('MJ: Files', user);
984
1055
  fileEntity.CheckPermissions(EntityPermissionType.Create, true);
1056
+ await this.assertAccountAccess(input.AccountID, 'Write', context);
1057
+ // An upload URL can overwrite: the caller must be able to read any MJ: Files row that tracks the target
1058
+ await this.assertTrackedObjectReadable(input.AccountID, [input.ObjectName], user, md);
985
1059
  await FileStorageEngine.Instance.Config(false, user, md);
986
1060
  const driver = await FileStorageEngine.Instance.GetDriver(input.AccountID, user);
987
1061
  if (!driver.SupportsPreAuthUpload) {
@@ -1008,7 +1082,7 @@ let FileResolver = class FileResolver extends FileResolverBase {
1008
1082
  return {
1009
1083
  Success: true,
1010
1084
  Token: mint.Token,
1011
- Url: '/media/upload-stage',
1085
+ Url: ApiRoute('/media/upload-stage'),
1012
1086
  };
1013
1087
  }
1014
1088
  catch (err) {
@@ -1022,11 +1096,14 @@ let FileResolver = class FileResolver extends FileResolverBase {
1022
1096
  async DeleteStorageObject(input, context) {
1023
1097
  const md = GetReadOnlyProvider(context.providers, { allowFallbackToReadWrite: true });
1024
1098
  const user = this.GetUserFromPayload(context.userPayload);
1099
+ // Account gate first — before the account is loaded or any driver is touched
1100
+ await this.assertAccountAccess(input.AccountID, 'Write', context);
1025
1101
  // Load the account and its provider
1026
1102
  const { account, provider: providerEntity } = await this.loadAccountAndProvider(input.AccountID, context);
1027
1103
  // Check permissions
1028
1104
  const fileEntity = await md.GetEntityObject('MJ: Files', user);
1029
1105
  fileEntity.CheckPermissions(EntityPermissionType.Delete, true);
1106
+ await this.assertTrackedObjectReadable(input.AccountID, [input.ObjectName], user, md);
1030
1107
  // Delete the object with extended user context (includes account for credential lookup)
1031
1108
  const userContext = this.buildExtendedUserContext(context, account);
1032
1109
  const success = await deleteObject(providerEntity, input.ObjectName, userContext);
@@ -1035,11 +1112,15 @@ let FileResolver = class FileResolver extends FileResolverBase {
1035
1112
  async MoveStorageObject(input, context) {
1036
1113
  const md = GetReadOnlyProvider(context.providers, { allowFallbackToReadWrite: true });
1037
1114
  const user = this.GetUserFromPayload(context.userPayload);
1115
+ // Account gate first — before the account is loaded or any driver is touched
1116
+ await this.assertAccountAccess(input.AccountID, 'Write', context);
1038
1117
  // Load the account and its provider
1039
1118
  const { account, provider: providerEntity } = await this.loadAccountAndProvider(input.AccountID, context);
1040
1119
  // Check permissions
1041
1120
  const fileEntity = await md.GetEntityObject('MJ: Files', user);
1042
1121
  fileEntity.CheckPermissions(EntityPermissionType.Update, true);
1122
+ // A move reads and removes the source and may overwrite the destination: both get the tracked-file rule
1123
+ await this.assertTrackedObjectReadable(input.AccountID, [input.OldName, input.NewName], user, md);
1043
1124
  // Move the object with extended user context (includes account for credential lookup)
1044
1125
  const userContext = this.buildExtendedUserContext(context, account);
1045
1126
  const success = await moveObject(providerEntity, input.OldName, input.NewName, userContext);
@@ -1048,12 +1129,17 @@ let FileResolver = class FileResolver extends FileResolverBase {
1048
1129
  async CopyStorageObject(input, context) {
1049
1130
  const md = GetReadOnlyProvider(context.providers, { allowFallbackToReadWrite: true });
1050
1131
  const user = this.GetUserFromPayload(context.userPayload);
1132
+ // Account gate first — a copy within one account writes to it — before the account is loaded or any driver is touched
1133
+ await this.assertAccountAccess(input.AccountID, 'Write', context);
1051
1134
  // Load the account and its provider
1052
1135
  const { account, provider: providerEntity } = await this.loadAccountAndProvider(input.AccountID, context);
1053
1136
  // Check permissions - copying requires both read (source) and create (destination)
1054
1137
  const fileEntity = await md.GetEntityObject('MJ: Files', user);
1055
1138
  fileEntity.CheckPermissions(EntityPermissionType.Read, true);
1056
1139
  fileEntity.CheckPermissions(EntityPermissionType.Create, true);
1140
+ // A copy of a tracked object is an untracked one the caller could then download, and the destination may be
1141
+ // overwritten: both get the tracked-file rule
1142
+ await this.assertTrackedObjectReadable(input.AccountID, [input.SourceName, input.DestinationName], user, md);
1057
1143
  // Copy the object with extended user context (includes account for credential lookup)
1058
1144
  const userContext = this.buildExtendedUserContext(context, account);
1059
1145
  const success = await copyObject(providerEntity, input.SourceName, input.DestinationName, userContext);
@@ -1062,6 +1148,8 @@ let FileResolver = class FileResolver extends FileResolverBase {
1062
1148
  async CreateDirectory(input, context) {
1063
1149
  const md = GetReadOnlyProvider(context.providers, { allowFallbackToReadWrite: true });
1064
1150
  const user = this.GetUserFromPayload(context.userPayload);
1151
+ // Account gate first — before the account is loaded or any driver is touched
1152
+ await this.assertAccountAccess(input.AccountID, 'Write', context);
1065
1153
  // Load the account and its provider
1066
1154
  const { account: accountEntity, provider: providerEntity } = await this.loadAccountAndProvider(input.AccountID, context);
1067
1155
  // Check permissions
@@ -1086,6 +1174,12 @@ let FileResolver = class FileResolver extends FileResolverBase {
1086
1174
  const fileEntity = await md.GetEntityObject('MJ: Files', user);
1087
1175
  fileEntity.CheckPermissions(EntityPermissionType.Read, true);
1088
1176
  fileEntity.CheckPermissions(EntityPermissionType.Create, true);
1177
+ // Account gates first: read the source, write the destination — before either account is loaded or touched
1178
+ await this.assertAccountAccess(input.SourceAccountID, 'Read', context);
1179
+ await this.assertAccountAccess(input.DestinationAccountID, 'Write', context);
1180
+ // Tracked-file rule on both ends: the source (its copy would be untracked), the destination (it may be overwritten)
1181
+ await this.assertTrackedObjectReadable(input.SourceAccountID, [input.SourcePath], user, md);
1182
+ await this.assertTrackedObjectReadable(input.DestinationAccountID, [input.DestinationPath], user, md);
1089
1183
  // Load the source account and its provider
1090
1184
  const { account: sourceAccount, provider: sourceProviderEntity } = await this.loadAccountAndProvider(input.SourceAccountID, context);
1091
1185
  // Load the destination account and its provider
@@ -1121,47 +1215,131 @@ let FileResolver = class FileResolver extends FileResolverBase {
1121
1215
  // Check permissions - searching requires read access
1122
1216
  const fileEntity = await md.GetEntityObject('MJ: Files', user);
1123
1217
  fileEntity.CheckPermissions(EntityPermissionType.Read, true);
1124
- // Use cached accounts from the engine — no RunView needed
1125
- await FileStorageEngine.Instance.Config(false, user);
1126
- const normalizedIDs = new Set(input.AccountIDs.map((id) => NormalizeUUID(id)));
1127
- const accountEntities = FileStorageEngine.Instance.Accounts
1128
- .filter(a => normalizedIDs.has(NormalizeUUID(a.ID)));
1129
- if (accountEntities.length === 0) {
1130
- throw new Error('No valid storage accounts found for the provided IDs');
1131
- }
1132
- // Log any accounts that weren't found
1133
- if (accountEntities.length < input.AccountIDs.length) {
1134
- const foundIDs = new Set(accountEntities.map((a) => NormalizeUUID(a.ID)));
1135
- const missingIDs = input.AccountIDs.filter((id) => !foundIDs.has(NormalizeUUID(id)));
1136
- console.warn(`[FileResolver] Accounts not found: ${missingIDs.join(', ')}`);
1137
- }
1138
- // Load providers from cached metadata
1139
- const providerMap = new Map();
1140
- for (const provider of FileStorageEngine.Instance.Providers) {
1141
- providerMap.set(provider.ID, provider);
1142
- }
1143
- // Build account/provider pairs for the search
1144
- const accountInputs = [];
1145
- for (const account of accountEntities) {
1146
- const provider = providerMap.get(account.ProviderID);
1147
- if (provider) {
1148
- accountInputs.push({ accountEntity: account, providerEntity: provider });
1149
- }
1150
- }
1151
- // Execute the search across all accounts with account-based credentials
1152
- const result = await searchAcrossAccounts(accountInputs, input.Query, {
1153
- maxResultsPerAccount: input.MaxResultsPerAccount,
1154
- fileTypes: input.FileTypes,
1155
- searchContent: input.SearchContent,
1156
- contextUser: user,
1157
- });
1218
+ // Account gate BEFORE any driver call: refused accounts are reported as failed and never searched
1219
+ const { searchable, refused } = await this.partitionSearchAccounts(input.AccountIDs, user, md);
1220
+ const result = searchable.length > 0
1221
+ ? await searchAcrossAccounts(searchable, input.Query, {
1222
+ maxResultsPerAccount: input.MaxResultsPerAccount,
1223
+ fileTypes: input.FileTypes,
1224
+ searchContent: input.SearchContent,
1225
+ contextUser: user,
1226
+ })
1227
+ : { accountResults: [], totalResultsReturned: 0, successfulAccounts: 0, failedAccounts: 0 };
1158
1228
  console.log('[FileResolver] SearchAcrossAccounts result:', {
1159
1229
  totalResultsReturned: result.totalResultsReturned,
1160
1230
  successfulAccounts: result.successfulAccounts,
1161
1231
  failedAccounts: result.failedAccounts,
1232
+ refusedAccounts: refused.length,
1162
1233
  });
1163
- // Convert results to GraphQL payload format
1164
- const accountResults = result.accountResults.map((ar) => ({
1234
+ const kept = await this.dropUnreadableTrackedHits(result.accountResults, user, md);
1235
+ return this.toSearchAcrossAccountsPayload(this.inRequestOrder(input.AccountIDs, [...kept, ...refused]));
1236
+ }
1237
+ /**
1238
+ * Per-account results in the order the accounts were requested (each requested ID once), whether searched or refused —
1239
+ * so a refused account sits where the caller put it rather than after every searched one.
1240
+ */
1241
+ inRequestOrder(requestedIDs, results) {
1242
+ const byID = new Map(results.map((r) => [NormalizeUUID(r.accountID), r]));
1243
+ const ordered = [];
1244
+ for (const id of requestedIDs) {
1245
+ const result = byID.get(NormalizeUUID(id));
1246
+ if (result) {
1247
+ ordered.push(result);
1248
+ byID.delete(NormalizeUUID(id));
1249
+ }
1250
+ }
1251
+ return [...ordered, ...byID.values()];
1252
+ }
1253
+ /**
1254
+ * Splits the requested accounts into those to search and those refused, before any driver call. An account the
1255
+ * caller may not read is refused with {@link SEARCH_ACCOUNT_REFUSED_MESSAGE} — and so is one that does not exist,
1256
+ * identically, so the response never reveals which IDs are real. Readable accounts are paired with their provider
1257
+ * from the engine's cache (refreshed once if a readable account is missing from it). Duplicate IDs are searched once.
1258
+ */
1259
+ async partitionSearchAccounts(accountIDs, user, md) {
1260
+ const readable = await StorageAccessEvaluator.Instance.AccessibleAccountIDs(accountIDs, user, 'Read', md);
1261
+ await FileStorageEngine.Instance.Config(false, user, md);
1262
+ if ([...readable].some((id) => !FileStorageEngine.Instance.GetAccountWithProvider(id))) {
1263
+ await FileStorageEngine.Instance.RefreshAfterMiss(md);
1264
+ }
1265
+ const searchable = [];
1266
+ const refused = [];
1267
+ const seen = new Set();
1268
+ for (const requestedID of accountIDs) {
1269
+ const key = NormalizeUUID(requestedID);
1270
+ if (seen.has(key))
1271
+ continue;
1272
+ seen.add(key);
1273
+ const pair = readable.has(key) ? FileStorageEngine.Instance.GetAccountWithProvider(requestedID) : null;
1274
+ if (pair) {
1275
+ searchable.push({ accountEntity: pair.account, providerEntity: pair.provider });
1276
+ }
1277
+ else {
1278
+ refused.push(this.refusedAccountResult(requestedID));
1279
+ }
1280
+ }
1281
+ return { searchable, refused };
1282
+ }
1283
+ /** A failed per-account entry for an account the caller may not search; it names nothing beyond the requested ID. */
1284
+ refusedAccountResult(accountID) {
1285
+ return {
1286
+ accountID,
1287
+ accountName: '',
1288
+ providerID: '',
1289
+ providerName: '',
1290
+ success: false,
1291
+ errorMessage: SEARCH_ACCOUNT_REFUSED_MESSAGE,
1292
+ results: [],
1293
+ hasMore: false,
1294
+ };
1295
+ }
1296
+ /**
1297
+ * Drops search hits that map to an `MJ: Files` row the caller may not read — one batched lookup per account, keyed on
1298
+ * each hit's path and provider object ID (`StorageAccessEvaluator.UnreadableTrackedObjectKeys`, compared the way the
1299
+ * account's driver addresses keys). Hits no row tracks stay; the account gate governs them.
1300
+ *
1301
+ * When any hit is dropped, the provider's `totalMatches` (which counted it) and `nextPageToken` (this route takes no
1302
+ * page token back) are withheld; `hasMore` is kept. How reliable the mapping is depends on the provider: for
1303
+ * object-key stores (S3, Azure Blob, GCS) a hit's path IS the row's `ProviderKey`; for hierarchical providers (Box,
1304
+ * Google Drive, Dropbox, SharePoint) a search path may not match the key a row was written with, and such a miss falls
1305
+ * back to the account gate. `MJ: Files` rows are keyed by provider, not account, so the lookup errs toward dropping.
1306
+ */
1307
+ async dropUnreadableTrackedHits(accountResults, user, md) {
1308
+ return Promise.all(accountResults.map((ar) => this.dropUnreadableAccountHits(ar, user, md)));
1309
+ }
1310
+ /** {@link dropUnreadableTrackedHits} for one account's results; if the check cannot run, the account's hits are dropped. */
1311
+ async dropUnreadableAccountHits(ar, user, md) {
1312
+ if (!ar.success || ar.results.length === 0)
1313
+ return ar;
1314
+ try {
1315
+ const driver = await FileStorageEngine.Instance.GetDriver(ar.accountID, user);
1316
+ const keys = ar.results.flatMap((r) => [r.path, r.objectId].filter((k) => !!k));
1317
+ const unreadable = await StorageAccessEvaluator.Instance.UnreadableTrackedObjectKeys(ar.providerID, keys, user, md, driver);
1318
+ const results = ar.results.filter((r) => !unreadable.has(r.path) && !(r.objectId && unreadable.has(r.objectId)));
1319
+ return results.length === ar.results.length ? ar : { ...ar, results, totalMatches: undefined, nextPageToken: undefined };
1320
+ }
1321
+ catch (error) {
1322
+ const reason = error instanceof Error ? error.message : String(error);
1323
+ LogError(`SearchAcrossAccounts: tracked-file check failed for account ${ar.accountID} — its hits are withheld: ${reason}`);
1324
+ return { ...ar, results: [], totalMatches: undefined, nextPageToken: undefined };
1325
+ }
1326
+ }
1327
+ /**
1328
+ * The listed objects minus those behind an `MJ: Files` row the caller may not read (directories are prefixes, never
1329
+ * tracked objects, and are kept). Keys are compared the way the account's driver addresses them.
1330
+ */
1331
+ async dropUnreadableListedObjects(accountId, objects, user, md) {
1332
+ const files = objects.filter((o) => !o.isDirectory);
1333
+ if (files.length === 0)
1334
+ return objects;
1335
+ const providerID = await this.accountProviderID(accountId, user, md);
1336
+ const driver = await FileStorageEngine.Instance.GetDriver(accountId, user);
1337
+ const unreadable = await StorageAccessEvaluator.Instance.UnreadableTrackedObjectKeys(providerID, files.map((o) => o.fullPath), user, md, driver);
1338
+ return objects.filter((o) => o.isDirectory || !unreadable.has(o.fullPath));
1339
+ }
1340
+ /** Converts per-account results to the GraphQL payload, recomputing the totals from what is actually returned. */
1341
+ toSearchAcrossAccountsPayload(accountResults) {
1342
+ const payloadResults = accountResults.map((ar) => ({
1165
1343
  accountID: ar.accountID,
1166
1344
  accountName: ar.accountName,
1167
1345
  success: ar.success,
@@ -1182,10 +1360,10 @@ let FileResolver = class FileResolver extends FileResolverBase {
1182
1360
  nextPageToken: ar.nextPageToken,
1183
1361
  }));
1184
1362
  return {
1185
- accountResults,
1186
- totalResultsReturned: result.totalResultsReturned,
1187
- successfulAccounts: result.successfulAccounts,
1188
- failedAccounts: result.failedAccounts,
1363
+ accountResults: payloadResults,
1364
+ totalResultsReturned: accountResults.reduce((sum, ar) => sum + (ar.success ? ar.results.length : 0), 0),
1365
+ successfulAccounts: accountResults.filter((ar) => ar.success).length,
1366
+ failedAccounts: accountResults.filter((ar) => !ar.success).length,
1189
1367
  };
1190
1368
  }
1191
1369
  };