@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
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for cross-instance fan-out of push-status updates (MJ #4222).
|
|
3
|
+
*
|
|
4
|
+
* Behind a load balancer the browser's WebSocket lives on one replica while the mutation driving
|
|
5
|
+
* the agent may be handled by another, so a completion can be published to a topic that has no
|
|
6
|
+
* subscriber. Replicating progress and completion closes that.
|
|
7
|
+
*
|
|
8
|
+
* Three properties are pinned here because each one, if wrong, produces a bug that looks like
|
|
9
|
+
* something else entirely:
|
|
10
|
+
*
|
|
11
|
+
* - **Streaming content is not replicated.** It is hundreds of messages per second, each carrying
|
|
12
|
+
* a full serialized agent run. Replicating it would look like a network problem, not a bug here.
|
|
13
|
+
* - **Every publish carries `SourceServerId`.** Without it, echo suppression on the receiving side
|
|
14
|
+
* has nothing to match and every push is delivered twice.
|
|
15
|
+
* - **A hook that throws does not break local delivery.** The message bus is an optimization over
|
|
16
|
+
* the durable tail query; the local path must work when it is down.
|
|
17
|
+
*/
|
|
18
|
+
import 'reflect-metadata';
|
|
19
|
+
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
|
20
|
+
import {
|
|
21
|
+
publishStatusUpdate,
|
|
22
|
+
ParseReplicatedStatusUpdate,
|
|
23
|
+
ShouldReplicateStatusUpdate,
|
|
24
|
+
SetPushStatusPublishHook,
|
|
25
|
+
type PushStatusNotificationPayload,
|
|
26
|
+
} from '../generic/PushStatusResolver.js';
|
|
27
|
+
import { MJGlobal } from '@memberjunction/global';
|
|
28
|
+
import type { PubSubEngine } from 'type-graphql';
|
|
29
|
+
|
|
30
|
+
const SESSION = 'session-1';
|
|
31
|
+
const OWNER = 'U0000000-0000-0000-0000-000000000000';
|
|
32
|
+
|
|
33
|
+
function fakePubSub() {
|
|
34
|
+
const published: Array<{ topic: string; payload: PushStatusNotificationPayload }> = [];
|
|
35
|
+
const engine = {
|
|
36
|
+
publish: (topic: string, payload: PushStatusNotificationPayload) => {
|
|
37
|
+
published.push({ topic, payload });
|
|
38
|
+
return Promise.resolve();
|
|
39
|
+
},
|
|
40
|
+
} as unknown as PubSubEngine;
|
|
41
|
+
return { engine, published };
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const envelope = (type: string) => JSON.stringify({ resolver: 'RunAIAgentResolver', type, status: 'ok', data: {} });
|
|
45
|
+
|
|
46
|
+
afterEach(() => {
|
|
47
|
+
SetPushStatusPublishHook(undefined);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
describe('ShouldReplicateStatusUpdate', () => {
|
|
51
|
+
it('replicates execution progress', () => {
|
|
52
|
+
expect(ShouldReplicateStatusUpdate(envelope('ExecutionProgress'))).toBe(true);
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
it('does not replicate streaming content', () => {
|
|
56
|
+
expect(ShouldReplicateStatusUpdate(envelope('StreamingContent'))).toBe(false);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it('replicates a plain-string message, which is rare and low-rate', () => {
|
|
60
|
+
expect(ShouldReplicateStatusUpdate('agent finished')).toBe(true);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
it('does not replicate an empty message', () => {
|
|
64
|
+
expect(ShouldReplicateStatusUpdate(undefined)).toBe(false);
|
|
65
|
+
expect(ShouldReplicateStatusUpdate('')).toBe(false);
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it('replicates JSON that is not the resolver envelope', () => {
|
|
69
|
+
expect(ShouldReplicateStatusUpdate(JSON.stringify({ hello: 'world' }))).toBe(true);
|
|
70
|
+
});
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
describe('publishStatusUpdate fan-out', () => {
|
|
74
|
+
beforeEach(() => {
|
|
75
|
+
SetPushStatusPublishHook(undefined);
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
it('stamps the publishing instance on every payload', () => {
|
|
79
|
+
const { engine, published } = fakePubSub();
|
|
80
|
+
|
|
81
|
+
publishStatusUpdate(engine, { sessionId: SESSION, ownerUserId: OWNER, message: envelope('ExecutionProgress') });
|
|
82
|
+
|
|
83
|
+
expect(published).toHaveLength(1);
|
|
84
|
+
expect(published[0].payload.SourceServerId).toBe(MJGlobal.Instance.ProcessUUID);
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
it('still publishes locally when no hook is registered', () => {
|
|
88
|
+
const { engine, published } = fakePubSub();
|
|
89
|
+
|
|
90
|
+
publishStatusUpdate(engine, { sessionId: SESSION, ownerUserId: OWNER, message: envelope('ExecutionProgress') });
|
|
91
|
+
|
|
92
|
+
expect(published[0].payload.ownerUserId).toBe(OWNER);
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
it('forwards a replicable update to the hook', () => {
|
|
96
|
+
const hook = vi.fn();
|
|
97
|
+
SetPushStatusPublishHook(hook);
|
|
98
|
+
const { engine } = fakePubSub();
|
|
99
|
+
|
|
100
|
+
publishStatusUpdate(engine, { sessionId: SESSION, ownerUserId: OWNER, message: envelope('ExecutionProgress') });
|
|
101
|
+
|
|
102
|
+
expect(hook).toHaveBeenCalledTimes(1);
|
|
103
|
+
const forwarded = hook.mock.calls[0][0] as PushStatusNotificationPayload;
|
|
104
|
+
expect(forwarded.sessionId).toBe(SESSION);
|
|
105
|
+
expect(forwarded.ownerUserId).toBe(OWNER);
|
|
106
|
+
expect(forwarded.SourceServerId).toBe(MJGlobal.Instance.ProcessUUID);
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
it('does not forward streaming content, but still delivers it locally', () => {
|
|
110
|
+
const hook = vi.fn();
|
|
111
|
+
SetPushStatusPublishHook(hook);
|
|
112
|
+
const { engine, published } = fakePubSub();
|
|
113
|
+
|
|
114
|
+
publishStatusUpdate(engine, { sessionId: SESSION, ownerUserId: OWNER, message: envelope('StreamingContent') });
|
|
115
|
+
|
|
116
|
+
expect(hook).not.toHaveBeenCalled();
|
|
117
|
+
expect(published).toHaveLength(1);
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
it('delivers locally even when the hook throws', () => {
|
|
121
|
+
SetPushStatusPublishHook(() => {
|
|
122
|
+
throw new Error('redis is down');
|
|
123
|
+
});
|
|
124
|
+
const { engine, published } = fakePubSub();
|
|
125
|
+
|
|
126
|
+
expect(() =>
|
|
127
|
+
publishStatusUpdate(engine, { sessionId: SESSION, ownerUserId: OWNER, message: envelope('ExecutionProgress') })
|
|
128
|
+
).not.toThrow();
|
|
129
|
+
expect(published).toHaveLength(1);
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
it('publishes locally before attempting fan-out', () => {
|
|
133
|
+
const order: string[] = [];
|
|
134
|
+
SetPushStatusPublishHook(() => order.push('hook'));
|
|
135
|
+
const engine = {
|
|
136
|
+
publish: () => {
|
|
137
|
+
order.push('local');
|
|
138
|
+
return Promise.resolve();
|
|
139
|
+
},
|
|
140
|
+
} as unknown as PubSubEngine;
|
|
141
|
+
|
|
142
|
+
publishStatusUpdate(engine, { sessionId: SESSION, ownerUserId: OWNER, message: envelope('ExecutionProgress') });
|
|
143
|
+
|
|
144
|
+
expect(order).toEqual(['local', 'hook']);
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
it('clears the hook when set with no argument', () => {
|
|
148
|
+
const hook = vi.fn();
|
|
149
|
+
SetPushStatusPublishHook(hook);
|
|
150
|
+
SetPushStatusPublishHook(undefined);
|
|
151
|
+
const { engine } = fakePubSub();
|
|
152
|
+
|
|
153
|
+
publishStatusUpdate(engine, { sessionId: SESSION, ownerUserId: OWNER, message: envelope('ExecutionProgress') });
|
|
154
|
+
|
|
155
|
+
expect(hook).not.toHaveBeenCalled();
|
|
156
|
+
});
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
describe('ParseReplicatedStatusUpdate', () => {
|
|
160
|
+
/**
|
|
161
|
+
* Messages on the shared channel come from other processes, so every field is checked before
|
|
162
|
+
* the update is republished onto the local topic, where the subscription filter compares
|
|
163
|
+
* `ownerUserId` against the connection's user.
|
|
164
|
+
*/
|
|
165
|
+
const LOCAL = 'LOCAL-PROCESS';
|
|
166
|
+
const raw = (over: Record<string, unknown> = {}) =>
|
|
167
|
+
JSON.stringify({ sessionId: SESSION, ownerUserId: OWNER, message: 'hello', SourceServerId: 'OTHER', ...over });
|
|
168
|
+
|
|
169
|
+
it('returns the update from another instance', () => {
|
|
170
|
+
expect(ParseReplicatedStatusUpdate(raw(), LOCAL)).toEqual({
|
|
171
|
+
sessionId: SESSION,
|
|
172
|
+
ownerUserId: OWNER,
|
|
173
|
+
message: 'hello',
|
|
174
|
+
SourceServerId: 'OTHER',
|
|
175
|
+
});
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
it('drops our own message echoed back by the bus', () => {
|
|
179
|
+
expect(ParseReplicatedStatusUpdate(raw({ SourceServerId: LOCAL }), LOCAL)).toBeNull();
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
it('drops an update with no identity', () => {
|
|
183
|
+
expect(ParseReplicatedStatusUpdate(raw({ ownerUserId: '' }), LOCAL)).toBeNull();
|
|
184
|
+
expect(ParseReplicatedStatusUpdate(raw({ sessionId: undefined }), LOCAL)).toBeNull();
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
it('drops identity fields that are not strings', () => {
|
|
188
|
+
// A truthy object would pass a presence check and reach the filter as the wrong type.
|
|
189
|
+
expect(ParseReplicatedStatusUpdate(raw({ ownerUserId: { ID: OWNER } }), LOCAL)).toBeNull();
|
|
190
|
+
expect(ParseReplicatedStatusUpdate(raw({ sessionId: 42 }), LOCAL)).toBeNull();
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
it('drops a message body that is not a string', () => {
|
|
194
|
+
expect(ParseReplicatedStatusUpdate(raw({ message: { type: 'x' } }), LOCAL)).toBeNull();
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
it('accepts an update with no message body', () => {
|
|
198
|
+
expect(ParseReplicatedStatusUpdate(raw({ message: undefined }), LOCAL)?.message).toBeUndefined();
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
it('drops malformed JSON and values that are not objects', () => {
|
|
202
|
+
expect(ParseReplicatedStatusUpdate('{not json', LOCAL)).toBeNull();
|
|
203
|
+
expect(ParseReplicatedStatusUpdate('null', LOCAL)).toBeNull();
|
|
204
|
+
expect(ParseReplicatedStatusUpdate('42', LOCAL)).toBeNull();
|
|
205
|
+
});
|
|
206
|
+
});
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
*/
|
|
11
11
|
import 'reflect-metadata';
|
|
12
12
|
import { describe, it, expect } from 'vitest';
|
|
13
|
-
import {
|
|
13
|
+
import { StatusUpdatesFilter, type PushStatusNotificationPayload, type StatusUpdatesFilterContext } from '../generic/PushStatusResolver';
|
|
14
14
|
import type { UserPayload } from '../types';
|
|
15
15
|
|
|
16
16
|
const OWNER = 'AA11BB22-0000-4000-8000-000000000001';
|
|
@@ -28,29 +28,29 @@ function push(overrides: Partial<PushStatusNotificationPayload> = {}): PushStatu
|
|
|
28
28
|
|
|
29
29
|
describe('statusUpdatesFilter (B49 session-hijack gate)', () => {
|
|
30
30
|
it('delivers when sessionId matches AND the connection identity owns the push', () => {
|
|
31
|
-
expect(
|
|
31
|
+
expect(StatusUpdatesFilter({ payload: push(), args: { sessionId: SESSION }, context: ctxFor(OWNER) })).toBe(true);
|
|
32
32
|
});
|
|
33
33
|
|
|
34
34
|
it('THE FIX: rejects a matching sessionId when the connection identity is a DIFFERENT user (hijack)', () => {
|
|
35
35
|
// Attacker lifted the victim's sessionId and subscribes with it — but their authenticated
|
|
36
36
|
// connection identity is their own, so delivery must be denied.
|
|
37
|
-
expect(
|
|
37
|
+
expect(StatusUpdatesFilter({ payload: push({ ownerUserId: OWNER }), args: { sessionId: SESSION }, context: ctxFor(ATTACKER) })).toBe(false);
|
|
38
38
|
});
|
|
39
39
|
|
|
40
40
|
it('rejects when the requested sessionId does not match the push', () => {
|
|
41
|
-
expect(
|
|
41
|
+
expect(StatusUpdatesFilter({ payload: push(), args: { sessionId: 'some-other-session' }, context: ctxFor(OWNER) })).toBe(false);
|
|
42
42
|
});
|
|
43
43
|
|
|
44
44
|
it('fails closed when the push carries no ownerUserId', () => {
|
|
45
|
-
expect(
|
|
45
|
+
expect(StatusUpdatesFilter({ payload: push({ ownerUserId: '' }), args: { sessionId: SESSION }, context: ctxFor(OWNER) })).toBe(false);
|
|
46
46
|
});
|
|
47
47
|
|
|
48
48
|
it('fails closed when the connection has no authenticated identity', () => {
|
|
49
|
-
expect(
|
|
50
|
-
expect(
|
|
49
|
+
expect(StatusUpdatesFilter({ payload: push(), args: { sessionId: SESSION }, context: ctxFor(undefined) })).toBe(false);
|
|
50
|
+
expect(StatusUpdatesFilter({ payload: push(), args: { sessionId: SESSION }, context: undefined })).toBe(false);
|
|
51
51
|
});
|
|
52
52
|
|
|
53
53
|
it('matches identity case-insensitively (SQL Server upper vs PostgreSQL lower UUIDs)', () => {
|
|
54
|
-
expect(
|
|
54
|
+
expect(StatusUpdatesFilter({ payload: push({ ownerUserId: OWNER.toUpperCase() }), args: { sessionId: SESSION }, context: ctxFor(OWNER.toLowerCase()) })).toBe(true);
|
|
55
55
|
});
|
|
56
56
|
});
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
|
2
2
|
import type { IMetadataProvider } from '@memberjunction/core';
|
|
3
|
-
import {
|
|
3
|
+
import { RedactArg, ResetUnboundCrudInputWarnings } from '../logging/secretRedactor.js';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* `redactArg` binds a GraphQL input type to an entity by NAME:
|
|
@@ -42,7 +42,7 @@ afterEach(() => {
|
|
|
42
42
|
|
|
43
43
|
describe('redactArg — entity-bound inputs (the codegen case)', () => {
|
|
44
44
|
it('redacts an encrypted column, keeping the rest', () => {
|
|
45
|
-
const out =
|
|
45
|
+
const out = RedactArg({
|
|
46
46
|
inputTypeName: 'CreateMJCredentialInput',
|
|
47
47
|
rawValue: { Name: 'Stripe', Values: SECRET },
|
|
48
48
|
provider: CREDENTIAL_ENTITY,
|
|
@@ -55,7 +55,7 @@ describe('redactArg — entity-bound inputs (the codegen case)', () => {
|
|
|
55
55
|
});
|
|
56
56
|
|
|
57
57
|
it('emits no warning, because the binding resolved', () => {
|
|
58
|
-
|
|
58
|
+
RedactArg({
|
|
59
59
|
inputTypeName: 'CreateMJCredentialInput',
|
|
60
60
|
rawValue: { Values: SECRET },
|
|
61
61
|
provider: CREDENTIAL_ENTITY,
|
|
@@ -76,14 +76,14 @@ describe('redactArg — name matches the convention but no entity exists', () =>
|
|
|
76
76
|
};
|
|
77
77
|
|
|
78
78
|
it('falls open — values are logged, which is the defect being surfaced', () => {
|
|
79
|
-
const out =
|
|
79
|
+
const out = RedactArg({ ...unbound });
|
|
80
80
|
// Documents current behaviour: no entity binding and no @NoLog means no
|
|
81
81
|
// redaction source, so the value survives into the log.
|
|
82
82
|
expect(JSON.stringify(out)).toContain(SECRET);
|
|
83
83
|
});
|
|
84
84
|
|
|
85
85
|
it('now warns, naming the input type and what to do about it', () => {
|
|
86
|
-
|
|
86
|
+
RedactArg({ ...unbound });
|
|
87
87
|
|
|
88
88
|
expect(console.warn).toHaveBeenCalledTimes(1);
|
|
89
89
|
const message = vi.mocked(console.warn).mock.calls[0][0] as string;
|
|
@@ -94,22 +94,22 @@ describe('redactArg — name matches the convention but no entity exists', () =>
|
|
|
94
94
|
});
|
|
95
95
|
|
|
96
96
|
it('warns only once per input type, however hot the mutation is', () => {
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
97
|
+
RedactArg({ ...unbound });
|
|
98
|
+
RedactArg({ ...unbound });
|
|
99
|
+
RedactArg({ ...unbound });
|
|
100
100
|
expect(console.warn).toHaveBeenCalledTimes(1);
|
|
101
101
|
});
|
|
102
102
|
|
|
103
103
|
it('warns separately for a different unbound input type', () => {
|
|
104
|
-
|
|
105
|
-
|
|
104
|
+
RedactArg({ ...unbound });
|
|
105
|
+
RedactArg({ ...unbound, inputTypeName: 'CreateScheduleInput' });
|
|
106
106
|
expect(console.warn).toHaveBeenCalledTimes(2);
|
|
107
107
|
});
|
|
108
108
|
});
|
|
109
109
|
|
|
110
110
|
describe('redactArg — @NoLog closes the gap the metadata cannot', () => {
|
|
111
111
|
it('redacts a marked field on an input with no entity binding', () => {
|
|
112
|
-
const out =
|
|
112
|
+
const out = RedactArg({
|
|
113
113
|
inputTypeName: 'CreateConnectionInput',
|
|
114
114
|
rawValue: { CredentialName: 'acme', CredentialValues: SECRET },
|
|
115
115
|
provider: CREDENTIAL_ENTITY,
|
|
@@ -123,7 +123,7 @@ describe('redactArg — @NoLog closes the gap the metadata cannot', () => {
|
|
|
123
123
|
});
|
|
124
124
|
|
|
125
125
|
it('a parameter-level mark redacts the whole arg regardless of binding', () => {
|
|
126
|
-
const out =
|
|
126
|
+
const out = RedactArg({
|
|
127
127
|
inputTypeName: 'CreateConnectionInput',
|
|
128
128
|
rawValue: { CredentialValues: SECRET },
|
|
129
129
|
provider: CREDENTIAL_ENTITY,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { describe, it, expect } from 'vitest';
|
|
2
2
|
import type { IMetadataProvider } from '@memberjunction/core';
|
|
3
|
-
import {
|
|
3
|
+
import { RedactArg } from '../logging/secretRedactor.js';
|
|
4
4
|
|
|
5
5
|
type EncryptedField = { Name: string };
|
|
6
6
|
type FakeEntity = { ClassName: string; EncryptedFields: EncryptedField[] };
|
|
@@ -22,7 +22,7 @@ describe('redactArg', () => {
|
|
|
22
22
|
};
|
|
23
23
|
|
|
24
24
|
it('returns "<redacted>" when noLogParameter is set, regardless of other inputs', () => {
|
|
25
|
-
const result =
|
|
25
|
+
const result = RedactArg({
|
|
26
26
|
inputTypeName: 'CreateMJCredentialInput',
|
|
27
27
|
rawValue: { Values: 'FAKE_SECRET_VALUE_DO_NOT_USE' },
|
|
28
28
|
provider: makeProvider([credentialEntity]),
|
|
@@ -33,7 +33,7 @@ describe('redactArg', () => {
|
|
|
33
33
|
});
|
|
34
34
|
|
|
35
35
|
it('returns "<metadata-not-ready>" when the provider has no entities (bootstrap window)', () => {
|
|
36
|
-
const result =
|
|
36
|
+
const result = RedactArg({
|
|
37
37
|
inputTypeName: 'CreateMJCredentialInput',
|
|
38
38
|
rawValue: { Values: 'FAKE_SECRET_VALUE_DO_NOT_USE' },
|
|
39
39
|
provider: makeProvider([]),
|
|
@@ -44,7 +44,7 @@ describe('redactArg', () => {
|
|
|
44
44
|
});
|
|
45
45
|
|
|
46
46
|
it('fails open (passes through) when input type does not match Create/Update<X>Input regex', () => {
|
|
47
|
-
const result =
|
|
47
|
+
const result = RedactArg({
|
|
48
48
|
inputTypeName: 'SomeCustomArgs',
|
|
49
49
|
rawValue: { accessToken: 'FAKE_TOKEN' },
|
|
50
50
|
provider: makeProvider([credentialEntity]),
|
|
@@ -55,7 +55,7 @@ describe('redactArg', () => {
|
|
|
55
55
|
});
|
|
56
56
|
|
|
57
57
|
it('fails open when input type matches regex but no entity is found', () => {
|
|
58
|
-
const result =
|
|
58
|
+
const result = RedactArg({
|
|
59
59
|
inputTypeName: 'CreateUnknownEntityInput',
|
|
60
60
|
rawValue: { Foo: 'bar' },
|
|
61
61
|
provider: makeProvider([credentialEntity]),
|
|
@@ -66,7 +66,7 @@ describe('redactArg', () => {
|
|
|
66
66
|
});
|
|
67
67
|
|
|
68
68
|
it('redacts top-level keys matching EntityFieldInfo.Encrypt=true on entity-bound CRUD inputs', () => {
|
|
69
|
-
const result =
|
|
69
|
+
const result = RedactArg({
|
|
70
70
|
inputTypeName: 'CreateMJCredentialInput',
|
|
71
71
|
rawValue: { ID: 'abc-123', Name: 'HubSpot', Values: 'FAKE_SECRET_VALUE_DO_NOT_USE' },
|
|
72
72
|
provider: makeProvider([credentialEntity]),
|
|
@@ -81,7 +81,7 @@ describe('redactArg', () => {
|
|
|
81
81
|
});
|
|
82
82
|
|
|
83
83
|
it('redacts top-level keys in noLogFields even when EncryptedFields does not include them', () => {
|
|
84
|
-
const result =
|
|
84
|
+
const result = RedactArg({
|
|
85
85
|
inputTypeName: 'CreateUserInput',
|
|
86
86
|
rawValue: { Name: 'Alice', SecretToken: 'oops' },
|
|
87
87
|
provider: makeProvider([userEntity]),
|
|
@@ -95,7 +95,7 @@ describe('redactArg', () => {
|
|
|
95
95
|
});
|
|
96
96
|
|
|
97
97
|
it('Update<X>Input is also handled (not just Create<X>Input)', () => {
|
|
98
|
-
const result =
|
|
98
|
+
const result = RedactArg({
|
|
99
99
|
inputTypeName: 'UpdateMJCredentialInput',
|
|
100
100
|
rawValue: { ID: 'abc-123', Values: 'NEW_FAKE_SECRET' },
|
|
101
101
|
provider: makeProvider([credentialEntity]),
|
|
@@ -113,7 +113,7 @@ describe('redactArg', () => {
|
|
|
113
113
|
// resolvers as metadata-bound instead of flooding with false positives. Security is identical:
|
|
114
114
|
// delete inputs carry PK + Options only — no encrypted field, so nothing is redacted, the keys
|
|
115
115
|
// just pass through. See docs/adr/0001-graphql-variables-logging-tiered-by-verbose.md.
|
|
116
|
-
const result =
|
|
116
|
+
const result = RedactArg({
|
|
117
117
|
inputTypeName: 'DeleteMJCredentialInput',
|
|
118
118
|
rawValue: { ID: 'abc-123' },
|
|
119
119
|
provider: makeProvider([credentialEntity]),
|
|
@@ -125,7 +125,7 @@ describe('redactArg', () => {
|
|
|
125
125
|
|
|
126
126
|
it('Delete<X>Input never exposes an encrypted value even if one were somehow present', () => {
|
|
127
127
|
// Defense-in-depth: even if a delete input carried an encrypted field name, it must still mask.
|
|
128
|
-
const result =
|
|
128
|
+
const result = RedactArg({
|
|
129
129
|
inputTypeName: 'DeleteMJCredentialInput',
|
|
130
130
|
rawValue: { ID: 'abc-123', Values: 'FAKE_SECRET_VALUE_DO_NOT_USE' },
|
|
131
131
|
provider: makeProvider([credentialEntity]),
|
|
@@ -136,7 +136,7 @@ describe('redactArg', () => {
|
|
|
136
136
|
});
|
|
137
137
|
|
|
138
138
|
it('only walks TOP-level keys — nested objects with encrypted-field names are not recursed', () => {
|
|
139
|
-
const result =
|
|
139
|
+
const result = RedactArg({
|
|
140
140
|
inputTypeName: 'CreateMJCredentialInput',
|
|
141
141
|
rawValue: {
|
|
142
142
|
Name: 'HubSpot',
|
|
@@ -151,7 +151,7 @@ describe('redactArg', () => {
|
|
|
151
151
|
});
|
|
152
152
|
|
|
153
153
|
it('passes non-object raw values through to shortenForLog', () => {
|
|
154
|
-
const result =
|
|
154
|
+
const result = RedactArg({
|
|
155
155
|
inputTypeName: 'CreateMJCredentialInput',
|
|
156
156
|
rawValue: 'plain-string',
|
|
157
157
|
provider: makeProvider([credentialEntity]),
|
|
@@ -11,7 +11,7 @@ import { fileURLToPath } from 'node:url';
|
|
|
11
11
|
import {
|
|
12
12
|
CORE_CONSTANT_RESERVED_SERVER_EXTENSION_ROOTS,
|
|
13
13
|
CORE_STATIC_RESERVED_SERVER_EXTENSION_ROOTS,
|
|
14
|
-
|
|
14
|
+
CoreReservedServerExtensionRoots,
|
|
15
15
|
} from '../serverExtensionReservedRoots.js';
|
|
16
16
|
import { validateServerExtensionRootPath } from '@memberjunction/server-extensions-core';
|
|
17
17
|
|
|
@@ -58,12 +58,12 @@ describe('coreReservedServerExtensionRoots', () => {
|
|
|
58
58
|
|
|
59
59
|
it('structurally guarantees every pre-auth app.use / app.get / app.post mount in index.ts is reserved', () => {
|
|
60
60
|
const indexSrc = readFileSync(join(SRC, 'index.ts'), 'utf8');
|
|
61
|
-
const authMiddlewareIndex = indexSrc.indexOf('app.use(
|
|
61
|
+
const authMiddlewareIndex = indexSrc.indexOf('app.use(CreateUnifiedAuthMiddleware');
|
|
62
62
|
expect(authMiddlewareIndex).toBeGreaterThan(0);
|
|
63
63
|
const preAuthSrc = indexSrc.substring(0, authMiddlewareIndex);
|
|
64
64
|
|
|
65
65
|
const mountRegex = /app\.(?:use|get|post)\s*\(\s*([^,\s)]+)/g;
|
|
66
|
-
const reservedRoots =
|
|
66
|
+
const reservedRoots = CoreReservedServerExtensionRoots('/');
|
|
67
67
|
|
|
68
68
|
// Explicit allowlist of known non-path middleware mounts (e.g. app.use(mw), app.use(cors()), app.use(compression(...)))
|
|
69
69
|
const knownNonPathTokens = [
|
|
@@ -71,7 +71,7 @@ describe('coreReservedServerExtensionRoots', () => {
|
|
|
71
71
|
'express.',
|
|
72
72
|
'compression',
|
|
73
73
|
'cookieParser',
|
|
74
|
-
'
|
|
74
|
+
'CreateUnifiedAuthMiddleware',
|
|
75
75
|
'mw',
|
|
76
76
|
];
|
|
77
77
|
const isKnownNonPath = (t: string): boolean =>
|
|
@@ -120,7 +120,7 @@ describe('coreReservedServerExtensionRoots', () => {
|
|
|
120
120
|
});
|
|
121
121
|
|
|
122
122
|
it('includes graphqlRootPath and every core mount', () => {
|
|
123
|
-
const roots =
|
|
123
|
+
const roots = CoreReservedServerExtensionRoots('/api');
|
|
124
124
|
expect(roots).toEqual(
|
|
125
125
|
expect.arrayContaining([
|
|
126
126
|
'/api',
|
|
@@ -139,7 +139,7 @@ describe('coreReservedServerExtensionRoots', () => {
|
|
|
139
139
|
});
|
|
140
140
|
|
|
141
141
|
it('closes the F-M2 bypasses: cased /auth and sibling /healthcheck', () => {
|
|
142
|
-
const extra =
|
|
142
|
+
const extra = CoreReservedServerExtensionRoots('/');
|
|
143
143
|
expect(validateServerExtensionRootPath('/Auth', extra)).toMatch(/reserved prefix/);
|
|
144
144
|
expect(validateServerExtensionRootPath('/healthcheck', extra)).toMatch(/reserved prefix/);
|
|
145
145
|
expect(validateServerExtensionRootPath('/Healthcheck', extra)).toMatch(/reserved prefix/);
|
|
@@ -156,7 +156,7 @@ describe('coreReservedServerExtensionRoots', () => {
|
|
|
156
156
|
});
|
|
157
157
|
|
|
158
158
|
it('reserves a non-default graphqlRootPath and its nested paths', () => {
|
|
159
|
-
const extra =
|
|
159
|
+
const extra = CoreReservedServerExtensionRoots('/api');
|
|
160
160
|
expect(validateServerExtensionRootPath('/api', extra)).toMatch(/reserved prefix/);
|
|
161
161
|
expect(validateServerExtensionRootPath('/api/graphql', extra)).toMatch(/reserved prefix/);
|
|
162
162
|
expect(validateServerExtensionRootPath('/apiv2', extra)).toBeNull();
|
|
@@ -45,7 +45,7 @@ vi.mock('../config.js', () => ({
|
|
|
45
45
|
},
|
|
46
46
|
}));
|
|
47
47
|
|
|
48
|
-
import {
|
|
48
|
+
import { VariablesLoggingMiddleware } from '../logging/variablesLoggingMiddleware.js';
|
|
49
49
|
|
|
50
50
|
/**
|
|
51
51
|
* Build a synthetic GraphQLResolveInfo-shaped object that matches what type-graphql passes
|
|
@@ -99,7 +99,7 @@ describe('variablesLoggingMiddleware', () => {
|
|
|
99
99
|
|
|
100
100
|
it('skips field resolvers (info.path.prev !== undefined) without logging', async () => {
|
|
101
101
|
const next = vi.fn(async () => 'next-result');
|
|
102
|
-
const result = await
|
|
102
|
+
const result = await VariablesLoggingMiddleware(
|
|
103
103
|
{
|
|
104
104
|
root: undefined,
|
|
105
105
|
args: { x: 1 },
|
|
@@ -126,7 +126,7 @@ describe('variablesLoggingMiddleware', () => {
|
|
|
126
126
|
],
|
|
127
127
|
};
|
|
128
128
|
|
|
129
|
-
await
|
|
129
|
+
await VariablesLoggingMiddleware(
|
|
130
130
|
{
|
|
131
131
|
root: undefined,
|
|
132
132
|
args: { input: { ID: 'abc', Name: 'HubSpot', Values: 'FAKE_SECRET' } },
|
|
@@ -165,7 +165,7 @@ describe('variablesLoggingMiddleware', () => {
|
|
|
165
165
|
Entities: [{ ClassName: 'SomeUnrelatedEntity', EncryptedFields: [] }],
|
|
166
166
|
};
|
|
167
167
|
|
|
168
|
-
await
|
|
168
|
+
await VariablesLoggingMiddleware(
|
|
169
169
|
{
|
|
170
170
|
root: undefined,
|
|
171
171
|
args: { token: 'plaintext-leak' },
|
|
@@ -196,7 +196,7 @@ describe('variablesLoggingMiddleware', () => {
|
|
|
196
196
|
const next = vi.fn(async () => undefined);
|
|
197
197
|
const fakeProvider = { Entities: [] };
|
|
198
198
|
|
|
199
|
-
await
|
|
199
|
+
await VariablesLoggingMiddleware(
|
|
200
200
|
{
|
|
201
201
|
root: undefined,
|
|
202
202
|
args: { input: { Values: 'FAKE_SECRET' } },
|
|
@@ -59,16 +59,22 @@ vi.mock('jsonwebtoken', () => ({
|
|
|
59
59
|
}));
|
|
60
60
|
|
|
61
61
|
vi.mock('../auth/index.js', () => ({
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
62
|
+
GetSigningKeys: mockGetSigningKeys,
|
|
63
|
+
get getSigningKeys() { return this.GetSigningKeys; },
|
|
64
|
+
GetSystemUser: vi.fn(),
|
|
65
|
+
get getSystemUser() { return this.GetSystemUser; },
|
|
66
|
+
GetValidationOptions: mockGetValidationOptions,
|
|
67
|
+
get getValidationOptions() { return this.GetValidationOptions; },
|
|
68
|
+
VerifyUserRecord: mockVerifyUserRecord,
|
|
69
|
+
get verifyUserRecord() { return this.VerifyUserRecord; },
|
|
70
|
+
ExtractUserInfoFromPayload: mockExtractUserInfo,
|
|
71
|
+
get extractUserInfoFromPayload() { return this.ExtractUserInfoFromPayload; },
|
|
67
72
|
}));
|
|
68
73
|
|
|
69
74
|
vi.mock('../cache.js', () => {
|
|
70
75
|
const map = new Map<string, boolean>();
|
|
71
|
-
return {
|
|
76
|
+
return { AuthCache: map,
|
|
77
|
+
get authCache() { return this.AuthCache; } };
|
|
72
78
|
});
|
|
73
79
|
|
|
74
80
|
vi.mock('../config.js', () => ({
|
|
@@ -95,7 +101,7 @@ vi.mock('@memberjunction/api-keys', () => ({
|
|
|
95
101
|
GetAPIKeyEngine: vi.fn(),
|
|
96
102
|
}));
|
|
97
103
|
|
|
98
|
-
import {
|
|
104
|
+
import { CreateUnifiedAuthMiddleware } from '../context.js';
|
|
99
105
|
import type { Request, Response, NextFunction } from 'express';
|
|
100
106
|
import type { DataSourceInfo } from '../types.js';
|
|
101
107
|
|
|
@@ -178,11 +184,11 @@ function setupSuccessfulAuth() {
|
|
|
178
184
|
// ─── Tests ──────────────────────────────────────────────────────────────────
|
|
179
185
|
|
|
180
186
|
describe('createUnifiedAuthMiddleware', () => {
|
|
181
|
-
let middleware: ReturnType<typeof
|
|
187
|
+
let middleware: ReturnType<typeof CreateUnifiedAuthMiddleware>;
|
|
182
188
|
|
|
183
189
|
beforeEach(() => {
|
|
184
190
|
vi.clearAllMocks();
|
|
185
|
-
middleware =
|
|
191
|
+
middleware = CreateUnifiedAuthMiddleware(mockDataSources);
|
|
186
192
|
});
|
|
187
193
|
|
|
188
194
|
describe('OPTIONS passthrough', () => {
|
|
@@ -13,7 +13,7 @@ vi.mock('@memberjunction/core', async (importOriginal) => {
|
|
|
13
13
|
};
|
|
14
14
|
});
|
|
15
15
|
|
|
16
|
-
import {
|
|
16
|
+
import { ResolveIdentityByEmail } from '../realtimeWidget/visitorIdentity.js';
|
|
17
17
|
import type { EntityInfo, IMetadataProvider, UserInfo } from '@memberjunction/core';
|
|
18
18
|
|
|
19
19
|
type LookupParams = { EntityName: string; ExtraFilter: string; Fields: string[] };
|
|
@@ -32,7 +32,7 @@ describe('resolveIdentityByEmail — the configured identity entity can have ANY
|
|
|
32
32
|
it('selects the real key column and returns its bare value for a single non-ID key', async () => {
|
|
33
33
|
runViewMock.mockResolvedValue({ Success: true, Results: [{ individual_id: 4711 }] });
|
|
34
34
|
const provider = makeProvider({ ID: 'ent-persons', PrimaryKeys: [{ Name: 'individual_id' }] });
|
|
35
|
-
const out = await
|
|
35
|
+
const out = await ResolveIdentityByEmail('ada@example.com', user, provider, { entityName: 'Persons', EmailField: 'EmailAddress' });
|
|
36
36
|
expect(lastParams().Fields).toEqual(['individual_id']);
|
|
37
37
|
expect(lastParams().ExtraFilter).toBe("EmailAddress = 'ada@example.com'");
|
|
38
38
|
expect(out).toEqual({ entityId: 'ent-persons', recordId: '4711' });
|
|
@@ -41,7 +41,7 @@ describe('resolveIdentityByEmail — the configured identity entity can have ANY
|
|
|
41
41
|
it('selects EVERY key column and carries a composite key as a full segment, not its first column', async () => {
|
|
42
42
|
runViewMock.mockResolvedValue({ Success: true, Results: [{ OrderID: '11055', LineNo: 3 }] });
|
|
43
43
|
const provider = makeProvider({ ID: 'ent-lines', PrimaryKeys: [{ Name: 'OrderID' }, { Name: 'LineNo' }] });
|
|
44
|
-
const out = await
|
|
44
|
+
const out = await ResolveIdentityByEmail('x@y.com', user, provider, { entityName: 'Order Lines' });
|
|
45
45
|
expect(lastParams().Fields).toEqual(['OrderID', 'LineNo']);
|
|
46
46
|
expect(out).toEqual({ entityId: 'ent-lines', recordId: 'OrderID|11055||LineNo|3' });
|
|
47
47
|
});
|
|
@@ -49,13 +49,13 @@ describe('resolveIdentityByEmail — the configured identity entity can have ANY
|
|
|
49
49
|
it('stays anonymous when no row matches or a key column is null', async () => {
|
|
50
50
|
const provider = makeProvider({ ID: 'ent-users', PrimaryKeys: [{ Name: 'ID' }] });
|
|
51
51
|
runViewMock.mockResolvedValue({ Success: true, Results: [] });
|
|
52
|
-
expect(await
|
|
52
|
+
expect(await ResolveIdentityByEmail('none@y.com', user, provider)).toBeUndefined();
|
|
53
53
|
runViewMock.mockResolvedValue({ Success: true, Results: [{ ID: null }] });
|
|
54
|
-
expect(await
|
|
54
|
+
expect(await ResolveIdentityByEmail('null@y.com', user, provider)).toBeUndefined();
|
|
55
55
|
});
|
|
56
56
|
|
|
57
57
|
it('stays anonymous when the configured entity is not in metadata', async () => {
|
|
58
|
-
expect(await
|
|
58
|
+
expect(await ResolveIdentityByEmail('a@b.com', user, makeProvider(undefined), { entityName: 'Nope' })).toBeUndefined();
|
|
59
59
|
expect(runViewMock).not.toHaveBeenCalled();
|
|
60
60
|
});
|
|
61
61
|
});
|