@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
@@ -10,18 +10,18 @@ var __metadata = (this && this.__metadata) || function (k, v) {
10
10
  var __param = (this && this.__param) || function (paramIndex, decorator) {
11
11
  return function (target, key) { decorator(target, key, paramIndex); }
12
12
  };
13
- import { EntityPermissionType, FieldValueCollection, LogError } from '@memberjunction/core';
13
+ import { AssertClientInputWritableForUser, 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';
20
+ import { ProjectEntityObjectForCaller } from '../generic/fieldSecurityTransport.js';
22
21
  import { MediaAccessKeyManager } from '../rest/MediaAccessKeys.js';
23
22
  import { UploadTokenManager } from '../rest/UploadTokenManager.js';
24
23
  import { DeriveSidecarPath, ParsePeaksSidecar } from './peaksSidecar.js';
24
+ import { ApiRoute } from '../apiRoot.js';
25
25
  let CreateUploadURLInput = class CreateUploadURLInput {
26
26
  };
27
27
  __decorate([
@@ -576,16 +576,9 @@ MediaAccessTokenResult = __decorate([
576
576
  ObjectType()
577
577
  ], MediaAccessTokenResult);
578
578
  export { MediaAccessTokenResult };
579
+ /** The failure reported for an account `SearchAcrossAccounts` refuses — the same whether it is restricted or does not exist. */
580
+ const SEARCH_ACCOUNT_REFUSED_MESSAGE = 'Access denied: you do not have access to this storage account or it does not exist.';
579
581
  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
582
  /**
590
583
  * Mints a short-lived signed media-access token for an `MJ: Files` record and returns the
591
584
  * authenticated streaming URL (`<publicBase>/media/<fileId>?token=<token>`). Permission-gated
@@ -607,14 +600,17 @@ let FileResolver = class FileResolver extends FileResolverBase {
607
600
  if (!loaded) {
608
601
  return { Success: false, ErrorMessage: 'You do not have access to this file or it does not exist.' };
609
602
  }
603
+ // Storage gate: the token is the capability the /media route honours, so it is minted only when the caller may read
604
+ // the account the row's provider resolves to and every row tracking the object (throws — caught below).
605
+ const resolved = await this.assertFileObjectAccess(fileEntity, 'Read', contextUser, provider);
610
606
  // Access authorized — mint the capability token.
611
607
  const { Token, ExpiresAt } = MediaAccessKeyManager.Instance.Sign(fileId, contextUser.ID);
612
608
  const fileNameSuffix = fileEntity.Name ? `/${encodeURIComponent(fileEntity.Name)}` : '';
613
- const url = `${this.resolvePublicBaseUrl()}/media/${encodeURIComponent(fileId)}${fileNameSuffix}?token=${encodeURIComponent(Token)}`;
609
+ const url = ApiRoute(`/media/${encodeURIComponent(fileId)}${fileNameSuffix}?token=${encodeURIComponent(Token)}`, { absolute: true });
614
610
  // Best-effort: surface precomputed waveform peaks from a peaks.json sidecar beside the file, so
615
611
  // the player renders the real waveform instantly without fetching/decoding the audio. Never
616
612
  // blocks token minting — any failure just omits Peaks.
617
- const peaks = await this.tryReadPeaksSidecar(fileEntity, contextUser);
613
+ const peaks = await this.tryReadPeaksSidecar(fileEntity, resolved?.Driver);
618
614
  return { Success: true, Token, Url: url, ExpiresAt, MimeType: fileEntity.ContentType ?? undefined, Peaks: peaks };
619
615
  }
620
616
  catch (error) {
@@ -627,33 +623,22 @@ let FileResolver = class FileResolver extends FileResolverBase {
627
623
  * Best-effort read of a `peaks.json` waveform sidecar that sits in the SAME storage folder as the
628
624
  * given file (a JSON array of normalized `0..1` numbers, written at capture time). Derives the
629
625
  * 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.
626
+ * through the file's own storage driver — the one {@link assertFileObjectAccess} resolved, so the account is already
627
+ * gated — parses + validates it, and returns sanitized peaks. Returns `undefined` on ANY failure (no ProviderKey, no
628
+ * driver, no sidecar, parse error, garbage) — the caller treats peaks as a pure optimization and must never let a
629
+ * sidecar problem affect token minting.
633
630
  *
634
631
  * @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.
632
+ * @param driver The gated driver for the file's account, or undefined when the provider has none.
636
633
  * @returns Sanitized `0..1` peaks (length-capped), or `undefined`.
637
634
  */
638
- async tryReadPeaksSidecar(file, contextUser) {
635
+ async tryReadPeaksSidecar(file, driver) {
639
636
  try {
640
637
  // The sidecar lives next to the recording: replace the final path segment with peaks.json.
641
638
  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) {
639
+ if (!sidecarPath || !driver) {
654
640
  return undefined;
655
641
  }
656
- const driver = await FileStorageEngine.Instance.GetDriver(account.ID, contextUser);
657
642
  const bytes = await driver.GetObject({ fullPath: sidecarPath });
658
643
  if (!bytes || bytes.length === 0) {
659
644
  return undefined;
@@ -689,6 +674,65 @@ let FileResolver = class FileResolver extends FileResolverBase {
689
674
  accountEntity,
690
675
  };
691
676
  }
677
+ /**
678
+ * The storage-account gate. Every route keyed by a client-supplied account ID runs it FIRST — before the account is
679
+ * loaded and before any driver call. Throws `StorageAccountAccessDeniedError` ("You do not have access to this storage
680
+ * account or it does not exist.") unless the caller may use the account for `access`, evaluated per call against
681
+ * `MJ: File Storage Account Permissions` by `StorageAccessEvaluator` (`@memberjunction/storage`). An unknown account
682
+ * gets the same refusal, so the error never reveals whether an account ID is real.
683
+ */
684
+ async assertAccountAccess(accountId, access, context) {
685
+ const md = GetReadOnlyProvider(context.providers, { allowFallbackToReadWrite: true });
686
+ const user = this.GetUserFromPayload(context.userPayload);
687
+ await StorageAccessEvaluator.Instance.AssertAccountAccess(accountId, user, access, md);
688
+ }
689
+ /**
690
+ * The tracked-file rule for an account-keyed route: when any of `objectNames` in the account backs an `MJ: Files` row,
691
+ * the caller must be able to read that row — the rule {@link GetFileContents} applies to a file ID — else this throws
692
+ * `TrackedFileAccessDeniedError` ("You do not have access to this file or it does not exist."). Keys are compared the
693
+ * way the account's driver addresses them (`FileStorageBase.NormalizeObjectKey`) and case-insensitively; a key whose
694
+ * meaning depends on the provider (`..`, `\`, `%2F`, ...) is refused outright. Applied to every object a route reads
695
+ * or signs, and to every object it overwrites, moves or deletes — the minimum for a write is that the caller may read
696
+ * what it replaces. An object no row tracks is governed by the account gate, which the caller has already passed.
697
+ */
698
+ async assertTrackedObjectReadable(accountId, objectNames, user, md) {
699
+ const providerID = await this.accountProviderID(accountId, user, md);
700
+ const driver = await FileStorageEngine.Instance.GetDriver(accountId, user);
701
+ await StorageAccessEvaluator.Instance.AssertTrackedObjectsReadable(providerID, objectNames, user, md, driver);
702
+ }
703
+ /**
704
+ * The storage gate for a route that goes from an `MJ: Files` row (already loaded as the caller) to its object: the
705
+ * account the row's provider resolves to must allow `access`, and every row tracking the object must be readable by
706
+ * the caller (`FileStorageEngine.ResolveFileObject`) — so a row whose client-written `ProviderID`/`ProviderKey`/`Name`
707
+ * aliases someone else's object is refused. `extraKeys` (a rename target) get the tracked-file rule too. When the
708
+ * provider has no storage account the legacy env-configured driver serves the row; there is no account to gate, so the
709
+ * tracked-file rule alone applies. Returns the resolved object, or `null` in that legacy case.
710
+ */
711
+ async assertFileObjectAccess(file, access, user, md, extraKeys = []) {
712
+ const location = { ProviderID: file.ProviderID, ProviderKey: file.ProviderKey ?? null, Name: file.Name };
713
+ const resolved = await FileStorageEngine.Instance.ResolveFileObject(location, user, access, md);
714
+ const keys = resolved ? extraKeys : [StorageObjectKeyOf(location), ...extraKeys];
715
+ if (keys.length > 0) {
716
+ await StorageAccessEvaluator.Instance.AssertTrackedObjectsReadable(file.ProviderID, keys, user, md, resolved?.Driver);
717
+ }
718
+ return resolved;
719
+ }
720
+ /**
721
+ * The storage provider ID of an account the caller has already passed the gate for, from the engine's cache (refreshed
722
+ * once on a miss). Refuses like the gate when the account still cannot be found.
723
+ */
724
+ async accountProviderID(accountId, user, md) {
725
+ await FileStorageEngine.Instance.Config(false, user, md);
726
+ let account = FileStorageEngine.Instance.GetAccountById(accountId);
727
+ if (!account) {
728
+ await FileStorageEngine.Instance.RefreshAfterMiss(md);
729
+ account = FileStorageEngine.Instance.GetAccountById(accountId);
730
+ }
731
+ if (!account) {
732
+ throw new StorageAccountAccessDeniedError();
733
+ }
734
+ return account.ProviderID;
735
+ }
692
736
  /**
693
737
  * Loads a FileStorageAccount and its associated FileStorageProvider.
694
738
  * This is the standard way to get provider information in the enterprise model.
@@ -729,6 +773,12 @@ let FileResolver = class FileResolver extends FileResolverBase {
729
773
  const fileEntity = await provider.GetEntityObject('MJ: Files', user);
730
774
  const providerEntity = await provider.GetEntityObject('MJ: File Storage Providers', user);
731
775
  fileEntity.CheckPermissions(EntityPermissionType.Create, true);
776
+ // Field-level security: refuse a key the caller may not read, or that names no field, before anything else
777
+ AssertClientInputWritableForUser(fileEntity.EntityInfo, { ...input }, user);
778
+ // The upload URL signs an overwrite of `Name` (and the row will point at `ProviderKey`): Write on the account the
779
+ // provider resolves to, and the tracked-file rule on both keys, as for every other route that writes an object.
780
+ const location = { ProviderID: input.ProviderID ?? '', ProviderKey: null, Name: input.Name ?? '' };
781
+ await this.assertFileObjectAccess(location, 'Write', user, provider, input.ProviderKey ? [input.ProviderKey] : []);
732
782
  const [sameName] = await this.findBy(provider, 'MJ: Files', { Name: input.Name, ProviderID: input.ProviderID }, context.userPayload.userRecord);
733
783
  const NameExists = Boolean(sameName);
734
784
  const success = fileEntity.NewRecord(FieldValueCollection.FromObject({ ...input, Status: 'Pending' }));
@@ -746,7 +796,7 @@ let FileResolver = class FileResolver extends FileResolverBase {
746
796
  if (!saved) {
747
797
  console.error('[CreateFile] File save failed:', fileEntity.LatestResult?.CompleteMessage);
748
798
  }
749
- const File = mapper.MapFields({ ...fileEntity.GetAll() });
799
+ const File = this.fileResponse(fileEntity, user, provider);
750
800
  return { File, UploadUrl, NameExists };
751
801
  }
752
802
  /**
@@ -761,6 +811,12 @@ let FileResolver = class FileResolver extends FileResolverBase {
761
811
  // Permission check on MJ: Files
762
812
  const fileEntity = await provider.GetEntityObject('MJ: Files', user);
763
813
  fileEntity.CheckPermissions(EntityPermissionType.Create, true);
814
+ // Write gate on the account the upload lands in — before the staged upload is consumed or any driver is touched.
815
+ // With no account at all there is nothing to gate; UploadFile then refuses with its documented error.
816
+ const targetAccountId = await this.resolveUploadAccountId(input.AccountID, user, provider);
817
+ if (targetAccountId) {
818
+ await this.assertAccountAccess(targetAccountId, 'Write', context);
819
+ }
764
820
  let buffer;
765
821
  let fileName = input.FileName || 'file';
766
822
  let mimeType = input.MimeType || 'application/octet-stream';
@@ -785,25 +841,19 @@ let FileResolver = class FileResolver extends FileResolverBase {
785
841
  ErrorMessage: 'Either UploadToken or Base64Data must be provided.',
786
842
  };
787
843
  }
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
844
  const uploadResult = await FileStorageEngine.Instance.UploadFile({
794
845
  content: buffer,
795
846
  fileName,
796
847
  mimeType,
797
848
  contextUser: user,
798
- storageAccountId: input.AccountID,
849
+ storageAccountId: targetAccountId,
799
850
  categoryID: input.CategoryID,
800
851
  description: input.Description,
801
852
  pathPrefix: input.PathPrefix,
802
853
  provider,
803
854
  });
804
855
  const loaded = await fileEntity.Load(uploadResult.FileID);
805
- const mapper = new FieldMapper();
806
- const File = loaded ? mapper.MapFields({ ...fileEntity.GetAll() }) : undefined;
856
+ const File = loaded ? this.fileResponse(fileEntity, user, provider) : undefined;
807
857
  return {
808
858
  Success: true,
809
859
  FileID: uploadResult.FileID,
@@ -819,32 +869,49 @@ let FileResolver = class FileResolver extends FileResolverBase {
819
869
  };
820
870
  }
821
871
  }
872
+ /**
873
+ * The file record for a response: its field values as the caller may read them, in GraphQL field
874
+ * names. The record was just saved or reloaded, and a save re-reads the whole row.
875
+ */
876
+ fileResponse(fileEntity, user, provider) {
877
+ return new FieldMapper().MapFields({ ...ProjectEntityObjectForCaller(fileEntity, user, provider) });
878
+ }
879
+ /**
880
+ * The account an upload will land in: the requested one, else the engine's default (the first active account) —
881
+ * returned as an explicit ID so the account the gate checks is the account `UploadFile` uses. A requested ID is
882
+ * returned as-is: the gate refuses an unknown one without revealing that it does not exist, and `UploadFile` refreshes
883
+ * the engine's cache itself when a permitted account is missing from it.
884
+ */
885
+ async resolveUploadAccountId(accountId, user, md) {
886
+ if (accountId) {
887
+ return accountId;
888
+ }
889
+ await FileStorageEngine.Instance.Config(false, user, md);
890
+ return FileStorageEngine.Instance.ResolveStorageAccount()?.account.ID;
891
+ }
822
892
  async DownloadUrl(file, context) {
823
893
  const md = GetReadOnlyProvider(context.providers, { allowFallbackToReadWrite: true });
824
894
  const user = this.GetUserFromPayload(context.userPayload);
825
895
  const fileEntity = await md.GetEntityObject('MJ: Files', user);
826
896
  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);
897
+ // Storage gate: Read on the account the row's provider resolves to, and the tracked-file rule on its object
898
+ const location = { ProviderID: file.ProviderID, ProviderKey: file.ProviderKey ?? null, Name: file.Name ?? '' };
899
+ const resolved = await this.assertFileObjectAccess(location, 'Read', user, md);
900
+ if (resolved) {
901
+ return await resolved.Driver.CreatePreAuthDownloadUrl(resolved.ObjectKey);
837
902
  }
838
903
  const providerEntity = await md.GetEntityObject('MJ: File Storage Providers', user);
839
904
  await providerEntity.Load(file.ProviderID);
840
905
  const userContext = this.buildUserContext(context);
841
- const url = await createDownloadUrl(providerEntity, file.ProviderKey ?? file.Name, userContext);
906
+ const url = await createDownloadUrl(providerEntity, StorageObjectKeyOf(file), userContext);
842
907
  return url;
843
908
  }
844
909
  /**
845
910
  * Returns an `MJ: Files` record's bytes as base64, read server-side through authenticated MJStorage
846
911
  * (`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.
912
+ * calling user's context, so MJ row-level security determines access; then the storage gate
913
+ * ({@link assertFileObjectAccess}) requires Read on the account the row's provider resolves to and refuses an object
914
+ * another row tracks when the caller cannot read that row. Never throws to the client.
848
915
  *
849
916
  * @param fileId The `MJ: Files` id whose bytes to return.
850
917
  * @returns `{ Success, Base64?, MimeType?, ErrorMessage? }`.
@@ -860,15 +927,17 @@ let FileResolver = class FileResolver extends FileResolverBase {
860
927
  if (!loaded) {
861
928
  return { Success: false, ErrorMessage: 'You do not have access to this file or it does not exist.' };
862
929
  }
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) {
930
+ // Storage gate, then read the bytes via authenticated MJStorage (server-side GetObject on the file's own account).
931
+ // A refusal throws and is reported below; a row whose provider has no account has no bytes to read.
932
+ const resolved = await this.assertFileObjectAccess(fileEntity, 'Read', contextUser, provider);
933
+ const bytes = resolved ? await resolved.Driver.GetObject({ fullPath: resolved.ObjectKey }) : null;
934
+ if (!bytes || bytes.length === 0) {
866
935
  return { Success: false, ErrorMessage: 'The file could not be read from storage.' };
867
936
  }
868
937
  return {
869
938
  Success: true,
870
- Base64: result.Bytes.toString('base64'),
871
- MimeType: result.MimeType ?? fileEntity.ContentType ?? undefined,
939
+ Base64: bytes.toString('base64'),
940
+ MimeType: fileEntity.ContentType ?? undefined,
872
941
  };
873
942
  }
874
943
  catch (error) {
@@ -885,6 +954,8 @@ let FileResolver = class FileResolver extends FileResolverBase {
885
954
  fileEntity.CheckPermissions(EntityPermissionType.Update, true);
886
955
  await fileEntity.Load(input.ID);
887
956
  if (fileEntity.Name !== input.Name) {
957
+ // Renaming moves the object: Write on its account, and the tracked-file rule on the object and the new name
958
+ await this.assertFileObjectAccess(fileEntity, 'Write', user, md, [fileEntity.Name, input.Name]);
888
959
  const providerEntity = await md.GetEntityObject('MJ: File Storage Providers', user);
889
960
  await providerEntity.Load(fileEntity.ProviderID);
890
961
  const userContext = this.buildUserContext(context);
@@ -905,12 +976,14 @@ let FileResolver = class FileResolver extends FileResolverBase {
905
976
  return null;
906
977
  }
907
978
  fileEntity.CheckPermissions(EntityPermissionType.Delete, true);
908
- // Only delete the object from the provider if it's actually been uploaded
979
+ // Only delete the object from the provider if it's actually been uploaded — after the storage gate: Write on its
980
+ // account, and no other row the caller cannot read may track the object (a row can alias another row's object)
909
981
  if (fileEntity.Status === 'Uploaded') {
982
+ await this.assertFileObjectAccess(fileEntity, 'Write', userInfo, md);
910
983
  const providerEntity = await md.GetEntityObject('MJ: File Storage Providers', userInfo);
911
984
  await providerEntity.Load(fileEntity.ProviderID);
912
985
  const userContext = this.buildUserContext(context);
913
- await deleteObject(providerEntity, fileEntity.ProviderKey ?? fileEntity.Name, userContext);
986
+ await deleteObject(providerEntity, StorageObjectKeyOf(fileEntity), userContext);
914
987
  }
915
988
  return super.DeleteMJFile(ID, options, context, pubSub);
916
989
  }
@@ -922,12 +995,14 @@ let FileResolver = class FileResolver extends FileResolverBase {
922
995
  });
923
996
  const md = GetReadOnlyProvider(context.providers, { allowFallbackToReadWrite: true });
924
997
  const user = this.GetUserFromPayload(context.userPayload);
998
+ // Account gate first — before the account is loaded or any driver is touched
999
+ await this.assertAccountAccess(input.AccountID, 'Read', context);
925
1000
  // Load the account and its provider
926
1001
  const { account, provider: providerEntity } = await this.loadAccountAndProvider(input.AccountID, context);
927
1002
  console.log('[FileResolver] Provider loaded:', {
928
1003
  Name: providerEntity.Name,
929
1004
  ServerDriverKey: providerEntity.ServerDriverKey,
930
- HasConfiguration: !!providerEntity.Get('Configuration'),
1005
+ HasConfiguration: !!providerEntity.Configuration,
931
1006
  });
932
1007
  // Check permissions - user must have read access to Files entity
933
1008
  const fileEntity = await md.GetEntityObject('MJ: Files', user);
@@ -941,8 +1016,10 @@ let FileResolver = class FileResolver extends FileResolverBase {
941
1016
  objects: result.objects.map((o) => ({ name: o.name, isDirectory: o.isDirectory })),
942
1017
  prefixes: result.prefixes,
943
1018
  });
944
- // Convert Date objects to ISO strings for GraphQL
945
- const objects = result.objects.map((obj) => ({
1019
+ // Drop objects behind an MJ: Files row the caller cannot read — a listing never names a tracked object the caller
1020
+ // is refused — then convert Date objects to ISO strings for GraphQL
1021
+ const visible = await this.dropUnreadableListedObjects(input.AccountID, result.objects, user, md);
1022
+ const objects = visible.map((obj) => ({
946
1023
  ...obj,
947
1024
  lastModified: obj.lastModified.toISOString(),
948
1025
  }));
@@ -957,6 +1034,9 @@ let FileResolver = class FileResolver extends FileResolverBase {
957
1034
  // Check permissions
958
1035
  const fileEntity = await md.GetEntityObject('MJ: Files', user);
959
1036
  fileEntity.CheckPermissions(EntityPermissionType.Read, true);
1037
+ // Account gate, then — when the object backs an MJ: Files row — that row must be readable by the caller
1038
+ await this.assertAccountAccess(input.AccountID, 'Read', context);
1039
+ await this.assertTrackedObjectReadable(input.AccountID, [input.ObjectName], user, md);
960
1040
  await FileStorageEngine.Instance.Config(false, user, md);
961
1041
  const driver = await FileStorageEngine.Instance.GetDriver(input.AccountID, user);
962
1042
  const downloadUrl = await driver.CreatePreAuthDownloadUrl(input.ObjectName);
@@ -982,6 +1062,9 @@ let FileResolver = class FileResolver extends FileResolverBase {
982
1062
  // Check permissions
983
1063
  const fileEntity = await md.GetEntityObject('MJ: Files', user);
984
1064
  fileEntity.CheckPermissions(EntityPermissionType.Create, true);
1065
+ await this.assertAccountAccess(input.AccountID, 'Write', context);
1066
+ // An upload URL can overwrite: the caller must be able to read any MJ: Files row that tracks the target
1067
+ await this.assertTrackedObjectReadable(input.AccountID, [input.ObjectName], user, md);
985
1068
  await FileStorageEngine.Instance.Config(false, user, md);
986
1069
  const driver = await FileStorageEngine.Instance.GetDriver(input.AccountID, user);
987
1070
  if (!driver.SupportsPreAuthUpload) {
@@ -1008,7 +1091,7 @@ let FileResolver = class FileResolver extends FileResolverBase {
1008
1091
  return {
1009
1092
  Success: true,
1010
1093
  Token: mint.Token,
1011
- Url: '/media/upload-stage',
1094
+ Url: ApiRoute('/media/upload-stage'),
1012
1095
  };
1013
1096
  }
1014
1097
  catch (err) {
@@ -1022,11 +1105,14 @@ let FileResolver = class FileResolver extends FileResolverBase {
1022
1105
  async DeleteStorageObject(input, context) {
1023
1106
  const md = GetReadOnlyProvider(context.providers, { allowFallbackToReadWrite: true });
1024
1107
  const user = this.GetUserFromPayload(context.userPayload);
1108
+ // Account gate first — before the account is loaded or any driver is touched
1109
+ await this.assertAccountAccess(input.AccountID, 'Write', context);
1025
1110
  // Load the account and its provider
1026
1111
  const { account, provider: providerEntity } = await this.loadAccountAndProvider(input.AccountID, context);
1027
1112
  // Check permissions
1028
1113
  const fileEntity = await md.GetEntityObject('MJ: Files', user);
1029
1114
  fileEntity.CheckPermissions(EntityPermissionType.Delete, true);
1115
+ await this.assertTrackedObjectReadable(input.AccountID, [input.ObjectName], user, md);
1030
1116
  // Delete the object with extended user context (includes account for credential lookup)
1031
1117
  const userContext = this.buildExtendedUserContext(context, account);
1032
1118
  const success = await deleteObject(providerEntity, input.ObjectName, userContext);
@@ -1035,11 +1121,15 @@ let FileResolver = class FileResolver extends FileResolverBase {
1035
1121
  async MoveStorageObject(input, context) {
1036
1122
  const md = GetReadOnlyProvider(context.providers, { allowFallbackToReadWrite: true });
1037
1123
  const user = this.GetUserFromPayload(context.userPayload);
1124
+ // Account gate first — before the account is loaded or any driver is touched
1125
+ await this.assertAccountAccess(input.AccountID, 'Write', context);
1038
1126
  // Load the account and its provider
1039
1127
  const { account, provider: providerEntity } = await this.loadAccountAndProvider(input.AccountID, context);
1040
1128
  // Check permissions
1041
1129
  const fileEntity = await md.GetEntityObject('MJ: Files', user);
1042
1130
  fileEntity.CheckPermissions(EntityPermissionType.Update, true);
1131
+ // A move reads and removes the source and may overwrite the destination: both get the tracked-file rule
1132
+ await this.assertTrackedObjectReadable(input.AccountID, [input.OldName, input.NewName], user, md);
1043
1133
  // Move the object with extended user context (includes account for credential lookup)
1044
1134
  const userContext = this.buildExtendedUserContext(context, account);
1045
1135
  const success = await moveObject(providerEntity, input.OldName, input.NewName, userContext);
@@ -1048,12 +1138,17 @@ let FileResolver = class FileResolver extends FileResolverBase {
1048
1138
  async CopyStorageObject(input, context) {
1049
1139
  const md = GetReadOnlyProvider(context.providers, { allowFallbackToReadWrite: true });
1050
1140
  const user = this.GetUserFromPayload(context.userPayload);
1141
+ // Account gate first — a copy within one account writes to it — before the account is loaded or any driver is touched
1142
+ await this.assertAccountAccess(input.AccountID, 'Write', context);
1051
1143
  // Load the account and its provider
1052
1144
  const { account, provider: providerEntity } = await this.loadAccountAndProvider(input.AccountID, context);
1053
1145
  // Check permissions - copying requires both read (source) and create (destination)
1054
1146
  const fileEntity = await md.GetEntityObject('MJ: Files', user);
1055
1147
  fileEntity.CheckPermissions(EntityPermissionType.Read, true);
1056
1148
  fileEntity.CheckPermissions(EntityPermissionType.Create, true);
1149
+ // A copy of a tracked object is an untracked one the caller could then download, and the destination may be
1150
+ // overwritten: both get the tracked-file rule
1151
+ await this.assertTrackedObjectReadable(input.AccountID, [input.SourceName, input.DestinationName], user, md);
1057
1152
  // Copy the object with extended user context (includes account for credential lookup)
1058
1153
  const userContext = this.buildExtendedUserContext(context, account);
1059
1154
  const success = await copyObject(providerEntity, input.SourceName, input.DestinationName, userContext);
@@ -1062,6 +1157,8 @@ let FileResolver = class FileResolver extends FileResolverBase {
1062
1157
  async CreateDirectory(input, context) {
1063
1158
  const md = GetReadOnlyProvider(context.providers, { allowFallbackToReadWrite: true });
1064
1159
  const user = this.GetUserFromPayload(context.userPayload);
1160
+ // Account gate first — before the account is loaded or any driver is touched
1161
+ await this.assertAccountAccess(input.AccountID, 'Write', context);
1065
1162
  // Load the account and its provider
1066
1163
  const { account: accountEntity, provider: providerEntity } = await this.loadAccountAndProvider(input.AccountID, context);
1067
1164
  // Check permissions
@@ -1086,6 +1183,12 @@ let FileResolver = class FileResolver extends FileResolverBase {
1086
1183
  const fileEntity = await md.GetEntityObject('MJ: Files', user);
1087
1184
  fileEntity.CheckPermissions(EntityPermissionType.Read, true);
1088
1185
  fileEntity.CheckPermissions(EntityPermissionType.Create, true);
1186
+ // Account gates first: read the source, write the destination — before either account is loaded or touched
1187
+ await this.assertAccountAccess(input.SourceAccountID, 'Read', context);
1188
+ await this.assertAccountAccess(input.DestinationAccountID, 'Write', context);
1189
+ // Tracked-file rule on both ends: the source (its copy would be untracked), the destination (it may be overwritten)
1190
+ await this.assertTrackedObjectReadable(input.SourceAccountID, [input.SourcePath], user, md);
1191
+ await this.assertTrackedObjectReadable(input.DestinationAccountID, [input.DestinationPath], user, md);
1089
1192
  // Load the source account and its provider
1090
1193
  const { account: sourceAccount, provider: sourceProviderEntity } = await this.loadAccountAndProvider(input.SourceAccountID, context);
1091
1194
  // Load the destination account and its provider
@@ -1121,47 +1224,131 @@ let FileResolver = class FileResolver extends FileResolverBase {
1121
1224
  // Check permissions - searching requires read access
1122
1225
  const fileEntity = await md.GetEntityObject('MJ: Files', user);
1123
1226
  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
- });
1227
+ // Account gate BEFORE any driver call: refused accounts are reported as failed and never searched
1228
+ const { searchable, refused } = await this.partitionSearchAccounts(input.AccountIDs, user, md);
1229
+ const result = searchable.length > 0
1230
+ ? await searchAcrossAccounts(searchable, input.Query, {
1231
+ maxResultsPerAccount: input.MaxResultsPerAccount,
1232
+ fileTypes: input.FileTypes,
1233
+ searchContent: input.SearchContent,
1234
+ contextUser: user,
1235
+ })
1236
+ : { accountResults: [], totalResultsReturned: 0, successfulAccounts: 0, failedAccounts: 0 };
1158
1237
  console.log('[FileResolver] SearchAcrossAccounts result:', {
1159
1238
  totalResultsReturned: result.totalResultsReturned,
1160
1239
  successfulAccounts: result.successfulAccounts,
1161
1240
  failedAccounts: result.failedAccounts,
1241
+ refusedAccounts: refused.length,
1162
1242
  });
1163
- // Convert results to GraphQL payload format
1164
- const accountResults = result.accountResults.map((ar) => ({
1243
+ const kept = await this.dropUnreadableTrackedHits(result.accountResults, user, md);
1244
+ return this.toSearchAcrossAccountsPayload(this.inRequestOrder(input.AccountIDs, [...kept, ...refused]));
1245
+ }
1246
+ /**
1247
+ * Per-account results in the order the accounts were requested (each requested ID once), whether searched or refused —
1248
+ * so a refused account sits where the caller put it rather than after every searched one.
1249
+ */
1250
+ inRequestOrder(requestedIDs, results) {
1251
+ const byID = new Map(results.map((r) => [NormalizeUUID(r.accountID), r]));
1252
+ const ordered = [];
1253
+ for (const id of requestedIDs) {
1254
+ const result = byID.get(NormalizeUUID(id));
1255
+ if (result) {
1256
+ ordered.push(result);
1257
+ byID.delete(NormalizeUUID(id));
1258
+ }
1259
+ }
1260
+ return [...ordered, ...byID.values()];
1261
+ }
1262
+ /**
1263
+ * Splits the requested accounts into those to search and those refused, before any driver call. An account the
1264
+ * caller may not read is refused with {@link SEARCH_ACCOUNT_REFUSED_MESSAGE} — and so is one that does not exist,
1265
+ * identically, so the response never reveals which IDs are real. Readable accounts are paired with their provider
1266
+ * from the engine's cache (refreshed once if a readable account is missing from it). Duplicate IDs are searched once.
1267
+ */
1268
+ async partitionSearchAccounts(accountIDs, user, md) {
1269
+ const readable = await StorageAccessEvaluator.Instance.AccessibleAccountIDs(accountIDs, user, 'Read', md);
1270
+ await FileStorageEngine.Instance.Config(false, user, md);
1271
+ if ([...readable].some((id) => !FileStorageEngine.Instance.GetAccountWithProvider(id))) {
1272
+ await FileStorageEngine.Instance.RefreshAfterMiss(md);
1273
+ }
1274
+ const searchable = [];
1275
+ const refused = [];
1276
+ const seen = new Set();
1277
+ for (const requestedID of accountIDs) {
1278
+ const key = NormalizeUUID(requestedID);
1279
+ if (seen.has(key))
1280
+ continue;
1281
+ seen.add(key);
1282
+ const pair = readable.has(key) ? FileStorageEngine.Instance.GetAccountWithProvider(requestedID) : null;
1283
+ if (pair) {
1284
+ searchable.push({ accountEntity: pair.account, providerEntity: pair.provider });
1285
+ }
1286
+ else {
1287
+ refused.push(this.refusedAccountResult(requestedID));
1288
+ }
1289
+ }
1290
+ return { searchable, refused };
1291
+ }
1292
+ /** A failed per-account entry for an account the caller may not search; it names nothing beyond the requested ID. */
1293
+ refusedAccountResult(accountID) {
1294
+ return {
1295
+ accountID,
1296
+ accountName: '',
1297
+ providerID: '',
1298
+ providerName: '',
1299
+ success: false,
1300
+ errorMessage: SEARCH_ACCOUNT_REFUSED_MESSAGE,
1301
+ results: [],
1302
+ hasMore: false,
1303
+ };
1304
+ }
1305
+ /**
1306
+ * Drops search hits that map to an `MJ: Files` row the caller may not read — one batched lookup per account, keyed on
1307
+ * each hit's path and provider object ID (`StorageAccessEvaluator.UnreadableTrackedObjectKeys`, compared the way the
1308
+ * account's driver addresses keys). Hits no row tracks stay; the account gate governs them.
1309
+ *
1310
+ * When any hit is dropped, the provider's `totalMatches` (which counted it) and `nextPageToken` (this route takes no
1311
+ * page token back) are withheld; `hasMore` is kept. How reliable the mapping is depends on the provider: for
1312
+ * object-key stores (S3, Azure Blob, GCS) a hit's path IS the row's `ProviderKey`; for hierarchical providers (Box,
1313
+ * Google Drive, Dropbox, SharePoint) a search path may not match the key a row was written with, and such a miss falls
1314
+ * back to the account gate. `MJ: Files` rows are keyed by provider, not account, so the lookup errs toward dropping.
1315
+ */
1316
+ async dropUnreadableTrackedHits(accountResults, user, md) {
1317
+ return Promise.all(accountResults.map((ar) => this.dropUnreadableAccountHits(ar, user, md)));
1318
+ }
1319
+ /** {@link dropUnreadableTrackedHits} for one account's results; if the check cannot run, the account's hits are dropped. */
1320
+ async dropUnreadableAccountHits(ar, user, md) {
1321
+ if (!ar.success || ar.results.length === 0)
1322
+ return ar;
1323
+ try {
1324
+ const driver = await FileStorageEngine.Instance.GetDriver(ar.accountID, user);
1325
+ const keys = ar.results.flatMap((r) => [r.path, r.objectId].filter((k) => !!k));
1326
+ const unreadable = await StorageAccessEvaluator.Instance.UnreadableTrackedObjectKeys(ar.providerID, keys, user, md, driver);
1327
+ const results = ar.results.filter((r) => !unreadable.has(r.path) && !(r.objectId && unreadable.has(r.objectId)));
1328
+ return results.length === ar.results.length ? ar : { ...ar, results, totalMatches: undefined, nextPageToken: undefined };
1329
+ }
1330
+ catch (error) {
1331
+ const reason = error instanceof Error ? error.message : String(error);
1332
+ LogError(`SearchAcrossAccounts: tracked-file check failed for account ${ar.accountID} — its hits are withheld: ${reason}`);
1333
+ return { ...ar, results: [], totalMatches: undefined, nextPageToken: undefined };
1334
+ }
1335
+ }
1336
+ /**
1337
+ * The listed objects minus those behind an `MJ: Files` row the caller may not read (directories are prefixes, never
1338
+ * tracked objects, and are kept). Keys are compared the way the account's driver addresses them.
1339
+ */
1340
+ async dropUnreadableListedObjects(accountId, objects, user, md) {
1341
+ const files = objects.filter((o) => !o.isDirectory);
1342
+ if (files.length === 0)
1343
+ return objects;
1344
+ const providerID = await this.accountProviderID(accountId, user, md);
1345
+ const driver = await FileStorageEngine.Instance.GetDriver(accountId, user);
1346
+ const unreadable = await StorageAccessEvaluator.Instance.UnreadableTrackedObjectKeys(providerID, files.map((o) => o.fullPath), user, md, driver);
1347
+ return objects.filter((o) => o.isDirectory || !unreadable.has(o.fullPath));
1348
+ }
1349
+ /** Converts per-account results to the GraphQL payload, recomputing the totals from what is actually returned. */
1350
+ toSearchAcrossAccountsPayload(accountResults) {
1351
+ const payloadResults = accountResults.map((ar) => ({
1165
1352
  accountID: ar.accountID,
1166
1353
  accountName: ar.accountName,
1167
1354
  success: ar.success,
@@ -1182,10 +1369,10 @@ let FileResolver = class FileResolver extends FileResolverBase {
1182
1369
  nextPageToken: ar.nextPageToken,
1183
1370
  }));
1184
1371
  return {
1185
- accountResults,
1186
- totalResultsReturned: result.totalResultsReturned,
1187
- successfulAccounts: result.successfulAccounts,
1188
- failedAccounts: result.failedAccounts,
1372
+ accountResults: payloadResults,
1373
+ totalResultsReturned: accountResults.reduce((sum, ar) => sum + (ar.success ? ar.results.length : 0), 0),
1374
+ successfulAccounts: accountResults.filter((ar) => ar.success).length,
1375
+ failedAccounts: accountResults.filter((ar) => !ar.success).length,
1189
1376
  };
1190
1377
  }
1191
1378
  };