@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
|
@@ -9,6 +9,7 @@ import { LoadConfig } from '../config.js';
|
|
|
9
9
|
import { ResolverBase } from '../generic/ResolverBase.js';
|
|
10
10
|
import { GetReadOnlyProvider } from '../util.js';
|
|
11
11
|
import { UserCache, SqlLoggingOptions as ProviderSqlLoggingOptions } from '@memberjunction/generic-database-provider';
|
|
12
|
+
import { IsScopeLimitedPrincipal } from '../auth/scopeLimitedPrincipal.js';
|
|
12
13
|
|
|
13
14
|
/**
|
|
14
15
|
* Configuration options for SQL logging sessions.
|
|
@@ -252,6 +253,9 @@ export class SqlLoggingConfigResolver extends ResolverBase {
|
|
|
252
253
|
* @private
|
|
253
254
|
*/
|
|
254
255
|
private async checkOwnerAccess(context: AppContext): Promise<UserInfo> {
|
|
256
|
+
if (context?.userPayload?.userRecord && IsScopeLimitedPrincipal(context.userPayload.userRecord)) {
|
|
257
|
+
throw new Error('Access denied. Scope-limited principals cannot access SQL logging.');
|
|
258
|
+
}
|
|
255
259
|
const userEmail = context.userPayload?.email;
|
|
256
260
|
if (!userEmail) {
|
|
257
261
|
throw new Error('User not authenticated');
|
|
@@ -263,15 +267,9 @@ export class SqlLoggingConfigResolver extends ResolverBase {
|
|
|
263
267
|
if (!user) {
|
|
264
268
|
throw new Error('User not found');
|
|
265
269
|
}
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
email: user.Email,
|
|
270
|
-
type: user.Type,
|
|
271
|
-
typeLength: user.Type?.length,
|
|
272
|
-
typeTrimmed: user.Type?.trim(),
|
|
273
|
-
isOwner: user.Type?.trim().toLowerCase() === 'owner'
|
|
274
|
-
});
|
|
270
|
+
if (IsScopeLimitedPrincipal(user)) {
|
|
271
|
+
throw new Error('Access denied. Scope-limited principals cannot access SQL logging.');
|
|
272
|
+
}
|
|
275
273
|
|
|
276
274
|
// Check if user has Type = 'Owner' (trim and case-insensitive for nchar fields)
|
|
277
275
|
if (user.Type?.trim().toLowerCase() !== 'owner') {
|
|
@@ -3,6 +3,7 @@ import { AppContext } from '../types.js';
|
|
|
3
3
|
import { LogError, LogStatus } from '@memberjunction/core';
|
|
4
4
|
import { ResolverBase } from '../generic/ResolverBase.js';
|
|
5
5
|
import { TagGovernanceEngine, TagHealthJob, DEFAULT_TAG_HEALTH_THRESHOLDS, TagEngine } from '@memberjunction/tag-engine';
|
|
6
|
+
import { TagCoOccurrenceEngine } from '@memberjunction/tag-engine-base';
|
|
6
7
|
|
|
7
8
|
@ObjectType()
|
|
8
9
|
export class PromoteSuggestionResult {
|
|
@@ -28,6 +29,30 @@ export class RejectSuggestionResult {
|
|
|
28
29
|
ErrorMessage?: string;
|
|
29
30
|
}
|
|
30
31
|
|
|
32
|
+
@ObjectType()
|
|
33
|
+
export class MergeTagsResult {
|
|
34
|
+
@Field()
|
|
35
|
+
Success: boolean;
|
|
36
|
+
|
|
37
|
+
@Field(() => Int)
|
|
38
|
+
ItemsMoved: number;
|
|
39
|
+
|
|
40
|
+
@Field(() => Int)
|
|
41
|
+
TaggedItemsMoved: number;
|
|
42
|
+
|
|
43
|
+
@Field({ nullable: true })
|
|
44
|
+
ErrorMessage?: string;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
@ObjectType()
|
|
48
|
+
export class RecomputeTagCoOccurrenceResult {
|
|
49
|
+
@Field(() => Int)
|
|
50
|
+
PairsUpdated: number;
|
|
51
|
+
|
|
52
|
+
@Field(() => Int)
|
|
53
|
+
PairsDeleted: number;
|
|
54
|
+
}
|
|
55
|
+
|
|
31
56
|
@ObjectType()
|
|
32
57
|
export class RebuildTagEmbeddingsResult {
|
|
33
58
|
@Field()
|
|
@@ -186,4 +211,44 @@ export class TagGovernanceResolver extends ResolverBase {
|
|
|
186
211
|
return { Success: false, MergeCount: 0, LowUsageCount: 0, WideNodeCount: 0, DurationMs: 0, ErrorMessage: msg };
|
|
187
212
|
}
|
|
188
213
|
}
|
|
214
|
+
|
|
215
|
+
@Mutation(() => MergeTagsResult)
|
|
216
|
+
async MergeTags(
|
|
217
|
+
@Arg('sourceTagIDs', () => [String]) sourceTagIDs: string[],
|
|
218
|
+
@Arg('survivingTagID') survivingTagID: string,
|
|
219
|
+
@Ctx() { userPayload }: AppContext = {} as AppContext
|
|
220
|
+
): Promise<MergeTagsResult> {
|
|
221
|
+
try {
|
|
222
|
+
const currentUser = this.GetUserFromPayload(userPayload);
|
|
223
|
+
if (!currentUser) {
|
|
224
|
+
return { Success: false, ItemsMoved: 0, TaggedItemsMoved: 0, ErrorMessage: 'Unable to determine current user' };
|
|
225
|
+
}
|
|
226
|
+
await TagEngine.Instance.Config(false, currentUser);
|
|
227
|
+
const result = await TagGovernanceEngine.Instance.MergeTags(sourceTagIDs, survivingTagID, currentUser);
|
|
228
|
+
return {
|
|
229
|
+
Success: true,
|
|
230
|
+
ItemsMoved: result.ItemsMoved,
|
|
231
|
+
TaggedItemsMoved: result.TaggedItemsMoved,
|
|
232
|
+
};
|
|
233
|
+
} catch (error) {
|
|
234
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
235
|
+
LogError(`MergeTags failed for survivingTagID ${survivingTagID}: ${msg}`);
|
|
236
|
+
return { Success: false, ItemsMoved: 0, TaggedItemsMoved: 0, ErrorMessage: msg };
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
@Mutation(() => RecomputeTagCoOccurrenceResult)
|
|
241
|
+
async RecomputeTagCoOccurrence(
|
|
242
|
+
@Ctx() { userPayload }: AppContext = {} as AppContext
|
|
243
|
+
): Promise<RecomputeTagCoOccurrenceResult> {
|
|
244
|
+
const currentUser = this.GetUserFromPayload(userPayload);
|
|
245
|
+
if (!currentUser) {
|
|
246
|
+
throw new Error('Unable to determine current user');
|
|
247
|
+
}
|
|
248
|
+
const result = await TagCoOccurrenceEngine.Instance.RecomputeCoOccurrence(currentUser);
|
|
249
|
+
return {
|
|
250
|
+
PairsUpdated: result.PairsUpdated,
|
|
251
|
+
PairsDeleted: result.PairsDeleted,
|
|
252
|
+
};
|
|
253
|
+
}
|
|
189
254
|
}
|
|
@@ -3,8 +3,11 @@ import { AppContext } from '../types.js';
|
|
|
3
3
|
import {
|
|
4
4
|
TelemetryManager,
|
|
5
5
|
TelemetryCategory,
|
|
6
|
-
TelemetryInsightSeverity
|
|
6
|
+
TelemetryInsightSeverity,
|
|
7
|
+
AuthorizationEvaluator,
|
|
7
8
|
} from '@memberjunction/core';
|
|
9
|
+
import { IsScopeLimitedPrincipal } from '../auth/scopeLimitedPrincipal.js';
|
|
10
|
+
import { GetReadOnlyProvider } from '../util.js';
|
|
8
11
|
|
|
9
12
|
// ============================================================================
|
|
10
13
|
// GraphQL Types for TelemetryEvent
|
|
@@ -318,12 +321,38 @@ export class TelemetryInsightFilterInput {
|
|
|
318
321
|
|
|
319
322
|
@Resolver()
|
|
320
323
|
export class TelemetryResolver {
|
|
324
|
+
/**
|
|
325
|
+
* Enforces that the caller is an authenticated, non-scope-limited user who holds either
|
|
326
|
+
* Owner privileges or the `'Diagnostics: Run Sessions'` authorization.
|
|
327
|
+
*/
|
|
328
|
+
private requireDiagnosticsAccess(context: AppContext): void {
|
|
329
|
+
const user = context?.userPayload?.userRecord;
|
|
330
|
+
if (!user || IsScopeLimitedPrincipal(user)) {
|
|
331
|
+
throw new Error('Access denied: server telemetry requires an authenticated non-scope-limited user.');
|
|
332
|
+
}
|
|
333
|
+
if (user.Type?.trim().toLowerCase() === 'owner') {
|
|
334
|
+
return;
|
|
335
|
+
}
|
|
336
|
+
const authorizations = GetReadOnlyProvider(context.providers, { allowFallbackToReadWrite: true })?.Authorizations ?? [];
|
|
337
|
+
const diagAuth = authorizations.find(
|
|
338
|
+
(a) => a.Name?.trim().toLowerCase() === 'diagnostics: run sessions'
|
|
339
|
+
);
|
|
340
|
+
if (diagAuth) {
|
|
341
|
+
const evaluator = new AuthorizationEvaluator();
|
|
342
|
+
if (evaluator.UserCanExecuteWithAncestors(diagAuth, user, authorizations)) {
|
|
343
|
+
return;
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
throw new Error('Access denied: server telemetry requires Owner privileges or the "Diagnostics: Run Sessions" authorization.');
|
|
347
|
+
}
|
|
348
|
+
|
|
321
349
|
/**
|
|
322
350
|
* Get telemetry settings
|
|
323
|
-
*
|
|
351
|
+
* Requires Owner privileges or 'Diagnostics: Run Sessions' authorization
|
|
324
352
|
*/
|
|
325
353
|
@Query(() => TelemetrySettingsGQL)
|
|
326
|
-
GetServerTelemetrySettings(@Ctx()
|
|
354
|
+
GetServerTelemetrySettings(@Ctx() context: AppContext): TelemetrySettingsGQL {
|
|
355
|
+
this.requireDiagnosticsAccess(context);
|
|
327
356
|
const tm = TelemetryManager.Instance;
|
|
328
357
|
const settings = tm.Settings;
|
|
329
358
|
|
|
@@ -365,7 +394,8 @@ export class TelemetryResolver {
|
|
|
365
394
|
* Get telemetry statistics
|
|
366
395
|
*/
|
|
367
396
|
@Query(() => TelemetryStatsGQL)
|
|
368
|
-
GetServerTelemetryStats(@Ctx()
|
|
397
|
+
GetServerTelemetryStats(@Ctx() context: AppContext): TelemetryStatsGQL {
|
|
398
|
+
this.requireDiagnosticsAccess(context);
|
|
369
399
|
const tm = TelemetryManager.Instance;
|
|
370
400
|
const stats = tm.GetStats();
|
|
371
401
|
|
|
@@ -391,9 +421,10 @@ export class TelemetryResolver {
|
|
|
391
421
|
*/
|
|
392
422
|
@Query(() => [TelemetryEventGQL])
|
|
393
423
|
GetServerTelemetryEvents(
|
|
394
|
-
@Ctx()
|
|
424
|
+
@Ctx() context: AppContext,
|
|
395
425
|
@Arg('filter', () => TelemetryEventFilterInput, { nullable: true }) filter?: TelemetryEventFilterInput
|
|
396
426
|
): TelemetryEventGQL[] {
|
|
427
|
+
this.requireDiagnosticsAccess(context);
|
|
397
428
|
const tm = TelemetryManager.Instance;
|
|
398
429
|
|
|
399
430
|
const events = tm.GetEvents({
|
|
@@ -427,9 +458,10 @@ export class TelemetryResolver {
|
|
|
427
458
|
*/
|
|
428
459
|
@Query(() => [TelemetryPatternGQL])
|
|
429
460
|
GetServerTelemetryPatterns(
|
|
430
|
-
@Ctx()
|
|
461
|
+
@Ctx() context: AppContext,
|
|
431
462
|
@Arg('filter', () => TelemetryPatternFilterInput, { nullable: true }) filter?: TelemetryPatternFilterInput
|
|
432
463
|
): TelemetryPatternGQL[] {
|
|
464
|
+
this.requireDiagnosticsAccess(context);
|
|
433
465
|
const tm = TelemetryManager.Instance;
|
|
434
466
|
|
|
435
467
|
const patterns = tm.GetPatterns({
|
|
@@ -468,9 +500,10 @@ export class TelemetryResolver {
|
|
|
468
500
|
*/
|
|
469
501
|
@Query(() => [TelemetryInsightGQL])
|
|
470
502
|
GetServerTelemetryInsights(
|
|
471
|
-
@Ctx()
|
|
503
|
+
@Ctx() context: AppContext,
|
|
472
504
|
@Arg('filter', () => TelemetryInsightFilterInput, { nullable: true }) filter?: TelemetryInsightFilterInput
|
|
473
505
|
): TelemetryInsightGQL[] {
|
|
506
|
+
this.requireDiagnosticsAccess(context);
|
|
474
507
|
const tm = TelemetryManager.Instance;
|
|
475
508
|
|
|
476
509
|
const insights = tm.GetInsights({
|
|
@@ -500,9 +533,10 @@ export class TelemetryResolver {
|
|
|
500
533
|
*/
|
|
501
534
|
@Query(() => [TelemetryPatternGQL])
|
|
502
535
|
GetServerTelemetryDuplicates(
|
|
503
|
-
@Ctx()
|
|
536
|
+
@Ctx() context: AppContext,
|
|
504
537
|
@Arg('minCount', () => Int, { defaultValue: 2 }) minCount: number
|
|
505
538
|
): TelemetryPatternGQL[] {
|
|
539
|
+
this.requireDiagnosticsAccess(context);
|
|
506
540
|
const tm = TelemetryManager.Instance;
|
|
507
541
|
const patterns = tm.GetDuplicates(minCount);
|
|
508
542
|
|
|
@@ -534,7 +568,8 @@ export class TelemetryResolver {
|
|
|
534
568
|
* Get currently active (in-progress) events
|
|
535
569
|
*/
|
|
536
570
|
@Query(() => [TelemetryEventGQL])
|
|
537
|
-
GetServerTelemetryActiveEvents(@Ctx()
|
|
571
|
+
GetServerTelemetryActiveEvents(@Ctx() context: AppContext): TelemetryEventGQL[] {
|
|
572
|
+
this.requireDiagnosticsAccess(context);
|
|
538
573
|
const tm = TelemetryManager.Instance;
|
|
539
574
|
const events = tm.GetActiveEvents();
|
|
540
575
|
|
|
@@ -23,6 +23,8 @@ vi.mock('../../auth/index.js', () => ({ GetSystemUser: vi.fn(),
|
|
|
23
23
|
vi.mock('mssql', () => ({ default: { Request: vi.fn() } }));
|
|
24
24
|
|
|
25
25
|
import {
|
|
26
|
+
GetDataResolver,
|
|
27
|
+
GetDataInputType,
|
|
26
28
|
RegisterAccessToken,
|
|
27
29
|
DeleteAccessToken,
|
|
28
30
|
TokenExists,
|
|
@@ -31,6 +33,7 @@ import {
|
|
|
31
33
|
PruneExpiredTokens,
|
|
32
34
|
GetAccessTokenCount,
|
|
33
35
|
} from '../GetDataResolver.js';
|
|
36
|
+
import type { AppContext } from '../../types.js';
|
|
34
37
|
|
|
35
38
|
// ---------------------------------------------------------------------------
|
|
36
39
|
// Regression coverage for a memory leak (Memory Leak Audit Round 7, Critical):
|
|
@@ -137,4 +140,53 @@ describe('GetDataResolver access-token lifecycle', () => {
|
|
|
137
140
|
expect(GetAccessTokenCount()).toBeLessThan(before + 51);
|
|
138
141
|
expect(TokenExists(survivorToken)).toBe(true);
|
|
139
142
|
});
|
|
143
|
+
|
|
144
|
+
it('never leaks raw secret token values in thrown or returned error messages', async () => {
|
|
145
|
+
const duplicateSecret = `secret-dup-${uniqueToken()}`;
|
|
146
|
+
RegisterAccessToken(duplicateSecret, 60_000);
|
|
147
|
+
|
|
148
|
+
try {
|
|
149
|
+
RegisterAccessToken(duplicateSecret, 60_000);
|
|
150
|
+
expect.unreachable('Expected duplicate registration to throw');
|
|
151
|
+
} catch (err) {
|
|
152
|
+
expect((err as Error).message).not.toContain(duplicateSecret);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const missingSecret = `secret-missing-${uniqueToken()}`;
|
|
156
|
+
try {
|
|
157
|
+
DeleteAccessToken(missingSecret);
|
|
158
|
+
expect.unreachable('Expected deleting unknown token to throw');
|
|
159
|
+
} catch (err) {
|
|
160
|
+
expect((err as Error).message).not.toContain(missingSecret);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
try {
|
|
164
|
+
RecordTokenUse(missingSecret, {});
|
|
165
|
+
expect.unreachable('Expected recording unknown token use to throw');
|
|
166
|
+
} catch (err) {
|
|
167
|
+
expect((err as Error).message).not.toContain(missingSecret);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
const resolver = new GetDataResolver();
|
|
171
|
+
const input = new GetDataInputType();
|
|
172
|
+
input.Token = missingSecret;
|
|
173
|
+
input.Queries = ['SELECT 1'];
|
|
174
|
+
const output = await resolver.GetData(input, { dataSources: [] } as Partial<AppContext> as AppContext);
|
|
175
|
+
expect(output.Success).toBe(false);
|
|
176
|
+
expect(JSON.stringify(output.ErrorMessages)).not.toContain(missingSecret);
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
it('rejects empty tokens and near-miss prefix tokens across multiple active slots', () => {
|
|
180
|
+
const first = `slot-a-${uniqueToken()}`;
|
|
181
|
+
const second = `slot-b-${uniqueToken()}`;
|
|
182
|
+
RegisterAccessToken(first, 60_000);
|
|
183
|
+
RegisterAccessToken(second, 60_000);
|
|
184
|
+
|
|
185
|
+
expect(IsTokenValid('')).toBe(false);
|
|
186
|
+
expect(TokenExists('')).toBe(false);
|
|
187
|
+
expect(IsTokenValid(first.slice(0, -1))).toBe(false);
|
|
188
|
+
expect(IsTokenValid(`${second}-extra`)).toBe(false);
|
|
189
|
+
expect(IsTokenValid(first)).toBe(true);
|
|
190
|
+
expect(IsTokenValid(second)).toBe(true);
|
|
191
|
+
});
|
|
140
192
|
});
|
|
@@ -268,6 +268,7 @@ async function findConversationByEgressID(egressID: string, contextUser: UserInf
|
|
|
268
268
|
Fields: ['ID'],
|
|
269
269
|
OrderBy: '__mj_CreatedAt DESC',
|
|
270
270
|
MaxRows: 1,
|
|
271
|
+
SkipTotalRowCount: true,
|
|
271
272
|
ResultType: 'simple',
|
|
272
273
|
},
|
|
273
274
|
contextUser,
|
|
@@ -288,6 +289,7 @@ async function findConversationByRoomName(roomName: string, contextUser: UserInf
|
|
|
288
289
|
Fields: ['ID'],
|
|
289
290
|
OrderBy: '__mj_CreatedAt DESC',
|
|
290
291
|
MaxRows: 1,
|
|
292
|
+
SkipTotalRowCount: true,
|
|
291
293
|
ResultType: 'simple',
|
|
292
294
|
},
|
|
293
295
|
contextUser,
|
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
RunView,
|
|
6
6
|
UserInfo,
|
|
7
7
|
} from '@memberjunction/core';
|
|
8
|
-
import { EscapeSQLString, UUIDsEqual } from '@memberjunction/global';
|
|
8
|
+
import { EscapeSQLString, IndexByUUID, UUIDsEqual } from '@memberjunction/global';
|
|
9
9
|
import type {
|
|
10
10
|
MJMeetingEntity,
|
|
11
11
|
MJMeetingParticipantEntity,
|
|
@@ -25,6 +25,7 @@ import {
|
|
|
25
25
|
UpdateMeetingInput,
|
|
26
26
|
RSVPMeetingInput,
|
|
27
27
|
MeetingParticipantType,
|
|
28
|
+
MeetingParticipantInput,
|
|
28
29
|
} from './meetingTypes.js';
|
|
29
30
|
import * as defaultHelpers from './meetingHelpers.js';
|
|
30
31
|
|
|
@@ -34,6 +35,39 @@ function resolveHelpers(overrides?: Partial<MeetingHelpers>): MeetingHelpers {
|
|
|
34
35
|
return { ...defaultHelpers, ...overrides };
|
|
35
36
|
}
|
|
36
37
|
|
|
38
|
+
/**
|
|
39
|
+
* Indexes a meeting's participants by UserID, AgentID and lower-cased ExternalEmail, and returns a
|
|
40
|
+
* lookup that finds the participant an update input refers to. When the input matches different
|
|
41
|
+
* participants on different keys, the one listed first in `existing` wins, as a single
|
|
42
|
+
* `existing.find` over all three keys would answer.
|
|
43
|
+
*/
|
|
44
|
+
function indexParticipants(
|
|
45
|
+
existing: MeetingParticipantType[],
|
|
46
|
+
): (input: MeetingParticipantInput) => MeetingParticipantType | undefined {
|
|
47
|
+
const byUserID = IndexByUUID(existing.filter((e) => e.UserID), (e) => e.UserID);
|
|
48
|
+
const byAgentID = IndexByUUID(existing.filter((e) => e.AgentID), (e) => e.AgentID);
|
|
49
|
+
const byEmail = new Map<string, MeetingParticipantType>();
|
|
50
|
+
const position = new Map<MeetingParticipantType, number>();
|
|
51
|
+
existing.forEach((e, i) => {
|
|
52
|
+
position.set(e, i);
|
|
53
|
+
const email = e.ExternalEmail?.toLowerCase();
|
|
54
|
+
if (email && !byEmail.has(email)) byEmail.set(email, e);
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
return (input) => {
|
|
58
|
+
const candidates = [
|
|
59
|
+
input.UserID ? byUserID.Get(input.UserID) : undefined,
|
|
60
|
+
input.AgentID ? byAgentID.Get(input.AgentID) : undefined,
|
|
61
|
+
input.ExternalEmail ? byEmail.get(input.ExternalEmail.toLowerCase()) : undefined,
|
|
62
|
+
];
|
|
63
|
+
let first: MeetingParticipantType | undefined;
|
|
64
|
+
for (const candidate of candidates) {
|
|
65
|
+
if (candidate && (!first || (position.get(candidate) ?? 0) < (position.get(first) ?? 0))) first = candidate;
|
|
66
|
+
}
|
|
67
|
+
return first;
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
|
|
37
71
|
export async function ExecuteCreateMeeting(
|
|
38
72
|
provider: IMetadataProvider,
|
|
39
73
|
user: UserInfo,
|
|
@@ -235,15 +269,11 @@ export async function ExecuteUpdateMeeting(
|
|
|
235
269
|
// If participants specified in update, sync participants (upsert by UserID/AgentID/ExternalEmail)
|
|
236
270
|
if (input.Participants !== undefined) {
|
|
237
271
|
const existing = await h.LoadParticipants(meeting.ID, user, provider);
|
|
272
|
+
const findExisting = indexParticipants(existing);
|
|
238
273
|
|
|
239
274
|
for (const pInput of input.Participants) {
|
|
240
275
|
const isSelf = pInput.UserID && UUIDsEqual(pInput.UserID, user.ID);
|
|
241
|
-
const match =
|
|
242
|
-
(e) =>
|
|
243
|
-
(pInput.UserID && e.UserID && UUIDsEqual(e.UserID, pInput.UserID)) ||
|
|
244
|
-
(pInput.AgentID && e.AgentID && UUIDsEqual(e.AgentID, pInput.AgentID)) ||
|
|
245
|
-
(pInput.ExternalEmail && e.ExternalEmail && e.ExternalEmail.toLowerCase() === pInput.ExternalEmail.toLowerCase()),
|
|
246
|
-
);
|
|
276
|
+
const match = findExisting(pInput);
|
|
247
277
|
|
|
248
278
|
if (match) {
|
|
249
279
|
if (!isSelf && pInput.Role) {
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
|
|
19
19
|
import { BaseSingleton } from '@memberjunction/global';
|
|
20
20
|
import { LogStatus } from '@memberjunction/core';
|
|
21
|
-
import { randomBytes } from 'node:crypto';
|
|
21
|
+
import { createHmac, randomBytes } from 'node:crypto';
|
|
22
22
|
import jwt from 'jsonwebtoken';
|
|
23
23
|
|
|
24
24
|
/** The token's `typ` claim — the route rejects any token whose `typ` differs. */
|
|
@@ -78,7 +78,11 @@ export class MediaAccessKeyManager extends BaseSingleton<MediaAccessKeyManager>
|
|
|
78
78
|
}
|
|
79
79
|
const configured = process.env.MJ_BASE_ENCRYPTION_KEY?.trim();
|
|
80
80
|
if (configured) {
|
|
81
|
-
|
|
81
|
+
// Key separation: never sign tokens with the field-encryption master key itself. Derive a
|
|
82
|
+
// dedicated HS256 signing key (HKDF-style HMAC expand) so a token-signing compromise and the
|
|
83
|
+
// data-at-rest key stay decoupled. Tokens are short-lived, so changing the derivation only
|
|
84
|
+
// invalidates in-flight URLs, exactly like a restart on the ephemeral path below.
|
|
85
|
+
this.secret = createHmac('sha256', configured).update('mj-media-access-token-signing-v1').digest('base64');
|
|
82
86
|
} else {
|
|
83
87
|
this.secret = randomBytes(32).toString('base64');
|
|
84
88
|
LogStatus(
|
|
@@ -28,6 +28,15 @@ const ENTITY_MCP_SERVERS = 'MJ: MCP Servers';
|
|
|
28
28
|
/** Entity name for OAuth Authorization States */
|
|
29
29
|
const ENTITY_OAUTH_AUTHORIZATION_STATES = 'MJ: O Auth Authorization States';
|
|
30
30
|
|
|
31
|
+
const UUID_SHAPE = /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/;
|
|
32
|
+
|
|
33
|
+
function sqlUuid(value: string, argName: string): string {
|
|
34
|
+
if (typeof value !== 'string' || !UUID_SHAPE.test(value)) {
|
|
35
|
+
throw new Error(`${argName} must be a UUID`);
|
|
36
|
+
}
|
|
37
|
+
return value;
|
|
38
|
+
}
|
|
39
|
+
|
|
31
40
|
/**
|
|
32
41
|
* Configuration for OAuth callback handler
|
|
33
42
|
*/
|
|
@@ -693,12 +702,11 @@ export class OAuthCallbackHandler {
|
|
|
693
702
|
const rv = new RunView();
|
|
694
703
|
|
|
695
704
|
// Get connection to get server ID.
|
|
696
|
-
// connectionId is caller-supplied (POST /oauth/initiate body)
|
|
697
|
-
//
|
|
698
|
-
// escaping used for stateParameter in loadAuthorizationState().
|
|
705
|
+
// connectionId is caller-supplied (POST /oauth/initiate body); enforce strict UUID shape
|
|
706
|
+
// (matching MCPResolver.getServerOAuthConfig) before interpolating into ExtraFilter.
|
|
699
707
|
const connResult = await rv.RunView<{ MCPServerID: string }>({
|
|
700
708
|
EntityName: ENTITY_MCP_SERVER_CONNECTIONS,
|
|
701
|
-
ExtraFilter: `ID='${connectionId
|
|
709
|
+
ExtraFilter: `ID='${sqlUuid(connectionId, 'connectionId')}'`,
|
|
702
710
|
Fields: ['MCPServerID'],
|
|
703
711
|
ResultType: 'simple'
|
|
704
712
|
}, contextUser);
|
|
@@ -719,7 +727,7 @@ export class OAuthCallbackHandler {
|
|
|
719
727
|
OAuthRequirePKCE: boolean | null;
|
|
720
728
|
}>({
|
|
721
729
|
EntityName: ENTITY_MCP_SERVERS,
|
|
722
|
-
ExtraFilter: `ID='${serverId}'`,
|
|
730
|
+
ExtraFilter: `ID='${sqlUuid(serverId, 'serverId')}'`,
|
|
723
731
|
Fields: [
|
|
724
732
|
'OAuthIssuerURL', 'OAuthScopes', 'OAuthMetadataCacheTTLMinutes',
|
|
725
733
|
'OAuthClientID', 'OAuthClientSecretEncrypted', 'OAuthRequirePKCE'
|
|
@@ -34,7 +34,7 @@ import {
|
|
|
34
34
|
LoadOwnedRealtimeSession, RealtimeSessionNotFoundError, RealtimeSessionOwnershipError,
|
|
35
35
|
} from '../realtimeSessions/ownedRealtimeSession.js';
|
|
36
36
|
import type { IRecordingAssemblyScheduler } from '../realtimeSessions/RecordingAssemblyScheduler.js';
|
|
37
|
-
import {
|
|
37
|
+
import { AuthorizeLoadedSession } from '../realtimeSessions/sessionAccess.js';
|
|
38
38
|
|
|
39
39
|
/** Where {@link CreateRealtimeRecordingRouter} is mounted (under the reserved `/realtime` root). */
|
|
40
40
|
export const REALTIME_RECORDING_MOUNT_PATH = '/realtime/recording';
|
|
@@ -76,8 +76,8 @@ export interface RealtimeRecordingRouterDeps {
|
|
|
76
76
|
/** The authenticated caller, or null (answered 401). */
|
|
77
77
|
ResolveCaller(req: Request): RealtimeRecordingCaller | null;
|
|
78
78
|
LoadOwnedSession: typeof LoadOwnedRealtimeSession;
|
|
79
|
-
/** The session
|
|
80
|
-
|
|
79
|
+
/** The per-session scope rule of the access decision, applied to the session {@link LoadOwnedSession} returned. */
|
|
80
|
+
AuthorizeSession: typeof AuthorizeLoadedSession;
|
|
81
81
|
/** The ledger for one request; `contextUser` is the caller (used for logging by the procs). */
|
|
82
82
|
CreateLedger(provider: DatabaseProviderBase, contextUser: UserInfo): IRealtimeRecordingLedger;
|
|
83
83
|
Scheduler: IRecordingAssemblyScheduler;
|
|
@@ -258,7 +258,7 @@ async function handleTerminal(deps: RealtimeRecordingRouterDeps, req: Request, r
|
|
|
258
258
|
|
|
259
259
|
/**
|
|
260
260
|
* Loads the session through the shared ownership gate, then applies the per-session scope rule of
|
|
261
|
-
* {@link
|
|
261
|
+
* {@link AuthorizeLoadedSession}. The production gate (`LoadOwnedRealtimeSession`) already applies the same
|
|
262
262
|
* rule, so this is a deliberate second check at the HTTP boundary: `LoadOwnedSession` is an injected dependency,
|
|
263
263
|
* and without the scope rule one anonymous guest could write shards to another guest's recording (every guest
|
|
264
264
|
* shares the one Anonymous user), and a different-bytes shard would mark it Failed. It costs one extra
|
|
@@ -266,11 +266,7 @@ async function handleTerminal(deps: RealtimeRecordingRouterDeps, req: Request, r
|
|
|
266
266
|
*/
|
|
267
267
|
async function loadAuthorizedSession(deps: RealtimeRecordingRouterDeps, sessionID: string, caller: RealtimeRecordingCaller): Promise<MJAIAgentSessionEntity> {
|
|
268
268
|
const session = await deps.LoadOwnedSession(sessionID, caller.ContextUser, caller.Provider);
|
|
269
|
-
const
|
|
270
|
-
const decision = AuthorizeSessionAccess(
|
|
271
|
-
{ SessionUserID: session.UserID, ConversationExternalID: conversation.ExternalID, ConversationReadable: conversation.Readable },
|
|
272
|
-
caller.ContextUser,
|
|
273
|
-
);
|
|
269
|
+
const decision = await deps.AuthorizeSession(session, caller.ContextUser, caller.Provider);
|
|
274
270
|
if (decision.Allowed === false) {
|
|
275
271
|
LogError(`RealtimeRecordingHandler: refused user ${caller.ContextUser.ID} on session ${sessionID}: ${decision.Reason}`);
|
|
276
272
|
throw new RecordingRequestError(403, 'Not authorized for this realtime session');
|
|
@@ -2,6 +2,7 @@ import {
|
|
|
2
2
|
BaseEntity, EntityInfo, LogError, Metadata, RunView, RunViewParams,
|
|
3
3
|
RunViewResult, UserInfo, type IRunViewProvider
|
|
4
4
|
} from '@memberjunction/core';
|
|
5
|
+
import { EscapeSQLString } from '@memberjunction/global';
|
|
5
6
|
import { ClientClauseScreen } from '@memberjunction/generic-database-provider';
|
|
6
7
|
import { ProjectRowsForCaller } from '../generic/fieldSecurityTransport.js';
|
|
7
8
|
|
|
@@ -163,7 +164,7 @@ export class ViewOperationsHandler {
|
|
|
163
164
|
// Run a view to get the available views
|
|
164
165
|
const params: RunViewParams = {
|
|
165
166
|
EntityName: 'MJ: User Views',
|
|
166
|
-
ExtraFilter: `Entity = '${
|
|
167
|
+
ExtraFilter: `Entity = '${EscapeSQLString(entity.Name)}'`
|
|
167
168
|
};
|
|
168
169
|
|
|
169
170
|
const runView = new RunView();
|
|
@@ -53,10 +53,12 @@ export interface RESTApiOptions {
|
|
|
53
53
|
}
|
|
54
54
|
|
|
55
55
|
/**
|
|
56
|
-
* Default REST API configuration
|
|
56
|
+
* Default REST API configuration. Disabled by default (matching the documented default and
|
|
57
|
+
* MJServer's own config default): mounting a full-entity REST API must be an explicit decision,
|
|
58
|
+
* never a side effect of calling SetupRESTEndpoints with no options.
|
|
57
59
|
*/
|
|
58
60
|
export const DEFAULT_REST_API_OPTIONS: RESTApiOptions = {
|
|
59
|
-
enabled:
|
|
61
|
+
enabled: false,
|
|
60
62
|
};
|
|
61
63
|
|
|
62
64
|
/**
|
|
@@ -95,6 +97,13 @@ export function SetupRESTEndpoints(
|
|
|
95
97
|
if (authMiddleware) {
|
|
96
98
|
app.use(basePath, BodyParser.json({ limit: '50mb' }), authMiddleware, restHandler.getRouter());
|
|
97
99
|
} else {
|
|
100
|
+
// MJServer itself is safe here (its unified auth middleware runs globally before this
|
|
101
|
+
// mount), but a third-party caller passing no authMiddleware may be mounting a fully
|
|
102
|
+
// UNauthenticated entity CRUD API — say so loudly.
|
|
103
|
+
LogStatusEx({
|
|
104
|
+
message: `REST API mounted at ${basePath} WITHOUT a route-level auth middleware. ` +
|
|
105
|
+
`Ensure an authentication middleware runs globally before this mount, or pass one to SetupRESTEndpoints.`,
|
|
106
|
+
});
|
|
98
107
|
app.use(basePath, BodyParser.json({ limit: '50mb' }), restHandler.getRouter());
|
|
99
108
|
}
|
|
100
109
|
|
|
@@ -15,7 +15,7 @@ import type {
|
|
|
15
15
|
DurableEntityActionSubmission,
|
|
16
16
|
DurableEntityActionSubmitter,
|
|
17
17
|
} from '@memberjunction/actions-base';
|
|
18
|
-
import { LogError } from '@memberjunction/core';
|
|
18
|
+
import { type IEntityDataProvider, type IMetadataProvider, LogError } from '@memberjunction/core';
|
|
19
19
|
import { TaskNode, type TaskGraphSpec } from '@memberjunction/ai-core-plus';
|
|
20
20
|
import { MJEnvironmentEntityExtended } from '@memberjunction/core-entities';
|
|
21
21
|
import { TaskGraphService, type ProviderFactory } from '@memberjunction/task-graph';
|
|
@@ -59,7 +59,9 @@ export class DurableEntityActionTaskSubmitter implements DurableEntityActionSubm
|
|
|
59
59
|
durable: true,
|
|
60
60
|
};
|
|
61
61
|
|
|
62
|
-
const provider =
|
|
62
|
+
const provider: IMetadataProvider = isMetadataProvider(request.Provider)
|
|
63
|
+
? request.Provider
|
|
64
|
+
: await this.providerFactory.CreateProvider();
|
|
63
65
|
const result = await new TaskGraphService().Submit(spec, {
|
|
64
66
|
EnvironmentID: this.environmentID,
|
|
65
67
|
ContextUser: request.ContextUser,
|
|
@@ -76,3 +78,9 @@ export class DurableEntityActionTaskSubmitter implements DurableEntityActionSubm
|
|
|
76
78
|
}
|
|
77
79
|
}
|
|
78
80
|
}
|
|
81
|
+
|
|
82
|
+
function isMetadataProvider(
|
|
83
|
+
provider: IMetadataProvider | IEntityDataProvider | undefined,
|
|
84
|
+
): provider is IMetadataProvider {
|
|
85
|
+
return !!provider && 'GetEntityObject' in provider && typeof provider.GetEntityObject === 'function';
|
|
86
|
+
}
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
*/
|
|
12
12
|
import { ActionEngineServer } from '@memberjunction/actions';
|
|
13
13
|
import { ActionParam } from '@memberjunction/actions-base';
|
|
14
|
+
import { BuildActionOutputObject } from '@memberjunction/ai-core-plus';
|
|
14
15
|
import { LogError } from '@memberjunction/core';
|
|
15
16
|
import { UUIDsEqual } from '@memberjunction/global';
|
|
16
17
|
import type { TaskActionRunner, TaskActionRunParams, TaskActionRunResult } from '@memberjunction/task-graph';
|
|
@@ -97,29 +98,7 @@ export class TaskGraphActionRunner implements TaskActionRunner {
|
|
|
97
98
|
* produced.
|
|
98
99
|
*/
|
|
99
100
|
private buildOutput(actionParams: ActionParam[], resultMessage?: string | null): Record<string, unknown> {
|
|
100
|
-
|
|
101
|
-
if (typeof resultMessage === 'string') {
|
|
102
|
-
const trimmed = resultMessage.trim();
|
|
103
|
-
if (trimmed.startsWith('{') && trimmed.endsWith('}')) {
|
|
104
|
-
try {
|
|
105
|
-
const parsed: unknown = JSON.parse(trimmed);
|
|
106
|
-
if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {
|
|
107
|
-
Object.assign(output, parsed as Record<string, unknown>);
|
|
108
|
-
}
|
|
109
|
-
} catch {
|
|
110
|
-
// Non-JSON prose message; ignore.
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
for (const p of actionParams ?? []) {
|
|
115
|
-
if (p.Type === 'Output' || p.Type === 'Both') output[p.Name] = p.Value;
|
|
116
|
-
}
|
|
117
|
-
if (typeof output.handle === 'string' && output.Handle === undefined) {
|
|
118
|
-
output.Handle = output.handle;
|
|
119
|
-
} else if (typeof output.Handle === 'string' && output.handle === undefined) {
|
|
120
|
-
output.handle = output.Handle;
|
|
121
|
-
}
|
|
122
|
-
return output;
|
|
101
|
+
return BuildActionOutputObject(actionParams, resultMessage);
|
|
123
102
|
}
|
|
124
103
|
|
|
125
104
|
/**
|