@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
|
@@ -19,6 +19,8 @@ import {
|
|
|
19
19
|
} from '@memberjunction/ai-agents';
|
|
20
20
|
import { GetCurrentHostInstance, GetHostInstanceID, HostInstanceIdentity } from './HostInstance.js';
|
|
21
21
|
import { SessionManager, SessionCloseReason } from './SessionManager.js';
|
|
22
|
+
import type { SessionJanitorSettings } from '../backgroundSweepConfigUnits.js';
|
|
23
|
+
import { ExpiryCloseReason, ExpiryReason } from './sessionDeadline.js';
|
|
22
24
|
|
|
23
25
|
/** Entity name for session rows (kept in sync with {@link SessionManager}). */
|
|
24
26
|
const SESSION_ENTITY = 'MJ: AI Agent Sessions';
|
|
@@ -195,6 +197,24 @@ interface RecoveryVisit {
|
|
|
195
197
|
Recovered: boolean;
|
|
196
198
|
}
|
|
197
199
|
|
|
200
|
+
/**
|
|
201
|
+
* The close reason for a row the staleness sweep's SQL already judged stale (`LastActiveAt < cutoff`).
|
|
202
|
+
* {@link ExpiryCloseReason} returns null for such a row only when `LastActiveAt` will not parse (or the
|
|
203
|
+
* threshold is non-finite); skipping it would leave it `Active` forever, so it closes as `'Idle'` and the
|
|
204
|
+
* fallback is logged with the session ID.
|
|
205
|
+
*/
|
|
206
|
+
function staleRowCloseReason(session: MJAIAgentSessionEntity, nowMs: number, thresholdMs: number): ExpiryReason {
|
|
207
|
+
const reason = ExpiryCloseReason(session, nowMs, thresholdMs);
|
|
208
|
+
if (reason !== null) {
|
|
209
|
+
return reason;
|
|
210
|
+
}
|
|
211
|
+
LogError(
|
|
212
|
+
`[SessionJanitor] Staleness sweep: session ${session.ID} has LastActiveAt ${String(session.LastActiveAt)}; ` +
|
|
213
|
+
'LastActiveAt unparseable; closing as Idle',
|
|
214
|
+
);
|
|
215
|
+
return 'Idle';
|
|
216
|
+
}
|
|
217
|
+
|
|
198
218
|
/**
|
|
199
219
|
* `BaseSingleton` background reconciler that keeps the durable `AIAgentSession` state from drifting
|
|
200
220
|
* away from volatile process reality. A crash/redeploy vaporizes a host's in-memory sockets but
|
|
@@ -212,9 +232,12 @@ interface RecoveryVisit {
|
|
|
212
232
|
* `closeThresholdMinutes`, regardless of host. Catches sessions whose owner died without a clean
|
|
213
233
|
* reboot (OOM, scaled-down pod).
|
|
214
234
|
* 3. **Max-duration sweep ({@link RunMaxDurationSweep})** — run on every periodic tick after the
|
|
215
|
-
* staleness sweep. Closes any session past its stored hard deadline (the public voice cost cap)
|
|
235
|
+
* staleness sweep. Closes any session past its stored hard deadline (the public voice cost cap), and
|
|
236
|
+
* any deadline-carrying session whose idle moment has passed; it stamps that row `'Idle'` when the idle
|
|
237
|
+
* moment came before the deadline.
|
|
216
238
|
*
|
|
217
|
-
*
|
|
239
|
+
* Startup recovery stamps `CloseReason = 'Janitor'`; the staleness and max-duration sweeps stamp `'Idle'` or
|
|
240
|
+
* `'TimeLimit'`, whichever expiry moment passed first ({@link ExpiryCloseReason}). A shutdown-time path — {@link RunShutdownDrain},
|
|
218
241
|
* invoked from {@link Shutdown} during the graceful ShutdownRegistry drain — closes this exact host
|
|
219
242
|
* instance's own live sessions with `CloseReason = 'Shutdown'`.
|
|
220
243
|
*
|
|
@@ -318,6 +341,39 @@ export class SessionJanitor extends BaseSingleton<SessionJanitor> implements ISh
|
|
|
318
341
|
this.scheduleSweep();
|
|
319
342
|
}
|
|
320
343
|
|
|
344
|
+
/**
|
|
345
|
+
* Starts the janitor the way the host's `agentSessions.janitor` settings ask: applies the interval
|
|
346
|
+
* and close threshold, then {@link Start} when enabled, or {@link StartRecoveryOnly} when not.
|
|
347
|
+
* Throws (before doing any work) on an invalid interval or threshold; see {@link Configure}.
|
|
348
|
+
*/
|
|
349
|
+
public async StartWithSettings(settings: SessionJanitorSettings, provider: DatabaseProviderBase, systemUser: UserInfo): Promise<void> {
|
|
350
|
+
this.Configure({ sweepIntervalMs: settings.sweepIntervalMs, closeThresholdMinutes: settings.closeThresholdMinutes });
|
|
351
|
+
if (settings.enabled) {
|
|
352
|
+
await this.Start(provider, systemUser);
|
|
353
|
+
} else {
|
|
354
|
+
await this.StartRecoveryOnly(provider, systemUser);
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
/**
|
|
359
|
+
* {@link Start} without the periodic tick, for hosts that turned the janitor off: run own-host
|
|
360
|
+
* startup recovery once and register for the graceful-shutdown drain, but schedule nothing, so an
|
|
361
|
+
* idle process issues no periodic queries. Stale-session, max-duration and recording-recovery
|
|
362
|
+
* sweeps then do not run in this process. Stops a tick an earlier {@link Start} scheduled.
|
|
363
|
+
*/
|
|
364
|
+
public async StartRecoveryOnly(provider: DatabaseProviderBase, systemUser: UserInfo): Promise<void> {
|
|
365
|
+
this.Stop();
|
|
366
|
+
this._provider = provider;
|
|
367
|
+
this._systemUser = systemUser;
|
|
368
|
+
this.ensureRegistered();
|
|
369
|
+
await this.RunStartupRecovery(provider, systemUser);
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
/** Whether the periodic tick is currently scheduled (diagnostics and tests). */
|
|
373
|
+
public get IsSweepScheduled(): boolean {
|
|
374
|
+
return this._sweepTimer !== null;
|
|
375
|
+
}
|
|
376
|
+
|
|
321
377
|
/**
|
|
322
378
|
* Stops the periodic tick and keeps any new recording recovery pass from starting until the next
|
|
323
379
|
* {@link Start}, including one a tick already in progress would have started. A pass that is already
|
|
@@ -335,7 +391,7 @@ export class SessionJanitor extends BaseSingleton<SessionJanitor> implements ISh
|
|
|
335
391
|
* {@link IShutdownable}: stop the timer, then drain this host's own live sessions so a
|
|
336
392
|
* graceful stop never strands `Active`/`Idle` rows for the next boot's janitor to mop up, then wait
|
|
337
393
|
* (at most {@link RECOVERY_SHUTDOWN_WAIT_MS}) for an in-flight recording recovery pass.
|
|
338
|
-
* Drained sessions are stamped `CloseReason = 'Shutdown'` (vs. `'Janitor'` for crash orphans),
|
|
394
|
+
* Drained sessions are stamped `CloseReason = 'Shutdown'` (vs. `'Janitor'` for crash orphans from an earlier boot),
|
|
339
395
|
* so the dashboards can tell a clean redeploy from a reconciled crash. Never throws; the drain
|
|
340
396
|
* is skipped when {@link Start} was never called (no captured provider/user).
|
|
341
397
|
*/
|
|
@@ -360,7 +416,7 @@ export class SessionJanitor extends BaseSingleton<SessionJanitor> implements ISh
|
|
|
360
416
|
public async RunShutdownDrain(provider: IMetadataProvider, systemUser: UserInfo): Promise<number> {
|
|
361
417
|
const current = GetHostInstanceID().replace(/'/g, "''");
|
|
362
418
|
const filter = `Status IN ('Active','Idle') AND HostInstanceID = '${current}'`;
|
|
363
|
-
const closed = await this.sweepAndClose(filter, provider, systemUser, 'Shutdown');
|
|
419
|
+
const closed = await this.sweepAndClose(filter, provider, systemUser, () => 'Shutdown');
|
|
364
420
|
if (closed > 0) {
|
|
365
421
|
LogStatus(`[SessionJanitor] Shutdown drain closed ${closed} live session(s) owned by this host instance`);
|
|
366
422
|
}
|
|
@@ -389,8 +445,9 @@ export class SessionJanitor extends BaseSingleton<SessionJanitor> implements ISh
|
|
|
389
445
|
`AND HostInstanceID LIKE '${prefix}%' ` +
|
|
390
446
|
`AND HostInstanceID <> '${current}'`;
|
|
391
447
|
// The LIKE narrows the read; the exact check decides, since LIKE reads a `_` in a host name as any character.
|
|
392
|
-
const
|
|
393
|
-
|
|
448
|
+
const reasonFor = (session: MJAIAgentSessionEntity): SessionCloseReason | null =>
|
|
449
|
+
hostInstance.IsPriorBoot(session.HostInstanceID) ? 'Janitor' : null;
|
|
450
|
+
const closed = await this.sweepAndClose(filter, provider, systemUser, reasonFor);
|
|
394
451
|
if (closed > 0) {
|
|
395
452
|
LogStatus(
|
|
396
453
|
`[SessionJanitor] Startup recovery closed ${closed} orphaned session(s) from an earlier boot of this instance ` +
|
|
@@ -402,12 +459,18 @@ export class SessionJanitor extends BaseSingleton<SessionJanitor> implements ISh
|
|
|
402
459
|
|
|
403
460
|
/**
|
|
404
461
|
* Force-close any `Active`/`Idle` session whose `LastActiveAt` is older than the configured
|
|
405
|
-
* close threshold, regardless of host.
|
|
462
|
+
* close threshold, regardless of host. Stamped `'Idle'`, or `'TimeLimit'` when the row's authored deadline
|
|
463
|
+
* passed before its idle moment ({@link ExpiryCloseReason}). A row the SQL judged stale is never skipped:
|
|
464
|
+
* when its `LastActiveAt` will not parse it closes as `'Idle'` with a `LogError` ({@link staleRowCloseReason}).
|
|
465
|
+
* Idempotent and concurrency-safe. Returns the count closed.
|
|
406
466
|
*/
|
|
407
467
|
public async RunStalenessSweep(provider: IMetadataProvider, systemUser: UserInfo): Promise<number> {
|
|
408
|
-
|
|
468
|
+
// One clock read feeds both the SQL cutoff and the per-row decision, so they cannot disagree.
|
|
469
|
+
const nowMs = Date.now();
|
|
470
|
+
const thresholdMs = this._config.closeThresholdMinutes * 60_000;
|
|
471
|
+
const cutoffIso = new Date(nowMs - thresholdMs).toISOString();
|
|
409
472
|
const filter = `Status IN ('Active','Idle') AND LastActiveAt < '${cutoffIso}'`;
|
|
410
|
-
const closed = await this.sweepAndClose(filter, provider, systemUser,
|
|
473
|
+
const closed = await this.sweepAndClose(filter, provider, systemUser, s => staleRowCloseReason(s, nowMs, thresholdMs));
|
|
411
474
|
if (closed > 0) {
|
|
412
475
|
LogStatus(`[SessionJanitor] Staleness sweep closed ${closed} stale session(s) (>${this._config.closeThresholdMinutes}m idle)`);
|
|
413
476
|
}
|
|
@@ -422,20 +485,18 @@ export class SessionJanitor extends BaseSingleton<SessionJanitor> implements ISh
|
|
|
422
485
|
*
|
|
423
486
|
* Only sessions that CARRY a deadline are loaded (a cheap `Config_ LIKE` pre-filter), then the exact
|
|
424
487
|
* ISO deadline is parsed and compared in JS (`Config_` is JSON, not a queryable column). Stamped
|
|
425
|
-
* `
|
|
488
|
+
* `'TimeLimit'`, or `'Idle'` when the row's idle moment passed before its deadline ({@link ExpiryCloseReason});
|
|
489
|
+
* idempotent + concurrency-safe like the other sweeps. Returns the count closed.
|
|
426
490
|
*/
|
|
427
491
|
public async RunMaxDurationSweep(provider: IMetadataProvider, systemUser: UserInfo): Promise<number> {
|
|
428
492
|
const nowMs = Date.now();
|
|
429
493
|
// Only consider sessions that carry a deadline marker — keeps this sweep cheap (it never touches
|
|
430
494
|
// the far-more-common uncapped sessions) while the JS check below enforces the exact deadline.
|
|
431
495
|
const filter = `Status IN ('Active','Idle') AND Config LIKE '%maxSessionDeadlineIso%'`;
|
|
432
|
-
const
|
|
433
|
-
|
|
434
|
-
return deadlineMs != null && deadlineMs <= nowMs;
|
|
435
|
-
};
|
|
436
|
-
const closed = await this.sweepAndClose(filter, provider, systemUser, 'Janitor', isExpired);
|
|
496
|
+
const thresholdMs = this._config.closeThresholdMinutes * 60_000;
|
|
497
|
+
const closed = await this.sweepAndClose(filter, provider, systemUser, s => ExpiryCloseReason(s, nowMs, thresholdMs));
|
|
437
498
|
if (closed > 0) {
|
|
438
|
-
LogStatus(`[SessionJanitor] Max-duration sweep closed ${closed} session(s) past their
|
|
499
|
+
LogStatus(`[SessionJanitor] Max-duration sweep closed ${closed} capped session(s) past their deadline or idle threshold`);
|
|
439
500
|
}
|
|
440
501
|
return closed;
|
|
441
502
|
}
|
|
@@ -705,23 +766,6 @@ export class SessionJanitor extends BaseSingleton<SessionJanitor> implements ISh
|
|
|
705
766
|
}
|
|
706
767
|
}
|
|
707
768
|
|
|
708
|
-
/** Parses the absolute deadline (ms) from a session's `Config_` JSON, or null when absent/malformed. */
|
|
709
|
-
private parseSessionDeadlineMs(configJson: string | null | undefined): number | null {
|
|
710
|
-
if (!configJson) {
|
|
711
|
-
return null;
|
|
712
|
-
}
|
|
713
|
-
try {
|
|
714
|
-
const parsed = JSON.parse(configJson) as { maxSessionDeadlineIso?: string };
|
|
715
|
-
if (typeof parsed.maxSessionDeadlineIso !== 'string') {
|
|
716
|
-
return null;
|
|
717
|
-
}
|
|
718
|
-
const ms = Date.parse(parsed.maxSessionDeadlineIso);
|
|
719
|
-
return Number.isNaN(ms) ? null : ms;
|
|
720
|
-
} catch {
|
|
721
|
-
return null;
|
|
722
|
-
}
|
|
723
|
-
}
|
|
724
|
-
|
|
725
769
|
// ----- internals -------------------------------------------------------------------------
|
|
726
770
|
|
|
727
771
|
/** Register for graceful shutdown exactly once. */
|
|
@@ -993,7 +1037,9 @@ export class SessionJanitor extends BaseSingleton<SessionJanitor> implements ISh
|
|
|
993
1037
|
|
|
994
1038
|
/**
|
|
995
1039
|
* Page through every session matching `filter` with keyset (`AfterKey`) pagination and close each
|
|
996
|
-
* via {@link SessionManager.CloseSession}, stamping `
|
|
1040
|
+
* via {@link SessionManager.CloseSession}, stamping the reason `reasonFor` returns for that row.
|
|
1041
|
+
* A `null` reason skips the row (the SQL filter only narrows; `reasonFor` decides), and skipped rows
|
|
1042
|
+
* are dropped before the channel batch load so it covers only rows that will close.
|
|
997
1043
|
* Returns the number successfully closed. A `Closed`-by-now row (raced by another instance) is a
|
|
998
1044
|
* harmless no-op that keeps its original reason.
|
|
999
1045
|
*/
|
|
@@ -1001,30 +1047,34 @@ export class SessionJanitor extends BaseSingleton<SessionJanitor> implements ISh
|
|
|
1001
1047
|
filter: string,
|
|
1002
1048
|
provider: IMetadataProvider,
|
|
1003
1049
|
systemUser: UserInfo,
|
|
1004
|
-
|
|
1005
|
-
shouldClose?: (session: MJAIAgentSessionEntity) => boolean,
|
|
1050
|
+
reasonFor: (session: MJAIAgentSessionEntity) => SessionCloseReason | null,
|
|
1006
1051
|
): Promise<number> {
|
|
1007
1052
|
let closedCount = 0;
|
|
1008
1053
|
await this.forEachSessionPage(filter, provider, systemUser, async fetched => {
|
|
1009
|
-
|
|
1010
|
-
const
|
|
1011
|
-
|
|
1054
|
+
const toClose: Array<{ session: MJAIAgentSessionEntity; reason: SessionCloseReason }> = [];
|
|
1055
|
+
for (const session of fetched) {
|
|
1056
|
+
const reason = reasonFor(session);
|
|
1057
|
+
if (reason !== null) {
|
|
1058
|
+
toClose.push({ session, reason });
|
|
1059
|
+
}
|
|
1060
|
+
}
|
|
1061
|
+
if (toClose.length === 0) {
|
|
1012
1062
|
return;
|
|
1013
1063
|
}
|
|
1014
1064
|
// Batch-load every channel for this whole page of sessions in ONE query, then hand each
|
|
1015
1065
|
// session its own slice to CloseSession — avoids the N+1 channel read (one RunView per
|
|
1016
1066
|
// closing session) that tripped the sequential / multiple-same-entity telemetry.
|
|
1017
1067
|
const channelsBySession = await this.sessionManager.LoadActiveChannelsBySession(
|
|
1018
|
-
|
|
1068
|
+
toClose.map(c => c.session.ID),
|
|
1019
1069
|
systemUser,
|
|
1020
1070
|
provider,
|
|
1021
1071
|
);
|
|
1022
|
-
for (const session of
|
|
1072
|
+
for (const { session, reason } of toClose) {
|
|
1023
1073
|
const closed = await this.sessionManager.CloseSession(
|
|
1024
1074
|
session.ID,
|
|
1025
1075
|
systemUser,
|
|
1026
1076
|
provider,
|
|
1027
|
-
|
|
1077
|
+
reason,
|
|
1028
1078
|
channelsBySession.get(session.ID.toLowerCase()) ?? [],
|
|
1029
1079
|
);
|
|
1030
1080
|
if (closed) {
|
|
@@ -1,13 +1,15 @@
|
|
|
1
|
-
import { IMetadataProvider, UserInfo, LogError, RunView } from '@memberjunction/core';
|
|
1
|
+
import { IMetadataProvider, UserInfo, LogError, LogStatus, RunView } from '@memberjunction/core';
|
|
2
2
|
import { MJLruCache } from '@memberjunction/global';
|
|
3
3
|
import {
|
|
4
4
|
MJAIAgentSessionEntity,
|
|
5
5
|
MJAIAgentSessionChannelEntity,
|
|
6
6
|
MJConversationEntity,
|
|
7
7
|
} from '@memberjunction/core-entities';
|
|
8
|
+
import { RunWithTrustedSessionConfigWrites } from '@memberjunction/core-entities-server';
|
|
8
9
|
import { AIAgentPermissionHelper } from '@memberjunction/ai-engine-base';
|
|
9
10
|
import { RealtimeClientSessionService, RealtimeChannelServerHost } from '@memberjunction/ai-agents';
|
|
10
11
|
import type { BridgeDisconnectReason } from '@memberjunction/ai-bridge-base';
|
|
12
|
+
import { ParseSessionDeadlineMs, VerifyTimeLimitClaim } from './sessionDeadline.js';
|
|
11
13
|
import { GetHostInstanceID } from './HostInstance.js';
|
|
12
14
|
import { WriteReturningVisitorRecap } from './ReturningVisitorRecap.js';
|
|
13
15
|
import { SessionRunIDVerifier, SessionRunIDs } from './SessionRunIDVerifier.js';
|
|
@@ -40,11 +42,14 @@ export interface CreateSessionInput {
|
|
|
40
42
|
/**
|
|
41
43
|
* Why a session was closed — persisted to `AIAgentSession.CloseReason` alongside the
|
|
42
44
|
* `Status = 'Closed'` transition so the admin dashboards can distinguish a user hanging up
|
|
43
|
-
* (`Explicit`) from janitor reconciliation (`Janitor
|
|
44
|
-
*
|
|
45
|
-
*
|
|
45
|
+
* (`Explicit`) from janitor reconciliation (`Janitor`, an orphan from an earlier boot), a graceful
|
|
46
|
+
* host drain (`Shutdown`), a failure-path teardown (`Error`), an expired authored deadline
|
|
47
|
+
* (`TimeLimit`) or a session left quiet past the idle threshold (`Idle`). `NULL` in the column means
|
|
48
|
+
* "legacy/unknown" — rows closed before this column existed.
|
|
49
|
+
*
|
|
50
|
+
* Derived from the generated entity so the column's CHECK constraint stays the single source of truth.
|
|
46
51
|
*/
|
|
47
|
-
export type SessionCloseReason = '
|
|
52
|
+
export type SessionCloseReason = NonNullable<MJAIAgentSessionEntity['CloseReason']>;
|
|
48
53
|
|
|
49
54
|
/**
|
|
50
55
|
* The {@link SessionCloseReason} for a session whose bridged agent has left (see
|
|
@@ -191,6 +196,8 @@ export class SessionManager {
|
|
|
191
196
|
* call sites (the user-initiated close mutations) stamp the common case without modification;
|
|
192
197
|
* background callers (janitor sweeps, shutdown drain, error teardowns) must pass their reason
|
|
193
198
|
* explicitly. `NULL` rows remain only for legacy data closed before the column existed.
|
|
199
|
+
* A `'TimeLimit'` is verified against the session's stored deadline ({@link VerifyTimeLimitClaim}); a claim the
|
|
200
|
+
* deadline does not back is recorded as `'Explicit'` and logged, never refused.
|
|
194
201
|
*
|
|
195
202
|
* @remarks P5 adds in-flight-run abort + `ClientToolRequestManager.ClearSession` here.
|
|
196
203
|
*/
|
|
@@ -215,7 +222,9 @@ export class SessionManager {
|
|
|
215
222
|
return true;
|
|
216
223
|
}
|
|
217
224
|
|
|
218
|
-
|
|
225
|
+
// Verified once: the stamped reason and the plugin notification must agree, so a client's unbacked claim steers neither.
|
|
226
|
+
const verifiedReason = this.verifiedCloseReason(session, closeReason);
|
|
227
|
+
const closed = await this.markSessionClosed(session, verifiedReason);
|
|
219
228
|
if (!closed) {
|
|
220
229
|
return false;
|
|
221
230
|
}
|
|
@@ -228,7 +237,7 @@ export class SessionManager {
|
|
|
228
237
|
}
|
|
229
238
|
await this.disconnectChannels(agentSessionID, contextUser, provider, preloadedChannels);
|
|
230
239
|
await this.finalizeObservabilityRuns(session, contextUser, provider);
|
|
231
|
-
await this.notifyChannelPluginsSessionClosed(agentSessionID,
|
|
240
|
+
await this.notifyChannelPluginsSessionClosed(agentSessionID, verifiedReason);
|
|
232
241
|
// Returning-visitor recap (RV2): if this session's conversation is returning-visitor-enabled,
|
|
233
242
|
// summarize it into an Active memory note so the visitor's next session opens with prior context.
|
|
234
243
|
// Best-effort + no-op for non-returning-visitor conversations; never blocks teardown.
|
|
@@ -236,6 +245,25 @@ export class SessionManager {
|
|
|
236
245
|
return true;
|
|
237
246
|
}
|
|
238
247
|
|
|
248
|
+
/**
|
|
249
|
+
* Returns the reason to stamp: a `'TimeLimit'` claim the session's stored deadline does not back becomes
|
|
250
|
+
* `'Explicit'` (logged with the stored deadline); every other reason passes through.
|
|
251
|
+
*/
|
|
252
|
+
private verifiedCloseReason(session: MJAIAgentSessionEntity, closeReason: SessionCloseReason): SessionCloseReason {
|
|
253
|
+
if (closeReason !== 'TimeLimit') {
|
|
254
|
+
return closeReason;
|
|
255
|
+
}
|
|
256
|
+
const verified = VerifyTimeLimitClaim(session, Date.now());
|
|
257
|
+
if (verified === 'Explicit') {
|
|
258
|
+
const deadlineMs = ParseSessionDeadlineMs(session.Config_);
|
|
259
|
+
LogStatus(
|
|
260
|
+
`SessionManager.CloseSession: TimeLimit claim for session ${session.ID} not backed by its stored deadline ` +
|
|
261
|
+
`(${deadlineMs === null ? 'none stored' : new Date(deadlineMs).toISOString()}); closing as Explicit.`,
|
|
262
|
+
);
|
|
263
|
+
}
|
|
264
|
+
return verified;
|
|
265
|
+
}
|
|
266
|
+
|
|
239
267
|
/**
|
|
240
268
|
* Closes a session that was created for one bridged agent (a bot in a meeting room) once that agent's bridge has
|
|
241
269
|
* ended, whatever ended it: the agent was stopped, the meeting was ended, everyone left, the model session was lost.
|
|
@@ -584,7 +612,9 @@ export class SessionManager {
|
|
|
584
612
|
session.Status = 'Closed';
|
|
585
613
|
session.ClosedAt = new Date();
|
|
586
614
|
session.CloseReason = closeReason;
|
|
587
|
-
|
|
615
|
+
// The session entity refuses a server-only CloseReason from an untrusted save; this is the server's own
|
|
616
|
+
// (already verified) close, so it saves inside the trusted session-write scope.
|
|
617
|
+
return RunWithTrustedSessionConfigWrites(() => this.saveOrLog(session, 'CloseSession'));
|
|
588
618
|
}
|
|
589
619
|
|
|
590
620
|
/** Marks every channel row of the session `Disconnected` (with `DisconnectedAt`), if not already. */
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview The remote browsers this process holds for TEXT conversations: who owns each and when it
|
|
3
|
+
* was last used. Backs the caps (`realtime.conversationBrowser.maxPerUser` and `maxAnonymousTotal`) and ends browsers
|
|
4
|
+
* nobody uses any more, since a reloaded or closed tab never ends its own
|
|
5
|
+
* (`realtime.conversationBrowser.idleMinutes`). Realtime calls' browsers are not tracked here: they end
|
|
6
|
+
* with their session.
|
|
7
|
+
*
|
|
8
|
+
* @module @memberjunction/server/agentSessions
|
|
9
|
+
*/
|
|
10
|
+
import { GraphQLError } from 'graphql';
|
|
11
|
+
import { LogError, LogStatus } from '@memberjunction/core';
|
|
12
|
+
import { BaseSingleton, IShutdownable, ShutdownRegistry } from '@memberjunction/global';
|
|
13
|
+
import { RemoteBrowserEngine } from '@memberjunction/remote-browser-server';
|
|
14
|
+
import type { ConversationBrowserConfig } from '../config.js';
|
|
15
|
+
|
|
16
|
+
/** How often idle conversation browsers are looked for. */
|
|
17
|
+
const REAP_INTERVAL_MS = 60_000;
|
|
18
|
+
|
|
19
|
+
/** The `extensions.code` of a {@link RemoteBrowserRefusedError}, which a client reads to stop asking. */
|
|
20
|
+
export const REMOTE_BROWSER_REFUSED = 'REMOTE_BROWSER_REFUSED';
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* A remote-browser request this server refuses however often it is asked: browsers not enabled, a session
|
|
24
|
+
* that is not the caller's or does not exist, or a cap reached.
|
|
25
|
+
*/
|
|
26
|
+
export class RemoteBrowserRefusedError extends GraphQLError {
|
|
27
|
+
constructor(message: string) {
|
|
28
|
+
super(message, { extensions: { code: REMOTE_BROWSER_REFUSED } });
|
|
29
|
+
this.name = 'RemoteBrowserRefusedError';
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** Who a conversation browser counts against: a user, or an anonymous guest's own scope. */
|
|
34
|
+
export interface ConversationBrowserOwner {
|
|
35
|
+
Key: string;
|
|
36
|
+
Anonymous: boolean;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
interface HeldBrowser {
|
|
40
|
+
Owner: ConversationBrowserOwner;
|
|
41
|
+
LastUsedAt: number;
|
|
42
|
+
IdleMs: number;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export class ConversationBrowserLedger extends BaseSingleton<ConversationBrowserLedger> implements IShutdownable {
|
|
46
|
+
public readonly ShutdownName = 'ConversationBrowserLedger';
|
|
47
|
+
private readonly held = new Map<string, HeldBrowser>();
|
|
48
|
+
private reapTimer: ReturnType<typeof setInterval> | null = null;
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* How an idle browser is ended. The resolver points this at its own end path, so a reaped browser
|
|
52
|
+
* leaves no stream bookkeeping behind; until then the engine ends it directly.
|
|
53
|
+
*/
|
|
54
|
+
public EndBrowser: (conversationID: string) => Promise<unknown> = (conversationID) =>
|
|
55
|
+
RemoteBrowserEngine.Instance.EndSessionForAgentSession(conversationID);
|
|
56
|
+
|
|
57
|
+
protected constructor() {
|
|
58
|
+
super();
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
public static get Instance(): ConversationBrowserLedger {
|
|
62
|
+
return super.getInstance<ConversationBrowserLedger>();
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Records that a conversation's browser is about to be used. A conversation the ledger does not hold
|
|
67
|
+
* yet counts against its owner's cap and, for an anonymous guest, against the cap all guests share: a
|
|
68
|
+
* guest can start a new scope cheaply, so its own cap alone bounds nothing.
|
|
69
|
+
*
|
|
70
|
+
* @throws RemoteBrowserRefusedError when the owner already holds `maxPerUser` conversation browsers, or
|
|
71
|
+
* guests together already hold `maxAnonymousTotal`.
|
|
72
|
+
*/
|
|
73
|
+
public Claim(conversationID: string, owner: ConversationBrowserOwner, limits: Pick<ConversationBrowserConfig, 'maxPerUser' | 'maxAnonymousTotal' | 'idleMinutes'>): void {
|
|
74
|
+
const key = conversationID.toLowerCase();
|
|
75
|
+
if (!this.held.has(key)) {
|
|
76
|
+
const held = Array.from(this.held.values());
|
|
77
|
+
const count = held.filter((b) => b.Owner.Key === owner.Key).length;
|
|
78
|
+
if (count >= limits.maxPerUser) {
|
|
79
|
+
throw new RemoteBrowserRefusedError(`You already have ${count} conversation browser(s) open, the most this server allows. Close one first.`);
|
|
80
|
+
}
|
|
81
|
+
if (owner.Anonymous && held.filter((b) => b.Owner.Anonymous).length >= limits.maxAnonymousTotal) {
|
|
82
|
+
throw new RemoteBrowserRefusedError('Every conversation browser this server allows for guests is in use. Try again later.');
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
this.held.set(key, { Owner: owner, LastUsedAt: Date.now(), IdleMs: limits.idleMinutes * 60_000 });
|
|
86
|
+
this.startReaper();
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/** Marks a held browser as used (an action, a snapshot poll, human input), which puts off its idle end. */
|
|
90
|
+
public Touch(conversationID: string): void {
|
|
91
|
+
const browser = this.held.get(conversationID.toLowerCase());
|
|
92
|
+
if (browser) {
|
|
93
|
+
browser.LastUsedAt = Date.now();
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/** Forgets a conversation's browser once it has been ended. */
|
|
98
|
+
public Release(conversationID: string): void {
|
|
99
|
+
this.held.delete(conversationID.toLowerCase());
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/** Ends every held browser unused for longer than its idle limit. Returns how many were ended. */
|
|
103
|
+
public async ReapIdle(): Promise<number> {
|
|
104
|
+
const now = Date.now();
|
|
105
|
+
const idle = Array.from(this.held).filter(([, b]) => now - b.LastUsedAt > b.IdleMs).map(([id]) => id);
|
|
106
|
+
for (const conversationID of idle) {
|
|
107
|
+
this.held.delete(conversationID);
|
|
108
|
+
try {
|
|
109
|
+
await this.EndBrowser(conversationID);
|
|
110
|
+
} catch (err) {
|
|
111
|
+
LogError(`[ConversationBrowsers] Failed to end the idle browser of conversation ${conversationID}: ${err instanceof Error ? err.message : String(err)}`);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
if (idle.length > 0) {
|
|
115
|
+
LogStatus(`[ConversationBrowsers] Ended ${idle.length} idle conversation browser(s).`);
|
|
116
|
+
}
|
|
117
|
+
return idle.length;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/** Ends every held browser on a graceful shutdown, so none outlives the server. Idempotent. */
|
|
121
|
+
public async Shutdown(): Promise<void> {
|
|
122
|
+
if (this.reapTimer) {
|
|
123
|
+
clearInterval(this.reapTimer);
|
|
124
|
+
this.reapTimer = null;
|
|
125
|
+
}
|
|
126
|
+
const held = Array.from(this.held.keys());
|
|
127
|
+
this.held.clear();
|
|
128
|
+
await Promise.allSettled(held.map((conversationID) => this.EndBrowser(conversationID)));
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* One unref'd timer per process, started with the first browser, so the reaper costs nothing until then.
|
|
133
|
+
* The ledger joins the graceful shutdown at the same time.
|
|
134
|
+
*/
|
|
135
|
+
private startReaper(): void {
|
|
136
|
+
if (this.reapTimer) {
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
this.reapTimer = setInterval(() => void this.ReapIdle(), REAP_INTERVAL_MS);
|
|
140
|
+
this.reapTimer.unref?.();
|
|
141
|
+
ShutdownRegistry.Instance.Register(this);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import type { SessionCloseReason } from './SessionManager.js';
|
|
2
|
+
|
|
3
|
+
/** The close reasons an expiry check can yield: the two ways a live session runs out of time. */
|
|
4
|
+
export type ExpiryReason = Extract<SessionCloseReason, 'Idle' | 'TimeLimit'>;
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Parses the absolute deadline (ms) from a session's `Config_` JSON, or null when absent/malformed.
|
|
8
|
+
* A malformed deadline counts as absent by design: a sweep must never throw on one bad row, so null
|
|
9
|
+
* is the documented failure result here, not a swallowed error.
|
|
10
|
+
*/
|
|
11
|
+
export function ParseSessionDeadlineMs(configJson: string | null | undefined): number | null {
|
|
12
|
+
if (!configJson) {
|
|
13
|
+
return null;
|
|
14
|
+
}
|
|
15
|
+
try {
|
|
16
|
+
const parsed = JSON.parse(configJson) as { maxSessionDeadlineIso?: unknown };
|
|
17
|
+
if (typeof parsed.maxSessionDeadlineIso !== 'string') {
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
20
|
+
const ms = Date.parse(parsed.maxSessionDeadlineIso);
|
|
21
|
+
return Number.isNaN(ms) ? null : ms;
|
|
22
|
+
} catch {
|
|
23
|
+
// Malformed JSON: treated as "no deadline" (see doc comment).
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Decides why a session has expired, or null when it has not.
|
|
30
|
+
*
|
|
31
|
+
* Two moments can expire a session: its idle moment (`LastActiveAt + closeThresholdMs`) and its authored
|
|
32
|
+
* hard deadline (`Config_.maxSessionDeadlineIso`). Only a moment that has already passed (`<= nowMs`)
|
|
33
|
+
* counts, and the EARLIER of the passed moments wins. A tie at the same instant yields `TimeLimit`:
|
|
34
|
+
* the authored rule beats the safety net. A row without a usable `LastActiveAt` (null, or a value that
|
|
35
|
+
* does not parse to a date) is never `Idle`, and neither is any row when `closeThresholdMs` is non-finite.
|
|
36
|
+
* `LastActiveAt` is typed `Date` but may arrive as an ISO string from a provider, so both are accepted.
|
|
37
|
+
*/
|
|
38
|
+
export function ExpiryCloseReason(
|
|
39
|
+
session: { LastActiveAt: Date | string | null | undefined; Config_: string | null | undefined },
|
|
40
|
+
nowMs: number,
|
|
41
|
+
closeThresholdMs: number,
|
|
42
|
+
): ExpiryReason | null {
|
|
43
|
+
const lastActiveMs = session.LastActiveAt ? new Date(session.LastActiveAt).getTime() : Number.NaN;
|
|
44
|
+
const idleMs = lastActiveMs + closeThresholdMs;
|
|
45
|
+
const idleAt = Number.isFinite(idleMs) ? idleMs : null;
|
|
46
|
+
const deadlineAt = ParseSessionDeadlineMs(session.Config_);
|
|
47
|
+
const idlePassed = idleAt !== null && idleAt <= nowMs;
|
|
48
|
+
const deadlinePassed = deadlineAt !== null && deadlineAt <= nowMs;
|
|
49
|
+
if (deadlinePassed && idlePassed) {
|
|
50
|
+
return deadlineAt <= idleAt ? 'TimeLimit' : 'Idle';
|
|
51
|
+
}
|
|
52
|
+
if (deadlinePassed) {
|
|
53
|
+
return 'TimeLimit';
|
|
54
|
+
}
|
|
55
|
+
return idlePassed ? 'Idle' : null;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* How far ahead of a stored deadline a browser's `TimeLimit` claim is still believed. The browser's
|
|
60
|
+
* alarm runs on its own clock: when it extends to a server ISO deadline it anchors that time against its
|
|
61
|
+
* own `Date.now()`, so a client clock running ahead of the server fires early by the difference (a throttled
|
|
62
|
+
* tab only fires late). A minute absorbs that skew without letting a client end a session meant to run for
|
|
63
|
+
* minutes more.
|
|
64
|
+
*/
|
|
65
|
+
export const DEADLINE_CLAIM_SKEW_MS = 60_000;
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Verifies a `TimeLimit` close claim against the session's stored deadline. The browser claims, the server
|
|
69
|
+
* verifies: the claim holds when the stored deadline has passed or falls within {@link DEADLINE_CLAIM_SKEW_MS},
|
|
70
|
+
* otherwise (no deadline, or one far in the future) the close is recorded as `Explicit`. The janitor's own
|
|
71
|
+
* `TimeLimit` trivially passes, so every caller shares one path, and this module remains the single place that
|
|
72
|
+
* answers "did the deadline pass?" for both the sweep and the client claim. Idleness never counts here.
|
|
73
|
+
*/
|
|
74
|
+
export function VerifyTimeLimitClaim(
|
|
75
|
+
session: { LastActiveAt: Date | string | null | undefined; Config_: string | null | undefined },
|
|
76
|
+
nowMs: number,
|
|
77
|
+
): 'TimeLimit' | 'Explicit' {
|
|
78
|
+
return ExpiryCloseReason(session, nowMs + DEADLINE_CLAIM_SKEW_MS, Number.POSITIVE_INFINITY) === 'TimeLimit'
|
|
79
|
+
? 'TimeLimit'
|
|
80
|
+
: 'Explicit';
|
|
81
|
+
}
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
import { AuthorizationError } from 'type-graphql';
|
|
8
8
|
import { GetAPIKeyEngine, AuthorizationResult, AuthorizationRequest } from '@memberjunction/api-keys';
|
|
9
9
|
import { UserInfo, RunView } from '@memberjunction/core';
|
|
10
|
+
import { EscapeSQLString } from '@memberjunction/global';
|
|
10
11
|
import { MJAPIKeyEntity, MJAPIApplicationEntity } from '@memberjunction/core-entities';
|
|
11
12
|
|
|
12
13
|
/**
|
|
@@ -96,7 +97,7 @@ export async function CheckAPIKeyScope(
|
|
|
96
97
|
const rv = new RunView();
|
|
97
98
|
const keyResult = await rv.RunView<MJAPIKeyEntity>({
|
|
98
99
|
EntityName: 'MJ: API Keys',
|
|
99
|
-
ExtraFilter: `ID='${apiKeyId}'`,
|
|
100
|
+
ExtraFilter: `ID='${EscapeSQLString(apiKeyId)}'`,
|
|
100
101
|
ResultType: 'entity_object'
|
|
101
102
|
}, contextUser);
|
|
102
103
|
|
|
@@ -117,7 +118,7 @@ export async function CheckAPIKeyScope(
|
|
|
117
118
|
// Get the application by name
|
|
118
119
|
const appResult = await rv.RunView<MJAPIApplicationEntity>({
|
|
119
120
|
EntityName: 'MJ: API Applications',
|
|
120
|
-
ExtraFilter: `Name='${applicationName}'`,
|
|
121
|
+
ExtraFilter: `Name='${EscapeSQLString(applicationName)}'`,
|
|
121
122
|
ResultType: 'entity_object'
|
|
122
123
|
}, contextUser);
|
|
123
124
|
|
|
@@ -224,7 +225,7 @@ export async function CheckAPIKeyScopeAndLog(
|
|
|
224
225
|
// Get the API key
|
|
225
226
|
const keyResult = await rv.RunView<MJAPIKeyEntity>({
|
|
226
227
|
EntityName: 'MJ: API Keys',
|
|
227
|
-
ExtraFilter: `ID='${apiKeyId}'`,
|
|
228
|
+
ExtraFilter: `ID='${EscapeSQLString(apiKeyId)}'`,
|
|
228
229
|
ResultType: 'entity_object'
|
|
229
230
|
}, contextUser);
|
|
230
231
|
|
|
@@ -245,7 +246,7 @@ export async function CheckAPIKeyScopeAndLog(
|
|
|
245
246
|
// Get the application
|
|
246
247
|
const appResult = await rv.RunView<MJAPIApplicationEntity>({
|
|
247
248
|
EntityName: 'MJ: API Applications',
|
|
248
|
-
ExtraFilter: `Name='${applicationName}'`,
|
|
249
|
+
ExtraFilter: `Name='${EscapeSQLString(applicationName)}'`,
|
|
249
250
|
ResultType: 'entity_object'
|
|
250
251
|
}, contextUser);
|
|
251
252
|
|