@memberjunction/server 6.2.0-edge.0 → 6.2.0-edge.2
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/dist/.tsbuildinfo +1 -1
- package/dist/agentSessions/ReturningVisitorRecap.d.ts +2 -0
- package/dist/agentSessions/ReturningVisitorRecap.d.ts.map +1 -1
- package/dist/agentSessions/ReturningVisitorRecap.js +5 -1
- package/dist/agentSessions/ReturningVisitorRecap.js.map +1 -1
- package/dist/agentSessions/SessionManager.js +2 -2
- package/dist/agentSessions/remoteBrowserGoalEngine.d.ts +8 -0
- package/dist/agentSessions/remoteBrowserGoalEngine.d.ts.map +1 -1
- package/dist/agentSessions/remoteBrowserGoalEngine.js +21 -5
- package/dist/agentSessions/remoteBrowserGoalEngine.js.map +1 -1
- package/dist/auth/AuthProviderCatalogRouter.d.ts +2 -0
- package/dist/auth/AuthProviderCatalogRouter.d.ts.map +1 -1
- package/dist/auth/AuthProviderCatalogRouter.js +5 -1
- package/dist/auth/AuthProviderCatalogRouter.js.map +1 -1
- package/dist/auth/index.d.ts +20 -1
- package/dist/auth/index.d.ts.map +1 -1
- package/dist/auth/index.js +20 -10
- package/dist/auth/index.js.map +1 -1
- package/dist/auth/initializeProviders.d.ts +8 -0
- package/dist/auth/initializeProviders.d.ts.map +1 -1
- package/dist/auth/initializeProviders.js +21 -5
- package/dist/auth/initializeProviders.js.map +1 -1
- package/dist/auth/magicLink/MagicLinkRouter.d.ts +9 -0
- package/dist/auth/magicLink/MagicLinkRouter.d.ts.map +1 -1
- package/dist/auth/magicLink/MagicLinkRouter.js +22 -10
- package/dist/auth/magicLink/MagicLinkRouter.js.map +1 -1
- package/dist/auth/magicLink/MagicLinkService.d.ts +17 -15
- package/dist/auth/magicLink/MagicLinkService.d.ts.map +1 -1
- package/dist/auth/magicLink/MagicLinkService.js +65 -51
- package/dist/auth/magicLink/MagicLinkService.js.map +1 -1
- package/dist/auth/magicLink/index.d.ts +2 -2
- package/dist/auth/magicLink/index.d.ts.map +1 -1
- package/dist/auth/magicLink/index.js +2 -2
- package/dist/auth/magicLink/index.js.map +1 -1
- package/dist/auth/magicLink/magicLinkCore.d.ts +54 -33
- package/dist/auth/magicLink/magicLinkCore.d.ts.map +1 -1
- package/dist/auth/magicLink/magicLinkCore.js +61 -61
- package/dist/auth/magicLink/magicLinkCore.js.map +1 -1
- package/dist/auth/magicLink/redeemLanding.d.ts +4 -0
- package/dist/auth/magicLink/redeemLanding.d.ts.map +1 -1
- package/dist/auth/magicLink/redeemLanding.js +12 -4
- package/dist/auth/magicLink/redeemLanding.js.map +1 -1
- package/dist/auth/magicLink/types.d.ts +4 -4
- package/dist/auth/principals.d.ts +2 -0
- package/dist/auth/principals.d.ts.map +1 -1
- package/dist/auth/principals.js +6 -2
- package/dist/auth/principals.js.map +1 -1
- package/dist/cache.d.ts +2 -0
- package/dist/cache.d.ts.map +1 -1
- package/dist/cache.js +3 -1
- package/dist/cache.js.map +1 -1
- package/dist/config.d.ts +346 -2
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +34 -4
- package/dist/config.js.map +1 -1
- package/dist/context.d.ts +20 -0
- package/dist/context.d.ts.map +1 -1
- package/dist/context.js +27 -19
- package/dist/context.js.map +1 -1
- package/dist/directives/Public.d.ts +2 -0
- package/dist/directives/Public.d.ts.map +1 -1
- package/dist/directives/Public.js +3 -1
- package/dist/directives/Public.js.map +1 -1
- package/dist/directives/RequireSystemUser.d.ts +2 -0
- package/dist/directives/RequireSystemUser.d.ts.map +1 -1
- package/dist/directives/RequireSystemUser.js +3 -1
- package/dist/directives/RequireSystemUser.js.map +1 -1
- package/dist/generated/graphql-schemas/__mj.d.ts +1021 -0
- package/dist/generated/graphql-schemas/__mj.d.ts.map +1 -1
- package/dist/generated/graphql-schemas/__mj.js +7368 -2145
- package/dist/generated/graphql-schemas/__mj.js.map +1 -1
- package/dist/generic/CacheInvalidationResolver.d.ts +94 -4
- package/dist/generic/CacheInvalidationResolver.d.ts.map +1 -1
- package/dist/generic/CacheInvalidationResolver.js +122 -3
- package/dist/generic/CacheInvalidationResolver.js.map +1 -1
- package/dist/generic/DeleteOptionsInput.d.ts +8 -1
- package/dist/generic/DeleteOptionsInput.d.ts.map +1 -1
- package/dist/generic/DeleteOptionsInput.js +1 -1
- package/dist/generic/DeleteOptionsInput.js.map +1 -1
- package/dist/generic/FireAndForgetHeartbeat.d.ts +18 -2
- package/dist/generic/FireAndForgetHeartbeat.d.ts.map +1 -1
- package/dist/generic/FireAndForgetHeartbeat.js +24 -6
- package/dist/generic/FireAndForgetHeartbeat.js.map +1 -1
- package/dist/generic/OrphanedConversationDetailReconciler.d.ts +31 -0
- package/dist/generic/OrphanedConversationDetailReconciler.d.ts.map +1 -0
- package/dist/generic/OrphanedConversationDetailReconciler.js +215 -0
- package/dist/generic/OrphanedConversationDetailReconciler.js.map +1 -0
- package/dist/generic/PushStatusResolver.d.ts +48 -1
- package/dist/generic/PushStatusResolver.d.ts.map +1 -1
- package/dist/generic/PushStatusResolver.js +95 -4
- package/dist/generic/PushStatusResolver.js.map +1 -1
- package/dist/generic/ResolverBase.d.ts +18 -2
- package/dist/generic/ResolverBase.d.ts.map +1 -1
- package/dist/generic/ResolverBase.js +64 -55
- package/dist/generic/ResolverBase.js.map +1 -1
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +120 -11
- package/dist/index.js.map +1 -1
- package/dist/integration/CustomColumnPromoter.d.ts +2 -0
- package/dist/integration/CustomColumnPromoter.d.ts.map +1 -1
- package/dist/integration/CustomColumnPromoter.js +5 -1
- package/dist/integration/CustomColumnPromoter.js.map +1 -1
- package/dist/integration/EntityMapLifecycle.d.ts +4 -0
- package/dist/integration/EntityMapLifecycle.d.ts.map +1 -1
- package/dist/integration/EntityMapLifecycle.js +10 -2
- package/dist/integration/EntityMapLifecycle.js.map +1 -1
- package/dist/logging/NoLog.d.ts +4 -0
- package/dist/logging/NoLog.d.ts.map +1 -1
- package/dist/logging/NoLog.js +10 -2
- package/dist/logging/NoLog.js.map +1 -1
- package/dist/logging/StartupLogger.d.ts +2 -0
- package/dist/logging/StartupLogger.d.ts.map +1 -1
- package/dist/logging/StartupLogger.js +5 -1
- package/dist/logging/StartupLogger.js.map +1 -1
- package/dist/logging/bootAudit.d.ts +11 -7
- package/dist/logging/bootAudit.d.ts.map +1 -1
- package/dist/logging/bootAudit.js +15 -7
- package/dist/logging/bootAudit.js.map +1 -1
- package/dist/logging/boundaryLogPayload.d.ts +4 -0
- package/dist/logging/boundaryLogPayload.d.ts.map +1 -1
- package/dist/logging/boundaryLogPayload.js +5 -1
- package/dist/logging/boundaryLogPayload.js.map +1 -1
- package/dist/logging/secretRedactor.d.ts +2 -0
- package/dist/logging/secretRedactor.d.ts.map +1 -1
- package/dist/logging/secretRedactor.js +8 -4
- package/dist/logging/secretRedactor.js.map +1 -1
- package/dist/logging/shortenForLog.d.ts +2 -0
- package/dist/logging/shortenForLog.d.ts.map +1 -1
- package/dist/logging/shortenForLog.js +7 -3
- package/dist/logging/shortenForLog.js.map +1 -1
- package/dist/logging/variablesLoggingMiddleware.d.ts +2 -0
- package/dist/logging/variablesLoggingMiddleware.d.ts.map +1 -1
- package/dist/logging/variablesLoggingMiddleware.js +8 -6
- package/dist/logging/variablesLoggingMiddleware.js.map +1 -1
- package/dist/metadataRefreshSignal.d.ts +19 -0
- package/dist/metadataRefreshSignal.d.ts.map +1 -0
- package/dist/metadataRefreshSignal.js +45 -0
- package/dist/metadataRefreshSignal.js.map +1 -0
- package/dist/middleware/MJTenantFilterMiddleware.js +4 -4
- package/dist/multiTenancy/index.d.ts +8 -0
- package/dist/multiTenancy/index.d.ts.map +1 -1
- package/dist/multiTenancy/index.js +21 -5
- package/dist/multiTenancy/index.js.map +1 -1
- package/dist/realtimeWidget/WidgetRouter.d.ts +5 -0
- package/dist/realtimeWidget/WidgetRouter.d.ts.map +1 -1
- package/dist/realtimeWidget/WidgetRouter.js +14 -10
- package/dist/realtimeWidget/WidgetRouter.js.map +1 -1
- package/dist/realtimeWidget/WidgetSessionService.d.ts +6 -6
- package/dist/realtimeWidget/WidgetSessionService.js +27 -27
- package/dist/realtimeWidget/host-identity.d.ts +5 -1
- package/dist/realtimeWidget/host-identity.d.ts.map +1 -1
- package/dist/realtimeWidget/host-identity.js +11 -3
- package/dist/realtimeWidget/host-identity.js.map +1 -1
- package/dist/realtimeWidget/index.d.ts +2 -2
- package/dist/realtimeWidget/index.d.ts.map +1 -1
- package/dist/realtimeWidget/index.js +2 -2
- package/dist/realtimeWidget/index.js.map +1 -1
- package/dist/realtimeWidget/visitorIdentity.d.ts +21 -1
- package/dist/realtimeWidget/visitorIdentity.d.ts.map +1 -1
- package/dist/realtimeWidget/visitorIdentity.js +16 -4
- package/dist/realtimeWidget/visitorIdentity.js.map +1 -1
- package/dist/realtimeWidget/widgetCore.d.ts +48 -0
- package/dist/realtimeWidget/widgetCore.d.ts.map +1 -1
- package/dist/realtimeWidget/widgetCore.js +38 -10
- package/dist/realtimeWidget/widgetCore.js.map +1 -1
- package/dist/realtimeWidget/widgetGuestElevation.d.ts +10 -6
- package/dist/realtimeWidget/widgetGuestElevation.d.ts.map +1 -1
- package/dist/realtimeWidget/widgetGuestElevation.js +14 -6
- package/dist/realtimeWidget/widgetGuestElevation.js.map +1 -1
- package/dist/resolvers/AdhocQueryResolver.js +3 -3
- package/dist/resolvers/AgentRunStatusPublisher.d.ts +55 -0
- package/dist/resolvers/AgentRunStatusPublisher.d.ts.map +1 -0
- package/dist/resolvers/AgentRunStatusPublisher.js +141 -0
- package/dist/resolvers/AgentRunStatusPublisher.js.map +1 -0
- package/dist/resolvers/ConversationRunEventsResolver.d.ts +90 -0
- package/dist/resolvers/ConversationRunEventsResolver.d.ts.map +1 -0
- package/dist/resolvers/ConversationRunEventsResolver.js +283 -0
- package/dist/resolvers/ConversationRunEventsResolver.js.map +1 -0
- package/dist/resolvers/DuplicateEntryCheckResolver.d.ts +98 -0
- package/dist/resolvers/DuplicateEntryCheckResolver.d.ts.map +1 -0
- package/dist/resolvers/DuplicateEntryCheckResolver.js +227 -0
- package/dist/resolvers/DuplicateEntryCheckResolver.js.map +1 -0
- package/dist/resolvers/FetchEntityVectorsResolver.d.ts.map +1 -1
- package/dist/resolvers/FetchEntityVectorsResolver.js +2 -1
- package/dist/resolvers/FetchEntityVectorsResolver.js.map +1 -1
- package/dist/resolvers/FileResolver.js +3 -3
- package/dist/resolvers/GeoResolver.d.ts +2 -2
- package/dist/resolvers/GeoResolver.js +4 -4
- package/dist/resolvers/GetDataResolver.d.ts +14 -0
- package/dist/resolvers/GetDataResolver.d.ts.map +1 -1
- package/dist/resolvers/GetDataResolver.js +43 -15
- package/dist/resolvers/GetDataResolver.js.map +1 -1
- package/dist/resolvers/IntegrationDiscoveryResolver.js +2 -2
- package/dist/resolvers/ListOperationsResolver.d.ts +20 -0
- package/dist/resolvers/ListOperationsResolver.d.ts.map +1 -1
- package/dist/resolvers/ListOperationsResolver.js +68 -28
- package/dist/resolvers/ListOperationsResolver.js.map +1 -1
- package/dist/resolvers/MCPResolver.d.ts +11 -1
- package/dist/resolvers/MCPResolver.d.ts.map +1 -1
- package/dist/resolvers/MCPResolver.js +7 -3
- package/dist/resolvers/MCPResolver.js.map +1 -1
- package/dist/resolvers/MergeRecordsResolver.d.ts +7 -0
- package/dist/resolvers/MergeRecordsResolver.d.ts.map +1 -1
- package/dist/resolvers/MergeRecordsResolver.js +16 -2
- package/dist/resolvers/MergeRecordsResolver.js.map +1 -1
- package/dist/resolvers/RealtimeBridgeResolver.js +3 -3
- package/dist/resolvers/RealtimeClientSessionResolver.d.ts +44 -6
- package/dist/resolvers/RealtimeClientSessionResolver.d.ts.map +1 -1
- package/dist/resolvers/RealtimeClientSessionResolver.js +93 -37
- package/dist/resolvers/RealtimeClientSessionResolver.js.map +1 -1
- package/dist/resolvers/RemoteBrowserActionResolver.js +5 -5
- package/dist/resolvers/RunAIAgentResolver.d.ts +8 -18
- package/dist/resolvers/RunAIAgentResolver.d.ts.map +1 -1
- package/dist/resolvers/RunAIAgentResolver.js +85 -184
- package/dist/resolvers/RunAIAgentResolver.js.map +1 -1
- package/dist/resolvers/RunAIPromptResolver.d.ts +1 -1
- package/dist/resolvers/RunAIPromptResolver.js +2 -2
- package/dist/resolvers/RunDecisionResolver.d.ts +149 -0
- package/dist/resolvers/RunDecisionResolver.d.ts.map +1 -0
- package/dist/resolvers/RunDecisionResolver.js +396 -0
- package/dist/resolvers/RunDecisionResolver.js.map +1 -0
- package/dist/resolvers/RunTestResolver.js +5 -5
- package/dist/resolvers/SqlLoggingConfigResolver.d.ts +8 -4
- package/dist/resolvers/SqlLoggingConfigResolver.d.ts.map +1 -1
- package/dist/resolvers/SqlLoggingConfigResolver.js +5 -5
- package/dist/resolvers/TaskGraphFrameResolver.d.ts +8 -1
- package/dist/resolvers/TaskGraphFrameResolver.d.ts.map +1 -1
- package/dist/resolvers/TaskGraphFrameResolver.js +6 -2
- package/dist/resolvers/TaskGraphFrameResolver.js.map +1 -1
- package/dist/resolvers/VectorizeEntityResolver.d.ts +1 -1
- package/dist/resolvers/VectorizeEntityResolver.d.ts.map +1 -1
- package/dist/resolvers/VectorizeEntityResolver.js +14 -10
- package/dist/resolvers/VectorizeEntityResolver.js.map +1 -1
- package/dist/resolvers/adhoc-query-helpers.d.ts +6 -0
- package/dist/resolvers/adhoc-query-helpers.d.ts.map +1 -1
- package/dist/resolvers/adhoc-query-helpers.js +10 -2
- package/dist/resolvers/adhoc-query-helpers.js.map +1 -1
- package/dist/resolvers/artifact-routing.d.ts +8 -8
- package/dist/resolvers/artifact-routing.js +2 -2
- package/dist/resolvers/artifact-routing.js.map +1 -1
- package/dist/resolvers/meetingRecordingRegistration.d.ts +10 -2
- package/dist/resolvers/meetingRecordingRegistration.d.ts.map +1 -1
- package/dist/resolvers/meetingRecordingRegistration.js +24 -8
- package/dist/resolvers/meetingRecordingRegistration.js.map +1 -1
- package/dist/resolvers/peaksSidecar.d.ts +4 -0
- package/dist/resolvers/peaksSidecar.d.ts.map +1 -1
- package/dist/resolvers/peaksSidecar.js +10 -2
- package/dist/resolvers/peaksSidecar.js.map +1 -1
- package/dist/rest/EntityCRUDHandler.d.ts +28 -0
- package/dist/rest/EntityCRUDHandler.d.ts.map +1 -1
- package/dist/rest/EntityCRUDHandler.js +20 -4
- package/dist/rest/EntityCRUDHandler.js.map +1 -1
- package/dist/rest/MediaStreamHandler.d.ts +2 -0
- package/dist/rest/MediaStreamHandler.d.ts.map +1 -1
- package/dist/rest/MediaStreamHandler.js +10 -6
- package/dist/rest/MediaStreamHandler.js.map +1 -1
- package/dist/rest/OAuthCallbackHandler.d.ts +9 -0
- package/dist/rest/OAuthCallbackHandler.d.ts.map +1 -1
- package/dist/rest/OAuthCallbackHandler.js +15 -3
- package/dist/rest/OAuthCallbackHandler.js.map +1 -1
- package/dist/rest/RESTEndpointHandler.d.ts +6 -4
- package/dist/rest/RESTEndpointHandler.d.ts.map +1 -1
- package/dist/rest/RESTEndpointHandler.js +15 -11
- package/dist/rest/RESTEndpointHandler.js.map +1 -1
- package/dist/rest/RealtimeSdpBrokerHandler.d.ts +5 -1
- package/dist/rest/RealtimeSdpBrokerHandler.d.ts.map +1 -1
- package/dist/rest/RealtimeSdpBrokerHandler.js +12 -4
- package/dist/rest/RealtimeSdpBrokerHandler.js.map +1 -1
- package/dist/rest/SignatureWebhookHandler.d.ts +2 -0
- package/dist/rest/SignatureWebhookHandler.d.ts.map +1 -1
- package/dist/rest/SignatureWebhookHandler.js +5 -1
- package/dist/rest/SignatureWebhookHandler.js.map +1 -1
- package/dist/rest/UploadTokenManager.d.ts +24 -8
- package/dist/rest/UploadTokenManager.d.ts.map +1 -1
- package/dist/rest/UploadTokenManager.js +53 -21
- package/dist/rest/UploadTokenManager.js.map +1 -1
- package/dist/rest/ViewOperationsHandler.d.ts +20 -0
- package/dist/rest/ViewOperationsHandler.d.ts.map +1 -1
- package/dist/rest/ViewOperationsHandler.js +20 -4
- package/dist/rest/ViewOperationsHandler.js.map +1 -1
- package/dist/rest/mediaRange.d.ts +4 -0
- package/dist/rest/mediaRange.d.ts.map +1 -1
- package/dist/rest/mediaRange.js +10 -2
- package/dist/rest/mediaRange.js.map +1 -1
- package/dist/rest/setupRESTEndpoints.d.ts +4 -0
- package/dist/rest/setupRESTEndpoints.d.ts.map +1 -1
- package/dist/rest/setupRESTEndpoints.js +13 -7
- package/dist/rest/setupRESTEndpoints.js.map +1 -1
- package/dist/serverExtensionReservedRoots.d.ts +2 -0
- package/dist/serverExtensionReservedRoots.d.ts.map +1 -1
- package/dist/serverExtensionReservedRoots.js +5 -1
- package/dist/serverExtensionReservedRoots.js.map +1 -1
- package/dist/services/TaskGraphActionRunner.d.ts +13 -8
- package/dist/services/TaskGraphActionRunner.d.ts.map +1 -1
- package/dist/services/TaskGraphActionRunner.js +16 -15
- package/dist/services/TaskGraphActionRunner.js.map +1 -1
- package/dist/types.d.ts +2 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +7 -3
- package/dist/types.js.map +1 -1
- package/dist/util.d.ts +6 -0
- package/dist/util.d.ts.map +1 -1
- package/dist/util.js +13 -5
- package/dist/util.js.map +1 -1
- package/package.json +94 -92
- package/src/__tests__/AgentRunStatusPublisher.test.ts +175 -0
- package/src/__tests__/DeleteOptionsInput.wireShape.test.ts +62 -0
- package/src/__tests__/DuplicateEntryCheckResolver.test.ts +300 -0
- package/src/__tests__/FileResolverPeaks.test.ts +13 -13
- package/src/__tests__/ListOperationsResolver.test.ts +17 -17
- package/src/__tests__/MediaStreamHandler.test.ts +28 -28
- package/src/__tests__/NoLog.test.ts +11 -11
- package/src/__tests__/PrimaryKeyAlwaysMapped.test.ts +10 -10
- package/src/__tests__/RESTEndpointHandler.test.ts +14 -14
- package/src/__tests__/RealtimeBridgeResolver.test.ts +4 -2
- package/src/__tests__/RealtimeClientSessionResolver.test.ts +216 -2
- package/src/__tests__/RealtimeSdpBrokerHandler.test.ts +6 -6
- package/src/__tests__/RefreshAdoptsNewColumns.test.ts +8 -8
- package/src/__tests__/RemoteBrowserActionRecovery.test.ts +7 -1
- package/src/__tests__/RemoteBrowserAudioStream.test.ts +7 -1
- package/src/__tests__/RemoteBrowserGoalEngine.test.ts +18 -18
- package/src/__tests__/RemoteBrowserSnapshot.test.ts +7 -1
- package/src/__tests__/ResolverBase.clientOldValues.test.ts +52 -0
- package/src/__tests__/RunAIAgentResolver.historyFrom.test.ts +398 -0
- package/src/__tests__/RunAIAgentResolver.streaming.test.ts +18 -27
- package/src/__tests__/RunDecisionResolver.test.ts +699 -0
- package/src/__tests__/TaskGraphActionRunner.durablePayload.test.ts +131 -0
- package/src/__tests__/TaskGraphFrameResolver.test.ts +2 -2
- package/src/__tests__/VectorizeEntityResolver.test.ts +106 -0
- package/src/__tests__/WidgetSessionService.test.ts +8 -4
- package/src/__tests__/adhoc-query-helpers.test.ts +21 -21
- package/src/__tests__/bootAudit.test.ts +11 -11
- package/src/__tests__/boundaryLogPayload.test.ts +6 -6
- package/src/__tests__/cacheInvalidationRecordData.test.ts +177 -0
- package/src/__tests__/conversationRunEvents.test.ts +399 -0
- package/src/__tests__/databaseAbstraction.test.ts +12 -6
- package/src/__tests__/getDataTokenRedaction.test.ts +7 -7
- package/src/__tests__/initializeProviders.refresh.test.ts +4 -4
- package/src/__tests__/magicLink.test.ts +73 -134
- package/src/__tests__/magicLinkService.consume.test.ts +224 -0
- package/src/__tests__/meetingRecordingRegistration.test.ts +12 -12
- package/src/__tests__/metadataRefreshSignal.test.ts +78 -0
- package/src/__tests__/multiTenancy.security.test.ts +40 -40
- package/src/__tests__/multiTenancy.test.ts +18 -18
- package/src/__tests__/newUsers.test.ts +24 -23
- package/src/__tests__/orphanedConversationDetailReconciler.test.ts +283 -0
- package/src/__tests__/principals.test.ts +22 -22
- package/src/__tests__/pushStatusFanOut.test.ts +206 -0
- package/src/__tests__/pushStatusResolver.filter.test.ts +8 -8
- package/src/__tests__/secretRedactor.failOpen.test.ts +12 -12
- package/src/__tests__/secretRedactor.test.ts +12 -12
- package/src/__tests__/serverExtensionReservedRoots.test.ts +7 -7
- package/src/__tests__/subscriptionRedaction.test.ts +5 -5
- package/src/__tests__/unifiedAuth.test.ts +15 -9
- package/src/__tests__/visitorIdentity.resolve.test.ts +6 -6
- package/src/__tests__/widget.test.ts +45 -45
- package/src/__tests__/widgetGuestElevation.test.ts +7 -7
- package/src/__tests__/widgetHostIdentity.test.ts +14 -14
- package/src/agentSessions/ReturningVisitorRecap.ts +12 -1
- package/src/agentSessions/SessionManager.ts +2 -2
- package/src/agentSessions/remoteBrowserGoalEngine.ts +30 -5
- package/src/auth/AuthProviderCatalogRouter.ts +6 -1
- package/src/auth/__tests__/backward-compatibility.test.ts +4 -3
- package/src/auth/index.ts +43 -28
- package/src/auth/initializeProviders.ts +25 -5
- package/src/auth/magicLink/MagicLinkRouter.ts +28 -10
- package/src/auth/magicLink/MagicLinkService.ts +71 -52
- package/src/auth/magicLink/index.ts +7 -7
- package/src/auth/magicLink/magicLinkCore.ts +96 -64
- package/src/auth/magicLink/redeemLanding.ts +14 -4
- package/src/auth/magicLink/types.ts +54 -54
- package/src/auth/newUsers.ts +1 -1
- package/src/auth/principals.ts +11 -2
- package/src/cache.ts +4 -1
- package/src/config.ts +37 -4
- package/src/context.ts +37 -24
- package/src/directives/Public.ts +4 -1
- package/src/directives/RequireSystemUser.ts +4 -1
- package/src/generated/graphql-schemas/__mj.ts +3674 -4
- package/src/generic/CacheInvalidationResolver.ts +148 -4
- package/src/generic/DeleteOptionsInput.ts +9 -2
- package/src/generic/FireAndForgetHeartbeat.ts +35 -16
- package/src/generic/OrphanedConversationDetailReconciler.ts +267 -0
- package/src/generic/PushStatusResolver.ts +118 -8
- package/src/generic/ResolverBase.ts +65 -57
- package/src/generic/RunViewResolver.ts +22 -22
- package/src/generic/__tests__/FireAndForgetHeartbeat.test.ts +7 -7
- package/src/index.ts +137 -11
- package/src/integration/CustomColumnPromoter.ts +6 -1
- package/src/integration/EntityMapLifecycle.ts +20 -2
- package/src/logging/NoLog.ts +16 -2
- package/src/logging/StartupLogger.ts +6 -1
- package/src/logging/bootAudit.ts +26 -16
- package/src/logging/boundaryLogPayload.ts +8 -1
- package/src/logging/secretRedactor.ts +14 -9
- package/src/logging/shortenForLog.ts +8 -3
- package/src/logging/variablesLoggingMiddleware.ts +9 -6
- package/src/metadataRefreshSignal.ts +45 -0
- package/src/middleware/MJTenantFilterMiddleware.ts +4 -4
- package/src/multiTenancy/index.ts +29 -5
- package/src/realtimeWidget/WidgetRouter.ts +15 -10
- package/src/realtimeWidget/WidgetSessionService.ts +76 -76
- package/src/realtimeWidget/host-identity.ts +24 -10
- package/src/realtimeWidget/index.ts +3 -3
- package/src/realtimeWidget/visitorIdentity.ts +39 -8
- package/src/realtimeWidget/widgetCore.ts +75 -10
- package/src/realtimeWidget/widgetGuestElevation.ts +25 -12
- package/src/resolvers/AdhocQueryResolver.ts +3 -3
- package/src/resolvers/AgentRunStatusPublisher.ts +175 -0
- package/src/resolvers/AgentSessionResolver.ts +3 -3
- package/src/resolvers/ColorResolver.ts +6 -6
- package/src/resolvers/ComponentRegistryResolver.ts +36 -36
- package/src/resolvers/ConversationRunEventsResolver.ts +308 -0
- package/src/resolvers/DuplicateEntryCheckResolver.ts +232 -0
- package/src/resolvers/EntityCommunicationsResolver.ts +4 -4
- package/src/resolvers/ExecuteRemoteOperationResolver.ts +9 -9
- package/src/resolvers/FetchEntityVectorsResolver.ts +2 -1
- package/src/resolvers/FileResolver.ts +42 -42
- package/src/resolvers/GeoResolver.ts +4 -4
- package/src/resolvers/GetDataResolver.ts +50 -15
- package/src/resolvers/IntegrationDiscoveryResolver.ts +2 -2
- package/src/resolvers/ListOperationsResolver.ts +78 -28
- package/src/resolvers/MCPResolver.ts +30 -15
- package/src/resolvers/MergeRecordsResolver.ts +14 -2
- package/src/resolvers/QueryResolver.ts +13 -13
- package/src/resolvers/RealtimeBridgeResolver.ts +3 -3
- package/src/resolvers/RealtimeClientSessionResolver.ts +96 -38
- package/src/resolvers/RemoteBrowserActionResolver.ts +5 -5
- package/src/resolvers/RunAIAgentResolver.ts +134 -239
- package/src/resolvers/RunAIPromptResolver.ts +22 -22
- package/src/resolvers/RunDecisionResolver.ts +435 -0
- package/src/resolvers/RunTemplateResolver.ts +4 -4
- package/src/resolvers/RunTestResolver.ts +25 -25
- package/src/resolvers/SqlLoggingConfigResolver.ts +56 -56
- package/src/resolvers/TaskGraphFrameResolver.ts +37 -28
- package/src/resolvers/TelemetryResolver.ts +79 -79
- package/src/resolvers/VectorizeEntityResolver.ts +16 -11
- package/src/resolvers/__tests__/GetDataResolver.tokens.test.ts +40 -38
- package/src/resolvers/__tests__/MergeRecordsResolver.test.ts +93 -0
- package/src/resolvers/__tests__/artifact-routing.test.ts +23 -23
- package/src/resolvers/adhoc-query-helpers.ts +19 -2
- package/src/resolvers/artifact-routing.ts +19 -19
- package/src/resolvers/meetingRecordingRegistration.ts +44 -10
- package/src/resolvers/peaksSidecar.ts +12 -2
- package/src/rest/EntityCRUDHandler.ts +24 -4
- package/src/rest/MediaStreamHandler.ts +11 -6
- package/src/rest/OAuthCallbackHandler.ts +25 -7
- package/src/rest/RESTEndpointHandler.ts +20 -15
- package/src/rest/RealtimeSdpBrokerHandler.ts +14 -4
- package/src/rest/SignatureWebhookHandler.ts +6 -1
- package/src/rest/UploadTokenManager.ts +66 -30
- package/src/rest/ViewOperationsHandler.ts +24 -4
- package/src/rest/mediaRange.ts +16 -6
- package/src/rest/setupRESTEndpoints.ts +24 -12
- package/src/serverExtensionReservedRoots.ts +6 -1
- package/src/services/TaskGraphActionRunner.ts +16 -15
- package/src/types.ts +21 -16
- package/src/util.ts +16 -6
|
@@ -168,7 +168,8 @@ vi.mock('@memberjunction/global', async (importOriginal) => {
|
|
|
168
168
|
};
|
|
169
169
|
});
|
|
170
170
|
|
|
171
|
-
vi.mock('../auth/initializeProviders.js', () => ({
|
|
171
|
+
vi.mock('../auth/initializeProviders.js', () => ({ InitializeAuthProviders: vi.fn(),
|
|
172
|
+
get initializeAuthProviders() { return this.InitializeAuthProviders; } }));
|
|
172
173
|
|
|
173
174
|
vi.mock('@memberjunction/auth-providers', () => ({
|
|
174
175
|
AuthProviderFactory: {
|
|
@@ -195,7 +196,7 @@ vi.mock('@memberjunction/api-keys', () => ({
|
|
|
195
196
|
|
|
196
197
|
// ─── Import after mocks ─────────────────────────────────────────────────────
|
|
197
198
|
import { NewUserBase } from '../auth/newUsers.js';
|
|
198
|
-
import {
|
|
199
|
+
import { VerifyUserRecord } from '../auth/index.js';
|
|
199
200
|
|
|
200
201
|
// ─── Mock-entity plumbing ───────────────────────────────────────────────────
|
|
201
202
|
|
|
@@ -557,7 +558,7 @@ describe('verifyUserRecord', () => {
|
|
|
557
558
|
}
|
|
558
559
|
|
|
559
560
|
it('returns undefined when no email is supplied', async () => {
|
|
560
|
-
const user = await
|
|
561
|
+
const user = await VerifyUserRecord(undefined, 'A', 'B');
|
|
561
562
|
|
|
562
563
|
expect(user).toBeUndefined();
|
|
563
564
|
expect(mockGetEntityObject).not.toHaveBeenCalled();
|
|
@@ -566,7 +567,7 @@ describe('verifyUserRecord', () => {
|
|
|
566
567
|
it('returns the cached user, matching email case-insensitively and trimmed', async () => {
|
|
567
568
|
addCacheUser('someone@example.com');
|
|
568
569
|
|
|
569
|
-
const user = await
|
|
570
|
+
const user = await VerifyUserRecord(' SomeOne@Example.COM ', 'A', 'B');
|
|
570
571
|
|
|
571
572
|
expect(user).toBeDefined();
|
|
572
573
|
expect((user as unknown as { Email: string }).Email).toBe('someone@example.com');
|
|
@@ -577,7 +578,7 @@ describe('verifyUserRecord', () => {
|
|
|
577
578
|
mockCacheUsers.push({ ID: 'broken-1', Name: 'Broken', Email: ' ', Type: 'User' });
|
|
578
579
|
const errorSpy = vi.spyOn(console, 'error').mockImplementation(() => undefined);
|
|
579
580
|
|
|
580
|
-
const user = await
|
|
581
|
+
const user = await VerifyUserRecord('missing@example.com', undefined, undefined);
|
|
581
582
|
|
|
582
583
|
expect(user).toBeUndefined();
|
|
583
584
|
expect(errorSpy).toHaveBeenCalledWith(expect.stringContaining('broken-1'));
|
|
@@ -587,15 +588,15 @@ describe('verifyUserRecord', () => {
|
|
|
587
588
|
it('does NOT create a user when autoCreateNewUsers is false', async () => {
|
|
588
589
|
mockConfig.userHandling.autoCreateNewUsers = false;
|
|
589
590
|
|
|
590
|
-
const user = await
|
|
591
|
+
const user = await VerifyUserRecord('new@example.com', 'New', 'User');
|
|
591
592
|
|
|
592
593
|
expect(user).toBeUndefined();
|
|
593
594
|
expect(mockGetEntityObject).not.toHaveBeenCalled();
|
|
594
595
|
});
|
|
595
596
|
|
|
596
597
|
it('does NOT create a user when firstName or lastName is missing', async () => {
|
|
597
|
-
const missingFirst = await
|
|
598
|
-
const missingLast = await
|
|
598
|
+
const missingFirst = await VerifyUserRecord('new@example.com', undefined, 'User');
|
|
599
|
+
const missingLast = await VerifyUserRecord('new@example.com', 'New', undefined);
|
|
599
600
|
|
|
600
601
|
expect(missingFirst).toBeUndefined();
|
|
601
602
|
expect(missingLast).toBeUndefined();
|
|
@@ -605,7 +606,7 @@ describe('verifyUserRecord', () => {
|
|
|
605
606
|
it('creates the user when enabled and domains are unrestricted, stamping config roles onto the returned UserInfo', async () => {
|
|
606
607
|
mockConfig.userHandling.newUserRoles = ['UI', 'Developer'];
|
|
607
608
|
|
|
608
|
-
const user = await
|
|
609
|
+
const user = await VerifyUserRecord('new@example.com', 'New', 'User');
|
|
609
610
|
|
|
610
611
|
expect(user).toBeDefined();
|
|
611
612
|
const stamped = user as unknown as { Email: string; UserRoles: Array<{ Role: string; RoleID: string; UserID: string }> };
|
|
@@ -621,7 +622,7 @@ describe('verifyUserRecord', () => {
|
|
|
621
622
|
it('returns undefined when the underlying createNewUser fails', async () => {
|
|
622
623
|
saveBehavior['MJ: Users'] = () => false;
|
|
623
624
|
|
|
624
|
-
const user = await
|
|
625
|
+
const user = await VerifyUserRecord('new@example.com', 'New', 'User');
|
|
625
626
|
|
|
626
627
|
expect(user).toBeUndefined();
|
|
627
628
|
// The cache is no longer empty at rest (it holds the configured context user), so
|
|
@@ -637,35 +638,35 @@ describe('verifyUserRecord', () => {
|
|
|
637
638
|
});
|
|
638
639
|
|
|
639
640
|
it('creates the user when the email domain is authorized', async () => {
|
|
640
|
-
const user = await
|
|
641
|
+
const user = await VerifyUserRecord('new@example.com', 'New', 'User');
|
|
641
642
|
|
|
642
643
|
expect(user).toBeDefined();
|
|
643
644
|
expect(entitiesOf('MJ: Users')).toHaveLength(1);
|
|
644
645
|
});
|
|
645
646
|
|
|
646
647
|
it('does NOT create when the email domain is not authorized', async () => {
|
|
647
|
-
const user = await
|
|
648
|
+
const user = await VerifyUserRecord('new@evil.com', 'New', 'User');
|
|
648
649
|
|
|
649
650
|
expect(user).toBeUndefined();
|
|
650
651
|
expect(mockGetEntityObject).not.toHaveBeenCalled();
|
|
651
652
|
});
|
|
652
653
|
|
|
653
654
|
it('creates when restricted and no request Origin is supplied, if the email domain is authorized', async () => {
|
|
654
|
-
const user = await
|
|
655
|
+
const user = await VerifyUserRecord('new@example.com', 'New', 'User', undefined);
|
|
655
656
|
|
|
656
657
|
expect(user).toBeDefined();
|
|
657
658
|
expect(entitiesOf('MJ: Users')).toHaveLength(1);
|
|
658
659
|
});
|
|
659
660
|
|
|
660
661
|
it('does NOT create when a forged Origin is authorized but the email domain is not', async () => {
|
|
661
|
-
const user = await
|
|
662
|
+
const user = await VerifyUserRecord('new@evil.com', 'New', 'User', 'example.com');
|
|
662
663
|
|
|
663
664
|
expect(user).toBeUndefined();
|
|
664
665
|
expect(mockGetEntityObject).not.toHaveBeenCalled();
|
|
665
666
|
});
|
|
666
667
|
|
|
667
668
|
it('matches email domains case-insensitively', async () => {
|
|
668
|
-
const user = await
|
|
669
|
+
const user = await VerifyUserRecord('new@EXAMPLE.COM', 'New', 'User');
|
|
669
670
|
|
|
670
671
|
expect(user).toBeDefined();
|
|
671
672
|
});
|
|
@@ -673,7 +674,7 @@ describe('verifyUserRecord', () => {
|
|
|
673
674
|
it('supports wildcard patterns for email subdomains', async () => {
|
|
674
675
|
mockConfig.userHandling.newUserAuthorizedDomains = ['*.example.com'];
|
|
675
676
|
|
|
676
|
-
const user = await
|
|
677
|
+
const user = await VerifyUserRecord('new@mail.example.com', 'New', 'User');
|
|
677
678
|
|
|
678
679
|
expect(user).toBeDefined();
|
|
679
680
|
});
|
|
@@ -681,7 +682,7 @@ describe('verifyUserRecord', () => {
|
|
|
681
682
|
it('does NOT match the apex against "*.example.com" (pattern is matched in full)', async () => {
|
|
682
683
|
mockConfig.userHandling.newUserAuthorizedDomains = ['*.example.com'];
|
|
683
684
|
|
|
684
|
-
const user = await
|
|
685
|
+
const user = await VerifyUserRecord('new@example.com', 'New', 'User');
|
|
685
686
|
|
|
686
687
|
expect(user).toBeUndefined();
|
|
687
688
|
expect(mockGetEntityObject).not.toHaveBeenCalled();
|
|
@@ -690,27 +691,27 @@ describe('verifyUserRecord', () => {
|
|
|
690
691
|
it('rejects prefix confusion: "*.example.com" does not match "evilexample.com" (dot is escaped)', async () => {
|
|
691
692
|
mockConfig.userHandling.newUserAuthorizedDomains = ['*.example.com'];
|
|
692
693
|
|
|
693
|
-
const user = await
|
|
694
|
+
const user = await VerifyUserRecord('new@evilexample.com', 'New', 'User');
|
|
694
695
|
|
|
695
696
|
expect(user).toBeUndefined();
|
|
696
697
|
expect(mockGetEntityObject).not.toHaveBeenCalled();
|
|
697
698
|
});
|
|
698
699
|
|
|
699
700
|
it('rejects suffix confusion: the pattern is anchored, so "example.com.evil.com" is not authorized', async () => {
|
|
700
|
-
const user = await
|
|
701
|
+
const user = await VerifyUserRecord('new@example.com.evil.com', 'New', 'User');
|
|
701
702
|
|
|
702
703
|
expect(user).toBeUndefined();
|
|
703
704
|
expect(mockGetEntityObject).not.toHaveBeenCalled();
|
|
704
705
|
});
|
|
705
706
|
|
|
706
707
|
it('rejects "evilexample.com" against the plain "example.com" pattern (anchored at both ends)', async () => {
|
|
707
|
-
const user = await
|
|
708
|
+
const user = await VerifyUserRecord('x@evilexample.com', 'New', 'User');
|
|
708
709
|
|
|
709
710
|
expect(user).toBeUndefined();
|
|
710
711
|
});
|
|
711
712
|
|
|
712
713
|
it('does NOT create when the identity has no email domain (username-only IdP)', async () => {
|
|
713
|
-
const user = await
|
|
714
|
+
const user = await VerifyUserRecord('bare-username', 'New', 'User');
|
|
714
715
|
|
|
715
716
|
expect(user).toBeUndefined();
|
|
716
717
|
expect(mockGetEntityObject).not.toHaveBeenCalled();
|
|
@@ -727,7 +728,7 @@ describe('verifyUserRecord', () => {
|
|
|
727
728
|
});
|
|
728
729
|
const dataSource = {} as unknown as import('mssql').ConnectionPool;
|
|
729
730
|
|
|
730
|
-
const user = await
|
|
731
|
+
const user = await VerifyUserRecord('late@example.com', undefined, undefined, undefined, dataSource);
|
|
731
732
|
|
|
732
733
|
expect(user).toBeDefined();
|
|
733
734
|
expect((user as unknown as { Email: string }).Email).toBe('late@example.com');
|
|
@@ -739,7 +740,7 @@ describe('verifyUserRecord', () => {
|
|
|
739
740
|
mockConfig.userHandling.updateCacheWhenNotFound = true;
|
|
740
741
|
const dataSource = {} as unknown as import('mssql').ConnectionPool;
|
|
741
742
|
|
|
742
|
-
const user = await
|
|
743
|
+
const user = await VerifyUserRecord('never@example.com', undefined, undefined, undefined, dataSource);
|
|
743
744
|
|
|
744
745
|
expect(user).toBeUndefined();
|
|
745
746
|
expect(mockRefresh).toHaveBeenCalledTimes(1);
|
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Conversation details left behind by a finished run must be closed (MJ #4222).
|
|
3
|
+
*
|
|
4
|
+
* `AgentRunner` closes the detail as the last step of a run, so a process that dies mid-run never
|
|
5
|
+
* gets there. The agent-run watchdog repairs the RUN but nothing repaired the detail — and the
|
|
6
|
+
* detail is the row the chat renders from. Observed directly: run B3E67FE3 force-failed by the
|
|
7
|
+
* watchdog while its detail stayed `In-Progress`, spinning for anyone who opened it.
|
|
8
|
+
*
|
|
9
|
+
* The properties that keep this safe to run unattended are pinned here: it never closes a message
|
|
10
|
+
* whose work is still going, and it never races the normal completion path.
|
|
11
|
+
*/
|
|
12
|
+
import 'reflect-metadata';
|
|
13
|
+
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
14
|
+
import type { RunViewParams, UserInfo } from '@memberjunction/core';
|
|
15
|
+
|
|
16
|
+
const { mockRunView } = vi.hoisted(() => ({ mockRunView: vi.fn() }));
|
|
17
|
+
|
|
18
|
+
const { mockGetEntityObject, writableStore } = vi.hoisted(() => ({
|
|
19
|
+
mockGetEntityObject: vi.fn(),
|
|
20
|
+
writableStore: { last: null as Record<string, unknown> | null },
|
|
21
|
+
}));
|
|
22
|
+
|
|
23
|
+
vi.mock('@memberjunction/global', () => ({
|
|
24
|
+
UUIDsEqual: (a: string, b: string) => String(a).toLowerCase() === String(b).toLowerCase(),
|
|
25
|
+
}));
|
|
26
|
+
|
|
27
|
+
vi.mock('@memberjunction/sqlserver-dataprovider', () => ({
|
|
28
|
+
UserCache: { Instance: { Users: [{ ID: 'OWNER-1', Email: 'owner@example.com' }] } },
|
|
29
|
+
}));
|
|
30
|
+
|
|
31
|
+
vi.mock('@memberjunction/core', () => {
|
|
32
|
+
class MockRunView {
|
|
33
|
+
public static FromMetadataProvider(): MockRunView {
|
|
34
|
+
return new MockRunView();
|
|
35
|
+
}
|
|
36
|
+
public async RunView(...args: unknown[]): Promise<unknown> {
|
|
37
|
+
return mockRunView(...args);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
return { LogError: vi.fn(), LogStatus: vi.fn(), RunView: MockRunView };
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
import { ReconcileOrphanedConversationDetails, ORPHAN_DETAIL_GRACE_MS } from '../generic/OrphanedConversationDetailReconciler.js';
|
|
44
|
+
|
|
45
|
+
const USER = { ID: 'U1' } as unknown as UserInfo;
|
|
46
|
+
const CONVERSATION_ID = 'C1';
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Provider stub: hands back the entity the reconciler actually writes through, and quotes names the
|
|
50
|
+
* way the SQL Server provider does.
|
|
51
|
+
*/
|
|
52
|
+
const PROVIDER = {
|
|
53
|
+
GetEntityObject: mockGetEntityObject,
|
|
54
|
+
EntityByName: (name: string) =>
|
|
55
|
+
name === 'MJ: AI Agent Runs' ? { SchemaName: '__mj', BaseView: 'vwAIAgentRuns' } : undefined,
|
|
56
|
+
QuoteIdentifier: (name: string) => `[${name}]`,
|
|
57
|
+
QuoteSchemaAndView: (schema: string, view: string) => `[${schema}].[${view}]`,
|
|
58
|
+
} as never;
|
|
59
|
+
const DETAIL_ID = 'D1';
|
|
60
|
+
|
|
61
|
+
function detail(over: Record<string, unknown> = {}) {
|
|
62
|
+
return { ID: DETAIL_ID, ConversationID: CONVERSATION_ID, Status: 'In-Progress', Message: '', ...over };
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* The record the reconciler loads and saves. Separate from the RunView projection on purpose: the
|
|
67
|
+
* reconciler deliberately does NOT write the entity RunView returned, because that one carries the
|
|
68
|
+
* system user and the conversation-detail permission gate refuses a non-owner.
|
|
69
|
+
*/
|
|
70
|
+
function writable(saveResult = true, loadedStatus = 'In-Progress') {
|
|
71
|
+
const w = {
|
|
72
|
+
ID: DETAIL_ID,
|
|
73
|
+
Status: loadedStatus,
|
|
74
|
+
Message: '',
|
|
75
|
+
Load: vi.fn(async () => true),
|
|
76
|
+
Save: vi.fn(async () => saveResult),
|
|
77
|
+
LatestResult: { CompleteMessage: 'denied' },
|
|
78
|
+
};
|
|
79
|
+
writableStore.last = w;
|
|
80
|
+
return w;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function script(details: unknown[], runs: unknown[], ownerId: string | null = 'OWNER-1', loadedStatus = 'In-Progress') {
|
|
84
|
+
mockRunView.mockImplementation(async (...args: unknown[]) => {
|
|
85
|
+
const params = args[0] as RunViewParams | undefined;
|
|
86
|
+
if (!params) throw new Error('RunView called with no params');
|
|
87
|
+
if (params.EntityName === 'MJ: Conversation Details') return { Success: true, Results: details };
|
|
88
|
+
if (params.EntityName === 'MJ: AI Agent Runs') return { Success: true, Results: runs };
|
|
89
|
+
if (params.EntityName === 'MJ: Conversations') {
|
|
90
|
+
return { Success: true, Results: ownerId ? [{ ID: CONVERSATION_ID, UserID: ownerId }] : [] };
|
|
91
|
+
}
|
|
92
|
+
throw new Error(`unexpected entity ${params.EntityName}`);
|
|
93
|
+
});
|
|
94
|
+
mockGetEntityObject.mockImplementation(async () => writable(true, loadedStatus));
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const longAgo = () => new Date(Date.now() - ORPHAN_DETAIL_GRACE_MS - 60_000).toISOString();
|
|
98
|
+
const justNow = () => new Date().toISOString();
|
|
99
|
+
|
|
100
|
+
describe('ReconcileOrphanedConversationDetails', () => {
|
|
101
|
+
// Block body, NOT a concise arrow: `mockReset()` returns the mock, which is callable, and
|
|
102
|
+
// vitest treats a function returned from beforeEach as a teardown hook — so a concise arrow
|
|
103
|
+
// hands vitest the mock itself to invoke with no arguments after every test.
|
|
104
|
+
beforeEach(() => {
|
|
105
|
+
mockRunView.mockReset();
|
|
106
|
+
mockGetEntityObject.mockReset();
|
|
107
|
+
writableStore.last = null;
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
it('closes a detail whose run failed long ago', async () => {
|
|
111
|
+
const d = detail();
|
|
112
|
+
script([d], [{ ConversationDetailID: DETAIL_ID, Status: 'Failed', CompletedAt: longAgo(), ErrorMessage: 'watchdog force-fail' }]);
|
|
113
|
+
|
|
114
|
+
const closed = await ReconcileOrphanedConversationDetails(PROVIDER, USER);
|
|
115
|
+
|
|
116
|
+
expect(closed).toBe(1);
|
|
117
|
+
expect(writableStore.last?.Status).toBe('Error');
|
|
118
|
+
expect(writableStore.last?.Message).toBe('watchdog force-fail');
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
it('falls back to a failure marker only when the failed run carries no error text', async () => {
|
|
122
|
+
script([detail()], [{ ConversationDetailID: DETAIL_ID, Status: 'Failed', CompletedAt: longAgo() }]);
|
|
123
|
+
|
|
124
|
+
await ReconcileOrphanedConversationDetails(PROVIDER, USER);
|
|
125
|
+
|
|
126
|
+
expect(writableStore.last?.Status).toBe('Error');
|
|
127
|
+
expect(writableStore.last?.Message).toBe('❌ Failed');
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
it('marks a completed run as Complete, not Error', async () => {
|
|
131
|
+
const d = detail();
|
|
132
|
+
script([d], [{ ConversationDetailID: DETAIL_ID, Status: 'Completed', CompletedAt: longAgo() }]);
|
|
133
|
+
|
|
134
|
+
await ReconcileOrphanedConversationDetails(PROVIDER, USER);
|
|
135
|
+
|
|
136
|
+
expect(writableStore.last?.Status).toBe('Complete');
|
|
137
|
+
// A successful run has no error to report, so an empty message stays empty rather than
|
|
138
|
+
// carrying a failure marker that contradicts the status beside it.
|
|
139
|
+
expect(writableStore.last?.Message).toBe('');
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
it('marks a run that ended awaiting the user\'s reply as Complete', async () => {
|
|
143
|
+
// AwaitingFeedback is how a normal chat turn ends: the run succeeded and the agent's
|
|
144
|
+
// reply is the message. A process that dies before closing the detail leaves exactly
|
|
145
|
+
// this pair behind, and the stale-run sweep never touches it because it is not Running.
|
|
146
|
+
script([detail()], [{ ConversationDetailID: DETAIL_ID, Status: 'AwaitingFeedback', CompletedAt: longAgo() }]);
|
|
147
|
+
|
|
148
|
+
expect(await ReconcileOrphanedConversationDetails(PROVIDER, USER)).toBe(1);
|
|
149
|
+
expect(writableStore.last?.Status).toBe('Complete');
|
|
150
|
+
expect(writableStore.last?.Message).toBe('');
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
it('leaves a message alone while its run is parked on a workflow', async () => {
|
|
154
|
+
// Paused means a task graph is still executing on the run's behalf.
|
|
155
|
+
script([detail()], [{ ConversationDetailID: DETAIL_ID, Status: 'Paused', CompletedAt: null }]);
|
|
156
|
+
|
|
157
|
+
expect(await ReconcileOrphanedConversationDetails(PROVIDER, USER)).toBe(0);
|
|
158
|
+
expect(mockGetEntityObject).not.toHaveBeenCalled();
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
it('leaves a message alone while its run is still executing', async () => {
|
|
162
|
+
const d = detail();
|
|
163
|
+
script([d], [{ ConversationDetailID: DETAIL_ID, Status: 'Running', CompletedAt: null }]);
|
|
164
|
+
|
|
165
|
+
expect(await ReconcileOrphanedConversationDetails(PROVIDER, USER)).toBe(0);
|
|
166
|
+
expect(mockGetEntityObject).not.toHaveBeenCalled();
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
it('does not race the normal completion path inside the grace window', async () => {
|
|
170
|
+
const d = detail();
|
|
171
|
+
script([d], [{ ConversationDetailID: DETAIL_ID, Status: 'Completed', CompletedAt: justNow() }]);
|
|
172
|
+
|
|
173
|
+
expect(await ReconcileOrphanedConversationDetails(PROVIDER, USER)).toBe(0);
|
|
174
|
+
expect(mockGetEntityObject).not.toHaveBeenCalled();
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
it('leaves a message alone when no run exists yet', async () => {
|
|
178
|
+
const d = detail();
|
|
179
|
+
script([d], []);
|
|
180
|
+
|
|
181
|
+
expect(await ReconcileOrphanedConversationDetails(PROVIDER, USER)).toBe(0);
|
|
182
|
+
expect(mockGetEntityObject).not.toHaveBeenCalled();
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
it('judges a retried message on its newest attempt, not an older finished one', async () => {
|
|
186
|
+
const d = detail();
|
|
187
|
+
// DESC order: newest first. The newest attempt is still running.
|
|
188
|
+
script([d], [
|
|
189
|
+
{ ConversationDetailID: DETAIL_ID, Status: 'Running', CompletedAt: null },
|
|
190
|
+
{ ConversationDetailID: DETAIL_ID, Status: 'Failed', CompletedAt: longAgo() },
|
|
191
|
+
]);
|
|
192
|
+
|
|
193
|
+
expect(await ReconcileOrphanedConversationDetails(PROVIDER, USER)).toBe(0);
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
it('preserves an existing message rather than overwriting the answer', async () => {
|
|
197
|
+
script([detail()], [{ ConversationDetailID: DETAIL_ID, Status: 'Completed', CompletedAt: longAgo() }]);
|
|
198
|
+
mockGetEntityObject.mockImplementation(async () => {
|
|
199
|
+
const w = writable();
|
|
200
|
+
w.Message = 'the real answer';
|
|
201
|
+
return w;
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
await ReconcileOrphanedConversationDetails(PROVIDER, USER);
|
|
205
|
+
|
|
206
|
+
// The loaded record already carries the answer; only the status should change.
|
|
207
|
+
expect(writableStore.last?.Message).toBe('the real answer');
|
|
208
|
+
expect(writableStore.last?.Status).toBe('Complete');
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
it('reports a refused save without throwing', async () => {
|
|
212
|
+
script([detail()], [{ ConversationDetailID: DETAIL_ID, Status: 'Failed', CompletedAt: longAgo() }]);
|
|
213
|
+
mockGetEntityObject.mockImplementation(async () => writable(false));
|
|
214
|
+
|
|
215
|
+
expect(await ReconcileOrphanedConversationDetails(PROVIDER, USER)).toBe(0);
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
it('writes as the conversation OWNER, never as the calling system user', async () => {
|
|
219
|
+
// REGRESSION, found only by running it against real data. The permission gate on
|
|
220
|
+
// MJ: Conversation Details refuses a non-owner and returns false with NO LatestResult, so
|
|
221
|
+
// every save was silently rejected while the reconciler reported success-shaped logs. The
|
|
222
|
+
// system user this pass runs as owns nothing, which is exactly who that gate stops.
|
|
223
|
+
script([detail()], [{ ConversationDetailID: DETAIL_ID, Status: 'Failed', CompletedAt: longAgo() }]);
|
|
224
|
+
|
|
225
|
+
await ReconcileOrphanedConversationDetails(PROVIDER, USER);
|
|
226
|
+
|
|
227
|
+
expect(mockGetEntityObject).toHaveBeenCalledWith(
|
|
228
|
+
'MJ: Conversation Details',
|
|
229
|
+
expect.objectContaining({ ID: 'OWNER-1' })
|
|
230
|
+
);
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
it('leaves the detail alone when no owner can be resolved', async () => {
|
|
234
|
+
script([detail()], [{ ConversationDetailID: DETAIL_ID, Status: 'Failed', CompletedAt: longAgo() }], null);
|
|
235
|
+
|
|
236
|
+
expect(await ReconcileOrphanedConversationDetails(PROVIDER, USER)).toBe(0);
|
|
237
|
+
expect(mockGetEntityObject).not.toHaveBeenCalled();
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
it('never throws out of a maintenance pass', async () => {
|
|
241
|
+
mockRunView.mockImplementation(async () => { throw new Error('db down'); });
|
|
242
|
+
|
|
243
|
+
await expect(ReconcileOrphanedConversationDetails(PROVIDER, USER)).resolves.toBe(0);
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
it('asks only for details that have a finished run, so stuck rows cannot fill the window', async () => {
|
|
247
|
+
// Details with no run, or with a run still executing, are never closed here. Read without
|
|
248
|
+
// this filter, 200 of them pin the oldest-first window and no closable orphan is reached.
|
|
249
|
+
script([detail()], [{ ConversationDetailID: DETAIL_ID, Status: 'Failed', CompletedAt: longAgo() }]);
|
|
250
|
+
|
|
251
|
+
await ReconcileOrphanedConversationDetails(PROVIDER, USER);
|
|
252
|
+
|
|
253
|
+
const detailCall = mockRunView.mock.calls.find(
|
|
254
|
+
c => (c[0] as RunViewParams).EntityName === 'MJ: Conversation Details'
|
|
255
|
+
);
|
|
256
|
+
expect((detailCall?.[0] as RunViewParams).ExtraFilter).toBe(
|
|
257
|
+
"Status = 'In-Progress' AND Role = 'AI' AND ID IN (SELECT [ConversationDetailID] FROM [__mj].[vwAIAgentRuns] " +
|
|
258
|
+
"WHERE [Status] IN ('Completed','AwaitingFeedback','Failed','Cancelled'))"
|
|
259
|
+
);
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
it('leaves a detail alone when something else closed it after the list query', async () => {
|
|
263
|
+
// The normal completion path can close the row between our read and our write. Writing
|
|
264
|
+
// anyway would stamp this pass's outcome over the one that actually happened.
|
|
265
|
+
script(
|
|
266
|
+
[detail()],
|
|
267
|
+
[{ ConversationDetailID: DETAIL_ID, Status: 'Failed', CompletedAt: longAgo(), ErrorMessage: 'late' }],
|
|
268
|
+
'OWNER-1',
|
|
269
|
+
'Complete'
|
|
270
|
+
);
|
|
271
|
+
|
|
272
|
+
const closed = await ReconcileOrphanedConversationDetails(PROVIDER, USER);
|
|
273
|
+
|
|
274
|
+
expect(closed).toBe(0);
|
|
275
|
+
expect(writableStore.last?.Status).toBe('Complete');
|
|
276
|
+
expect((writableStore.last?.Save as ReturnType<typeof vi.fn>)).not.toHaveBeenCalled();
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
it('does nothing when no message is in progress', async () => {
|
|
280
|
+
script([], []);
|
|
281
|
+
expect(await ReconcileOrphanedConversationDetails(PROVIDER, USER)).toBe(0);
|
|
282
|
+
});
|
|
283
|
+
});
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
* singleton nor a database — the seam is `(candidate, users) -> resolution`.
|
|
14
14
|
*/
|
|
15
15
|
import { describe, it, expect } from 'vitest';
|
|
16
|
-
import {
|
|
16
|
+
import { ResolvePrincipalFrom, type ResolvablePrincipal } from '../auth/principals.js';
|
|
17
17
|
|
|
18
18
|
/**
|
|
19
19
|
* The system user's ID, pinned by `UserCache` (`SYSTEM_USER_ID`) and seeded by every baseline.
|
|
@@ -63,14 +63,14 @@ const STOCK_HOST: ResolvablePrincipal[] = [SYSTEM, ANONYMOUS, HOST_ADMIN];
|
|
|
63
63
|
describe('resolvePrincipalFrom', () => {
|
|
64
64
|
describe('the shipped default (#4209)', () => {
|
|
65
65
|
it('resolves the email-shaped default to the seeded System user', () => {
|
|
66
|
-
const result =
|
|
66
|
+
const result = ResolvePrincipalFrom(SHIPPED_DEFAULT, STOCK_HOST, SYSTEM_USER_ID);
|
|
67
67
|
|
|
68
68
|
expect(result.user).toBe(SYSTEM);
|
|
69
69
|
expect(result.reason).toBe('email');
|
|
70
70
|
});
|
|
71
71
|
|
|
72
72
|
it('reports no warning when the default resolves, so a stock host logs nothing', () => {
|
|
73
|
-
const result =
|
|
73
|
+
const result = ResolvePrincipalFrom(SHIPPED_DEFAULT, STOCK_HOST, SYSTEM_USER_ID);
|
|
74
74
|
|
|
75
75
|
expect(result.warning).toBeUndefined();
|
|
76
76
|
});
|
|
@@ -81,8 +81,8 @@ describe('resolvePrincipalFrom', () => {
|
|
|
81
81
|
// `SELECT * FROM vwUsers` carries no ORDER BY and the cache is mutated in place at
|
|
82
82
|
// runtime, so row order differs between boots AND within a process. Attribution must
|
|
83
83
|
// not depend on it: this is what made CreatedByUserID incidental.
|
|
84
|
-
const ordered =
|
|
85
|
-
const reversed =
|
|
84
|
+
const ordered = ResolvePrincipalFrom(SHIPPED_DEFAULT, STOCK_HOST, SYSTEM_USER_ID);
|
|
85
|
+
const reversed = ResolvePrincipalFrom(SHIPPED_DEFAULT, [...STOCK_HOST].reverse(), SYSTEM_USER_ID);
|
|
86
86
|
|
|
87
87
|
expect(reversed.user).toBe(ordered.user);
|
|
88
88
|
});
|
|
@@ -107,7 +107,7 @@ describe('resolvePrincipalFrom', () => {
|
|
|
107
107
|
IsActive: true,
|
|
108
108
|
};
|
|
109
109
|
|
|
110
|
-
const result =
|
|
110
|
+
const result = ResolvePrincipalFrom('support@acme.com', [emailSupport, namedSupport], SYSTEM_USER_ID);
|
|
111
111
|
|
|
112
112
|
expect(result.user).toBe(namedSupport);
|
|
113
113
|
expect(result.reason).toBe('name');
|
|
@@ -125,15 +125,15 @@ describe('resolvePrincipalFrom', () => {
|
|
|
125
125
|
const HIGH: ResolvablePrincipal = { ID: 'FFFFFFFF-0000-0000-0000-00000000000F', Name: 'shared.name', Email: 'high@acme.com', Type: 'User', IsActive: true };
|
|
126
126
|
|
|
127
127
|
it('picks the lowest-ID user when two users share the configured Name', () => {
|
|
128
|
-
const result =
|
|
128
|
+
const result = ResolvePrincipalFrom('shared.name', [HIGH, LOW, SYSTEM], SYSTEM_USER_ID);
|
|
129
129
|
|
|
130
130
|
expect(result.user).toBe(LOW);
|
|
131
131
|
expect(result.reason).toBe('name');
|
|
132
132
|
});
|
|
133
133
|
|
|
134
134
|
it('resolves a duplicated Name identically however the cache is ordered', () => {
|
|
135
|
-
const forward =
|
|
136
|
-
const reversed =
|
|
135
|
+
const forward = ResolvePrincipalFrom('shared.name', [HIGH, LOW, SYSTEM], SYSTEM_USER_ID);
|
|
136
|
+
const reversed = ResolvePrincipalFrom('shared.name', [SYSTEM, LOW, HIGH], SYSTEM_USER_ID);
|
|
137
137
|
|
|
138
138
|
expect(reversed.user).toBe(forward.user);
|
|
139
139
|
});
|
|
@@ -145,7 +145,7 @@ describe('resolvePrincipalFrom', () => {
|
|
|
145
145
|
const lowEmail: ResolvablePrincipal = { ...LOW, Name: 'a.person', Email: 'shared@acme.com' };
|
|
146
146
|
const highEmail: ResolvablePrincipal = { ...HIGH, Name: 'b.person', Email: 'shared@acme.com' };
|
|
147
147
|
|
|
148
|
-
const result =
|
|
148
|
+
const result = ResolvePrincipalFrom('shared@acme.com', [highEmail, lowEmail, SYSTEM], SYSTEM_USER_ID);
|
|
149
149
|
|
|
150
150
|
expect(result.user).toBe(lowEmail);
|
|
151
151
|
expect(result.reason).toBe('email');
|
|
@@ -169,7 +169,7 @@ describe('resolvePrincipalFrom', () => {
|
|
|
169
169
|
};
|
|
170
170
|
|
|
171
171
|
it('does not resolve a deactivated candidate by Email', () => {
|
|
172
|
-
const result =
|
|
172
|
+
const result = ResolvePrincipalFrom('departed@acme.com', [...STOCK_HOST, RETIRED], SYSTEM_USER_ID);
|
|
173
173
|
|
|
174
174
|
expect(result.user).toBe(SYSTEM);
|
|
175
175
|
expect(result.reason).toBe('system');
|
|
@@ -180,14 +180,14 @@ describe('resolvePrincipalFrom', () => {
|
|
|
180
180
|
// most: without it the guarantee holds only for the rungs nobody configures.
|
|
181
181
|
const renamed: ResolvablePrincipal = { ...RETIRED, Name: 'ops-service-account', Email: 'ops@acme.com' };
|
|
182
182
|
|
|
183
|
-
const result =
|
|
183
|
+
const result = ResolvePrincipalFrom('ops-service-account', [...STOCK_HOST, renamed], SYSTEM_USER_ID);
|
|
184
184
|
|
|
185
185
|
expect(result.user).toBe(SYSTEM);
|
|
186
186
|
expect(result.reason).toBe('system');
|
|
187
187
|
});
|
|
188
188
|
|
|
189
189
|
it('reports that the candidate matched but is inactive, not that it matched nothing', () => {
|
|
190
|
-
const result =
|
|
190
|
+
const result = ResolvePrincipalFrom('departed@acme.com', [...STOCK_HOST, RETIRED], SYSTEM_USER_ID);
|
|
191
191
|
|
|
192
192
|
// "matched no user's Name or Email" would send the operator hunting for a typo in a
|
|
193
193
|
// value that is spelled correctly. The fix here is to reactivate the account or name a
|
|
@@ -215,7 +215,7 @@ describe('resolvePrincipalFrom', () => {
|
|
|
215
215
|
IsActive: true,
|
|
216
216
|
};
|
|
217
217
|
|
|
218
|
-
const result =
|
|
218
|
+
const result = ResolvePrincipalFrom('ops@acme.com', [inactiveByName, activeByEmail], SYSTEM_USER_ID);
|
|
219
219
|
|
|
220
220
|
expect(result.user).toBe(activeByEmail);
|
|
221
221
|
expect(result.reason).toBe('email');
|
|
@@ -224,14 +224,14 @@ describe('resolvePrincipalFrom', () => {
|
|
|
224
224
|
});
|
|
225
225
|
describe('fallbacks', () => {
|
|
226
226
|
it('falls back to the System user rather than an arbitrary Owner when the candidate is unresolvable', () => {
|
|
227
|
-
const result =
|
|
227
|
+
const result = ResolvePrincipalFrom('nobody@nowhere.example', STOCK_HOST, SYSTEM_USER_ID);
|
|
228
228
|
|
|
229
229
|
expect(result.user).toBe(SYSTEM);
|
|
230
230
|
expect(result.reason).toBe('system');
|
|
231
231
|
});
|
|
232
232
|
|
|
233
233
|
it('warns when a configured candidate did not resolve, naming the candidate', () => {
|
|
234
|
-
const result =
|
|
234
|
+
const result = ResolvePrincipalFrom('nobody@nowhere.example', STOCK_HOST, SYSTEM_USER_ID);
|
|
235
235
|
|
|
236
236
|
expect(result.warning).toContain('nobody@nowhere.example');
|
|
237
237
|
});
|
|
@@ -239,7 +239,7 @@ describe('resolvePrincipalFrom', () => {
|
|
|
239
239
|
it('falls back to the System user WITHOUT warning when no candidate is configured', () => {
|
|
240
240
|
// Zod defaults this setting to '', so an unset/blank value must not be reported as a
|
|
241
241
|
// misconfiguration — but it must still land somewhere deterministic.
|
|
242
|
-
const result =
|
|
242
|
+
const result = ResolvePrincipalFrom('', STOCK_HOST, SYSTEM_USER_ID);
|
|
243
243
|
|
|
244
244
|
expect(result.user).toBe(SYSTEM);
|
|
245
245
|
expect(result.warning).toBeUndefined();
|
|
@@ -256,7 +256,7 @@ describe('resolvePrincipalFrom', () => {
|
|
|
256
256
|
};
|
|
257
257
|
const users = [laterOwner, HOST_ADMIN, ANONYMOUS];
|
|
258
258
|
|
|
259
|
-
const result =
|
|
259
|
+
const result = ResolvePrincipalFrom('nobody@nowhere.example', users, SYSTEM_USER_ID);
|
|
260
260
|
|
|
261
261
|
expect(result.user).toBe(HOST_ADMIN);
|
|
262
262
|
expect(result.reason).toBe('owner');
|
|
@@ -271,7 +271,7 @@ describe('resolvePrincipalFrom', () => {
|
|
|
271
271
|
IsActive: false,
|
|
272
272
|
};
|
|
273
273
|
|
|
274
|
-
const result =
|
|
274
|
+
const result = ResolvePrincipalFrom('nobody@nowhere.example', [inactiveOwner, HOST_ADMIN], SYSTEM_USER_ID);
|
|
275
275
|
|
|
276
276
|
expect(result.user).toBe(HOST_ADMIN);
|
|
277
277
|
});
|
|
@@ -281,7 +281,7 @@ describe('resolvePrincipalFrom', () => {
|
|
|
281
281
|
// "we never act as a disabled user, except as the one user we reach first".
|
|
282
282
|
const inactiveSystem: ResolvablePrincipal = { ...SYSTEM, IsActive: false };
|
|
283
283
|
|
|
284
|
-
const result =
|
|
284
|
+
const result = ResolvePrincipalFrom('nobody@nowhere.example', [inactiveSystem, HOST_ADMIN], SYSTEM_USER_ID);
|
|
285
285
|
|
|
286
286
|
expect(result.user).toBe(HOST_ADMIN);
|
|
287
287
|
expect(result.reason).toBe('owner');
|
|
@@ -290,13 +290,13 @@ describe('resolvePrincipalFrom', () => {
|
|
|
290
290
|
it('returns no principal when the system user is deactivated and no active Owner remains', () => {
|
|
291
291
|
const inactiveSystem: ResolvablePrincipal = { ...SYSTEM, IsActive: false };
|
|
292
292
|
|
|
293
|
-
const result =
|
|
293
|
+
const result = ResolvePrincipalFrom('nobody@nowhere.example', [inactiveSystem, ANONYMOUS], SYSTEM_USER_ID);
|
|
294
294
|
|
|
295
295
|
expect(result.user).toBeNull();
|
|
296
296
|
});
|
|
297
297
|
|
|
298
298
|
it('returns no principal at all when the cache is empty', () => {
|
|
299
|
-
const result =
|
|
299
|
+
const result = ResolvePrincipalFrom(SHIPPED_DEFAULT, [], SYSTEM_USER_ID);
|
|
300
300
|
|
|
301
301
|
expect(result.user).toBeNull();
|
|
302
302
|
expect(result.reason).toBe('none');
|