@memberjunction/server 6.2.0-edge.6 → 6.2.0-edge.8
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 +69 -0
- package/dist/.tsbuildinfo +1 -1
- package/dist/agentSessions/BridgeHostInstance.d.ts +8 -3
- package/dist/agentSessions/BridgeHostInstance.d.ts.map +1 -1
- package/dist/agentSessions/BridgeHostInstance.js +14 -4
- package/dist/agentSessions/BridgeHostInstance.js.map +1 -1
- package/dist/agentSessions/HandoffOfferExpiry.d.ts +10 -0
- package/dist/agentSessions/HandoffOfferExpiry.d.ts.map +1 -0
- package/dist/agentSessions/HandoffOfferExpiry.js +18 -0
- package/dist/agentSessions/HandoffOfferExpiry.js.map +1 -0
- package/dist/agentSessions/ReturningVisitorRecap.js +1 -0
- package/dist/agentSessions/ReturningVisitorRecap.js.map +1 -1
- package/dist/agentSessions/SessionJanitor.d.ts +31 -8
- package/dist/agentSessions/SessionJanitor.d.ts.map +1 -1
- package/dist/agentSessions/SessionJanitor.js +85 -41
- package/dist/agentSessions/SessionJanitor.js.map +1 -1
- package/dist/agentSessions/SessionManager.d.ts +14 -4
- package/dist/agentSessions/SessionManager.d.ts.map +1 -1
- package/dist/agentSessions/SessionManager.js +28 -4
- package/dist/agentSessions/SessionManager.js.map +1 -1
- package/dist/agentSessions/conversationBrowsers.d.ts +61 -0
- package/dist/agentSessions/conversationBrowsers.d.ts.map +1 -0
- package/dist/agentSessions/conversationBrowsers.js +118 -0
- package/dist/agentSessions/conversationBrowsers.js.map +1 -0
- package/dist/agentSessions/sessionDeadline.d.ts +43 -0
- package/dist/agentSessions/sessionDeadline.d.ts.map +1 -0
- package/dist/agentSessions/sessionDeadline.js +68 -0
- package/dist/agentSessions/sessionDeadline.js.map +1 -0
- package/dist/auth/APIKeyScopeAuth.d.ts.map +1 -1
- package/dist/auth/APIKeyScopeAuth.js +5 -4
- package/dist/auth/APIKeyScopeAuth.js.map +1 -1
- package/dist/auth/loginEmailResolution.d.ts +41 -0
- package/dist/auth/loginEmailResolution.d.ts.map +1 -0
- package/dist/auth/loginEmailResolution.js +47 -0
- package/dist/auth/loginEmailResolution.js.map +1 -0
- package/dist/auth/magicLink/MagicLinkKeys.d.ts +50 -2
- package/dist/auth/magicLink/MagicLinkKeys.d.ts.map +1 -1
- package/dist/auth/magicLink/MagicLinkKeys.js +129 -12
- package/dist/auth/magicLink/MagicLinkKeys.js.map +1 -1
- package/dist/auth/magicLink/MagicLinkRouter.d.ts +3 -0
- package/dist/auth/magicLink/MagicLinkRouter.d.ts.map +1 -1
- package/dist/auth/magicLink/MagicLinkRouter.js +23 -5
- package/dist/auth/magicLink/MagicLinkRouter.js.map +1 -1
- package/dist/auth/magicLink/MagicLinkService.d.ts +39 -5
- package/dist/auth/magicLink/MagicLinkService.d.ts.map +1 -1
- package/dist/auth/magicLink/MagicLinkService.js +221 -9
- package/dist/auth/magicLink/MagicLinkService.js.map +1 -1
- package/dist/auth/magicLink/index.d.ts +1 -1
- package/dist/auth/magicLink/index.d.ts.map +1 -1
- package/dist/auth/magicLink/index.js +1 -1
- package/dist/auth/magicLink/index.js.map +1 -1
- package/dist/auth/magicLink/redeemLanding.d.ts +2 -2
- package/dist/auth/magicLink/redeemLanding.d.ts.map +1 -1
- package/dist/auth/magicLink/redeemLanding.js +7 -4
- package/dist/auth/magicLink/redeemLanding.js.map +1 -1
- package/dist/auth/magicLink/types.d.ts +27 -3
- package/dist/auth/magicLink/types.d.ts.map +1 -1
- package/dist/auth/magicLink/types.js +0 -4
- package/dist/auth/magicLink/types.js.map +1 -1
- package/dist/auth/newUsers.d.ts.map +1 -1
- package/dist/auth/newUsers.js +1 -0
- package/dist/auth/newUsers.js.map +1 -1
- package/dist/backgroundSweepConfigUnits.d.ts +231 -0
- package/dist/backgroundSweepConfigUnits.d.ts.map +1 -0
- package/dist/backgroundSweepConfigUnits.js +180 -0
- package/dist/backgroundSweepConfigUnits.js.map +1 -0
- package/dist/broadcast/BaseCacheInvalidationBroadcastProvider.d.ts.map +1 -1
- package/dist/broadcast/BaseCacheInvalidationBroadcastProvider.js +2 -1
- package/dist/broadcast/BaseCacheInvalidationBroadcastProvider.js.map +1 -1
- package/dist/config.d.ts +906 -8
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +59 -0
- package/dist/config.js.map +1 -1
- package/dist/context.d.ts +2 -0
- package/dist/context.d.ts.map +1 -1
- package/dist/context.js +41 -12
- package/dist/context.js.map +1 -1
- package/dist/coreRoutes.d.ts +3 -1
- package/dist/coreRoutes.d.ts.map +1 -1
- package/dist/coreRoutes.js +6 -4
- package/dist/coreRoutes.js.map +1 -1
- package/dist/generated/graphql-schemas/__mj.d.ts +1072 -5
- package/dist/generated/graphql-schemas/__mj.d.ts.map +1 -1
- package/dist/generated/graphql-schemas/__mj.js +10174 -4618
- package/dist/generated/graphql-schemas/__mj.js.map +1 -1
- package/dist/generic/OrphanedConversationDetailReconciler.d.ts +11 -0
- package/dist/generic/OrphanedConversationDetailReconciler.d.ts.map +1 -1
- package/dist/generic/OrphanedConversationDetailReconciler.js +27 -1
- package/dist/generic/OrphanedConversationDetailReconciler.js.map +1 -1
- package/dist/generic/ResolverBase.d.ts +50 -5
- package/dist/generic/ResolverBase.d.ts.map +1 -1
- package/dist/generic/ResolverBase.js +329 -132
- package/dist/generic/ResolverBase.js.map +1 -1
- package/dist/generic/RunViewResolver.d.ts +4 -0
- package/dist/generic/RunViewResolver.d.ts.map +1 -1
- package/dist/generic/RunViewResolver.js +47 -7
- package/dist/generic/RunViewResolver.js.map +1 -1
- package/dist/globalCors.d.ts.map +1 -1
- package/dist/globalCors.js +1 -0
- package/dist/globalCors.js.map +1 -1
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +201 -43
- package/dist/index.js.map +1 -1
- package/dist/integration/ConnectionPause.d.ts +121 -0
- package/dist/integration/ConnectionPause.d.ts.map +1 -0
- package/dist/integration/ConnectionPause.js +183 -0
- package/dist/integration/ConnectionPause.js.map +1 -0
- package/dist/integration/CustomColumnPromoter.d.ts +72 -6
- package/dist/integration/CustomColumnPromoter.d.ts.map +1 -1
- package/dist/integration/CustomColumnPromoter.js +87 -46
- package/dist/integration/CustomColumnPromoter.js.map +1 -1
- package/dist/integration/EntityMapLifecycle.d.ts +78 -4
- package/dist/integration/EntityMapLifecycle.d.ts.map +1 -1
- package/dist/integration/EntityMapLifecycle.js +199 -5
- package/dist/integration/EntityMapLifecycle.js.map +1 -1
- package/dist/integration/InactiveRowWarnings.d.ts +31 -0
- package/dist/integration/InactiveRowWarnings.d.ts.map +1 -1
- package/dist/integration/InactiveRowWarnings.js +29 -0
- package/dist/integration/InactiveRowWarnings.js.map +1 -1
- package/dist/integration/ObjectNamesByID.d.ts +28 -0
- package/dist/integration/ObjectNamesByID.d.ts.map +1 -0
- package/dist/integration/ObjectNamesByID.js +87 -0
- package/dist/integration/ObjectNamesByID.js.map +1 -0
- package/dist/integration/PostRestartIntrospect.d.ts +26 -0
- package/dist/integration/PostRestartIntrospect.d.ts.map +1 -0
- package/dist/integration/PostRestartIntrospect.js +27 -0
- package/dist/integration/PostRestartIntrospect.js.map +1 -0
- package/dist/integration/RSUPostRestartProgress.d.ts +176 -0
- package/dist/integration/RSUPostRestartProgress.d.ts.map +1 -0
- package/dist/integration/RSUPostRestartProgress.js +323 -0
- package/dist/integration/RSUPostRestartProgress.js.map +1 -0
- package/dist/integration/RSUProgressBridge.d.ts +41 -2
- package/dist/integration/RSUProgressBridge.d.ts.map +1 -1
- package/dist/integration/RSUProgressBridge.js +84 -1
- package/dist/integration/RSUProgressBridge.js.map +1 -1
- package/dist/integration/RunArtifactAuthorization.d.ts +39 -0
- package/dist/integration/RunArtifactAuthorization.d.ts.map +1 -0
- package/dist/integration/RunArtifactAuthorization.js +45 -0
- package/dist/integration/RunArtifactAuthorization.js.map +1 -0
- package/dist/integration/SchemaRefreshLaunch.d.ts +9 -0
- package/dist/integration/SchemaRefreshLaunch.d.ts.map +1 -1
- package/dist/integration/SchemaRefreshLaunch.js +3 -2
- package/dist/integration/SchemaRefreshLaunch.js.map +1 -1
- package/dist/integration/ScopedFieldsForBuild.d.ts +46 -0
- package/dist/integration/ScopedFieldsForBuild.d.ts.map +1 -0
- package/dist/integration/ScopedFieldsForBuild.js +91 -0
- package/dist/integration/ScopedFieldsForBuild.js.map +1 -0
- package/dist/integration/TargetConfigGuard.d.ts +40 -0
- package/dist/integration/TargetConfigGuard.d.ts.map +1 -0
- package/dist/integration/TargetConfigGuard.js +40 -0
- package/dist/integration/TargetConfigGuard.js.map +1 -0
- package/dist/logging/StartupLogger.d.ts +50 -0
- package/dist/logging/StartupLogger.d.ts.map +1 -1
- package/dist/logging/StartupLogger.js +15 -22
- package/dist/logging/StartupLogger.js.map +1 -1
- package/dist/middleware/BaseServerMiddleware.d.ts +21 -3
- package/dist/middleware/BaseServerMiddleware.d.ts.map +1 -1
- package/dist/middleware/BaseServerMiddleware.js +4 -2
- package/dist/middleware/BaseServerMiddleware.js.map +1 -1
- package/dist/realtimeSessions/ownedRealtimeSession.d.ts.map +1 -1
- package/dist/realtimeSessions/ownedRealtimeSession.js +2 -3
- package/dist/realtimeSessions/ownedRealtimeSession.js.map +1 -1
- package/dist/realtimeSessions/sessionAccess.d.ts +15 -0
- package/dist/realtimeSessions/sessionAccess.d.ts.map +1 -1
- package/dist/realtimeSessions/sessionAccess.js +22 -0
- package/dist/realtimeSessions/sessionAccess.js.map +1 -1
- package/dist/realtimeWidget/WidgetSessionService.d.ts.map +1 -1
- package/dist/realtimeWidget/WidgetSessionService.js +2 -0
- package/dist/realtimeWidget/WidgetSessionService.js.map +1 -1
- package/dist/realtimeWidget/visitorIdentity.d.ts.map +1 -1
- package/dist/realtimeWidget/visitorIdentity.js +1 -0
- package/dist/realtimeWidget/visitorIdentity.js.map +1 -1
- package/dist/realtimeWidget/widgetCore.d.ts.map +1 -1
- package/dist/realtimeWidget/widgetCore.js +4 -7
- package/dist/realtimeWidget/widgetCore.js.map +1 -1
- package/dist/realtimeWidget/widgetGuestElevation.js +1 -0
- package/dist/realtimeWidget/widgetGuestElevation.js.map +1 -1
- package/dist/requestOrigin.d.ts +22 -0
- package/dist/requestOrigin.d.ts.map +1 -0
- package/dist/requestOrigin.js +60 -0
- package/dist/requestOrigin.js.map +1 -0
- package/dist/resolvers/AgentRunStatusPublisher.d.ts +10 -0
- package/dist/resolvers/AgentRunStatusPublisher.d.ts.map +1 -1
- package/dist/resolvers/AgentRunStatusPublisher.js +18 -2
- package/dist/resolvers/AgentRunStatusPublisher.js.map +1 -1
- package/dist/resolvers/AgentSessionResolver.d.ts.map +1 -1
- package/dist/resolvers/AgentSessionResolver.js +6 -2
- package/dist/resolvers/AgentSessionResolver.js.map +1 -1
- package/dist/resolvers/AutotagPipelineResolver.d.ts +1 -1
- package/dist/resolvers/AutotagPipelineResolver.d.ts.map +1 -1
- package/dist/resolvers/AutotagPipelineResolver.js +61 -16
- package/dist/resolvers/AutotagPipelineResolver.js.map +1 -1
- package/dist/resolvers/ConversationRunEventsResolver.d.ts.map +1 -1
- package/dist/resolvers/ConversationRunEventsResolver.js +2 -0
- package/dist/resolvers/ConversationRunEventsResolver.js.map +1 -1
- package/dist/resolvers/ExecuteRemoteOperationResolver.d.ts +6 -0
- package/dist/resolvers/ExecuteRemoteOperationResolver.d.ts.map +1 -1
- package/dist/resolvers/ExecuteRemoteOperationResolver.js +30 -7
- package/dist/resolvers/ExecuteRemoteOperationResolver.js.map +1 -1
- package/dist/resolvers/GetDataResolver.d.ts.map +1 -1
- package/dist/resolvers/GetDataResolver.js +34 -13
- package/dist/resolvers/GetDataResolver.js.map +1 -1
- package/dist/resolvers/InfoResolver.js +1 -1
- package/dist/resolvers/InfoResolver.js.map +1 -1
- package/dist/resolvers/IntegrationDiscoveryResolver.d.ts +401 -11
- package/dist/resolvers/IntegrationDiscoveryResolver.d.ts.map +1 -1
- package/dist/resolvers/IntegrationDiscoveryResolver.js +2690 -1305
- package/dist/resolvers/IntegrationDiscoveryResolver.js.map +1 -1
- package/dist/resolvers/MCPResolver.d.ts.map +1 -1
- package/dist/resolvers/MCPResolver.js +13 -2
- package/dist/resolvers/MCPResolver.js.map +1 -1
- package/dist/resolvers/QuerySystemUserResolver.d.ts +38 -1
- package/dist/resolvers/QuerySystemUserResolver.d.ts.map +1 -1
- package/dist/resolvers/QuerySystemUserResolver.js +181 -88
- package/dist/resolvers/QuerySystemUserResolver.js.map +1 -1
- package/dist/resolvers/RealtimeBridgeResolver.d.ts.map +1 -1
- package/dist/resolvers/RealtimeBridgeResolver.js +1 -0
- package/dist/resolvers/RealtimeBridgeResolver.js.map +1 -1
- package/dist/resolvers/RealtimeClientSessionResolver.d.ts +6 -0
- package/dist/resolvers/RealtimeClientSessionResolver.d.ts.map +1 -1
- package/dist/resolvers/RealtimeClientSessionResolver.js +36 -5
- package/dist/resolvers/RealtimeClientSessionResolver.js.map +1 -1
- package/dist/resolvers/RemoteBrowserActionResolver.d.ts +62 -10
- package/dist/resolvers/RemoteBrowserActionResolver.d.ts.map +1 -1
- package/dist/resolvers/RemoteBrowserActionResolver.js +217 -34
- package/dist/resolvers/RemoteBrowserActionResolver.js.map +1 -1
- package/dist/resolvers/RunAIAgentResolver.d.ts +24 -8
- package/dist/resolvers/RunAIAgentResolver.d.ts.map +1 -1
- package/dist/resolvers/RunAIAgentResolver.js +185 -67
- package/dist/resolvers/RunAIAgentResolver.js.map +1 -1
- package/dist/resolvers/RunAIPromptResolver.d.ts +5 -3
- package/dist/resolvers/RunAIPromptResolver.d.ts.map +1 -1
- package/dist/resolvers/RunAIPromptResolver.js +19 -18
- package/dist/resolvers/RunAIPromptResolver.js.map +1 -1
- package/dist/resolvers/RunTemplateResolver.d.ts.map +1 -1
- package/dist/resolvers/RunTemplateResolver.js +3 -1
- package/dist/resolvers/RunTemplateResolver.js.map +1 -1
- package/dist/resolvers/SqlLoggingConfigResolver.d.ts.map +1 -1
- package/dist/resolvers/SqlLoggingConfigResolver.js +7 -8
- package/dist/resolvers/SqlLoggingConfigResolver.js.map +1 -1
- package/dist/resolvers/TagGovernanceResolver.d.ts +12 -0
- package/dist/resolvers/TagGovernanceResolver.d.ts.map +1 -1
- package/dist/resolvers/TagGovernanceResolver.js +84 -0
- package/dist/resolvers/TagGovernanceResolver.js.map +1 -1
- package/dist/resolvers/TelemetryResolver.d.ts +13 -8
- package/dist/resolvers/TelemetryResolver.d.ts.map +1 -1
- package/dist/resolvers/TelemetryResolver.js +40 -9
- package/dist/resolvers/TelemetryResolver.js.map +1 -1
- package/dist/resolvers/meetingRecordingRegistration.d.ts.map +1 -1
- package/dist/resolvers/meetingRecordingRegistration.js +2 -0
- package/dist/resolvers/meetingRecordingRegistration.js.map +1 -1
- package/dist/resolvers/meetings/meetingMutations.d.ts.map +1 -1
- package/dist/resolvers/meetings/meetingMutations.js +34 -4
- package/dist/resolvers/meetings/meetingMutations.js.map +1 -1
- package/dist/rest/MediaAccessKeys.d.ts.map +1 -1
- package/dist/rest/MediaAccessKeys.js +6 -2
- package/dist/rest/MediaAccessKeys.js.map +1 -1
- package/dist/rest/OAuthCallbackHandler.d.ts.map +1 -1
- package/dist/rest/OAuthCallbackHandler.js +11 -5
- package/dist/rest/OAuthCallbackHandler.js.map +1 -1
- package/dist/rest/RealtimeRecordingHandler.d.ts +3 -3
- package/dist/rest/RealtimeRecordingHandler.d.ts.map +1 -1
- package/dist/rest/RealtimeRecordingHandler.js +2 -4
- package/dist/rest/RealtimeRecordingHandler.js.map +1 -1
- package/dist/rest/ViewOperationsHandler.d.ts.map +1 -1
- package/dist/rest/ViewOperationsHandler.js +2 -1
- package/dist/rest/ViewOperationsHandler.js.map +1 -1
- package/dist/rest/setupRESTEndpoints.d.ts +3 -1
- package/dist/rest/setupRESTEndpoints.d.ts.map +1 -1
- package/dist/rest/setupRESTEndpoints.js +11 -2
- package/dist/rest/setupRESTEndpoints.js.map +1 -1
- package/dist/services/DurableEntityActionTaskSubmitter.d.ts.map +1 -1
- package/dist/services/DurableEntityActionTaskSubmitter.js +6 -1
- package/dist/services/DurableEntityActionTaskSubmitter.js.map +1 -1
- package/dist/services/TaskGraphActionRunner.d.ts.map +1 -1
- package/dist/services/TaskGraphActionRunner.js +2 -26
- package/dist/services/TaskGraphActionRunner.js.map +1 -1
- package/dist/services/TaskGraphAgentRunner.d.ts +6 -0
- package/dist/services/TaskGraphAgentRunner.d.ts.map +1 -1
- package/dist/services/TaskGraphAgentRunner.js +38 -0
- package/dist/services/TaskGraphAgentRunner.js.map +1 -1
- package/dist/services/TaskGraphContinuationDeliverer.d.ts.map +1 -1
- package/dist/services/TaskGraphContinuationDeliverer.js +1 -0
- package/dist/services/TaskGraphContinuationDeliverer.js.map +1 -1
- package/dist/services/WorkQueueHostService.d.ts +2 -1
- package/dist/services/WorkQueueHostService.d.ts.map +1 -1
- package/dist/services/WorkQueueHostService.js +19 -4
- package/dist/services/WorkQueueHostService.js.map +1 -1
- package/dist/services/workQueueConfig.d.ts +2 -2
- package/dist/services/workQueueConfig.js +5 -5
- package/dist/services/workQueueConfig.js.map +1 -1
- package/dist/sharedCache.js +1 -1
- package/dist/sharedCache.js.map +1 -1
- package/dist/types.d.ts +17 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/util.d.ts.map +1 -1
- package/dist/util.js +55 -2
- package/dist/util.js.map +1 -1
- package/package.json +107 -100
- package/src/__tests__/APIKeyScopeAuth.test.ts +4 -8
- package/src/__tests__/ActiveOperations.test.ts +68 -0
- package/src/__tests__/AgentSessionResolver.ownership.test.ts +13 -0
- package/src/__tests__/AssertTargetConfigs.test.ts +75 -0
- package/src/__tests__/BatchApplyParity.test.ts +109 -0
- package/src/__tests__/BridgeHostInstance.test.ts +42 -0
- package/src/__tests__/CollectInactiveRowWarnings.test.ts +115 -0
- package/src/__tests__/ConnectionPause.test.ts +192 -0
- package/src/__tests__/ConnectionPauseResolver.test.ts +166 -0
- package/src/__tests__/ConnectionPauseSave.test.ts +143 -0
- package/src/__tests__/CustomColumnPromoterCompletion.test.ts +142 -0
- package/src/__tests__/DeleteConnectionRemovesPerConnectionCatalog.test.ts +51 -0
- package/src/__tests__/DetachedDiscoveryStart.test.ts +65 -0
- package/src/__tests__/DurableEntityActionTaskSubmitter.test.ts +73 -0
- package/src/__tests__/EntityMapLifecycle.rekey.test.ts +252 -0
- package/src/__tests__/ExecuteRemoteOperationResolver.test.ts +213 -0
- package/src/__tests__/HandoffOfferExpiry.test.ts +40 -0
- package/src/__tests__/IntegrationRunArtifactAuthorization.test.ts +126 -0
- package/src/__tests__/MagicLinkKeys.test.ts +102 -0
- package/src/__tests__/MediaAccessKeys.test.ts +57 -10
- package/src/__tests__/NewArrivalsDefaultDisabled.test.ts +50 -0
- package/src/__tests__/ObjectNamesByID.test.ts +176 -0
- package/src/__tests__/PostRestartMapsInCatalogScope.test.ts +146 -0
- package/src/__tests__/QuerySystemUserResolver.childRows.test.ts +80 -0
- package/src/__tests__/QuerySystemUserResolver.collision.test.ts +200 -0
- package/src/__tests__/QuerySystemUserResolver.permissions.test.ts +132 -0
- package/src/__tests__/RSUPostRestartProgress.test.ts +343 -0
- package/src/__tests__/RSUProgressBridge.test.ts +188 -5
- package/src/__tests__/RSUStartupSweepWiring.test.ts +30 -0
- package/src/__tests__/ReactivateRefreshMessage.test.ts +10 -3
- package/src/__tests__/RealtimeClientSessionResolver.test.ts +22 -0
- package/src/__tests__/{RefreshAdoptsNewColumns.test.ts → RefreshReportsNewColumns.test.ts} +25 -16
- package/src/__tests__/RemoteBrowserActionRecovery.test.ts +287 -0
- package/src/__tests__/RemoteBrowserActionResolver.goalRunLink.test.ts +3 -0
- package/src/__tests__/RemoteBrowserActionResolver.ownership.test.ts +6 -4
- package/src/__tests__/RemoteBrowserAudioStream.test.ts +3 -0
- package/src/__tests__/RemoteBrowserSnapshot.test.ts +3 -0
- package/src/__tests__/ResolverBase.clientOldValues.test.ts +327 -26
- package/src/__tests__/ResolverBase.diagnosticSenderRegistrations.test.ts +26 -0
- package/src/__tests__/ResolverBase.encryptedApiZeroReencrypt.test.ts +307 -0
- package/src/__tests__/ResolverBase.entityMessageRelay.test.ts +53 -2
- package/src/__tests__/ResolverBase.includeBinaryFields.test.ts +60 -0
- package/src/__tests__/RunAIAgentResolver.branch.test.ts +4 -0
- package/src/__tests__/RunAIAgentResolver.consultPredecessor.test.ts +289 -0
- package/src/__tests__/RunAIAgentResolver.handoff.test.ts +649 -0
- package/src/__tests__/RunAIAgentResolver.historyFrom.test.ts +2 -0
- package/src/__tests__/RunAIAgentResolver.runDataGuard.test.ts +2 -0
- package/src/__tests__/RunAIAgentResolver.stoppedPredecessor.test.ts +87 -12
- package/src/__tests__/RunAIAgentResolver.streaming.test.ts +28 -4
- package/src/__tests__/SchemaEvolutionCustomColumns.test.ts +119 -0
- package/src/__tests__/SchemaRefreshLaunch.test.ts +24 -2
- package/src/__tests__/ScopedFieldsForBuild.test.ts +192 -0
- package/src/__tests__/SessionJanitor.test.ts +195 -4
- package/src/__tests__/SessionManager.test.ts +69 -0
- package/src/__tests__/TagGovernanceAndAutotagResolvers.test.ts +261 -0
- package/src/__tests__/TagGovernanceResolver.test.ts +60 -2
- package/src/__tests__/TaskGraphActionRunner.durablePayload.test.ts +2 -2
- package/src/__tests__/TaskGraphAgentRunner.test.ts +272 -16
- package/src/__tests__/ViewOperationsHandler.test.ts +2 -5
- package/src/__tests__/WorkQueueHostService.test.ts +25 -7
- package/src/__tests__/backgroundSweepConfigUnits.test.ts +153 -0
- package/src/__tests__/contextRequestOrigin.test.ts +105 -0
- package/src/__tests__/conversationBrowsers.test.ts +62 -0
- package/src/__tests__/globalCors.test.ts +12 -0
- package/src/__tests__/magicLinkExtensionCapabilities.test.ts +413 -0
- package/src/__tests__/orphanedConversationDetailReconciler.start.test.ts +82 -0
- package/src/__tests__/ownedRealtimeSession.test.ts +14 -0
- package/src/__tests__/realtimeRecordingHandler.test.ts +7 -2
- package/src/__tests__/realtimeSessions/sessionAccess.test.ts +79 -1
- package/src/__tests__/requestOrigin.test.ts +104 -0
- package/src/__tests__/review-capture-valid-token-log.test.ts +98 -0
- package/src/__tests__/runStreams.fieldSecurity.test.ts +5 -5
- package/src/__tests__/securityHardeningPr6034.test.ts +120 -0
- package/src/__tests__/sessionDeadline.test.ts +51 -0
- package/src/__tests__/sharedCache.test.ts +11 -0
- package/src/__tests__/unifiedAuth.test.ts +73 -1
- package/src/agentSessions/BridgeHostInstance.ts +20 -5
- package/src/agentSessions/HandoffOfferExpiry.ts +18 -0
- package/src/agentSessions/ReturningVisitorRecap.ts +1 -0
- package/src/agentSessions/SessionJanitor.ts +92 -42
- package/src/agentSessions/SessionManager.ts +38 -8
- package/src/agentSessions/conversationBrowsers.ts +143 -0
- package/src/agentSessions/sessionDeadline.ts +81 -0
- package/src/auth/APIKeyScopeAuth.ts +5 -4
- package/src/auth/__tests__/loginEmailResolution.test.ts +67 -0
- package/src/auth/loginEmailResolution.ts +67 -0
- package/src/auth/magicLink/MagicLinkKeys.ts +146 -14
- package/src/auth/magicLink/MagicLinkRouter.ts +33 -6
- package/src/auth/magicLink/MagicLinkService.ts +250 -10
- package/src/auth/magicLink/index.ts +1 -1
- package/src/auth/magicLink/redeemLanding.ts +7 -4
- package/src/auth/magicLink/types.ts +28 -3
- package/src/auth/newUsers.ts +1 -0
- package/src/backgroundSweepConfigUnits.ts +213 -0
- package/src/broadcast/BaseCacheInvalidationBroadcastProvider.ts +2 -1
- package/src/config.ts +69 -0
- package/src/context.ts +46 -15
- package/src/coreRoutes.ts +14 -7
- package/src/generated/graphql-schemas/__mj.ts +10360 -6479
- package/src/generic/OrphanedConversationDetailReconciler.ts +32 -1
- package/src/generic/ResolverBase.ts +422 -143
- package/src/generic/RunViewResolver.ts +96 -49
- package/src/globalCors.ts +1 -0
- package/src/index.ts +246 -44
- package/src/integration/ConnectionPause.ts +222 -0
- package/src/integration/CustomColumnPromoter.ts +123 -53
- package/src/integration/EntityMapLifecycle.ts +229 -6
- package/src/integration/InactiveRowWarnings.ts +50 -0
- package/src/integration/ObjectNamesByID.ts +97 -0
- package/src/integration/PostRestartIntrospect.ts +49 -0
- package/src/integration/RSUPostRestartProgress.ts +359 -0
- package/src/integration/RSUProgressBridge.ts +85 -2
- package/src/integration/RunArtifactAuthorization.ts +65 -0
- package/src/integration/SchemaRefreshLaunch.ts +12 -2
- package/src/integration/ScopedFieldsForBuild.ts +113 -0
- package/src/integration/TargetConfigGuard.ts +71 -0
- package/src/logging/StartupLogger.ts +24 -0
- package/src/middleware/BaseServerMiddleware.ts +20 -3
- package/src/realtimeSessions/ownedRealtimeSession.ts +2 -6
- package/src/realtimeSessions/sessionAccess.ts +30 -0
- package/src/realtimeWidget/WidgetSessionService.ts +2 -0
- package/src/realtimeWidget/visitorIdentity.ts +1 -0
- package/src/realtimeWidget/widgetCore.ts +4 -8
- package/src/realtimeWidget/widgetGuestElevation.ts +1 -0
- package/src/requestOrigin.ts +64 -0
- package/src/resolvers/AgentRunStatusPublisher.ts +27 -2
- package/src/resolvers/AgentSessionResolver.ts +6 -2
- package/src/resolvers/AutotagPipelineResolver.ts +59 -14
- package/src/resolvers/ConversationRunEventsResolver.ts +2 -0
- package/src/resolvers/ExecuteRemoteOperationResolver.ts +41 -9
- package/src/resolvers/GetDataResolver.ts +35 -13
- package/src/resolvers/InfoResolver.ts +1 -1
- package/src/resolvers/IntegrationDiscoveryResolver.ts +1522 -203
- package/src/resolvers/MCPResolver.ts +15 -2
- package/src/resolvers/QuerySystemUserResolver.ts +211 -100
- package/src/resolvers/RealtimeBridgeResolver.ts +1 -0
- package/src/resolvers/RealtimeClientSessionResolver.ts +40 -7
- package/src/resolvers/RemoteBrowserActionResolver.ts +242 -32
- package/src/resolvers/RunAIAgentResolver.ts +265 -86
- package/src/resolvers/RunAIPromptResolver.ts +18 -18
- package/src/resolvers/RunTemplateResolver.ts +3 -1
- package/src/resolvers/SqlLoggingConfigResolver.ts +7 -9
- package/src/resolvers/TagGovernanceResolver.ts +65 -0
- package/src/resolvers/TelemetryResolver.ts +44 -9
- package/src/resolvers/__tests__/GetDataResolver.tokens.test.ts +52 -0
- package/src/resolvers/meetingRecordingRegistration.ts +2 -0
- package/src/resolvers/meetings/meetingMutations.ts +37 -7
- package/src/rest/MediaAccessKeys.ts +6 -2
- package/src/rest/OAuthCallbackHandler.ts +13 -5
- package/src/rest/RealtimeRecordingHandler.ts +5 -9
- package/src/rest/ViewOperationsHandler.ts +2 -1
- package/src/rest/setupRESTEndpoints.ts +11 -2
- package/src/services/DurableEntityActionTaskSubmitter.ts +10 -2
- package/src/services/TaskGraphActionRunner.ts +2 -23
- package/src/services/TaskGraphAgentRunner.ts +43 -0
- package/src/services/TaskGraphContinuationDeliverer.ts +1 -0
- package/src/services/WorkQueueHostService.ts +26 -4
- package/src/services/workQueueConfig.ts +5 -5
- package/src/sharedCache.ts +1 -1
- package/src/types.ts +18 -0
- package/src/util.ts +55 -3
- package/src/services/TaskOrchestration-Integration.md +0 -188
|
@@ -4,28 +4,37 @@ import { join } from 'node:path';
|
|
|
4
4
|
import { DecideFieldMapReconcile } from '../integration/EntityMapLifecycle.js';
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
|
-
* A REFRESH
|
|
7
|
+
* A REFRESH REPORTS the source's current shape; it does not adopt it. New objects and new columns
|
|
8
|
+
* are created but arrive DISABLED, and the user turns them on.
|
|
8
9
|
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
10
|
+
* everything.txt: "we create entity maps for all objects that are determined as things that should
|
|
11
|
+
* be now added but arent, we enable nothing (because the user needs to, after the refresh, then go
|
|
12
|
+
* turn them on)". plan.md says the same for a schema refresh: "New tables are default deselected
|
|
13
|
+
* during schema refresh, and same with columns."
|
|
12
14
|
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
+
* This file previously asserted the OPPOSITE, and that is how the regression survived: the flip to
|
|
16
|
+
* auto-adopt was pinned by a test whose own premise contradicted the spec. Its stated reason was
|
|
17
|
+
* that a disabled new object never reached a migration, which is false — CreateDisabled governs
|
|
18
|
+
* only the entity/field MAP status, the IntegrationObject row is written Active regardless, and
|
|
19
|
+
* phase 4 evolves over continuing + new objects either way. The table is always created; the flag
|
|
20
|
+
* only decides whether it starts SYNCING unasked.
|
|
21
|
+
*
|
|
22
|
+
* The asymmetry with SYNC-discovered columns still holds and is sharper now: a sync never creates
|
|
23
|
+
* anything, it only captures a candidate for acceptance.
|
|
15
24
|
*/
|
|
16
25
|
const fm = (SourceFieldName: string, Status: string) => ({ SourceFieldName, Status });
|
|
17
26
|
|
|
18
|
-
describe('decideFieldMapReconcile — a refresh
|
|
19
|
-
it('creates a new column
|
|
27
|
+
describe('decideFieldMapReconcile — a refresh reports new columns, disabled', () => {
|
|
28
|
+
it('creates a new column DISABLED on an enabled map, by default', () => {
|
|
20
29
|
const plan = DecideFieldMapReconcile(['id', 'brand_new_col'], [fm('id', 'Active')], true);
|
|
21
|
-
expect(plan.Create).toEqual([{ SourceFieldName: 'brand_new_col', Status: '
|
|
30
|
+
expect(plan.Create).toEqual([{ SourceFieldName: 'brand_new_col', Status: 'Inactive' }]);
|
|
22
31
|
expect(plan.Enable).toEqual([]);
|
|
23
32
|
expect(plan.Disable).toEqual([]);
|
|
24
33
|
});
|
|
25
34
|
|
|
26
|
-
it('autoEnableNewColumns:
|
|
27
|
-
const plan = DecideFieldMapReconcile(['id', 'brand_new_col'], [fm('id', 'Active')], true,
|
|
28
|
-
expect(plan.Create).toEqual([{ SourceFieldName: 'brand_new_col', Status: '
|
|
35
|
+
it('autoEnableNewColumns:true is the explicit opt-in for a connection that wants adoption', () => {
|
|
36
|
+
const plan = DecideFieldMapReconcile(['id', 'brand_new_col'], [fm('id', 'Active')], true, true);
|
|
37
|
+
expect(plan.Create).toEqual([{ SourceFieldName: 'brand_new_col', Status: 'Active' }]);
|
|
29
38
|
});
|
|
30
39
|
|
|
31
40
|
it('a DISABLED map never gets an Active column — the map always bounds the column', () => {
|
|
@@ -73,12 +82,12 @@ describe('refresh adopts, sync only suggests — the defaults that encode it', (
|
|
|
73
82
|
const promoter = readFileSync(
|
|
74
83
|
join(__dirname, '..', 'integration', 'CustomColumnPromoter.ts'), 'utf8');
|
|
75
84
|
|
|
76
|
-
it('a REFRESH
|
|
77
|
-
expect(resolver).toMatch(/@Arg\("autoEnableNewObjects",\s*\{\s*defaultValue:\s*
|
|
85
|
+
it('a REFRESH creates new objects DISABLED', () => {
|
|
86
|
+
expect(resolver).toMatch(/@Arg\("autoEnableNewObjects",\s*\{\s*defaultValue:\s*false/);
|
|
78
87
|
});
|
|
79
88
|
|
|
80
|
-
it('a REFRESH
|
|
81
|
-
expect(resolver).toMatch(/@Arg\("autoEnableNewColumns",\s*\{\s*defaultValue:\s*
|
|
89
|
+
it('a REFRESH creates new columns DISABLED', () => {
|
|
90
|
+
expect(resolver).toMatch(/@Arg\("autoEnableNewColumns",\s*\{\s*defaultValue:\s*false/);
|
|
82
91
|
});
|
|
83
92
|
|
|
84
93
|
it('a SYNC never auto-creates a column — it captures a candidate and waits for acceptance', () => {
|
|
@@ -49,11 +49,17 @@ const startSessionMock = vi.fn<[], Promise<FakeSession>>(async () => deadSession
|
|
|
49
49
|
// documented outcome for anything that is not a dead handle, so it is the default: the pre-existing
|
|
50
50
|
// error-reporting contract must survive untouched for every ordinary failure.
|
|
51
51
|
const recoverDeadSessionMock = vi.fn<[], Promise<FakeSession | null>>(async () => null);
|
|
52
|
+
const endSessionMock = vi.fn(async (_agentSessionID: string) => true);
|
|
53
|
+
const restrictMock = vi.fn(async (_agentSessionID: string, _allowedHosts: string[]) => undefined);
|
|
52
54
|
vi.mock('@memberjunction/remote-browser-server', () => ({
|
|
55
|
+
NormalizeInstanceKey: (key?: string) => key?.trim() || null,
|
|
53
56
|
RemoteBrowserEngine: {
|
|
54
57
|
Instance: {
|
|
55
58
|
StartSessionForAgentSession: (...args: unknown[]) => startSessionMock(...(args as [])),
|
|
56
59
|
RecoverDeadAgentSession: (...args: unknown[]) => recoverDeadSessionMock(...(args as [])),
|
|
60
|
+
EndSessionForAgentSession: (agentSessionID: string) => endSessionMock(agentSessionID),
|
|
61
|
+
RestrictPrivateNetworkForAgentSession: (agentSessionID: string, allowedHosts: string[]) => restrictMock(agentSessionID, allowedHosts),
|
|
62
|
+
GetSessionForAgentSession: () => undefined,
|
|
57
63
|
},
|
|
58
64
|
},
|
|
59
65
|
}));
|
|
@@ -71,7 +77,12 @@ vi.mock('@memberjunction/ai-core-plus', async (importOriginal) => ({
|
|
|
71
77
|
AIPromptParams: class {},
|
|
72
78
|
}));
|
|
73
79
|
|
|
80
|
+
// The resolver reads its conversation-browser settings from the server config; off, as by default.
|
|
81
|
+
vi.mock('../config.js', () => ({ configInfo: { realtime: { conversationBrowser: { enabled: false } } } }));
|
|
82
|
+
|
|
74
83
|
import { RemoteBrowserActionResolver } from '../resolvers/RemoteBrowserActionResolver.js';
|
|
84
|
+
import { ConversationBrowserLedger } from '../agentSessions/conversationBrowsers.js';
|
|
85
|
+
import type { ConversationBrowserConfig } from '../config.js';
|
|
75
86
|
|
|
76
87
|
/** Bypasses the ownership/provider plumbing (covered elsewhere) so the recovery branch stands alone. */
|
|
77
88
|
class TestableResolver extends RemoteBrowserActionResolver {
|
|
@@ -171,3 +182,279 @@ describe('RemoteBrowserActionResolver — dead-handle recovery on the action pat
|
|
|
171
182
|
expect(recoverDeadSessionMock).toHaveBeenCalledTimes(1);
|
|
172
183
|
});
|
|
173
184
|
});
|
|
185
|
+
|
|
186
|
+
describe('RemoteBrowserActionResolver — conversation-scoped ownership & structured text actions', () => {
|
|
187
|
+
const userID = 'a1a1a1a1-0000-4000-8000-000000000001';
|
|
188
|
+
const otherUserID = 'c3c3c3c3-0000-4000-8000-000000000003';
|
|
189
|
+
const convID = 'b2b2b2b2-0000-4000-8000-000000000002';
|
|
190
|
+
|
|
191
|
+
const ENABLED: ConversationBrowserConfig = { enabled: true, allowAnonymous: false, maxPerUser: 2, maxAnonymousTotal: 4, idleMinutes: 15, allowedPrivateHosts: ['localhost:4200'] };
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* A resolver whose provider has no agent session by that ID, only conversations owned by `ownerID`
|
|
195
|
+
* (stamped with `externalID`, an anonymous guest's scope id), called by `caller`, under `settings`.
|
|
196
|
+
*/
|
|
197
|
+
function conversationResolver(
|
|
198
|
+
ownerID: string,
|
|
199
|
+
caller: Record<string, unknown> = { ID: userID },
|
|
200
|
+
externalID: string | null = null,
|
|
201
|
+
settings: ConversationBrowserConfig = ENABLED,
|
|
202
|
+
): RemoteBrowserActionResolver {
|
|
203
|
+
const getEntityObject = vi.fn(async (entityName: string) => {
|
|
204
|
+
if (entityName !== 'MJ: Conversations') {
|
|
205
|
+
return { Load: vi.fn(async () => false) };
|
|
206
|
+
}
|
|
207
|
+
const conversation = { ID: '', UserID: ownerID, ExternalID: externalID, Load: vi.fn(async (id: string) => {
|
|
208
|
+
conversation.ID = id;
|
|
209
|
+
return id.startsWith('b2b2b2b2');
|
|
210
|
+
}) };
|
|
211
|
+
return conversation;
|
|
212
|
+
});
|
|
213
|
+
class OwnershipResolver extends RemoteBrowserActionResolver {
|
|
214
|
+
protected override requireUserAndProvider() {
|
|
215
|
+
return { contextUser: caller, provider: { GetEntityObject: getEntityObject } } as never;
|
|
216
|
+
}
|
|
217
|
+
protected override ConversationBrowserSettings(): ConversationBrowserConfig {
|
|
218
|
+
return settings;
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
return new OwnershipResolver();
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
beforeEach(() => {
|
|
225
|
+
recoverDeadSessionMock.mockReset().mockResolvedValue(null);
|
|
226
|
+
for (const id of [convID, convID2, convID3]) {
|
|
227
|
+
ConversationBrowserLedger.Instance.Release(id);
|
|
228
|
+
}
|
|
229
|
+
});
|
|
230
|
+
const convID2 = 'b2b2b2b2-0000-4000-8000-000000000012';
|
|
231
|
+
const convID3 = 'b2b2b2b2-0000-4000-8000-000000000013';
|
|
232
|
+
|
|
233
|
+
it('refuses an instance key for a conversation, which would key a second, uncounted browser', async () => {
|
|
234
|
+
startSessionMock.mockReset();
|
|
235
|
+
const resolver = conversationResolver(userID);
|
|
236
|
+
await expect(
|
|
237
|
+
resolver.ExecuteRemoteBrowserAction(convID, 'inspect', ctx, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, 'x'),
|
|
238
|
+
).rejects.toThrow('instanceKey is not accepted');
|
|
239
|
+
await expect(resolver.EndRemoteBrowserSession(convID, ctx, 'x')).rejects.toThrow('instanceKey is not accepted');
|
|
240
|
+
expect(startSessionMock).not.toHaveBeenCalled();
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
it('applies the network policy when the browser starts, not on every poll', async () => {
|
|
244
|
+
deadSession = makeSession();
|
|
245
|
+
startSessionMock.mockReset().mockResolvedValue(deadSession);
|
|
246
|
+
restrictMock.mockClear();
|
|
247
|
+
const resolver = conversationResolver(userID);
|
|
248
|
+
await resolver.RemoteBrowserSnapshot(convID, ctx);
|
|
249
|
+
expect(restrictMock).not.toHaveBeenCalled();
|
|
250
|
+
await resolver.ExecuteRemoteBrowserAction(convID, 'inspect', ctx);
|
|
251
|
+
expect(restrictMock).toHaveBeenCalledWith(convID, ['localhost:4200']);
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
it('after the idle reaper ends a browser, the next open streams the new one instead of answering "already streaming"', async () => {
|
|
255
|
+
const streamingSession = { ...makeSession(), StartScreencast: vi.fn(async () => undefined) };
|
|
256
|
+
startSessionMock.mockReset().mockResolvedValue(streamingSession as never);
|
|
257
|
+
endSessionMock.mockClear();
|
|
258
|
+
const resolver = conversationResolver(userID, { ID: userID }, null, { ...ENABLED, idleMinutes: -1 });
|
|
259
|
+
const pubSub = { publish: vi.fn() } as never;
|
|
260
|
+
|
|
261
|
+
expect((await resolver.StartRemoteBrowserScreencast(convID, ctx, pubSub)).Streaming).toBe(true);
|
|
262
|
+
expect(await ConversationBrowserLedger.Instance.ReapIdle()).toBe(1);
|
|
263
|
+
expect(endSessionMock).toHaveBeenCalledWith(convID);
|
|
264
|
+
|
|
265
|
+
expect((await resolver.StartRemoteBrowserScreencast(convID, ctx, pubSub)).Streaming).toBe(true);
|
|
266
|
+
expect(streamingSession.StartScreencast).toHaveBeenCalledTimes(2);
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
it('caps each anonymous guest on its own, not every guest as one shared Anonymous user', async () => {
|
|
270
|
+
deadSession = makeSession();
|
|
271
|
+
startSessionMock.mockReset().mockResolvedValue(deadSession);
|
|
272
|
+
const settings = { ...ENABLED, allowAnonymous: true, maxPerUser: 1 };
|
|
273
|
+
const guestA = { ID: userID, IsMagicLinkAnonymous: true, AnonymousVisitorID: 'visitor-a' };
|
|
274
|
+
const guestB = { ID: userID, IsMagicLinkAnonymous: true, AnonymousVisitorID: 'visitor-b' };
|
|
275
|
+
expect((await conversationResolver(userID, guestA, 'visitor-a', settings).ExecuteRemoteBrowserAction(convID, 'inspect', ctx)).Success).toBe(true);
|
|
276
|
+
expect((await conversationResolver(userID, guestB, 'visitor-b', settings).ExecuteRemoteBrowserAction(convID2, 'inspect', ctx)).Success).toBe(true);
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
it('caps all anonymous guests together, since a guest can start a new scope cheaply; signed-in users are not counted', async () => {
|
|
280
|
+
deadSession = makeSession();
|
|
281
|
+
startSessionMock.mockReset().mockResolvedValue(deadSession);
|
|
282
|
+
const settings = { ...ENABLED, allowAnonymous: true, maxPerUser: 1, maxAnonymousTotal: 2 };
|
|
283
|
+
const guest = (visitor: string) => ({ ID: userID, IsMagicLinkAnonymous: true, AnonymousVisitorID: visitor });
|
|
284
|
+
const open = (visitor: string, conversationID: string) =>
|
|
285
|
+
conversationResolver(userID, guest(visitor), visitor, settings).ExecuteRemoteBrowserAction(conversationID, 'inspect', ctx);
|
|
286
|
+
expect((await open('visitor-a', convID)).Success).toBe(true);
|
|
287
|
+
expect((await open('visitor-b', convID2)).Success).toBe(true);
|
|
288
|
+
const third = await open('visitor-c', convID3);
|
|
289
|
+
expect(third.Success).toBe(false);
|
|
290
|
+
expect(third.Detail).toContain('allows for guests is in use');
|
|
291
|
+
expect((await conversationResolver(userID, { ID: userID }, null, settings).ExecuteRemoteBrowserAction(convID3, 'inspect', ctx)).Success).toBe(true);
|
|
292
|
+
});
|
|
293
|
+
|
|
294
|
+
it('every refusal carries the REMOTE_BROWSER_REFUSED code, so a client stops asking', async () => {
|
|
295
|
+
deadSession = makeSession();
|
|
296
|
+
startSessionMock.mockReset().mockResolvedValue({ ...deadSession, StartScreencast: vi.fn(async () => undefined) } as never);
|
|
297
|
+
const refused = { extensions: { code: 'REMOTE_BROWSER_REFUSED' } };
|
|
298
|
+
const guest = { ID: userID, IsMagicLinkAnonymous: true, AnonymousVisitorID: 'visitor-a' };
|
|
299
|
+
const pubSub = { publish: vi.fn() } as never;
|
|
300
|
+
await expect(conversationResolver(userID, { ID: userID }, null, { ...ENABLED, enabled: false }).RemoteBrowserSnapshot(convID, ctx)).rejects.toMatchObject(refused);
|
|
301
|
+
await expect(conversationResolver(userID, guest, 'visitor-a').RemoteBrowserSnapshot(convID, ctx)).rejects.toMatchObject(refused);
|
|
302
|
+
await expect(conversationResolver(userID).EndRemoteBrowserSession(convID, ctx, 'x')).rejects.toMatchObject(refused);
|
|
303
|
+
await expect(conversationResolver(otherUserID).RemoteBrowserSnapshot(convID, ctx)).rejects.toMatchObject(refused);
|
|
304
|
+
await expect(conversationResolver(userID).RemoteBrowserSnapshot('d4d4d4d4-0000-4000-8000-000000000004', ctx)).rejects.toMatchObject(refused);
|
|
305
|
+
const capped = conversationResolver(userID, { ID: userID }, null, { ...ENABLED, maxPerUser: 1 });
|
|
306
|
+
await capped.StartRemoteBrowserScreencast(convID, ctx, pubSub);
|
|
307
|
+
await expect(capped.StartRemoteBrowserScreencast(convID2, ctx, pubSub)).rejects.toMatchObject(refused);
|
|
308
|
+
});
|
|
309
|
+
|
|
310
|
+
it('forgets where a stopped stream went, so a browser that never ends cannot leak its targets', async () => {
|
|
311
|
+
const streamingSession = { ...makeSession(), StartScreencast: vi.fn(async () => undefined) };
|
|
312
|
+
startSessionMock.mockReset().mockResolvedValue(streamingSession as never);
|
|
313
|
+
const resolver = conversationResolver(userID);
|
|
314
|
+
const pubSub = { publish: vi.fn() } as never;
|
|
315
|
+
const targets = (resolver as unknown as { streamTargets: Map<string, unknown> }).streamTargets;
|
|
316
|
+
|
|
317
|
+
await resolver.StartRemoteBrowserScreencast(convID, ctx, pubSub);
|
|
318
|
+
await resolver.StopRemoteBrowserScreencast(convID, ctx);
|
|
319
|
+
await resolver.StartRemoteBrowserScreencast(convID2, ctx, pubSub);
|
|
320
|
+
expect(targets.size).toBe(1);
|
|
321
|
+
});
|
|
322
|
+
|
|
323
|
+
it('a reloaded tab (a new push session) takes the frames over instead of hearing "already streaming"', async () => {
|
|
324
|
+
let onFrame: ((frame: { DataBase64: string; Width: number; Height: number; SequenceNumber: number }) => void) | null = null;
|
|
325
|
+
const streamingSession = { ...makeSession(), StartScreencast: vi.fn(async (cb: typeof onFrame) => { onFrame = cb; }) };
|
|
326
|
+
startSessionMock.mockReset().mockResolvedValue(streamingSession as never);
|
|
327
|
+
const resolver = conversationResolver(userID);
|
|
328
|
+
const publish = vi.spyOn(resolver as unknown as { PublishStatusUpdate: (...args: unknown[]) => void }, 'PublishStatusUpdate').mockImplementation(() => undefined);
|
|
329
|
+
const pubSub = { publish: vi.fn() } as never;
|
|
330
|
+
const tab = (sessionId: string) => ({ userPayload: { sessionId }, providers: {} }) as never;
|
|
331
|
+
|
|
332
|
+
await resolver.StartRemoteBrowserScreencast(convID, tab('push-A'), pubSub);
|
|
333
|
+
await resolver.StartRemoteBrowserScreencast(convID, tab('push-B'), pubSub);
|
|
334
|
+
expect(streamingSession.StartScreencast).toHaveBeenCalledTimes(1);
|
|
335
|
+
onFrame!({ DataBase64: 'f', Width: 1, Height: 1, SequenceNumber: 1 });
|
|
336
|
+
expect(publish.mock.calls[0][1]).toBe('push-B');
|
|
337
|
+
});
|
|
338
|
+
|
|
339
|
+
it('a cap refusal on opening the live view reaches the client as an error, not a silent "not streaming"', async () => {
|
|
340
|
+
deadSession = makeSession();
|
|
341
|
+
startSessionMock.mockReset().mockResolvedValue({ ...deadSession, StartScreencast: vi.fn(async () => undefined) } as never);
|
|
342
|
+
const resolver = conversationResolver(userID, { ID: userID }, null, { ...ENABLED, maxPerUser: 1 });
|
|
343
|
+
const pubSub = { publish: vi.fn() } as never;
|
|
344
|
+
expect((await resolver.StartRemoteBrowserScreencast(convID, ctx, pubSub)).Streaming).toBe(true);
|
|
345
|
+
await expect(resolver.StartRemoteBrowserScreencast(convID2, ctx, pubSub)).rejects.toThrow('the most this server allows');
|
|
346
|
+
});
|
|
347
|
+
|
|
348
|
+
it('refuses every conversation operation while the server has not enabled conversation browsers', async () => {
|
|
349
|
+
startSessionMock.mockReset();
|
|
350
|
+
const off = conversationResolver(userID, { ID: userID }, null, { ...ENABLED, enabled: false });
|
|
351
|
+
await expect(off.ExecuteRemoteBrowserAction(convID, 'inspect', ctx)).rejects.toThrow('not enabled');
|
|
352
|
+
expect(startSessionMock).not.toHaveBeenCalled();
|
|
353
|
+
});
|
|
354
|
+
|
|
355
|
+
it('refuses an anonymous guest its own conversation browser unless anonymous use is enabled', async () => {
|
|
356
|
+
deadSession = makeSession();
|
|
357
|
+
startSessionMock.mockReset().mockResolvedValue(deadSession);
|
|
358
|
+
const guest = { ID: userID, IsMagicLinkAnonymous: true, AnonymousVisitorID: 'visitor-a' };
|
|
359
|
+
await expect(conversationResolver(userID, guest, 'visitor-a').ExecuteRemoteBrowserAction(convID, 'inspect', ctx)).rejects.toThrow('anonymous');
|
|
360
|
+
expect(startSessionMock).not.toHaveBeenCalled();
|
|
361
|
+
const allowed = conversationResolver(userID, guest, 'visitor-a', { ...ENABLED, allowAnonymous: true });
|
|
362
|
+
expect((await allowed.ExecuteRemoteBrowserAction(convID, 'inspect', ctx)).Success).toBe(true);
|
|
363
|
+
});
|
|
364
|
+
|
|
365
|
+
it('caps the conversation browsers one user holds, keeps it off the private network, and refuses goal runs', async () => {
|
|
366
|
+
deadSession = makeSession();
|
|
367
|
+
startSessionMock.mockReset().mockResolvedValue(deadSession);
|
|
368
|
+
restrictMock.mockClear();
|
|
369
|
+
const resolver = conversationResolver(userID);
|
|
370
|
+
expect((await resolver.ExecuteRemoteBrowserAction(convID, 'inspect', ctx)).Success).toBe(true);
|
|
371
|
+
expect((await resolver.ExecuteRemoteBrowserAction(convID2, 'inspect', ctx)).Success).toBe(true);
|
|
372
|
+
expect((await resolver.ExecuteRemoteBrowserAction(convID, 'inspect', ctx)).Success).toBe(true); // already held
|
|
373
|
+
const third = await resolver.ExecuteRemoteBrowserAction(convID3, 'inspect', ctx);
|
|
374
|
+
expect(third.Success).toBe(false);
|
|
375
|
+
expect(third.Detail).toContain('the most this server allows');
|
|
376
|
+
expect(startSessionMock).toHaveBeenCalledTimes(3);
|
|
377
|
+
expect(restrictMock).toHaveBeenCalledWith(convID, ['localhost:4200']);
|
|
378
|
+
|
|
379
|
+
const goal = await resolver.ExecuteRemoteBrowserGoal(convID, 'buy the thing', ctx);
|
|
380
|
+
expect(goal.Success).toBe(false);
|
|
381
|
+
expect(goal.Detail).toContain('not available in a text conversation');
|
|
382
|
+
});
|
|
383
|
+
|
|
384
|
+
it('acts on a conversation the caller owns', async () => {
|
|
385
|
+
deadSession = makeSession();
|
|
386
|
+
deadSession.ExecuteAction.mockResolvedValue({
|
|
387
|
+
Success: true,
|
|
388
|
+
CurrentUrl: 'https://httpbin.org/forms/post',
|
|
389
|
+
Detail: 'Inspected',
|
|
390
|
+
Observation: { ObservationID: 'obs-1', Url: 'https://httpbin.org/forms/post', Title: 'Order Form', Text: '', Fields: [], Actions: [], Links: [] },
|
|
391
|
+
});
|
|
392
|
+
startSessionMock.mockReset().mockResolvedValue(deadSession);
|
|
393
|
+
|
|
394
|
+
const res = await conversationResolver(userID).ExecuteRemoteBrowserAction(convID, 'inspect', ctx);
|
|
395
|
+
expect(res.Success).toBe(true);
|
|
396
|
+
expect(JSON.parse(res.ObservationJson!).ObservationID).toBe('obs-1');
|
|
397
|
+
});
|
|
398
|
+
|
|
399
|
+
it("refuses another user's conversation, for actions and for ending its browser", async () => {
|
|
400
|
+
startSessionMock.mockReset();
|
|
401
|
+
endSessionMock.mockClear();
|
|
402
|
+
const resolver = conversationResolver(otherUserID);
|
|
403
|
+
await expect(resolver.ExecuteRemoteBrowserAction(convID, 'inspect', ctx)).rejects.toThrow('Not authorized');
|
|
404
|
+
await expect(resolver.EndRemoteBrowserSession(convID, ctx)).rejects.toThrow('Not authorized');
|
|
405
|
+
expect(startSessionMock).not.toHaveBeenCalled();
|
|
406
|
+
expect(endSessionMock).not.toHaveBeenCalled();
|
|
407
|
+
});
|
|
408
|
+
|
|
409
|
+
it("refuses an anonymous guest another guest's conversation, though both share the Anonymous user", async () => {
|
|
410
|
+
startSessionMock.mockReset();
|
|
411
|
+
const guest = { ID: userID, IsMagicLinkAnonymous: true, AnonymousVisitorID: 'visitor-b' };
|
|
412
|
+
await expect(conversationResolver(userID, guest, 'visitor-a').ExecuteRemoteBrowserAction(convID, 'inspect', ctx)).rejects.toThrow('Not authorized');
|
|
413
|
+
expect(startSessionMock).not.toHaveBeenCalled();
|
|
414
|
+
});
|
|
415
|
+
|
|
416
|
+
it("ends the caller's own conversation browser", async () => {
|
|
417
|
+
endSessionMock.mockClear();
|
|
418
|
+
await expect(conversationResolver(userID).EndRemoteBrowserSession(convID, ctx)).resolves.toBe(true);
|
|
419
|
+
expect(endSessionMock).toHaveBeenCalledWith(convID);
|
|
420
|
+
});
|
|
421
|
+
|
|
422
|
+
it('maps a fill values object to field inputs and returns FilledJson + ObservationJson; any other shape is refused', async () => {
|
|
423
|
+
const resolver = new TestableResolver();
|
|
424
|
+
deadSession = makeSession();
|
|
425
|
+
deadSession.ExecuteAction.mockResolvedValue({
|
|
426
|
+
Success: true,
|
|
427
|
+
CurrentUrl: 'https://httpbin.org/forms/post',
|
|
428
|
+
Detail: 'Filled 1 field(s).',
|
|
429
|
+
Filled: [{ Ref: 'f1', Label: 'Customer name', Value: 'Colin' }],
|
|
430
|
+
Observation: { ObservationID: 'obs-2', Url: 'https://httpbin.org/forms/post', Title: 'Order Form', Text: '', Fields: [], Actions: [], Links: [] },
|
|
431
|
+
});
|
|
432
|
+
startSessionMock.mockReset().mockResolvedValue(deadSession);
|
|
433
|
+
const fill = (valuesJson: string) =>
|
|
434
|
+
resolver.ExecuteRemoteBrowserAction('sess-1', 'fill', ctx, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, valuesJson);
|
|
435
|
+
|
|
436
|
+
const res = await fill(JSON.stringify({ f1: 'Colin', f9: true }));
|
|
437
|
+
expect(res.Success).toBe(true);
|
|
438
|
+
expect(deadSession.ExecuteAction).toHaveBeenCalledWith({ Kind: 'fill', Values: [{ Target: 'f1', Value: 'Colin' }, { Target: 'f9', Value: true }] });
|
|
439
|
+
expect(JSON.parse(res.FilledJson!)).toEqual([{ Ref: 'f1', Label: 'Customer name', Value: 'Colin' }]);
|
|
440
|
+
|
|
441
|
+
expect((await fill(JSON.stringify([{ ref: 'f1', value: 'Colin' }]))).Success).toBe(false);
|
|
442
|
+
expect((await fill('not json')).Success).toBe(false);
|
|
443
|
+
});
|
|
444
|
+
|
|
445
|
+
it('maps getPageText and a ref/text click', async () => {
|
|
446
|
+
const resolver = new TestableResolver();
|
|
447
|
+
deadSession = makeSession();
|
|
448
|
+
deadSession.ExecuteAction.mockResolvedValue({ Success: true, CurrentUrl: 'https://example.com/', Detail: 'Example Domain' });
|
|
449
|
+
startSessionMock.mockReset().mockResolvedValue(deadSession);
|
|
450
|
+
|
|
451
|
+
expect((await resolver.ExecuteRemoteBrowserAction('sess-1', 'getPageText', ctx)).Success).toBe(true);
|
|
452
|
+
expect(deadSession.ExecuteAction).toHaveBeenCalledWith({ Kind: 'getPageText' });
|
|
453
|
+
|
|
454
|
+
const clickRes = await resolver.ExecuteRemoteBrowserAction(
|
|
455
|
+
'sess-1', 'click', ctx, undefined, undefined, undefined, undefined, 'Learn more', undefined, undefined, undefined, undefined, undefined, 'a1',
|
|
456
|
+
);
|
|
457
|
+
expect(clickRes.Success).toBe(true);
|
|
458
|
+
expect(deadSession.ExecuteAction).toHaveBeenLastCalledWith({ Kind: 'click', Selector: undefined, Ref: 'a1', Text: 'Learn more', X: undefined, Y: undefined });
|
|
459
|
+
});
|
|
460
|
+
});
|
|
@@ -58,6 +58,9 @@ vi.mock('@memberjunction/ai-core-plus', async (importOriginal) => ({
|
|
|
58
58
|
AIPromptParams: class {},
|
|
59
59
|
}));
|
|
60
60
|
|
|
61
|
+
// The resolver reads its conversation-browser settings from the server config; off, as by default.
|
|
62
|
+
vi.mock('../config.js', () => ({ configInfo: { realtime: { conversationBrowser: { enabled: false } } } }));
|
|
63
|
+
|
|
61
64
|
import { RemoteBrowserActionResolver } from '../resolvers/RemoteBrowserActionResolver.js';
|
|
62
65
|
import type { AppContext } from '../types.js';
|
|
63
66
|
import type { IMetadataProvider, UserInfo } from '@memberjunction/core';
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import 'reflect-metadata';
|
|
2
2
|
import { describe, it, expect, vi } from 'vitest';
|
|
3
3
|
import type { IMetadataProvider, UserInfo } from '@memberjunction/core';
|
|
4
|
-
import type { MJAIAgentSessionEntity } from '@memberjunction/core-entities';
|
|
5
4
|
|
|
6
5
|
vi.mock('type-graphql', () => {
|
|
7
6
|
const noopDecorator = () => () => undefined;
|
|
@@ -38,7 +37,10 @@ vi.mock('@memberjunction/ai-core-plus', async (importOriginal) => ({
|
|
|
38
37
|
AIPromptParams: class {},
|
|
39
38
|
}));
|
|
40
39
|
|
|
41
|
-
|
|
40
|
+
// The resolver reads its conversation-browser settings from the server config; off, as by default.
|
|
41
|
+
vi.mock('../config.js', () => ({ configInfo: { realtime: { conversationBrowser: { enabled: false } } } }));
|
|
42
|
+
|
|
43
|
+
import { RemoteBrowserActionResolver, type OwnedBrowserScope } from '../resolvers/RemoteBrowserActionResolver.js';
|
|
42
44
|
|
|
43
45
|
const uuid = (a: string, b: string) =>
|
|
44
46
|
[a.repeat(8), b.repeat(4), '4' + b.repeat(3), '8' + b.repeat(3), b.repeat(12)].join('-');
|
|
@@ -59,7 +61,7 @@ function makeProvider(sessionFound = true, externalId = 'visitor-1'): IMetadataP
|
|
|
59
61
|
GetEntityObject: vi.fn(async (entityName: string) =>
|
|
60
62
|
entityName === 'MJ: AI Agent Sessions'
|
|
61
63
|
? { ID: SESSION, UserID: ANON, ConversationID: CONVERSATION, Load: vi.fn(async () => sessionFound) }
|
|
62
|
-
: { ExternalID: externalId, Load: vi.fn(async () =>
|
|
64
|
+
: { ExternalID: externalId, Load: vi.fn(async (id: string) => id === CONVERSATION) },
|
|
63
65
|
),
|
|
64
66
|
} as unknown as IMetadataProvider;
|
|
65
67
|
}
|
|
@@ -69,7 +71,7 @@ class TestableRemoteBrowserResolver extends RemoteBrowserActionResolver {
|
|
|
69
71
|
agentSessionID: string,
|
|
70
72
|
contextUser: UserInfo,
|
|
71
73
|
provider: IMetadataProvider,
|
|
72
|
-
): Promise<
|
|
74
|
+
): Promise<OwnedBrowserScope> {
|
|
73
75
|
return this.loadOwnedSession(agentSessionID, contextUser, provider);
|
|
74
76
|
}
|
|
75
77
|
}
|
|
@@ -88,6 +88,9 @@ vi.mock('@memberjunction/ai-core-plus', async (importOriginal) => ({
|
|
|
88
88
|
AIPromptParams: class {},
|
|
89
89
|
}));
|
|
90
90
|
|
|
91
|
+
// The resolver reads its conversation-browser settings from the server config; off, as by default.
|
|
92
|
+
vi.mock('../config.js', () => ({ configInfo: { realtime: { conversationBrowser: { enabled: false } } } }));
|
|
93
|
+
|
|
91
94
|
import { RemoteBrowserActionResolver } from '../resolvers/RemoteBrowserActionResolver.js';
|
|
92
95
|
|
|
93
96
|
/**
|
|
@@ -76,6 +76,9 @@ vi.mock('@memberjunction/ai-core-plus', async (importOriginal) => ({
|
|
|
76
76
|
AIPromptParams: class {},
|
|
77
77
|
}));
|
|
78
78
|
|
|
79
|
+
// The resolver reads its conversation-browser settings from the server config; off, as by default.
|
|
80
|
+
vi.mock('../config.js', () => ({ configInfo: { realtime: { conversationBrowser: { enabled: false } } } }));
|
|
81
|
+
|
|
79
82
|
import { RemoteBrowserActionResolver } from '../resolvers/RemoteBrowserActionResolver.js';
|
|
80
83
|
|
|
81
84
|
/**
|