@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.
- package/README.md +20 -0
- package/dist/.tsbuildinfo +1 -1
- package/dist/agentSessions/BridgeHostInstance.d.ts +25 -0
- package/dist/agentSessions/BridgeHostInstance.d.ts.map +1 -0
- package/dist/agentSessions/BridgeHostInstance.js +30 -0
- package/dist/agentSessions/BridgeHostInstance.js.map +1 -0
- package/dist/agentSessions/HostInstance.d.ts +75 -11
- package/dist/agentSessions/HostInstance.d.ts.map +1 -1
- package/dist/agentSessions/HostInstance.js +99 -28
- package/dist/agentSessions/HostInstance.js.map +1 -1
- package/dist/agentSessions/ReturningVisitorRecap.d.ts.map +1 -1
- package/dist/agentSessions/ReturningVisitorRecap.js +7 -3
- package/dist/agentSessions/ReturningVisitorRecap.js.map +1 -1
- package/dist/agentSessions/SessionJanitor.d.ts +236 -27
- package/dist/agentSessions/SessionJanitor.d.ts.map +1 -1
- package/dist/agentSessions/SessionJanitor.js +682 -54
- package/dist/agentSessions/SessionJanitor.js.map +1 -1
- package/dist/agentSessions/SessionManager.d.ts +48 -3
- package/dist/agentSessions/SessionManager.d.ts.map +1 -1
- package/dist/agentSessions/SessionManager.js +112 -9
- package/dist/agentSessions/SessionManager.js.map +1 -1
- package/dist/agentSessions/SessionRunIDVerifier.d.ts +63 -0
- package/dist/agentSessions/SessionRunIDVerifier.d.ts.map +1 -0
- package/dist/agentSessions/SessionRunIDVerifier.js +129 -0
- package/dist/agentSessions/SessionRunIDVerifier.js.map +1 -0
- package/dist/agentSessions/index.d.ts +2 -0
- package/dist/agentSessions/index.d.ts.map +1 -1
- package/dist/agentSessions/index.js +10 -3
- package/dist/agentSessions/index.js.map +1 -1
- package/dist/apiRoot.d.ts +32 -0
- package/dist/apiRoot.d.ts.map +1 -0
- package/dist/apiRoot.js +67 -0
- package/dist/apiRoot.js.map +1 -0
- package/dist/auth/AuthProviderCatalogRouter.d.ts.map +1 -1
- package/dist/auth/AuthProviderCatalogRouter.js +25 -0
- package/dist/auth/AuthProviderCatalogRouter.js.map +1 -1
- package/dist/auth/LoginScreenEngine.d.ts +22 -0
- package/dist/auth/LoginScreenEngine.d.ts.map +1 -0
- package/dist/auth/LoginScreenEngine.js +57 -0
- package/dist/auth/LoginScreenEngine.js.map +1 -0
- package/dist/auth/actionAuthorization.d.ts +40 -0
- package/dist/auth/actionAuthorization.d.ts.map +1 -0
- package/dist/auth/actionAuthorization.js +94 -0
- package/dist/auth/actionAuthorization.js.map +1 -0
- package/dist/auth/index.d.ts +1 -0
- package/dist/auth/index.d.ts.map +1 -1
- package/dist/auth/index.js +4 -10
- package/dist/auth/index.js.map +1 -1
- package/dist/auth/magicLink/MagicLinkRouter.d.ts.map +1 -1
- package/dist/auth/magicLink/MagicLinkRouter.js +3 -2
- package/dist/auth/magicLink/MagicLinkRouter.js.map +1 -1
- package/dist/auth/magicLink/MagicLinkService.d.ts.map +1 -1
- package/dist/auth/magicLink/MagicLinkService.js +6 -3
- package/dist/auth/magicLink/MagicLinkService.js.map +1 -1
- package/dist/auth/newUsers.d.ts +10 -0
- package/dist/auth/newUsers.d.ts.map +1 -1
- package/dist/auth/newUsers.js +80 -11
- package/dist/auth/newUsers.js.map +1 -1
- package/dist/config.d.ts +2514 -22
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +166 -4
- package/dist/config.js.map +1 -1
- package/dist/context.d.ts +8 -0
- package/dist/context.d.ts.map +1 -1
- package/dist/context.js +14 -11
- package/dist/context.js.map +1 -1
- package/dist/coreRoutes.d.ts +29 -0
- package/dist/coreRoutes.d.ts.map +1 -0
- package/dist/coreRoutes.js +142 -0
- package/dist/coreRoutes.js.map +1 -0
- package/dist/generated/graphql-schemas/__mj.d.ts +504 -3
- package/dist/generated/graphql-schemas/__mj.d.ts.map +1 -1
- package/dist/generated/graphql-schemas/__mj.js +2456 -7
- package/dist/generated/graphql-schemas/__mj.js.map +1 -1
- package/dist/generic/CacheInvalidationResolver.d.ts +43 -8
- package/dist/generic/CacheInvalidationResolver.d.ts.map +1 -1
- package/dist/generic/CacheInvalidationResolver.js +59 -19
- package/dist/generic/CacheInvalidationResolver.js.map +1 -1
- package/dist/generic/OrphanedConversationDetailReconciler.js +3 -3
- package/dist/generic/OrphanedConversationDetailReconciler.js.map +1 -1
- package/dist/generic/PushStatusResolver.js +1 -1
- package/dist/generic/PushStatusResolver.js.map +1 -1
- package/dist/generic/ResolverBase.d.ts +123 -48
- package/dist/generic/ResolverBase.d.ts.map +1 -1
- package/dist/generic/ResolverBase.js +293 -305
- package/dist/generic/ResolverBase.js.map +1 -1
- package/dist/generic/RunViewResolver.d.ts.map +1 -1
- package/dist/generic/RunViewResolver.js +16 -0
- package/dist/generic/RunViewResolver.js.map +1 -1
- package/dist/globalCors.d.ts +33 -0
- package/dist/globalCors.d.ts.map +1 -0
- package/dist/globalCors.js +58 -0
- package/dist/globalCors.js.map +1 -0
- package/dist/index.d.ts +17 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +219 -278
- package/dist/index.js.map +1 -1
- package/dist/isolatedProvider.d.ts +20 -0
- package/dist/isolatedProvider.d.ts.map +1 -0
- package/dist/isolatedProvider.js +22 -0
- package/dist/isolatedProvider.js.map +1 -0
- package/dist/multiTenancy/index.d.ts.map +1 -1
- package/dist/multiTenancy/index.js +22 -2
- package/dist/multiTenancy/index.js.map +1 -1
- package/dist/realtimeProxy/RealtimeProxyServer.d.ts +44 -4
- package/dist/realtimeProxy/RealtimeProxyServer.d.ts.map +1 -1
- package/dist/realtimeProxy/RealtimeProxyServer.js +121 -16
- package/dist/realtimeProxy/RealtimeProxyServer.js.map +1 -1
- package/dist/realtimeProxy/RealtimeRelayTunnel.d.ts +171 -0
- package/dist/realtimeProxy/RealtimeRelayTunnel.d.ts.map +1 -0
- package/dist/realtimeProxy/RealtimeRelayTunnel.js +532 -0
- package/dist/realtimeProxy/RealtimeRelayTunnel.js.map +1 -0
- package/dist/realtimeSessions/RealtimeSessionEventService.d.ts +148 -0
- package/dist/realtimeSessions/RealtimeSessionEventService.d.ts.map +1 -0
- package/dist/realtimeSessions/RealtimeSessionEventService.js +191 -0
- package/dist/realtimeSessions/RealtimeSessionEventService.js.map +1 -0
- package/dist/realtimeSessions/RealtimeSessionVerificationService.d.ts +66 -0
- package/dist/realtimeSessions/RealtimeSessionVerificationService.d.ts.map +1 -0
- package/dist/realtimeSessions/RealtimeSessionVerificationService.js +139 -0
- package/dist/realtimeSessions/RealtimeSessionVerificationService.js.map +1 -0
- package/dist/realtimeSessions/RealtimeVerifyRouter.d.ts +38 -0
- package/dist/realtimeSessions/RealtimeVerifyRouter.d.ts.map +1 -0
- package/dist/realtimeSessions/RealtimeVerifyRouter.js +99 -0
- package/dist/realtimeSessions/RealtimeVerifyRouter.js.map +1 -0
- package/dist/realtimeSessions/RecordingAssemblyScheduler.d.ts +100 -0
- package/dist/realtimeSessions/RecordingAssemblyScheduler.d.ts.map +1 -0
- package/dist/realtimeSessions/RecordingAssemblyScheduler.js +173 -0
- package/dist/realtimeSessions/RecordingAssemblyScheduler.js.map +1 -0
- package/dist/realtimeSessions/SessionVerificationPortsBase.d.ts +61 -0
- package/dist/realtimeSessions/SessionVerificationPortsBase.d.ts.map +1 -0
- package/dist/realtimeSessions/SessionVerificationPortsBase.js +149 -0
- package/dist/realtimeSessions/SessionVerificationPortsBase.js.map +1 -0
- package/dist/realtimeSessions/SlidingWindowRateLimiter.d.ts +57 -0
- package/dist/realtimeSessions/SlidingWindowRateLimiter.d.ts.map +1 -0
- package/dist/realtimeSessions/SlidingWindowRateLimiter.js +69 -0
- package/dist/realtimeSessions/SlidingWindowRateLimiter.js.map +1 -0
- package/dist/realtimeSessions/index.d.ts +19 -0
- package/dist/realtimeSessions/index.d.ts.map +1 -0
- package/dist/realtimeSessions/index.js +19 -0
- package/dist/realtimeSessions/index.js.map +1 -0
- package/dist/realtimeSessions/ownedRealtimeSession.d.ts +30 -0
- package/dist/realtimeSessions/ownedRealtimeSession.d.ts.map +1 -0
- package/dist/realtimeSessions/ownedRealtimeSession.js +47 -0
- package/dist/realtimeSessions/ownedRealtimeSession.js.map +1 -0
- package/dist/realtimeSessions/sessionAccess.d.ts +92 -0
- package/dist/realtimeSessions/sessionAccess.d.ts.map +1 -0
- package/dist/realtimeSessions/sessionAccess.js +94 -0
- package/dist/realtimeSessions/sessionAccess.js.map +1 -0
- package/dist/realtimeSessions/sessionConfigStamps.d.ts +70 -0
- package/dist/realtimeSessions/sessionConfigStamps.d.ts.map +1 -0
- package/dist/realtimeSessions/sessionConfigStamps.js +93 -0
- package/dist/realtimeSessions/sessionConfigStamps.js.map +1 -0
- package/dist/realtimeSessions/subscriptionAuthorization.d.ts +65 -0
- package/dist/realtimeSessions/subscriptionAuthorization.d.ts.map +1 -0
- package/dist/realtimeSessions/subscriptionAuthorization.js +71 -0
- package/dist/realtimeSessions/subscriptionAuthorization.js.map +1 -0
- package/dist/realtimeSessions/verificationCore.d.ts +243 -0
- package/dist/realtimeSessions/verificationCore.d.ts.map +1 -0
- package/dist/realtimeSessions/verificationCore.js +356 -0
- package/dist/realtimeSessions/verificationCore.js.map +1 -0
- package/dist/realtimeSessions/verificationPages.d.ts +66 -0
- package/dist/realtimeSessions/verificationPages.d.ts.map +1 -0
- package/dist/realtimeSessions/verificationPages.js +110 -0
- package/dist/realtimeSessions/verificationPages.js.map +1 -0
- package/dist/realtimeSessions/verificationPolicy.d.ts +147 -0
- package/dist/realtimeSessions/verificationPolicy.d.ts.map +1 -0
- package/dist/realtimeSessions/verificationPolicy.js +249 -0
- package/dist/realtimeSessions/verificationPolicy.js.map +1 -0
- package/dist/realtimeSessions/verificationWorkflow.d.ts +263 -0
- package/dist/realtimeSessions/verificationWorkflow.d.ts.map +1 -0
- package/dist/realtimeSessions/verificationWorkflow.js +435 -0
- package/dist/realtimeSessions/verificationWorkflow.js.map +1 -0
- package/dist/realtimeWidget/widgetGuestElevation.d.ts +20 -2
- package/dist/realtimeWidget/widgetGuestElevation.d.ts.map +1 -1
- package/dist/realtimeWidget/widgetGuestElevation.js +31 -2
- package/dist/realtimeWidget/widgetGuestElevation.js.map +1 -1
- package/dist/resolvers/ActionResolver.d.ts +31 -5
- package/dist/resolvers/ActionResolver.d.ts.map +1 -1
- package/dist/resolvers/ActionResolver.js +75 -14
- package/dist/resolvers/ActionResolver.js.map +1 -1
- package/dist/resolvers/AdhocQueryResolver.d.ts +14 -1
- package/dist/resolvers/AdhocQueryResolver.d.ts.map +1 -1
- package/dist/resolvers/AdhocQueryResolver.js +21 -5
- package/dist/resolvers/AdhocQueryResolver.js.map +1 -1
- package/dist/resolvers/ArtifactFileResolver.d.ts +5 -1
- package/dist/resolvers/ArtifactFileResolver.d.ts.map +1 -1
- package/dist/resolvers/ArtifactFileResolver.js +9 -7
- package/dist/resolvers/ArtifactFileResolver.js.map +1 -1
- package/dist/resolvers/CacheStatsResolver.d.ts +27 -0
- package/dist/resolvers/CacheStatsResolver.d.ts.map +1 -1
- package/dist/resolvers/CacheStatsResolver.js +113 -2
- package/dist/resolvers/CacheStatsResolver.js.map +1 -1
- package/dist/resolvers/ClientToolRequestResolver.d.ts +4 -2
- package/dist/resolvers/ClientToolRequestResolver.d.ts.map +1 -1
- package/dist/resolvers/ClientToolRequestResolver.js +28 -10
- package/dist/resolvers/ClientToolRequestResolver.js.map +1 -1
- package/dist/resolvers/ConversationRunEventsResolver.d.ts +8 -4
- package/dist/resolvers/ConversationRunEventsResolver.d.ts.map +1 -1
- package/dist/resolvers/ConversationRunEventsResolver.js +6 -6
- package/dist/resolvers/ConversationRunEventsResolver.js.map +1 -1
- package/dist/resolvers/DatasetResolver.d.ts +24 -3
- package/dist/resolvers/DatasetResolver.d.ts.map +1 -1
- package/dist/resolvers/DatasetResolver.js +57 -8
- package/dist/resolvers/DatasetResolver.js.map +1 -1
- package/dist/resolvers/EntityRecordNameResolver.d.ts +2 -1
- package/dist/resolvers/EntityRecordNameResolver.d.ts.map +1 -1
- package/dist/resolvers/EntityRecordNameResolver.js +3 -2
- package/dist/resolvers/EntityRecordNameResolver.js.map +1 -1
- package/dist/resolvers/FetchEntityVectorsResolver.d.ts +27 -1
- package/dist/resolvers/FetchEntityVectorsResolver.d.ts.map +1 -1
- package/dist/resolvers/FetchEntityVectorsResolver.js +148 -2
- package/dist/resolvers/FetchEntityVectorsResolver.js.map +1 -1
- package/dist/resolvers/FileResolver.d.ts +82 -11
- package/dist/resolvers/FileResolver.d.ts.map +1 -1
- package/dist/resolvers/FileResolver.js +279 -101
- package/dist/resolvers/FileResolver.js.map +1 -1
- package/dist/resolvers/GetDataContextDataResolver.d.ts +33 -7
- package/dist/resolvers/GetDataContextDataResolver.d.ts.map +1 -1
- package/dist/resolvers/GetDataContextDataResolver.js +117 -38
- package/dist/resolvers/GetDataContextDataResolver.js.map +1 -1
- package/dist/resolvers/GetDataResolver.d.ts.map +1 -1
- package/dist/resolvers/GetDataResolver.js +6 -3
- package/dist/resolvers/GetDataResolver.js.map +1 -1
- package/dist/resolvers/HumanHandoffResolver.d.ts.map +1 -1
- package/dist/resolvers/HumanHandoffResolver.js +7 -1
- package/dist/resolvers/HumanHandoffResolver.js.map +1 -1
- package/dist/resolvers/IntegrationDiscoveryResolver.d.ts.map +1 -1
- package/dist/resolvers/IntegrationDiscoveryResolver.js +4 -2
- package/dist/resolvers/IntegrationDiscoveryResolver.js.map +1 -1
- package/dist/resolvers/MCPResolver.d.ts.map +1 -1
- package/dist/resolvers/MCPResolver.js +9 -4
- package/dist/resolvers/MCPResolver.js.map +1 -1
- package/dist/resolvers/QueryResolver.d.ts +2 -0
- package/dist/resolvers/QueryResolver.d.ts.map +1 -1
- package/dist/resolvers/QueryResolver.js +13 -0
- package/dist/resolvers/QueryResolver.js.map +1 -1
- package/dist/resolvers/RealtimeBridgeResolver.d.ts +54 -2
- package/dist/resolvers/RealtimeBridgeResolver.d.ts.map +1 -1
- package/dist/resolvers/RealtimeBridgeResolver.js +155 -10
- package/dist/resolvers/RealtimeBridgeResolver.js.map +1 -1
- package/dist/resolvers/RealtimeClientSessionResolver.d.ts +157 -33
- package/dist/resolvers/RealtimeClientSessionResolver.d.ts.map +1 -1
- package/dist/resolvers/RealtimeClientSessionResolver.js +404 -116
- package/dist/resolvers/RealtimeClientSessionResolver.js.map +1 -1
- package/dist/resolvers/RealtimeSessionEventsResolver.d.ts +61 -0
- package/dist/resolvers/RealtimeSessionEventsResolver.d.ts.map +1 -0
- package/dist/resolvers/RealtimeSessionEventsResolver.js +130 -0
- package/dist/resolvers/RealtimeSessionEventsResolver.js.map +1 -0
- package/dist/resolvers/RealtimeSessionVerificationResolver.d.ts +85 -0
- package/dist/resolvers/RealtimeSessionVerificationResolver.d.ts.map +1 -0
- package/dist/resolvers/RealtimeSessionVerificationResolver.js +188 -0
- package/dist/resolvers/RealtimeSessionVerificationResolver.js.map +1 -0
- package/dist/resolvers/RemoteBrowserActionResolver.d.ts +2 -0
- package/dist/resolvers/RemoteBrowserActionResolver.d.ts.map +1 -1
- package/dist/resolvers/RemoteBrowserActionResolver.js +5 -1
- package/dist/resolvers/RemoteBrowserActionResolver.js.map +1 -1
- package/dist/resolvers/RunAIAgentResolver.d.ts +10 -0
- package/dist/resolvers/RunAIAgentResolver.d.ts.map +1 -1
- package/dist/resolvers/RunAIAgentResolver.js +100 -14
- package/dist/resolvers/RunAIAgentResolver.js.map +1 -1
- package/dist/resolvers/RunTemplateResolver.d.ts.map +1 -1
- package/dist/resolvers/RunTemplateResolver.js +12 -11
- package/dist/resolvers/RunTemplateResolver.js.map +1 -1
- package/dist/resolvers/SearchKnowledgeStreamResolver.d.ts +12 -1
- package/dist/resolvers/SearchKnowledgeStreamResolver.d.ts.map +1 -1
- package/dist/resolvers/SearchKnowledgeStreamResolver.js +15 -2
- package/dist/resolvers/SearchKnowledgeStreamResolver.js.map +1 -1
- package/dist/resolvers/SqlLoggingConfigResolver.d.ts.map +1 -1
- package/dist/resolvers/SqlLoggingConfigResolver.js +2 -3
- package/dist/resolvers/SqlLoggingConfigResolver.js.map +1 -1
- package/dist/resolvers/SyncDataResolver.d.ts.map +1 -1
- package/dist/resolvers/SyncDataResolver.js +6 -2
- package/dist/resolvers/SyncDataResolver.js.map +1 -1
- package/dist/resolvers/SyncRolesUsersResolver.d.ts +2 -0
- package/dist/resolvers/SyncRolesUsersResolver.d.ts.map +1 -1
- package/dist/resolvers/SyncRolesUsersResolver.js +23 -6
- package/dist/resolvers/SyncRolesUsersResolver.js.map +1 -1
- package/dist/resolvers/TaskGraphFrameResolver.d.ts +2 -1
- package/dist/resolvers/TaskGraphFrameResolver.d.ts.map +1 -1
- package/dist/resolvers/TaskGraphFrameResolver.js +28 -1
- package/dist/resolvers/TaskGraphFrameResolver.js.map +1 -1
- package/dist/resolvers/TestQuerySQLResolver.d.ts +24 -0
- package/dist/resolvers/TestQuerySQLResolver.d.ts.map +1 -1
- package/dist/resolvers/TestQuerySQLResolver.js +65 -13
- package/dist/resolvers/TestQuerySQLResolver.js.map +1 -1
- package/dist/resolvers/TransactionGroupResolver.d.ts +19 -2
- package/dist/resolvers/TransactionGroupResolver.d.ts.map +1 -1
- package/dist/resolvers/TransactionGroupResolver.js +102 -24
- package/dist/resolvers/TransactionGroupResolver.js.map +1 -1
- package/dist/resolvers/UserAvatarResolver.d.ts +16 -0
- package/dist/resolvers/UserAvatarResolver.d.ts.map +1 -0
- package/dist/resolvers/UserAvatarResolver.js +124 -0
- package/dist/resolvers/UserAvatarResolver.js.map +1 -0
- package/dist/resolvers/UserResolver.d.ts.map +1 -1
- package/dist/resolvers/UserResolver.js +2 -17
- package/dist/resolvers/UserResolver.js.map +1 -1
- package/dist/resolvers/agent-run-data-guard.d.ts +31 -0
- package/dist/resolvers/agent-run-data-guard.d.ts.map +1 -0
- package/dist/resolvers/agent-run-data-guard.js +72 -0
- package/dist/resolvers/agent-run-data-guard.js.map +1 -0
- package/dist/resolvers/agentVisionConsentAudit.d.ts +37 -0
- package/dist/resolvers/agentVisionConsentAudit.d.ts.map +1 -0
- package/dist/resolvers/agentVisionConsentAudit.js +63 -0
- package/dist/resolvers/agentVisionConsentAudit.js.map +1 -0
- package/dist/resolvers/avatarInputValidation.d.ts +40 -0
- package/dist/resolvers/avatarInputValidation.d.ts.map +1 -0
- package/dist/resolvers/avatarInputValidation.js +97 -0
- package/dist/resolvers/avatarInputValidation.js.map +1 -0
- package/dist/resolvers/currentUserRoles.d.ts +29 -0
- package/dist/resolvers/currentUserRoles.d.ts.map +1 -1
- package/dist/resolvers/currentUserRoles.js +30 -0
- package/dist/resolvers/currentUserRoles.js.map +1 -1
- package/dist/resolvers/meetings/meetingMutations.d.ts +6 -0
- package/dist/resolvers/meetings/meetingMutations.d.ts.map +1 -1
- package/dist/resolvers/meetings/meetingMutations.js +21 -0
- package/dist/resolvers/meetings/meetingMutations.js.map +1 -1
- package/dist/rest/RealtimeRecordingHandler.d.ts +49 -0
- package/dist/rest/RealtimeRecordingHandler.d.ts.map +1 -0
- package/dist/rest/RealtimeRecordingHandler.js +383 -0
- package/dist/rest/RealtimeRecordingHandler.js.map +1 -0
- package/dist/rest/ViewOperationsHandler.d.ts +3 -1
- package/dist/rest/ViewOperationsHandler.d.ts.map +1 -1
- package/dist/rest/ViewOperationsHandler.js +21 -12
- package/dist/rest/ViewOperationsHandler.js.map +1 -1
- package/dist/rest/setupRESTEndpoints.d.ts +6 -0
- package/dist/rest/setupRESTEndpoints.d.ts.map +1 -1
- package/dist/rest/setupRESTEndpoints.js +6 -0
- package/dist/rest/setupRESTEndpoints.js.map +1 -1
- package/dist/serverExtensionReservedRoots.d.ts +12 -10
- package/dist/serverExtensionReservedRoots.d.ts.map +1 -1
- package/dist/serverExtensionReservedRoots.js +14 -17
- package/dist/serverExtensionReservedRoots.js.map +1 -1
- package/dist/services/MJStorageBlobStore.d.ts +8 -1
- package/dist/services/MJStorageBlobStore.d.ts.map +1 -1
- package/dist/services/MJStorageBlobStore.js +25 -19
- package/dist/services/MJStorageBlobStore.js.map +1 -1
- package/dist/services/StartTaskGraphDispatcher.js +2 -2
- package/dist/services/StartTaskGraphDispatcher.js.map +1 -1
- package/dist/services/TaskGraphActionRunner.d.ts +5 -3
- package/dist/services/TaskGraphActionRunner.d.ts.map +1 -1
- package/dist/services/TaskGraphActionRunner.js +27 -5
- package/dist/services/TaskGraphActionRunner.js.map +1 -1
- package/dist/services/TaskGraphAgentRunner.d.ts +14 -2
- package/dist/services/TaskGraphAgentRunner.d.ts.map +1 -1
- package/dist/services/TaskGraphAgentRunner.js +157 -9
- package/dist/services/TaskGraphAgentRunner.js.map +1 -1
- package/dist/services/TaskGraphContinuationDeliverer.d.ts +57 -4
- package/dist/services/TaskGraphContinuationDeliverer.d.ts.map +1 -1
- package/dist/services/TaskGraphContinuationDeliverer.js +607 -18
- package/dist/services/TaskGraphContinuationDeliverer.js.map +1 -1
- package/dist/sharedCache.d.ts +105 -0
- package/dist/sharedCache.d.ts.map +1 -0
- package/dist/sharedCache.js +279 -0
- package/dist/sharedCache.js.map +1 -0
- package/dist/types.d.ts +6 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/package.json +99 -96
- package/src/__tests__/ActionResolver.authorization.test.ts +353 -0
- package/src/__tests__/AdhocQueryResolver.sqlAuthorizer.test.ts +104 -0
- package/src/__tests__/BridgeHostInstance.test.ts +232 -0
- package/src/__tests__/ClientToolRequestResolver.test.ts +116 -0
- package/src/__tests__/DatasetResolver.itemFilters.test.ts +201 -0
- package/src/__tests__/EntityRecordNameResolver.rowSecurity.test.ts +152 -0
- package/src/__tests__/FetchEntityVectorsResolver.test.ts +433 -0
- package/src/__tests__/FileResolver.storageAccess.test.ts +562 -0
- package/src/__tests__/GetDataContextDataResolver.sqlItems.test.ts +416 -0
- package/src/__tests__/HostInstance.test.ts +84 -0
- package/src/__tests__/HumanHandoffResolver.test.ts +17 -0
- package/src/__tests__/ListOperationsResolver.adhocScreen.test.ts +137 -0
- package/src/__tests__/LoginScreenEngine.test.ts +86 -0
- package/src/__tests__/MJStorageBlobStore.storageGate.test.ts +63 -0
- package/src/__tests__/MeetingResolver.test.ts +75 -1
- package/src/__tests__/RealtimeBridgeResolver.test.ts +415 -6
- package/src/__tests__/RealtimeClientSessionResolver.configTamper.test.ts +383 -0
- package/src/__tests__/RealtimeClientSessionResolver.test.ts +682 -58
- package/src/__tests__/RealtimeProxyServer.test.ts +16 -1
- package/src/__tests__/RealtimeRelay.test.ts +876 -0
- package/src/__tests__/RecordingAssemblyScheduler.test.ts +178 -0
- package/src/__tests__/RemoteBrowserActionResolver.goalRunLink.test.ts +129 -0
- package/src/__tests__/ResolverBase.adhocReadScope.test.ts +133 -2
- package/src/__tests__/ResolverBase.aggregateScreen.test.ts +174 -0
- package/src/__tests__/ResolverBase.balancedClause.test.ts +155 -0
- package/src/__tests__/ResolverBase.clauseUnscopedRead.test.ts +127 -0
- package/src/__tests__/ResolverBase.clientOldValues.test.ts +212 -6
- package/src/__tests__/ResolverBase.filterEscaping.test.ts +5 -2
- package/src/__tests__/ReturningVisitorRecap.test.ts +115 -0
- package/src/__tests__/RunAIAgentResolver.branch.test.ts +170 -0
- package/src/__tests__/RunAIAgentResolver.historyFrom.test.ts +6 -4
- package/src/__tests__/RunAIAgentResolver.runDataGuard.test.ts +258 -0
- package/src/__tests__/RunAIAgentResolver.stoppedPredecessor.test.ts +166 -0
- package/src/__tests__/RunTemplateResolver.test.ts +132 -0
- package/src/__tests__/RunViewResolver.cacheCheckForwarding.test.ts +63 -0
- package/src/__tests__/RunViewResolver.cacheCheckScreen.test.ts +165 -0
- package/src/__tests__/SearchKnowledgeStreamResolver.test.ts +161 -0
- package/src/__tests__/SessionJanitor.test.ts +1203 -21
- package/src/__tests__/SessionManager.test.ts +240 -17
- package/src/__tests__/SessionRunIDVerifier.test.ts +251 -0
- package/src/__tests__/SyncRolesUsersResolver.userRoles.test.ts +97 -0
- package/src/__tests__/TaskGraphActionRunner.durablePayload.test.ts +25 -0
- package/src/__tests__/TaskGraphAgentRunner.test.ts +266 -0
- package/src/__tests__/TaskGraphContinuationDeliverer.test.ts +403 -22
- package/src/__tests__/TaskGraphFrameResolver.test.ts +22 -0
- package/src/__tests__/TestQuerySQLResolver.test.ts +274 -0
- package/src/__tests__/TransactionGroupResolver.refusals.test.ts +98 -1
- package/src/__tests__/UpdateMJDataContextItem.oldValues.test.ts +220 -0
- package/src/__tests__/UserAvatarResolver.test.ts +255 -0
- package/src/__tests__/UserResolver.currentUser.test.ts +16 -1
- package/src/__tests__/ViewOperationsHandler.clauseScreen.test.ts +157 -0
- package/src/__tests__/actionAuthorization.test.ts +130 -0
- package/src/__tests__/agent-run-data-guard.test.ts +130 -0
- package/src/__tests__/agentVisionConsentAudit.test.ts +118 -0
- package/src/__tests__/apiRootBoot.test.ts +202 -0
- package/src/__tests__/avatarInputValidation.test.ts +160 -0
- package/src/__tests__/cacheInvalidationRecordData.test.ts +64 -0
- package/src/__tests__/conversationRunEvents.test.ts +56 -14
- package/src/__tests__/globalCors.test.ts +217 -0
- package/src/__tests__/isolatedProviderTransactions.test.ts +169 -0
- package/src/__tests__/magicLinkService.consume.test.ts +6 -0
- package/src/__tests__/magicLinkService.provisionLog.test.ts +5 -0
- package/src/__tests__/multiTenancy.postgresRewrite.test.ts +181 -0
- package/src/__tests__/multiTenancy.security.test.ts +51 -0
- package/src/__tests__/multiTenancy.test.ts +1 -1
- package/src/__tests__/newUsers.template.test.ts +151 -0
- package/src/__tests__/newUsers.test.ts +74 -5
- package/src/__tests__/orphanedConversationDetailReconciler.test.ts +17 -2
- package/src/__tests__/ownedRealtimeSession.test.ts +39 -0
- package/src/__tests__/pushStatusResolver.filter.test.ts +7 -0
- package/src/__tests__/realtimeRecordingHandler.test.ts +652 -0
- package/src/__tests__/realtimeSessions/RealtimeSessionEventService.test.ts +201 -0
- package/src/__tests__/realtimeSessions/RealtimeSessionEventsResolver.test.ts +215 -0
- package/src/__tests__/realtimeSessions/RealtimeSessionVerificationResolver.test.ts +102 -0
- package/src/__tests__/realtimeSessions/RealtimeSessionVerificationService.test.ts +349 -0
- package/src/__tests__/realtimeSessions/RealtimeVerifyRouter.test.ts +131 -0
- package/src/__tests__/realtimeSessions/SlidingWindowRateLimiter.test.ts +56 -0
- package/src/__tests__/realtimeSessions/helpers.ts +4 -0
- package/src/__tests__/realtimeSessions/sessionAccess.test.ts +129 -0
- package/src/__tests__/realtimeSessions/sessionConfigStamps.test.ts +124 -0
- package/src/__tests__/realtimeSessions/verificationCore.test.ts +355 -0
- package/src/__tests__/realtimeSessions/verificationPages.test.ts +84 -0
- package/src/__tests__/realtimeSessions/verificationPolicy.test.ts +226 -0
- package/src/__tests__/realtimeSessions/verificationWorkflow.test.ts +633 -0
- package/src/__tests__/resolverBase.fls.test.ts +87 -4
- package/src/__tests__/serverExtensionHostOverlay.test.ts +66 -0
- package/src/__tests__/serverExtensionReservedRoots.test.ts +11 -96
- package/src/__tests__/sharedCache.test.ts +312 -0
- package/src/__tests__/widgetGuestElevation.test.ts +44 -0
- package/src/agentSessions/BridgeHostInstance.ts +32 -0
- package/src/agentSessions/HostInstance.ts +124 -28
- package/src/agentSessions/ReturningVisitorRecap.ts +7 -3
- package/src/agentSessions/SessionJanitor.ts +821 -59
- package/src/agentSessions/SessionManager.ts +138 -26
- package/src/agentSessions/SessionRunIDVerifier.ts +171 -0
- package/src/agentSessions/index.ts +10 -3
- package/src/apiRoot.ts +78 -0
- package/src/auth/AuthProviderCatalogRouter.ts +26 -0
- package/src/auth/LoginScreenEngine.ts +50 -0
- package/src/auth/actionAuthorization.ts +116 -0
- package/src/auth/index.ts +4 -8
- package/src/auth/magicLink/MagicLinkRouter.ts +3 -2
- package/src/auth/magicLink/MagicLinkService.ts +6 -3
- package/src/auth/newUsers.ts +99 -11
- package/src/config.ts +171 -5
- package/src/context.ts +15 -10
- package/src/coreRoutes.ts +166 -0
- package/src/generated/graphql-schemas/__mj.ts +1815 -57
- package/src/generic/CacheInvalidationResolver.ts +73 -18
- package/src/generic/OrphanedConversationDetailReconciler.ts +3 -3
- package/src/generic/PushStatusResolver.ts +1 -1
- package/src/generic/ResolverBase.ts +335 -326
- package/src/generic/RunViewResolver.ts +21 -0
- package/src/globalCors.ts +71 -0
- package/src/index.ts +231 -300
- package/src/isolatedProvider.ts +22 -0
- package/src/multiTenancy/index.ts +24 -2
- package/src/realtimeProxy/RealtimeProxyServer.ts +148 -16
- package/src/realtimeProxy/RealtimeRelayTunnel.ts +645 -0
- package/src/realtimeSessions/README.md +179 -0
- package/src/realtimeSessions/RealtimeSessionEventService.ts +274 -0
- package/src/realtimeSessions/RealtimeSessionVerificationService.ts +168 -0
- package/src/realtimeSessions/RealtimeVerifyRouter.ts +109 -0
- package/src/realtimeSessions/RecordingAssemblyScheduler.ts +234 -0
- package/src/realtimeSessions/SessionVerificationPortsBase.ts +189 -0
- package/src/realtimeSessions/SlidingWindowRateLimiter.ts +92 -0
- package/src/realtimeSessions/index.ts +18 -0
- package/src/realtimeSessions/ownedRealtimeSession.ts +55 -0
- package/src/realtimeSessions/sessionAccess.ts +145 -0
- package/src/realtimeSessions/sessionConfigStamps.ts +123 -0
- package/src/realtimeSessions/subscriptionAuthorization.ts +116 -0
- package/src/realtimeSessions/verificationCore.ts +502 -0
- package/src/realtimeSessions/verificationPages.ts +149 -0
- package/src/realtimeSessions/verificationPolicy.ts +321 -0
- package/src/realtimeSessions/verificationWorkflow.ts +702 -0
- package/src/realtimeWidget/widgetGuestElevation.ts +34 -2
- package/src/resolvers/ActionResolver.ts +84 -21
- package/src/resolvers/AdhocQueryResolver.ts +24 -6
- package/src/resolvers/ArtifactFileResolver.ts +9 -8
- package/src/resolvers/CacheStatsResolver.ts +89 -2
- package/src/resolvers/ClientToolRequestResolver.ts +25 -7
- package/src/resolvers/ConversationRunEventsResolver.ts +15 -11
- package/src/resolvers/DatasetResolver.ts +70 -10
- package/src/resolvers/EntityRecordNameResolver.ts +3 -2
- package/src/resolvers/FetchEntityVectorsResolver.ts +179 -2
- package/src/resolvers/FileResolver.ts +321 -106
- package/src/resolvers/GetDataContextDataResolver.ts +139 -41
- package/src/resolvers/GetDataResolver.ts +6 -3
- package/src/resolvers/HumanHandoffResolver.ts +8 -0
- package/src/resolvers/IntegrationDiscoveryResolver.ts +4 -2
- package/src/resolvers/MCPResolver.ts +8 -5
- package/src/resolvers/QueryResolver.ts +11 -0
- package/src/resolvers/RealtimeBridgeResolver.ts +163 -11
- package/src/resolvers/RealtimeClientSessionResolver.ts +511 -129
- package/src/resolvers/RealtimeSessionEventsResolver.ts +133 -0
- package/src/resolvers/RealtimeSessionVerificationResolver.ts +174 -0
- package/src/resolvers/RemoteBrowserActionResolver.ts +11 -1
- package/src/resolvers/RunAIAgentResolver.ts +116 -14
- package/src/resolvers/RunTemplateResolver.ts +13 -12
- package/src/resolvers/SearchKnowledgeStreamResolver.ts +19 -3
- package/src/resolvers/SqlLoggingConfigResolver.ts +2 -4
- package/src/resolvers/SyncDataResolver.ts +6 -2
- package/src/resolvers/SyncRolesUsersResolver.ts +24 -6
- package/src/resolvers/TaskGraphFrameResolver.ts +33 -1
- package/src/resolvers/TestQuerySQLResolver.ts +68 -13
- package/src/resolvers/TransactionGroupResolver.ts +131 -27
- package/src/resolvers/UserAvatarResolver.ts +111 -0
- package/src/resolvers/UserResolver.ts +2 -16
- package/src/resolvers/__tests__/GetDataResolver.validation.test.ts +95 -0
- package/src/resolvers/agent-run-data-guard.ts +89 -0
- package/src/resolvers/agentVisionConsentAudit.ts +82 -0
- package/src/resolvers/avatarInputValidation.ts +123 -0
- package/src/resolvers/currentUserRoles.ts +32 -1
- package/src/resolvers/meetings/meetingMutations.ts +21 -0
- package/src/rest/RealtimeRecordingHandler.ts +464 -0
- package/src/rest/ViewOperationsHandler.ts +29 -13
- package/src/rest/setupRESTEndpoints.ts +6 -0
- package/src/serverExtensionReservedRoots.ts +14 -17
- package/src/services/MJStorageBlobStore.ts +31 -30
- package/src/services/StartTaskGraphDispatcher.ts +2 -2
- package/src/services/TaskGraphActionRunner.ts +25 -5
- package/src/services/TaskGraphAgentRunner.ts +188 -11
- package/src/services/TaskGraphContinuationDeliverer.ts +713 -17
- package/src/sharedCache.ts +305 -0
- package/src/types.ts +7 -0
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
// type-graphql decorators call `Reflect.getMetadata`, which only exists once this polyfill is
|
|
2
|
+
// loaded. MUST precede any import that pulls in the resolver file.
|
|
3
|
+
import 'reflect-metadata';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* `RunTemplate` renders a stored template on demand. Its API-key scope check is a no-op for JWT,
|
|
7
|
+
* magic-link and widget sessions, so the resolver applies its own rule. These tests pin that it
|
|
8
|
+
* refuses scope-limited sessions and callers who cannot read templates before it loads anything.
|
|
9
|
+
*/
|
|
10
|
+
import { describe, it, expect, vi } from 'vitest';
|
|
11
|
+
|
|
12
|
+
// As in RunDecisionResolver.test.ts: vitest's esbuild transform does not apply
|
|
13
|
+
// `emitDecoratorMetadata`, so the decorators are no-ops and the resolver runs as a plain class.
|
|
14
|
+
vi.mock('type-graphql', () => {
|
|
15
|
+
const noopDecorator = () => () => undefined;
|
|
16
|
+
return {
|
|
17
|
+
Resolver: noopDecorator, Mutation: noopDecorator, Query: noopDecorator, Subscription: noopDecorator,
|
|
18
|
+
ObjectType: noopDecorator, InputType: noopDecorator, Field: noopDecorator, Arg: noopDecorator,
|
|
19
|
+
Args: noopDecorator, Ctx: noopDecorator, PubSub: noopDecorator, Root: noopDecorator,
|
|
20
|
+
Directive: noopDecorator, Authorized: noopDecorator, UseMiddleware: noopDecorator,
|
|
21
|
+
createUnionType: () => class {},
|
|
22
|
+
AuthorizationError: class AuthorizationError extends Error {
|
|
23
|
+
constructor(message?: string) {
|
|
24
|
+
super(message);
|
|
25
|
+
this.name = 'AuthorizationError';
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
Float: class {}, Int: class {}, ID: class {},
|
|
29
|
+
};
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
import { EntityInfo, UserInfo, UserRoleInfo } from '@memberjunction/core';
|
|
33
|
+
import { RunTemplateResolver } from '../resolvers/RunTemplateResolver.js';
|
|
34
|
+
import type { AppContext, UserPayload } from '../types.js';
|
|
35
|
+
|
|
36
|
+
const UI_ROLE_ID = '0b9b7b55-0000-4000-8000-0000000000a1';
|
|
37
|
+
const TEMPLATE_ID = '0b9b7b55-0000-4000-8000-0000000000c1';
|
|
38
|
+
|
|
39
|
+
/** An entity whose read permission is granted to the given roles. */
|
|
40
|
+
function entityReadableBy(name: string, roleIds: string[]): EntityInfo {
|
|
41
|
+
const entityId = `entity-${name}`;
|
|
42
|
+
return new EntityInfo({
|
|
43
|
+
ID: entityId, Name: name, SchemaName: '__mj', BaseTable: name, BaseView: `vw${name}`, IncludeInAPI: true,
|
|
44
|
+
EntityPermissions: roleIds.map((roleId, index) => ({
|
|
45
|
+
ID: `${entityId}-permission-${index}`, EntityID: entityId, RoleID: roleId,
|
|
46
|
+
CanCreate: false, CanRead: true, CanUpdate: false, CanDelete: false, Type: 'Allow',
|
|
47
|
+
})),
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Metadata plus the one data call the resolver makes first. The data call fails on purpose, so a
|
|
53
|
+
* test can tell whether the resolver got past authorization without needing a database.
|
|
54
|
+
*/
|
|
55
|
+
class FakeProvider {
|
|
56
|
+
public readonly EntityObjectRequests: string[] = [];
|
|
57
|
+
|
|
58
|
+
constructor(private readonly entities: EntityInfo[]) {}
|
|
59
|
+
|
|
60
|
+
public EntityByName(name: string): EntityInfo | undefined {
|
|
61
|
+
return this.entities.find((e) => e.Name.trim().toLowerCase() === name.trim().toLowerCase());
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
public async GetEntityObject(entityName: string): Promise<never> {
|
|
65
|
+
this.EntityObjectRequests.push(entityName);
|
|
66
|
+
throw new Error(`reached data access for ${entityName}`);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function makeUser(roleIds: string[]): UserInfo {
|
|
71
|
+
const userId = '0b9b7b55-0000-4000-8000-0000000000b1';
|
|
72
|
+
return new UserInfo(null, {
|
|
73
|
+
ID: userId, Name: 'Template Runner', Email: 'runner@example.com',
|
|
74
|
+
UserRoles: roleIds.map((roleId) => new UserRoleInfo({ UserID: userId, RoleID: roleId })),
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const readableEverywhere = () => [
|
|
79
|
+
entityReadableBy('MJ: Templates', [UI_ROLE_ID]),
|
|
80
|
+
entityReadableBy('MJ: Template Contents', [UI_ROLE_ID]),
|
|
81
|
+
];
|
|
82
|
+
|
|
83
|
+
/** A JWT-style session (no API key), so the API-key scope check is a no-op. */
|
|
84
|
+
function contextFor(user: UserInfo, provider: FakeProvider): AppContext {
|
|
85
|
+
const userPayload: UserPayload = { email: user.Email, userRecord: user, sessionId: 'session-1' };
|
|
86
|
+
return { userPayload, providers: [{ type: 'Read-Write', provider }] } as unknown as AppContext;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const runTemplate = (user: UserInfo, provider: FakeProvider) =>
|
|
90
|
+
new RunTemplateResolver().RunTemplate(TEMPLATE_ID, contextFor(user, provider));
|
|
91
|
+
|
|
92
|
+
describe('RunTemplate authorization', () => {
|
|
93
|
+
it('refuses an anonymous magic-link guest before loading the template', async () => {
|
|
94
|
+
const provider = new FakeProvider(readableEverywhere());
|
|
95
|
+
const guest = makeUser([UI_ROLE_ID]);
|
|
96
|
+
guest.IsMagicLinkAnonymous = true;
|
|
97
|
+
|
|
98
|
+
const result = await runTemplate(guest, provider);
|
|
99
|
+
|
|
100
|
+
expect(result).toMatchObject({ success: false, error: 'Running templates is not permitted for scope-limited sessions' });
|
|
101
|
+
expect(provider.EntityObjectRequests).toEqual([]);
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
it('refuses a resource-scoped magic-link session before loading the template', async () => {
|
|
105
|
+
const provider = new FakeProvider(readableEverywhere());
|
|
106
|
+
const scoped = makeUser([UI_ROLE_ID]);
|
|
107
|
+
scoped.MagicLinkScope = { ResourceID: '0b9b7b55-0000-4000-8000-0000000000d1', ResourceType: 'Dashboards' };
|
|
108
|
+
|
|
109
|
+
const result = await runTemplate(scoped, provider);
|
|
110
|
+
|
|
111
|
+
expect(result).toMatchObject({ success: false, error: 'Running templates is not permitted for scope-limited sessions' });
|
|
112
|
+
expect(provider.EntityObjectRequests).toEqual([]);
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
it('refuses a user without read permission on MJ: Templates before loading the template', async () => {
|
|
116
|
+
const provider = new FakeProvider(readableEverywhere());
|
|
117
|
+
|
|
118
|
+
const result = await runTemplate(makeUser([]), provider);
|
|
119
|
+
|
|
120
|
+
expect(result).toMatchObject({ success: false, error: 'You do not have permission to read MJ: Templates' });
|
|
121
|
+
expect(provider.EntityObjectRequests).toEqual([]);
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
it('lets a user who can read templates and their content through to the template load', async () => {
|
|
125
|
+
const provider = new FakeProvider(readableEverywhere());
|
|
126
|
+
|
|
127
|
+
const result = await runTemplate(makeUser([UI_ROLE_ID]), provider);
|
|
128
|
+
|
|
129
|
+
expect(result).toMatchObject({ success: false, error: 'reached data access for MJ: Templates' });
|
|
130
|
+
expect(provider.EntityObjectRequests).toEqual(['MJ: Templates']);
|
|
131
|
+
});
|
|
132
|
+
});
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import 'reflect-metadata';
|
|
2
|
+
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* The cache-check transport's param forwarding, at the resolver.
|
|
6
|
+
*
|
|
7
|
+
* This map is the middle of three places a param has to be carried for the smart-cache-check
|
|
8
|
+
* transport to behave like the ordinary one, and it is where three separate fields have now been
|
|
9
|
+
* found missing: `Aggregates` (B40), `DataSource`, and `BypassCache`. Each omission was invisible
|
|
10
|
+
* locally — the schema accepted the field, the provider could act on it, the response could carry
|
|
11
|
+
* it, and only the request never asked. These pin the two that silently change what the database
|
|
12
|
+
* is asked, so a fourth omission fails here rather than in a consumer's UI.
|
|
13
|
+
*/
|
|
14
|
+
const runViewsWithCacheCheck = vi.fn(async () => ({ success: true, errorMessage: null, results: [] }));
|
|
15
|
+
|
|
16
|
+
vi.mock('../util.js', () => ({
|
|
17
|
+
GetReadOnlyProvider: () => ({ RunViewsWithCacheCheck: runViewsWithCacheCheck }),
|
|
18
|
+
GetReadWriteProvider: () => ({ RunViewsWithCacheCheck: runViewsWithCacheCheck }),
|
|
19
|
+
}));
|
|
20
|
+
|
|
21
|
+
import { RunViewResolver } from '../generic/RunViewResolver.js';
|
|
22
|
+
import type { AppContext } from '../types.js';
|
|
23
|
+
|
|
24
|
+
/** Minimal context: the resolver only needs a provider (mocked above) and a user record. */
|
|
25
|
+
function context(): AppContext {
|
|
26
|
+
return { providers: [], userPayload: { userRecord: { ID: 'u1', Name: 'Test' } } } as unknown as AppContext;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/** The params the resolver handed the provider for the single view in the batch. */
|
|
30
|
+
async function forwardedParams(params: Record<string, unknown>): Promise<Record<string, unknown>> {
|
|
31
|
+
runViewsWithCacheCheck.mockClear();
|
|
32
|
+
const resolver = new RunViewResolver();
|
|
33
|
+
await resolver.RunViewsWithCacheCheck([{ params } as never], context());
|
|
34
|
+
expect(runViewsWithCacheCheck).toHaveBeenCalledTimes(1);
|
|
35
|
+
const call = runViewsWithCacheCheck.mock.calls[0] as unknown as [Array<{ params: Record<string, unknown> }>];
|
|
36
|
+
return call[0][0].params;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
describe('RunViewsWithCacheCheck resolver — param forwarding', () => {
|
|
40
|
+
beforeEach(() => {
|
|
41
|
+
runViewsWithCacheCheck.mockClear();
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
it('forwards BypassCache, so a deliberate database read is not answered from the server cache', async () => {
|
|
45
|
+
const forwarded = await forwardedParams({ EntityName: 'Customers', BypassCache: true });
|
|
46
|
+
expect(forwarded.BypassCache).toBe(true);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it('leaves BypassCache undefined when the caller did not ask for it', async () => {
|
|
50
|
+
const forwarded = await forwardedParams({ EntityName: 'Customers' });
|
|
51
|
+
expect(forwarded.BypassCache).toBeUndefined();
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it('still forwards DataSource and Aggregates — the two fields previously found missing here', async () => {
|
|
55
|
+
const forwarded = await forwardedParams({
|
|
56
|
+
EntityName: 'Customers',
|
|
57
|
+
DataSource: 'Materialized',
|
|
58
|
+
Aggregates: [{ expression: 'COUNT(*)', alias: 'total' }],
|
|
59
|
+
});
|
|
60
|
+
expect(forwarded.DataSource).toBe('Materialized');
|
|
61
|
+
expect(forwarded.Aggregates).toEqual([{ expression: 'COUNT(*)', alias: 'total' }]);
|
|
62
|
+
});
|
|
63
|
+
});
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
// ResolverBase pulls in type-graphql decorators, which need the Reflect.metadata polyfill first.
|
|
2
|
+
import 'reflect-metadata';
|
|
3
|
+
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* `RunViewsWithCacheCheck` is the transport `RunViews` switches to when any view in the batch is
|
|
7
|
+
* `CacheLocal`, so it carries the same client-supplied clauses as `RunViews`. These tests pin that
|
|
8
|
+
* it applies the same GraphQL-boundary protections before the provider is called: the base-view
|
|
9
|
+
* AST screen on ExtraFilter / OrderBy / OverrideExcludeFilter, and the API-key scope check.
|
|
10
|
+
*
|
|
11
|
+
* Every payload here is harmless (stacked `SELECT`s); the point is only whether it reaches the
|
|
12
|
+
* provider. The provider's own keyword denylist accepts the bracket-identifier payload, because a
|
|
13
|
+
* quote inside `[a']` hides the rest of the text from its literal stripper.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
const { authorize } = vi.hoisted(() => ({ authorize: vi.fn() }));
|
|
17
|
+
|
|
18
|
+
vi.mock('@memberjunction/api-keys', async (importOriginal) => {
|
|
19
|
+
const actual = await importOriginal<typeof import('@memberjunction/api-keys')>();
|
|
20
|
+
return { ...actual, GetAPIKeyEngine: () => ({ Authorize: authorize }) };
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
import type { DatabaseProviderBase, EntityInfo, RunViewWithCacheCheckParams, UserInfo } from '@memberjunction/core';
|
|
24
|
+
import { UserCache } from '@memberjunction/generic-database-provider';
|
|
25
|
+
import { RunDynamicViewInput, RunViewResolver, RunViewWithCacheCheckInput } from '../generic/RunViewResolver.js';
|
|
26
|
+
import type { AppContext, UserPayload } from '../types.js';
|
|
27
|
+
|
|
28
|
+
/** A stacked statement hidden from the keyword denylist by a quote inside a bracket identifier. */
|
|
29
|
+
const BRACKET_STACKED =
|
|
30
|
+
"1 = (SELECT 1 AS [a'])) ; SELECT 1 AS [x] ; SELECT * FROM __mj.vwUserViews WHERE (1 = (SELECT 1 AS [b'])";
|
|
31
|
+
|
|
32
|
+
const USER = { ID: 'user-1', Email: 'reader@example.com', Name: 'Reader' } as UserInfo;
|
|
33
|
+
|
|
34
|
+
function entity(name: string, schema: string, baseView: string): EntityInfo {
|
|
35
|
+
return {
|
|
36
|
+
Name: name,
|
|
37
|
+
SchemaName: schema,
|
|
38
|
+
BaseView: baseView,
|
|
39
|
+
PrimaryKeys: [{ Name: 'ID' }],
|
|
40
|
+
GetUserPermisions: () => ({ CanRead: true }),
|
|
41
|
+
GetEffectiveRowFilterWhereClause: () => '',
|
|
42
|
+
GetDeniedReadFields: () => new Set<string>(),
|
|
43
|
+
} as unknown as EntityInfo;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/** Provider double: the metadata the screen reads, and a cache-check method that records calls. */
|
|
47
|
+
function fakeProvider() {
|
|
48
|
+
const calls: RunViewWithCacheCheckParams[][] = [];
|
|
49
|
+
const entities = [
|
|
50
|
+
entity('MJ: User Views', '__mj', 'vwUserViews'),
|
|
51
|
+
entity('MJ: Users', '__mj', 'vwUsers'),
|
|
52
|
+
];
|
|
53
|
+
const provider = {
|
|
54
|
+
Entities: entities,
|
|
55
|
+
EntityByName: (name: string) => entities.find((e) => e.Name === name),
|
|
56
|
+
RunViewsWithCacheCheck: async (params: RunViewWithCacheCheckParams[]) => {
|
|
57
|
+
calls.push(params);
|
|
58
|
+
return { success: true, results: [] };
|
|
59
|
+
},
|
|
60
|
+
};
|
|
61
|
+
return { provider: provider as unknown as DatabaseProviderBase, calls };
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function context(provider: DatabaseProviderBase, payload: Partial<UserPayload> = {}): AppContext {
|
|
65
|
+
const userPayload = { email: USER.Email, userRecord: USER, ...payload } as UserPayload;
|
|
66
|
+
return { providers: [{ type: 'Read-Only', provider }], userPayload } as unknown as AppContext;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function cacheCheckInput(params: Partial<RunDynamicViewInput>): RunViewWithCacheCheckInput[] {
|
|
70
|
+
const dynamic = Object.assign(new RunDynamicViewInput(), { EntityName: 'MJ: User Views', ...params });
|
|
71
|
+
return [Object.assign(new RunViewWithCacheCheckInput(), { params: dynamic })];
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
describe('RunViewsWithCacheCheck — GraphQL-boundary clause screen', () => {
|
|
75
|
+
it('refuses a stacked statement hidden behind a bracket identifier, before the provider runs', async () => {
|
|
76
|
+
const { provider, calls } = fakeProvider();
|
|
77
|
+
|
|
78
|
+
const result = await new RunViewResolver().RunViewsWithCacheCheck(
|
|
79
|
+
cacheCheckInput({ ExtraFilter: BRACKET_STACKED }),
|
|
80
|
+
context(provider),
|
|
81
|
+
);
|
|
82
|
+
|
|
83
|
+
expect(calls).toHaveLength(0);
|
|
84
|
+
expect(result.success).toBe(false);
|
|
85
|
+
expect(result.errorMessage).toMatch(/Invalid ExtraFilter: multiple statements/);
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
it('refuses a subquery against a base table', async () => {
|
|
89
|
+
const { provider, calls } = fakeProvider();
|
|
90
|
+
|
|
91
|
+
const result = await new RunViewResolver().RunViewsWithCacheCheck(
|
|
92
|
+
cacheCheckInput({ ExtraFilter: `EXISTS (SELECT 1 FROM __mj.[User] WHERE [Type] = 'Owner')` }),
|
|
93
|
+
context(provider),
|
|
94
|
+
);
|
|
95
|
+
|
|
96
|
+
expect(calls).toHaveLength(0);
|
|
97
|
+
expect(result.success).toBe(false);
|
|
98
|
+
expect(result.errorMessage).toMatch(/entity base view/);
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
it('screens OrderBy and OverrideExcludeFilter too', async () => {
|
|
102
|
+
for (const params of [
|
|
103
|
+
{ OrderBy: "[a'] ; SELECT 1 AS [x] ; SELECT 1 AS [b']" },
|
|
104
|
+
{ OverrideExcludeFilter: BRACKET_STACKED },
|
|
105
|
+
]) {
|
|
106
|
+
const { provider, calls } = fakeProvider();
|
|
107
|
+
|
|
108
|
+
const result = await new RunViewResolver().RunViewsWithCacheCheck(cacheCheckInput(params), context(provider));
|
|
109
|
+
|
|
110
|
+
expect(calls).toHaveLength(0);
|
|
111
|
+
expect(result.success).toBe(false);
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
it('passes a filter that only reads entity base views through to the provider', async () => {
|
|
116
|
+
const { provider, calls } = fakeProvider();
|
|
117
|
+
const filter = `UserID IN (SELECT ID FROM [__mj].[vwUsers] WHERE Email = 'o''brien@example.com')`;
|
|
118
|
+
|
|
119
|
+
const result = await new RunViewResolver().RunViewsWithCacheCheck(
|
|
120
|
+
cacheCheckInput({ ExtraFilter: filter, OrderBy: '[Name] DESC' }),
|
|
121
|
+
context(provider),
|
|
122
|
+
);
|
|
123
|
+
|
|
124
|
+
expect(result.success).toBe(true);
|
|
125
|
+
expect(calls).toHaveLength(1);
|
|
126
|
+
expect(calls[0][0].params.ExtraFilter).toBe(filter);
|
|
127
|
+
});
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
describe('RunViewsWithCacheCheck — API-key scope', () => {
|
|
131
|
+
beforeEach(() => {
|
|
132
|
+
authorize.mockReset();
|
|
133
|
+
vi.spyOn(UserCache.Instance, 'GetSystemUser').mockReturnValue({ ID: 'system', Email: 'system@example.com' } as UserInfo);
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
it('refuses an API key without the batch view scope, before the provider runs', async () => {
|
|
137
|
+
authorize.mockResolvedValue({ Allowed: false, Reason: 'scope not granted' });
|
|
138
|
+
const { provider, calls } = fakeProvider();
|
|
139
|
+
|
|
140
|
+
const result = await new RunViewResolver().RunViewsWithCacheCheck(
|
|
141
|
+
cacheCheckInput({ ExtraFilter: `Name = 'x'` }),
|
|
142
|
+
context(provider, { apiKeyHash: 'hash-of-a-narrow-key' }),
|
|
143
|
+
);
|
|
144
|
+
|
|
145
|
+
expect(calls).toHaveLength(0);
|
|
146
|
+
expect(result.success).toBe(false);
|
|
147
|
+
expect(result.errorMessage).toMatch(/'view:batch' scope/);
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
it('lets an API key holding the scope through', async () => {
|
|
151
|
+
authorize.mockImplementation(async (_hash: string, _app: string, scope: string) => ({
|
|
152
|
+
Allowed: scope === 'view:batch',
|
|
153
|
+
Reason: scope === 'view:batch' ? 'granted' : 'not full access',
|
|
154
|
+
}));
|
|
155
|
+
const { provider, calls } = fakeProvider();
|
|
156
|
+
|
|
157
|
+
const result = await new RunViewResolver().RunViewsWithCacheCheck(
|
|
158
|
+
cacheCheckInput({ ExtraFilter: `Name = 'x'` }),
|
|
159
|
+
context(provider, { apiKeyHash: 'hash-of-a-batch-key' }),
|
|
160
|
+
);
|
|
161
|
+
|
|
162
|
+
expect(result.success).toBe(true);
|
|
163
|
+
expect(calls).toHaveLength(1);
|
|
164
|
+
});
|
|
165
|
+
});
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Tests for SearchKnowledgeStreamResolver's wire mapping: a `provider` event is progress only.
|
|
5
|
+
*
|
|
6
|
+
* A provider's hits arrive before the engine's permission pass — rows the caller's row filters would drop,
|
|
7
|
+
* external-index hits not yet verified against the entity they name, content whose origin the caller may not
|
|
8
|
+
* read. The engine no longer puts them on its `provider` events, and the resolver does not forward them either,
|
|
9
|
+
* whatever the event holds: the client gets ProviderName, DurationMs and ResultCount, and results only in
|
|
10
|
+
* `fused`/`final`.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
const { streamEvents, streamCalls, resolveEffectivePermission, logForbiddenSearch } = vi.hoisted(() => ({
|
|
14
|
+
streamEvents: { current: [] as unknown[] },
|
|
15
|
+
streamCalls: { count: 0 },
|
|
16
|
+
resolveEffectivePermission: vi.fn(),
|
|
17
|
+
logForbiddenSearch: vi.fn(),
|
|
18
|
+
}));
|
|
19
|
+
|
|
20
|
+
vi.mock('@memberjunction/search-engine', () => ({
|
|
21
|
+
SearchEngine: {
|
|
22
|
+
Instance: {
|
|
23
|
+
streamSearch: async function* () {
|
|
24
|
+
streamCalls.count++;
|
|
25
|
+
for (const ev of streamEvents.current) yield ev;
|
|
26
|
+
},
|
|
27
|
+
LogForbiddenSearch: logForbiddenSearch,
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
GetSearchScopePermissionResolver: () => ({ ResolveEffectivePermission: resolveEffectivePermission }),
|
|
31
|
+
}));
|
|
32
|
+
|
|
33
|
+
vi.mock('@memberjunction/core', async (importOriginal) => {
|
|
34
|
+
const actual = await importOriginal<Record<string, unknown>>();
|
|
35
|
+
return { ...actual, LogError: vi.fn(), LogStatus: vi.fn() };
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
// Mock type-graphql to avoid the reflect-metadata dependency (the SearchKnowledgeResolver tests do the same).
|
|
39
|
+
vi.mock('type-graphql', () => {
|
|
40
|
+
const noop = () => () => undefined;
|
|
41
|
+
const passthrough = () => (target: unknown) => target;
|
|
42
|
+
return {
|
|
43
|
+
Resolver: passthrough, Mutation: noop, Query: noop, Subscription: noop, Arg: noop, Args: noop, Ctx: noop,
|
|
44
|
+
ObjectType: passthrough, InputType: passthrough, ArgsType: passthrough, Field: noop,
|
|
45
|
+
Float: Number, Int: Number, ID: String, Authorized: noop, PubSub: noop, Root: noop,
|
|
46
|
+
};
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
import type { PubSubEngine } from 'type-graphql';
|
|
50
|
+
import type { UserInfo } from '@memberjunction/core';
|
|
51
|
+
import { SearchKnowledgeStreamResolver, SearchStreamNotification } from '../resolvers/SearchKnowledgeStreamResolver.js';
|
|
52
|
+
import type { UserPayload } from '../types.js';
|
|
53
|
+
|
|
54
|
+
class TestStreamResolver extends SearchKnowledgeStreamResolver {
|
|
55
|
+
protected override GetUserFromPayload(_userPayload: UserPayload): UserInfo | undefined {
|
|
56
|
+
return { ID: 'test-user-id', Email: 'test@test.com', Name: 'Test User' } as UserInfo;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const hit = (recordID: string) => ({
|
|
61
|
+
ID: `r-${recordID}`, EntityName: 'Customers', RecordID: recordID, SourceType: 'fulltext', ResultType: 'entity-record',
|
|
62
|
+
Title: `title of ${recordID}`, Snippet: `snippet of ${recordID}`, Score: 0.5, ScoreBreakdown: {}, Tags: [],
|
|
63
|
+
MatchedAt: new Date(), RawMetadata: `{"secret":"${recordID}"}`, ProviderId: 'prov-azure',
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
/** Run a stream to completion and return what the resolver published, in order. */
|
|
67
|
+
async function publishedFor(events: unknown[]): Promise<SearchStreamNotification[]> {
|
|
68
|
+
streamEvents.current = events;
|
|
69
|
+
const published: SearchStreamNotification[] = [];
|
|
70
|
+
const pubSub = { publish: vi.fn(async (_topic: string, payload: SearchStreamNotification) => { published.push(payload); }) };
|
|
71
|
+
const start = await new TestStreamResolver().StreamScopedSearch(
|
|
72
|
+
'budget', 20, undefined, undefined, undefined, undefined,
|
|
73
|
+
pubSub as unknown as PubSubEngine, { userPayload: {} } as never,
|
|
74
|
+
);
|
|
75
|
+
expect(start.Success).toBe(true);
|
|
76
|
+
await vi.waitFor(() => expect(published.some(p => p.Phase === 'final' || p.Phase === 'error')).toBe(true),
|
|
77
|
+
{ timeout: 500, interval: 5 });
|
|
78
|
+
return published;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
describe('SearchKnowledgeStreamResolver — provider events are progress only', () => {
|
|
82
|
+
beforeEach(() => { vi.clearAllMocks(); });
|
|
83
|
+
|
|
84
|
+
it('publishes the count and no result, even when a provider event carries hits', async () => {
|
|
85
|
+
// An engine that (wrongly) put hits on a provider event: the resolver still must not forward them.
|
|
86
|
+
const published = await publishedFor([
|
|
87
|
+
{ phase: 'provider', providerName: 'FullText', results: [hit('row-filtered')], resultCount: 1, durationMs: 7 },
|
|
88
|
+
{ phase: 'fused', results: [] },
|
|
89
|
+
{ phase: 'final', results: [], sourceCounts: { Vector: 0, FullText: 1, Entity: 0, Storage: 0 }, elapsedMs: 9 },
|
|
90
|
+
]);
|
|
91
|
+
|
|
92
|
+
const progress = published.find(p => p.Phase === 'provider');
|
|
93
|
+
expect(progress).toMatchObject({ Phase: 'provider', ProviderName: 'FullText', DurationMs: 7, ResultCount: 1, Results: [] });
|
|
94
|
+
expect(JSON.stringify(progress)).not.toContain('row-filtered');
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
it('carries results on final, mapped as the synchronous resolver maps them', async () => {
|
|
98
|
+
const published = await publishedFor([
|
|
99
|
+
{ phase: 'provider', providerName: 'FullText', results: [], resultCount: 1, durationMs: 7 },
|
|
100
|
+
{ phase: 'fused', results: [hit('readable')] },
|
|
101
|
+
{ phase: 'final', results: [hit('readable')], sourceCounts: { Vector: 0, FullText: 1, Entity: 0, Storage: 0 }, elapsedMs: 9 },
|
|
102
|
+
]);
|
|
103
|
+
|
|
104
|
+
const final = published.find(p => p.Phase === 'final');
|
|
105
|
+
expect(final?.Results?.map(r => r.RecordID)).toEqual(['readable']);
|
|
106
|
+
expect(final?.ResultCount).toBeUndefined();
|
|
107
|
+
});
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
describe('SearchKnowledgeStreamResolver — the scope permission gate runs before the stream starts', () => {
|
|
111
|
+
const verdict = (Allowed: boolean, Level: string) => ({ Allowed, Level, Source: 'NoGrant', Reason: 'judged in test' });
|
|
112
|
+
|
|
113
|
+
beforeEach(() => {
|
|
114
|
+
streamEvents.current = [{ phase: 'final', results: [], sourceCounts: { Vector: 0, FullText: 0, Entity: 0, Storage: 0 }, elapsedMs: 1 }];
|
|
115
|
+
streamCalls.count = 0;
|
|
116
|
+
resolveEffectivePermission.mockReset();
|
|
117
|
+
logForbiddenSearch.mockReset();
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
async function start(scopeIDs: string[]) {
|
|
121
|
+
const pubSub = { publish: vi.fn(async () => undefined) };
|
|
122
|
+
const result = await new TestStreamResolver().StreamScopedSearch(
|
|
123
|
+
'budget', 20, undefined, scopeIDs, undefined, undefined,
|
|
124
|
+
pubSub as unknown as PubSubEngine, { userPayload: {} } as never,
|
|
125
|
+
);
|
|
126
|
+
return { result, pubSub };
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
it('refuses a denied scope, logs it, and never starts the stream or publishes', async () => {
|
|
130
|
+
resolveEffectivePermission.mockResolvedValue(verdict(false, 'None'));
|
|
131
|
+
|
|
132
|
+
const { result, pubSub } = await start(['scope-a']);
|
|
133
|
+
|
|
134
|
+
expect(result.Success).toBe(false);
|
|
135
|
+
expect(result.ErrorMessage).toMatch(/^Forbidden: refused for this user on scope 'scope-a'/);
|
|
136
|
+
expect(logForbiddenSearch).toHaveBeenCalledTimes(1);
|
|
137
|
+
await new Promise((r) => setTimeout(r, 20));
|
|
138
|
+
expect(streamCalls.count).toBe(0);
|
|
139
|
+
expect(pubSub.publish).not.toHaveBeenCalled();
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
it('refuses a Read-level grant: Read is metadata only, not search', async () => {
|
|
143
|
+
resolveEffectivePermission.mockResolvedValue(verdict(true, 'Read'));
|
|
144
|
+
|
|
145
|
+
const { result } = await start(['scope-a']);
|
|
146
|
+
|
|
147
|
+
expect(result.Success).toBe(false);
|
|
148
|
+
await new Promise((r) => setTimeout(r, 20));
|
|
149
|
+
expect(streamCalls.count).toBe(0);
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
it('judges every requested scope and starts the stream when all may be searched', async () => {
|
|
153
|
+
resolveEffectivePermission.mockResolvedValue(verdict(true, 'Search'));
|
|
154
|
+
|
|
155
|
+
const { result } = await start(['scope-a', 'scope-b']);
|
|
156
|
+
|
|
157
|
+
expect(result.Success).toBe(true);
|
|
158
|
+
expect(resolveEffectivePermission).toHaveBeenCalledTimes(2);
|
|
159
|
+
await vi.waitFor(() => expect(streamCalls.count).toBe(1), { timeout: 500, interval: 5 });
|
|
160
|
+
});
|
|
161
|
+
});
|