@memberjunction/server 6.2.0-edge.6 → 6.2.0-edge.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +69 -0
- package/dist/.tsbuildinfo +1 -1
- package/dist/agentSessions/BridgeHostInstance.d.ts +8 -3
- package/dist/agentSessions/BridgeHostInstance.d.ts.map +1 -1
- package/dist/agentSessions/BridgeHostInstance.js +14 -4
- package/dist/agentSessions/BridgeHostInstance.js.map +1 -1
- package/dist/agentSessions/HandoffOfferExpiry.d.ts +10 -0
- package/dist/agentSessions/HandoffOfferExpiry.d.ts.map +1 -0
- package/dist/agentSessions/HandoffOfferExpiry.js +18 -0
- package/dist/agentSessions/HandoffOfferExpiry.js.map +1 -0
- package/dist/agentSessions/ReturningVisitorRecap.js +1 -0
- package/dist/agentSessions/ReturningVisitorRecap.js.map +1 -1
- package/dist/agentSessions/SessionJanitor.d.ts +31 -8
- package/dist/agentSessions/SessionJanitor.d.ts.map +1 -1
- package/dist/agentSessions/SessionJanitor.js +85 -41
- package/dist/agentSessions/SessionJanitor.js.map +1 -1
- package/dist/agentSessions/SessionManager.d.ts +14 -4
- package/dist/agentSessions/SessionManager.d.ts.map +1 -1
- package/dist/agentSessions/SessionManager.js +28 -4
- package/dist/agentSessions/SessionManager.js.map +1 -1
- package/dist/agentSessions/conversationBrowsers.d.ts +61 -0
- package/dist/agentSessions/conversationBrowsers.d.ts.map +1 -0
- package/dist/agentSessions/conversationBrowsers.js +118 -0
- package/dist/agentSessions/conversationBrowsers.js.map +1 -0
- package/dist/agentSessions/sessionDeadline.d.ts +43 -0
- package/dist/agentSessions/sessionDeadline.d.ts.map +1 -0
- package/dist/agentSessions/sessionDeadline.js +68 -0
- package/dist/agentSessions/sessionDeadline.js.map +1 -0
- package/dist/auth/APIKeyScopeAuth.d.ts.map +1 -1
- package/dist/auth/APIKeyScopeAuth.js +5 -4
- package/dist/auth/APIKeyScopeAuth.js.map +1 -1
- package/dist/auth/loginEmailResolution.d.ts +41 -0
- package/dist/auth/loginEmailResolution.d.ts.map +1 -0
- package/dist/auth/loginEmailResolution.js +47 -0
- package/dist/auth/loginEmailResolution.js.map +1 -0
- package/dist/auth/magicLink/MagicLinkKeys.d.ts +50 -2
- package/dist/auth/magicLink/MagicLinkKeys.d.ts.map +1 -1
- package/dist/auth/magicLink/MagicLinkKeys.js +129 -12
- package/dist/auth/magicLink/MagicLinkKeys.js.map +1 -1
- package/dist/auth/magicLink/MagicLinkRouter.d.ts +3 -0
- package/dist/auth/magicLink/MagicLinkRouter.d.ts.map +1 -1
- package/dist/auth/magicLink/MagicLinkRouter.js +23 -5
- package/dist/auth/magicLink/MagicLinkRouter.js.map +1 -1
- package/dist/auth/magicLink/MagicLinkService.d.ts +39 -5
- package/dist/auth/magicLink/MagicLinkService.d.ts.map +1 -1
- package/dist/auth/magicLink/MagicLinkService.js +221 -9
- package/dist/auth/magicLink/MagicLinkService.js.map +1 -1
- package/dist/auth/magicLink/index.d.ts +1 -1
- package/dist/auth/magicLink/index.d.ts.map +1 -1
- package/dist/auth/magicLink/index.js +1 -1
- package/dist/auth/magicLink/index.js.map +1 -1
- package/dist/auth/magicLink/redeemLanding.d.ts +2 -2
- package/dist/auth/magicLink/redeemLanding.d.ts.map +1 -1
- package/dist/auth/magicLink/redeemLanding.js +7 -4
- package/dist/auth/magicLink/redeemLanding.js.map +1 -1
- package/dist/auth/magicLink/types.d.ts +27 -3
- package/dist/auth/magicLink/types.d.ts.map +1 -1
- package/dist/auth/magicLink/types.js +0 -4
- package/dist/auth/magicLink/types.js.map +1 -1
- package/dist/auth/newUsers.d.ts.map +1 -1
- package/dist/auth/newUsers.js +1 -0
- package/dist/auth/newUsers.js.map +1 -1
- package/dist/backgroundSweepConfigUnits.d.ts +231 -0
- package/dist/backgroundSweepConfigUnits.d.ts.map +1 -0
- package/dist/backgroundSweepConfigUnits.js +180 -0
- package/dist/backgroundSweepConfigUnits.js.map +1 -0
- package/dist/broadcast/BaseCacheInvalidationBroadcastProvider.d.ts.map +1 -1
- package/dist/broadcast/BaseCacheInvalidationBroadcastProvider.js +2 -1
- package/dist/broadcast/BaseCacheInvalidationBroadcastProvider.js.map +1 -1
- package/dist/config.d.ts +906 -8
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +59 -0
- package/dist/config.js.map +1 -1
- package/dist/context.d.ts +2 -0
- package/dist/context.d.ts.map +1 -1
- package/dist/context.js +41 -12
- package/dist/context.js.map +1 -1
- package/dist/coreRoutes.d.ts +3 -1
- package/dist/coreRoutes.d.ts.map +1 -1
- package/dist/coreRoutes.js +6 -4
- package/dist/coreRoutes.js.map +1 -1
- package/dist/generated/graphql-schemas/__mj.d.ts +1072 -5
- package/dist/generated/graphql-schemas/__mj.d.ts.map +1 -1
- package/dist/generated/graphql-schemas/__mj.js +10174 -4618
- package/dist/generated/graphql-schemas/__mj.js.map +1 -1
- package/dist/generic/OrphanedConversationDetailReconciler.d.ts +11 -0
- package/dist/generic/OrphanedConversationDetailReconciler.d.ts.map +1 -1
- package/dist/generic/OrphanedConversationDetailReconciler.js +27 -1
- package/dist/generic/OrphanedConversationDetailReconciler.js.map +1 -1
- package/dist/generic/ResolverBase.d.ts +50 -5
- package/dist/generic/ResolverBase.d.ts.map +1 -1
- package/dist/generic/ResolverBase.js +329 -132
- package/dist/generic/ResolverBase.js.map +1 -1
- package/dist/generic/RunViewResolver.d.ts +4 -0
- package/dist/generic/RunViewResolver.d.ts.map +1 -1
- package/dist/generic/RunViewResolver.js +47 -7
- package/dist/generic/RunViewResolver.js.map +1 -1
- package/dist/globalCors.d.ts.map +1 -1
- package/dist/globalCors.js +1 -0
- package/dist/globalCors.js.map +1 -1
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +201 -43
- package/dist/index.js.map +1 -1
- package/dist/integration/ConnectionPause.d.ts +121 -0
- package/dist/integration/ConnectionPause.d.ts.map +1 -0
- package/dist/integration/ConnectionPause.js +183 -0
- package/dist/integration/ConnectionPause.js.map +1 -0
- package/dist/integration/CustomColumnPromoter.d.ts +72 -6
- package/dist/integration/CustomColumnPromoter.d.ts.map +1 -1
- package/dist/integration/CustomColumnPromoter.js +87 -46
- package/dist/integration/CustomColumnPromoter.js.map +1 -1
- package/dist/integration/EntityMapLifecycle.d.ts +78 -4
- package/dist/integration/EntityMapLifecycle.d.ts.map +1 -1
- package/dist/integration/EntityMapLifecycle.js +199 -5
- package/dist/integration/EntityMapLifecycle.js.map +1 -1
- package/dist/integration/InactiveRowWarnings.d.ts +31 -0
- package/dist/integration/InactiveRowWarnings.d.ts.map +1 -1
- package/dist/integration/InactiveRowWarnings.js +29 -0
- package/dist/integration/InactiveRowWarnings.js.map +1 -1
- package/dist/integration/ObjectNamesByID.d.ts +28 -0
- package/dist/integration/ObjectNamesByID.d.ts.map +1 -0
- package/dist/integration/ObjectNamesByID.js +87 -0
- package/dist/integration/ObjectNamesByID.js.map +1 -0
- package/dist/integration/PostRestartIntrospect.d.ts +26 -0
- package/dist/integration/PostRestartIntrospect.d.ts.map +1 -0
- package/dist/integration/PostRestartIntrospect.js +27 -0
- package/dist/integration/PostRestartIntrospect.js.map +1 -0
- package/dist/integration/RSUPostRestartProgress.d.ts +176 -0
- package/dist/integration/RSUPostRestartProgress.d.ts.map +1 -0
- package/dist/integration/RSUPostRestartProgress.js +323 -0
- package/dist/integration/RSUPostRestartProgress.js.map +1 -0
- package/dist/integration/RSUProgressBridge.d.ts +41 -2
- package/dist/integration/RSUProgressBridge.d.ts.map +1 -1
- package/dist/integration/RSUProgressBridge.js +84 -1
- package/dist/integration/RSUProgressBridge.js.map +1 -1
- package/dist/integration/RunArtifactAuthorization.d.ts +39 -0
- package/dist/integration/RunArtifactAuthorization.d.ts.map +1 -0
- package/dist/integration/RunArtifactAuthorization.js +45 -0
- package/dist/integration/RunArtifactAuthorization.js.map +1 -0
- package/dist/integration/SchemaRefreshLaunch.d.ts +9 -0
- package/dist/integration/SchemaRefreshLaunch.d.ts.map +1 -1
- package/dist/integration/SchemaRefreshLaunch.js +3 -2
- package/dist/integration/SchemaRefreshLaunch.js.map +1 -1
- package/dist/integration/ScopedFieldsForBuild.d.ts +46 -0
- package/dist/integration/ScopedFieldsForBuild.d.ts.map +1 -0
- package/dist/integration/ScopedFieldsForBuild.js +91 -0
- package/dist/integration/ScopedFieldsForBuild.js.map +1 -0
- package/dist/integration/TargetConfigGuard.d.ts +40 -0
- package/dist/integration/TargetConfigGuard.d.ts.map +1 -0
- package/dist/integration/TargetConfigGuard.js +40 -0
- package/dist/integration/TargetConfigGuard.js.map +1 -0
- package/dist/logging/StartupLogger.d.ts +50 -0
- package/dist/logging/StartupLogger.d.ts.map +1 -1
- package/dist/logging/StartupLogger.js +15 -22
- package/dist/logging/StartupLogger.js.map +1 -1
- package/dist/middleware/BaseServerMiddleware.d.ts +21 -3
- package/dist/middleware/BaseServerMiddleware.d.ts.map +1 -1
- package/dist/middleware/BaseServerMiddleware.js +4 -2
- package/dist/middleware/BaseServerMiddleware.js.map +1 -1
- package/dist/realtimeSessions/ownedRealtimeSession.d.ts.map +1 -1
- package/dist/realtimeSessions/ownedRealtimeSession.js +2 -3
- package/dist/realtimeSessions/ownedRealtimeSession.js.map +1 -1
- package/dist/realtimeSessions/sessionAccess.d.ts +15 -0
- package/dist/realtimeSessions/sessionAccess.d.ts.map +1 -1
- package/dist/realtimeSessions/sessionAccess.js +22 -0
- package/dist/realtimeSessions/sessionAccess.js.map +1 -1
- package/dist/realtimeWidget/WidgetSessionService.d.ts.map +1 -1
- package/dist/realtimeWidget/WidgetSessionService.js +2 -0
- package/dist/realtimeWidget/WidgetSessionService.js.map +1 -1
- package/dist/realtimeWidget/visitorIdentity.d.ts.map +1 -1
- package/dist/realtimeWidget/visitorIdentity.js +1 -0
- package/dist/realtimeWidget/visitorIdentity.js.map +1 -1
- package/dist/realtimeWidget/widgetCore.d.ts.map +1 -1
- package/dist/realtimeWidget/widgetCore.js +4 -7
- package/dist/realtimeWidget/widgetCore.js.map +1 -1
- package/dist/realtimeWidget/widgetGuestElevation.js +1 -0
- package/dist/realtimeWidget/widgetGuestElevation.js.map +1 -1
- package/dist/requestOrigin.d.ts +22 -0
- package/dist/requestOrigin.d.ts.map +1 -0
- package/dist/requestOrigin.js +60 -0
- package/dist/requestOrigin.js.map +1 -0
- package/dist/resolvers/AgentRunStatusPublisher.d.ts +10 -0
- package/dist/resolvers/AgentRunStatusPublisher.d.ts.map +1 -1
- package/dist/resolvers/AgentRunStatusPublisher.js +18 -2
- package/dist/resolvers/AgentRunStatusPublisher.js.map +1 -1
- package/dist/resolvers/AgentSessionResolver.d.ts.map +1 -1
- package/dist/resolvers/AgentSessionResolver.js +6 -2
- package/dist/resolvers/AgentSessionResolver.js.map +1 -1
- package/dist/resolvers/AutotagPipelineResolver.d.ts +1 -1
- package/dist/resolvers/AutotagPipelineResolver.d.ts.map +1 -1
- package/dist/resolvers/AutotagPipelineResolver.js +61 -16
- package/dist/resolvers/AutotagPipelineResolver.js.map +1 -1
- package/dist/resolvers/ConversationRunEventsResolver.d.ts.map +1 -1
- package/dist/resolvers/ConversationRunEventsResolver.js +2 -0
- package/dist/resolvers/ConversationRunEventsResolver.js.map +1 -1
- package/dist/resolvers/ExecuteRemoteOperationResolver.d.ts +6 -0
- package/dist/resolvers/ExecuteRemoteOperationResolver.d.ts.map +1 -1
- package/dist/resolvers/ExecuteRemoteOperationResolver.js +30 -7
- package/dist/resolvers/ExecuteRemoteOperationResolver.js.map +1 -1
- package/dist/resolvers/GetDataResolver.d.ts.map +1 -1
- package/dist/resolvers/GetDataResolver.js +34 -13
- package/dist/resolvers/GetDataResolver.js.map +1 -1
- package/dist/resolvers/InfoResolver.js +1 -1
- package/dist/resolvers/InfoResolver.js.map +1 -1
- package/dist/resolvers/IntegrationDiscoveryResolver.d.ts +401 -11
- package/dist/resolvers/IntegrationDiscoveryResolver.d.ts.map +1 -1
- package/dist/resolvers/IntegrationDiscoveryResolver.js +2690 -1305
- package/dist/resolvers/IntegrationDiscoveryResolver.js.map +1 -1
- package/dist/resolvers/MCPResolver.d.ts.map +1 -1
- package/dist/resolvers/MCPResolver.js +13 -2
- package/dist/resolvers/MCPResolver.js.map +1 -1
- package/dist/resolvers/QuerySystemUserResolver.d.ts +38 -1
- package/dist/resolvers/QuerySystemUserResolver.d.ts.map +1 -1
- package/dist/resolvers/QuerySystemUserResolver.js +181 -88
- package/dist/resolvers/QuerySystemUserResolver.js.map +1 -1
- package/dist/resolvers/RealtimeBridgeResolver.d.ts.map +1 -1
- package/dist/resolvers/RealtimeBridgeResolver.js +1 -0
- package/dist/resolvers/RealtimeBridgeResolver.js.map +1 -1
- package/dist/resolvers/RealtimeClientSessionResolver.d.ts +6 -0
- package/dist/resolvers/RealtimeClientSessionResolver.d.ts.map +1 -1
- package/dist/resolvers/RealtimeClientSessionResolver.js +36 -5
- package/dist/resolvers/RealtimeClientSessionResolver.js.map +1 -1
- package/dist/resolvers/RemoteBrowserActionResolver.d.ts +62 -10
- package/dist/resolvers/RemoteBrowserActionResolver.d.ts.map +1 -1
- package/dist/resolvers/RemoteBrowserActionResolver.js +217 -34
- package/dist/resolvers/RemoteBrowserActionResolver.js.map +1 -1
- package/dist/resolvers/RunAIAgentResolver.d.ts +24 -8
- package/dist/resolvers/RunAIAgentResolver.d.ts.map +1 -1
- package/dist/resolvers/RunAIAgentResolver.js +185 -67
- package/dist/resolvers/RunAIAgentResolver.js.map +1 -1
- package/dist/resolvers/RunAIPromptResolver.d.ts +5 -3
- package/dist/resolvers/RunAIPromptResolver.d.ts.map +1 -1
- package/dist/resolvers/RunAIPromptResolver.js +19 -18
- package/dist/resolvers/RunAIPromptResolver.js.map +1 -1
- package/dist/resolvers/RunTemplateResolver.d.ts.map +1 -1
- package/dist/resolvers/RunTemplateResolver.js +3 -1
- package/dist/resolvers/RunTemplateResolver.js.map +1 -1
- package/dist/resolvers/SqlLoggingConfigResolver.d.ts.map +1 -1
- package/dist/resolvers/SqlLoggingConfigResolver.js +7 -8
- package/dist/resolvers/SqlLoggingConfigResolver.js.map +1 -1
- package/dist/resolvers/TagGovernanceResolver.d.ts +12 -0
- package/dist/resolvers/TagGovernanceResolver.d.ts.map +1 -1
- package/dist/resolvers/TagGovernanceResolver.js +84 -0
- package/dist/resolvers/TagGovernanceResolver.js.map +1 -1
- package/dist/resolvers/TelemetryResolver.d.ts +13 -8
- package/dist/resolvers/TelemetryResolver.d.ts.map +1 -1
- package/dist/resolvers/TelemetryResolver.js +40 -9
- package/dist/resolvers/TelemetryResolver.js.map +1 -1
- package/dist/resolvers/meetingRecordingRegistration.d.ts.map +1 -1
- package/dist/resolvers/meetingRecordingRegistration.js +2 -0
- package/dist/resolvers/meetingRecordingRegistration.js.map +1 -1
- package/dist/resolvers/meetings/meetingMutations.d.ts.map +1 -1
- package/dist/resolvers/meetings/meetingMutations.js +34 -4
- package/dist/resolvers/meetings/meetingMutations.js.map +1 -1
- package/dist/rest/MediaAccessKeys.d.ts.map +1 -1
- package/dist/rest/MediaAccessKeys.js +6 -2
- package/dist/rest/MediaAccessKeys.js.map +1 -1
- package/dist/rest/OAuthCallbackHandler.d.ts.map +1 -1
- package/dist/rest/OAuthCallbackHandler.js +11 -5
- package/dist/rest/OAuthCallbackHandler.js.map +1 -1
- package/dist/rest/RealtimeRecordingHandler.d.ts +3 -3
- package/dist/rest/RealtimeRecordingHandler.d.ts.map +1 -1
- package/dist/rest/RealtimeRecordingHandler.js +2 -4
- package/dist/rest/RealtimeRecordingHandler.js.map +1 -1
- package/dist/rest/ViewOperationsHandler.d.ts.map +1 -1
- package/dist/rest/ViewOperationsHandler.js +2 -1
- package/dist/rest/ViewOperationsHandler.js.map +1 -1
- package/dist/rest/setupRESTEndpoints.d.ts +3 -1
- package/dist/rest/setupRESTEndpoints.d.ts.map +1 -1
- package/dist/rest/setupRESTEndpoints.js +11 -2
- package/dist/rest/setupRESTEndpoints.js.map +1 -1
- package/dist/services/DurableEntityActionTaskSubmitter.d.ts.map +1 -1
- package/dist/services/DurableEntityActionTaskSubmitter.js +6 -1
- package/dist/services/DurableEntityActionTaskSubmitter.js.map +1 -1
- package/dist/services/TaskGraphActionRunner.d.ts.map +1 -1
- package/dist/services/TaskGraphActionRunner.js +2 -26
- package/dist/services/TaskGraphActionRunner.js.map +1 -1
- package/dist/services/TaskGraphAgentRunner.d.ts +6 -0
- package/dist/services/TaskGraphAgentRunner.d.ts.map +1 -1
- package/dist/services/TaskGraphAgentRunner.js +38 -0
- package/dist/services/TaskGraphAgentRunner.js.map +1 -1
- package/dist/services/TaskGraphContinuationDeliverer.d.ts.map +1 -1
- package/dist/services/TaskGraphContinuationDeliverer.js +1 -0
- package/dist/services/TaskGraphContinuationDeliverer.js.map +1 -1
- package/dist/services/WorkQueueHostService.d.ts +2 -1
- package/dist/services/WorkQueueHostService.d.ts.map +1 -1
- package/dist/services/WorkQueueHostService.js +19 -4
- package/dist/services/WorkQueueHostService.js.map +1 -1
- package/dist/services/workQueueConfig.d.ts +2 -2
- package/dist/services/workQueueConfig.js +5 -5
- package/dist/services/workQueueConfig.js.map +1 -1
- package/dist/sharedCache.js +1 -1
- package/dist/sharedCache.js.map +1 -1
- package/dist/types.d.ts +17 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/util.d.ts.map +1 -1
- package/dist/util.js +55 -2
- package/dist/util.js.map +1 -1
- package/package.json +107 -100
- package/src/__tests__/APIKeyScopeAuth.test.ts +4 -8
- package/src/__tests__/ActiveOperations.test.ts +68 -0
- package/src/__tests__/AgentSessionResolver.ownership.test.ts +13 -0
- package/src/__tests__/AssertTargetConfigs.test.ts +75 -0
- package/src/__tests__/BatchApplyParity.test.ts +109 -0
- package/src/__tests__/BridgeHostInstance.test.ts +42 -0
- package/src/__tests__/CollectInactiveRowWarnings.test.ts +115 -0
- package/src/__tests__/ConnectionPause.test.ts +192 -0
- package/src/__tests__/ConnectionPauseResolver.test.ts +166 -0
- package/src/__tests__/ConnectionPauseSave.test.ts +143 -0
- package/src/__tests__/CustomColumnPromoterCompletion.test.ts +142 -0
- package/src/__tests__/DeleteConnectionRemovesPerConnectionCatalog.test.ts +51 -0
- package/src/__tests__/DetachedDiscoveryStart.test.ts +65 -0
- package/src/__tests__/DurableEntityActionTaskSubmitter.test.ts +73 -0
- package/src/__tests__/EntityMapLifecycle.rekey.test.ts +252 -0
- package/src/__tests__/ExecuteRemoteOperationResolver.test.ts +213 -0
- package/src/__tests__/HandoffOfferExpiry.test.ts +40 -0
- package/src/__tests__/IntegrationRunArtifactAuthorization.test.ts +126 -0
- package/src/__tests__/MagicLinkKeys.test.ts +102 -0
- package/src/__tests__/MediaAccessKeys.test.ts +57 -10
- package/src/__tests__/NewArrivalsDefaultDisabled.test.ts +50 -0
- package/src/__tests__/ObjectNamesByID.test.ts +176 -0
- package/src/__tests__/PostRestartMapsInCatalogScope.test.ts +146 -0
- package/src/__tests__/QuerySystemUserResolver.childRows.test.ts +80 -0
- package/src/__tests__/QuerySystemUserResolver.collision.test.ts +200 -0
- package/src/__tests__/QuerySystemUserResolver.permissions.test.ts +132 -0
- package/src/__tests__/RSUPostRestartProgress.test.ts +343 -0
- package/src/__tests__/RSUProgressBridge.test.ts +188 -5
- package/src/__tests__/RSUStartupSweepWiring.test.ts +30 -0
- package/src/__tests__/ReactivateRefreshMessage.test.ts +10 -3
- package/src/__tests__/RealtimeClientSessionResolver.test.ts +22 -0
- package/src/__tests__/{RefreshAdoptsNewColumns.test.ts → RefreshReportsNewColumns.test.ts} +25 -16
- package/src/__tests__/RemoteBrowserActionRecovery.test.ts +287 -0
- package/src/__tests__/RemoteBrowserActionResolver.goalRunLink.test.ts +3 -0
- package/src/__tests__/RemoteBrowserActionResolver.ownership.test.ts +6 -4
- package/src/__tests__/RemoteBrowserAudioStream.test.ts +3 -0
- package/src/__tests__/RemoteBrowserSnapshot.test.ts +3 -0
- package/src/__tests__/ResolverBase.clientOldValues.test.ts +327 -26
- package/src/__tests__/ResolverBase.diagnosticSenderRegistrations.test.ts +26 -0
- package/src/__tests__/ResolverBase.encryptedApiZeroReencrypt.test.ts +307 -0
- package/src/__tests__/ResolverBase.entityMessageRelay.test.ts +53 -2
- package/src/__tests__/ResolverBase.includeBinaryFields.test.ts +60 -0
- package/src/__tests__/RunAIAgentResolver.branch.test.ts +4 -0
- package/src/__tests__/RunAIAgentResolver.consultPredecessor.test.ts +289 -0
- package/src/__tests__/RunAIAgentResolver.handoff.test.ts +649 -0
- package/src/__tests__/RunAIAgentResolver.historyFrom.test.ts +2 -0
- package/src/__tests__/RunAIAgentResolver.runDataGuard.test.ts +2 -0
- package/src/__tests__/RunAIAgentResolver.stoppedPredecessor.test.ts +87 -12
- package/src/__tests__/RunAIAgentResolver.streaming.test.ts +28 -4
- package/src/__tests__/SchemaEvolutionCustomColumns.test.ts +119 -0
- package/src/__tests__/SchemaRefreshLaunch.test.ts +24 -2
- package/src/__tests__/ScopedFieldsForBuild.test.ts +192 -0
- package/src/__tests__/SessionJanitor.test.ts +195 -4
- package/src/__tests__/SessionManager.test.ts +69 -0
- package/src/__tests__/TagGovernanceAndAutotagResolvers.test.ts +261 -0
- package/src/__tests__/TagGovernanceResolver.test.ts +60 -2
- package/src/__tests__/TaskGraphActionRunner.durablePayload.test.ts +2 -2
- package/src/__tests__/TaskGraphAgentRunner.test.ts +272 -16
- package/src/__tests__/ViewOperationsHandler.test.ts +2 -5
- package/src/__tests__/WorkQueueHostService.test.ts +25 -7
- package/src/__tests__/backgroundSweepConfigUnits.test.ts +153 -0
- package/src/__tests__/contextRequestOrigin.test.ts +105 -0
- package/src/__tests__/conversationBrowsers.test.ts +62 -0
- package/src/__tests__/globalCors.test.ts +12 -0
- package/src/__tests__/magicLinkExtensionCapabilities.test.ts +413 -0
- package/src/__tests__/orphanedConversationDetailReconciler.start.test.ts +82 -0
- package/src/__tests__/ownedRealtimeSession.test.ts +14 -0
- package/src/__tests__/realtimeRecordingHandler.test.ts +7 -2
- package/src/__tests__/realtimeSessions/sessionAccess.test.ts +79 -1
- package/src/__tests__/requestOrigin.test.ts +104 -0
- package/src/__tests__/review-capture-valid-token-log.test.ts +98 -0
- package/src/__tests__/runStreams.fieldSecurity.test.ts +5 -5
- package/src/__tests__/securityHardeningPr6034.test.ts +120 -0
- package/src/__tests__/sessionDeadline.test.ts +51 -0
- package/src/__tests__/sharedCache.test.ts +11 -0
- package/src/__tests__/unifiedAuth.test.ts +73 -1
- package/src/agentSessions/BridgeHostInstance.ts +20 -5
- package/src/agentSessions/HandoffOfferExpiry.ts +18 -0
- package/src/agentSessions/ReturningVisitorRecap.ts +1 -0
- package/src/agentSessions/SessionJanitor.ts +92 -42
- package/src/agentSessions/SessionManager.ts +38 -8
- package/src/agentSessions/conversationBrowsers.ts +143 -0
- package/src/agentSessions/sessionDeadline.ts +81 -0
- package/src/auth/APIKeyScopeAuth.ts +5 -4
- package/src/auth/__tests__/loginEmailResolution.test.ts +67 -0
- package/src/auth/loginEmailResolution.ts +67 -0
- package/src/auth/magicLink/MagicLinkKeys.ts +146 -14
- package/src/auth/magicLink/MagicLinkRouter.ts +33 -6
- package/src/auth/magicLink/MagicLinkService.ts +250 -10
- package/src/auth/magicLink/index.ts +1 -1
- package/src/auth/magicLink/redeemLanding.ts +7 -4
- package/src/auth/magicLink/types.ts +28 -3
- package/src/auth/newUsers.ts +1 -0
- package/src/backgroundSweepConfigUnits.ts +213 -0
- package/src/broadcast/BaseCacheInvalidationBroadcastProvider.ts +2 -1
- package/src/config.ts +69 -0
- package/src/context.ts +46 -15
- package/src/coreRoutes.ts +14 -7
- package/src/generated/graphql-schemas/__mj.ts +10360 -6479
- package/src/generic/OrphanedConversationDetailReconciler.ts +32 -1
- package/src/generic/ResolverBase.ts +422 -143
- package/src/generic/RunViewResolver.ts +96 -49
- package/src/globalCors.ts +1 -0
- package/src/index.ts +246 -44
- package/src/integration/ConnectionPause.ts +222 -0
- package/src/integration/CustomColumnPromoter.ts +123 -53
- package/src/integration/EntityMapLifecycle.ts +229 -6
- package/src/integration/InactiveRowWarnings.ts +50 -0
- package/src/integration/ObjectNamesByID.ts +97 -0
- package/src/integration/PostRestartIntrospect.ts +49 -0
- package/src/integration/RSUPostRestartProgress.ts +359 -0
- package/src/integration/RSUProgressBridge.ts +85 -2
- package/src/integration/RunArtifactAuthorization.ts +65 -0
- package/src/integration/SchemaRefreshLaunch.ts +12 -2
- package/src/integration/ScopedFieldsForBuild.ts +113 -0
- package/src/integration/TargetConfigGuard.ts +71 -0
- package/src/logging/StartupLogger.ts +24 -0
- package/src/middleware/BaseServerMiddleware.ts +20 -3
- package/src/realtimeSessions/ownedRealtimeSession.ts +2 -6
- package/src/realtimeSessions/sessionAccess.ts +30 -0
- package/src/realtimeWidget/WidgetSessionService.ts +2 -0
- package/src/realtimeWidget/visitorIdentity.ts +1 -0
- package/src/realtimeWidget/widgetCore.ts +4 -8
- package/src/realtimeWidget/widgetGuestElevation.ts +1 -0
- package/src/requestOrigin.ts +64 -0
- package/src/resolvers/AgentRunStatusPublisher.ts +27 -2
- package/src/resolvers/AgentSessionResolver.ts +6 -2
- package/src/resolvers/AutotagPipelineResolver.ts +59 -14
- package/src/resolvers/ConversationRunEventsResolver.ts +2 -0
- package/src/resolvers/ExecuteRemoteOperationResolver.ts +41 -9
- package/src/resolvers/GetDataResolver.ts +35 -13
- package/src/resolvers/InfoResolver.ts +1 -1
- package/src/resolvers/IntegrationDiscoveryResolver.ts +1522 -203
- package/src/resolvers/MCPResolver.ts +15 -2
- package/src/resolvers/QuerySystemUserResolver.ts +211 -100
- package/src/resolvers/RealtimeBridgeResolver.ts +1 -0
- package/src/resolvers/RealtimeClientSessionResolver.ts +40 -7
- package/src/resolvers/RemoteBrowserActionResolver.ts +242 -32
- package/src/resolvers/RunAIAgentResolver.ts +265 -86
- package/src/resolvers/RunAIPromptResolver.ts +18 -18
- package/src/resolvers/RunTemplateResolver.ts +3 -1
- package/src/resolvers/SqlLoggingConfigResolver.ts +7 -9
- package/src/resolvers/TagGovernanceResolver.ts +65 -0
- package/src/resolvers/TelemetryResolver.ts +44 -9
- package/src/resolvers/__tests__/GetDataResolver.tokens.test.ts +52 -0
- package/src/resolvers/meetingRecordingRegistration.ts +2 -0
- package/src/resolvers/meetings/meetingMutations.ts +37 -7
- package/src/rest/MediaAccessKeys.ts +6 -2
- package/src/rest/OAuthCallbackHandler.ts +13 -5
- package/src/rest/RealtimeRecordingHandler.ts +5 -9
- package/src/rest/ViewOperationsHandler.ts +2 -1
- package/src/rest/setupRESTEndpoints.ts +11 -2
- package/src/services/DurableEntityActionTaskSubmitter.ts +10 -2
- package/src/services/TaskGraphActionRunner.ts +2 -23
- package/src/services/TaskGraphAgentRunner.ts +43 -0
- package/src/services/TaskGraphContinuationDeliverer.ts +1 -0
- package/src/services/WorkQueueHostService.ts +26 -4
- package/src/services/workQueueConfig.ts +5 -5
- package/src/sharedCache.ts +1 -1
- package/src/types.ts +18 -0
- package/src/util.ts +55 -3
- package/src/services/TaskOrchestration-Integration.md +0 -188
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* An apply whose selected objects all drop out of buildTargetConfigs must fail, not run RSU on
|
|
3
|
+
* nothing (PropFuel, 2026-10-29). A selection that produces even one table must still apply.
|
|
4
|
+
*/
|
|
5
|
+
import { describe, it, expect } from 'vitest';
|
|
6
|
+
import { readFileSync } from 'node:fs';
|
|
7
|
+
import { join } from 'node:path';
|
|
8
|
+
import type { TargetTableConfig } from '@memberjunction/integration-schema-builder';
|
|
9
|
+
import { AssertTargetConfigs, NewTargetConfigDrops, SampleNames } from '../integration/TargetConfigGuard.js';
|
|
10
|
+
|
|
11
|
+
const selected = [{ SourceObjectName: 'clicks' }, { SourceObjectName: 'sends' }, { SourceObjectName: 'opens' }];
|
|
12
|
+
const oneTable = [{ SchemaName: 'propfuel', TableName: 'clicks' } as TargetTableConfig];
|
|
13
|
+
|
|
14
|
+
describe('AssertTargetConfigs', () => {
|
|
15
|
+
it('refuses a selection none of whose objects produced a table, naming what was asked for', () => {
|
|
16
|
+
expect(() => AssertTargetConfigs(selected, [], 'PropFuel')).toThrow(
|
|
17
|
+
/NO_TARGET_TABLES: none of the 3 selected object\(s\) for PropFuel produced a target table \(clicks, sends, opens\)/
|
|
18
|
+
);
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
it('names why each object was dropped, so a caller does not need the server log', () => {
|
|
22
|
+
const drops = NewTargetConfigDrops();
|
|
23
|
+
drops.NotInSchema.push('clicks', 'sends');
|
|
24
|
+
drops.NoPrimaryKey.push('opens');
|
|
25
|
+
expect(() => AssertTargetConfigs(selected, [], 'PropFuel', drops)).toThrow(
|
|
26
|
+
'Dropped as not in the source schema: clicks, sends; no primary key: opens.'
|
|
27
|
+
);
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it('says nothing was applied by default, and what was already committed when the caller says so', () => {
|
|
31
|
+
expect(() => AssertTargetConfigs(selected, [], 'PropFuel')).toThrow('Nothing was applied.');
|
|
32
|
+
expect(() => AssertTargetConfigs(selected, [], 'PropFuel', undefined, 'The catalog had already been saved.'))
|
|
33
|
+
.toThrow(/The catalog had already been saved\. Re-run discovery/);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it('caps the names it lists, so a large selection cannot make a multi-KB error', () => {
|
|
37
|
+
const many = Array.from({ length: 168 }, (_v, i) => ({ SourceObjectName: `obj${i}` }));
|
|
38
|
+
expect(() => AssertTargetConfigs(many, [], 'NetForum')).toThrow('(obj0, obj1, obj2, obj3, obj4 and 163 more)');
|
|
39
|
+
expect(SampleNames(['a', 'b'])).toBe('a, b');
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it('lets a selection through when at least one object produced a table', () => {
|
|
43
|
+
expect(() => AssertTargetConfigs(selected, oneTable, 'PropFuel')).not.toThrow();
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it('lets an empty selection through — there was nothing to refuse', () => {
|
|
47
|
+
expect(() => AssertTargetConfigs([], [], 'PropFuel')).not.toThrow();
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Read from source: the call sites are resolver mutations that need a provider, a connector and an
|
|
53
|
+
* RSU pipeline to run.
|
|
54
|
+
*/
|
|
55
|
+
describe('the guard\'s callers', () => {
|
|
56
|
+
const SRC = readFileSync(join(__dirname, '..', 'resolvers', 'IntegrationDiscoveryResolver.ts'), 'utf-8');
|
|
57
|
+
|
|
58
|
+
it('IntegrationApplySchema filters on names whose casing resolveObjectInputs has corrected', () => {
|
|
59
|
+
// A filter built from the raw input is case-sensitive: `Clicks` for a stored `clicks` selected
|
|
60
|
+
// nothing, and the guard then told the caller to re-run discovery, which cannot fix casing.
|
|
61
|
+
const start = SRC.indexOf('async IntegrationApplySchema(');
|
|
62
|
+
const body = SRC.slice(start, SRC.indexOf('AssertTargetConfigs(', start));
|
|
63
|
+
const resolve = body.indexOf('await this.resolveObjectInputs(objects, sourceSchema, user);');
|
|
64
|
+
const names = body.indexOf('const resolvedNames = new Set(objects.map(o => o.SourceObjectName));');
|
|
65
|
+
expect(resolve).toBeGreaterThan(-1);
|
|
66
|
+
expect(names).toBeGreaterThan(resolve);
|
|
67
|
+
expect(body).toMatch(/sourceSchema\.Objects\.filter\(o => resolvedNames\.has\(o\.ExternalName\)\)/);
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it('every call passes the drop reasons, and schema evolution says what it had already committed', () => {
|
|
71
|
+
const calls = [...SRC.matchAll(/AssertTargetConfigs\(objects, targetConfigs, companyIntegration\.Integration, drops([\s\S]*?)\);/g)];
|
|
72
|
+
expect(calls).toHaveLength(3);
|
|
73
|
+
expect(calls.filter(c => /already been saved/.test(c[1]))).toHaveLength(1);
|
|
74
|
+
});
|
|
75
|
+
});
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The two apply paths must agree, because the shared connector surface collapses onto the batch
|
|
3
|
+
* one. Two defects made them differ, and both are invisible until a surface migrates:
|
|
4
|
+
*
|
|
5
|
+
* - MJ-APPLY-12: the batch never made the first-apply full-sync determination, and its input
|
|
6
|
+
* declared `defaultValue: false` for FullSync, which made the null-coalesce dead code. A
|
|
7
|
+
* re-apply the caller left unspecified therefore ran INCREMENTAL against objects whose
|
|
8
|
+
* watermark the schema change had just reset. everything.txt: "for the very first RSU of a
|
|
9
|
+
* connector, the sync afterwards should be chosen to be full/incremental, but always full
|
|
10
|
+
* generally as the default."
|
|
11
|
+
* - MJ-APPLY-13: the batch's skipRestart branch never applied remove-as-disable, so
|
|
12
|
+
* UnselectedAction was a silent no-op and a deselected table kept syncing.
|
|
13
|
+
*
|
|
14
|
+
* Read from source: both live in a resolver that needs a provider, a connector and an RSU
|
|
15
|
+
* pipeline to invoke, and the property under test is a one-line policy, not a behaviour worth
|
|
16
|
+
* standing all that up to observe.
|
|
17
|
+
*/
|
|
18
|
+
import { describe, it, expect } from 'vitest';
|
|
19
|
+
import { readFileSync } from 'node:fs';
|
|
20
|
+
import { join } from 'node:path';
|
|
21
|
+
|
|
22
|
+
const SRC = readFileSync(
|
|
23
|
+
join(__dirname, '..', 'resolvers', 'IntegrationDiscoveryResolver.ts'),
|
|
24
|
+
'utf-8',
|
|
25
|
+
);
|
|
26
|
+
|
|
27
|
+
/** The body of a named input class, up to its closing brace. */
|
|
28
|
+
function classBody(name: string): string {
|
|
29
|
+
const start = SRC.indexOf(`class ${name} {`);
|
|
30
|
+
expect(start, `${name} not found`).toBeGreaterThan(-1);
|
|
31
|
+
const rest = SRC.slice(start);
|
|
32
|
+
const end = rest.indexOf('\n}');
|
|
33
|
+
return end === -1 ? rest : rest.slice(0, end);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/** The body of a named resolver method, up to the next method at the same indent. */
|
|
37
|
+
function methodBody(name: string): string {
|
|
38
|
+
const start = SRC.indexOf(`async ${name}(`);
|
|
39
|
+
expect(start, `${name} not found`).toBeGreaterThan(-1);
|
|
40
|
+
const rest = SRC.slice(start);
|
|
41
|
+
const end = rest.search(/\n @(Query|Mutation)\(/);
|
|
42
|
+
return end === -1 ? rest : rest.slice(0, end);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
describe('MJ-APPLY-12 first-apply full sync', () => {
|
|
46
|
+
it('the batch resolves FullSync from prior maps, not from a bare false', () => {
|
|
47
|
+
const body = methodBody('IntegrationApplyAllBatch');
|
|
48
|
+
expect(body).toMatch(/const fullSync = input\.FullSync\s*\?\?\s*!hadPriorMaps;/);
|
|
49
|
+
expect(body).toMatch(/FullSync:\s*fullSync,/);
|
|
50
|
+
expect(body).not.toMatch(/input\.FullSync\s*\?\?\s*false/);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it('ApplyAllBatchInput.FullSync has NO defaultValue, or the coalesce is dead again', () => {
|
|
54
|
+
// Scope to the CLASS. There are several `FullSync?: boolean` fields in this file and a bare
|
|
55
|
+
// find() picks ApplyAllInput's, which would pass no matter what the batch input declared.
|
|
56
|
+
const body = classBody('ApplyAllBatchInput');
|
|
57
|
+
const line = body.split('\n').find(l => l.includes('FullSync?: boolean') && l.includes('@Field'));
|
|
58
|
+
expect(line, 'ApplyAllBatchInput.FullSync not found').toBeDefined();
|
|
59
|
+
expect(line!).not.toMatch(/defaultValue/);
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it('the single-connector path still does the same thing', () => {
|
|
63
|
+
expect(methodBody('IntegrationApplyAll')).toMatch(/input\.FullSync\s*\?\?\s*!hadPriorMaps/);
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it('the prior-maps read UUID-validates the connection id, like every other caller-supplied id', () => {
|
|
67
|
+
// The batch takes its CompanyIntegrationIDs from the caller. The rest of this resolver refuses a
|
|
68
|
+
// non-UUID id with sqlUuid before it reaches an ExtraFilter; quote-doubling is not that check.
|
|
69
|
+
const body = methodBody('IntegrationApplyAllBatch');
|
|
70
|
+
expect(body).toMatch(/CompanyIntegrationID='\$\{sqlUuid\(connInput\.CompanyIntegrationID, 'CompanyIntegrationID'\)\}'/);
|
|
71
|
+
expect(body).not.toMatch(/connInput\.CompanyIntegrationID\.replace\(/);
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
it('the skipRestart branch starts its sync with the RESOLVED FullSync, as the restart path queues it', () => {
|
|
75
|
+
// The restart path carries the coalesced value in its PendingWork; the skipRestart branch starts
|
|
76
|
+
// the sync itself. Passing the raw input there means an omitted FullSync is undefined, which
|
|
77
|
+
// startSyncAfterApply reads as incremental — against tables the apply just created.
|
|
78
|
+
const body = methodBody('IntegrationApplyAllBatch');
|
|
79
|
+
const call = body.match(/this\.startSyncAfterApply\(build\.connInput\.CompanyIntegrationID,[^)]*\)/);
|
|
80
|
+
expect(call, 'skipRestart sync start not found').not.toBeNull();
|
|
81
|
+
expect(call![0]).not.toMatch(/input\.FullSync/);
|
|
82
|
+
expect(call![0]).toMatch(/build\.fullSync\)$/);
|
|
83
|
+
expect(body).toMatch(/^\s*fullSync,$/m);
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
it('the prior-maps probe skips the row count, as the single path does', () => {
|
|
87
|
+
// MaxRows: 1 is an existence probe; without SkipTotalRowCount the provider follows every hit
|
|
88
|
+
// with a COUNT(*) over the connection's maps.
|
|
89
|
+
const body = methodBody('IntegrationApplyAllBatch');
|
|
90
|
+
const probe = body.slice(body.indexOf('const priorMapsResult'), body.indexOf('const hadPriorMaps'));
|
|
91
|
+
expect(probe).toMatch(/MaxRows:\s*1,/);
|
|
92
|
+
expect(probe).toMatch(/SkipTotalRowCount:\s*true/);
|
|
93
|
+
});
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
describe('MJ-APPLY-13 remove-as-disable', () => {
|
|
97
|
+
it('the batch skipRestart branch disables unselected maps', () => {
|
|
98
|
+
const body = methodBody('IntegrationApplyAllBatch');
|
|
99
|
+
expect(body).toMatch(/UnselectedAction\s*\?\?\s*'disable'\)\s*!==\s*'ignore'/);
|
|
100
|
+
expect(body).toMatch(/DisableUnselectedEntityMaps\(/);
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
it('and honours the ignore opt-out rather than always disabling', () => {
|
|
104
|
+
const body = methodBody('IntegrationApplyAllBatch');
|
|
105
|
+
const at = body.indexOf('DisableUnselectedEntityMaps(');
|
|
106
|
+
// the guard must precede the call, not follow it
|
|
107
|
+
expect(body.lastIndexOf("!== 'ignore'", at)).toBeGreaterThan(-1);
|
|
108
|
+
});
|
|
109
|
+
});
|
|
@@ -230,3 +230,45 @@ describe('StartBridgeOrphanReconciliation', () => {
|
|
|
230
230
|
expect(runView.mock.calls[0][1]).toBe(user);
|
|
231
231
|
});
|
|
232
232
|
});
|
|
233
|
+
|
|
234
|
+
describe('StartBridgeOrphanReconciliation with agentSessions.bridgeReconciler settings', () => {
|
|
235
|
+
const HOUR = 60 * 60_000;
|
|
236
|
+
afterEach(() => {
|
|
237
|
+
vi.restoreAllMocks();
|
|
238
|
+
vi.useRealTimers();
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
it('enabled: reconciles now, then on the configured interval', async () => {
|
|
242
|
+
vi.useFakeTimers();
|
|
243
|
+
const reconcile = vi.spyOn(AIBridgeEngine.Instance, 'ReconcileOrphans').mockResolvedValue(0);
|
|
244
|
+
const { provider } = bridgeTable([]);
|
|
245
|
+
|
|
246
|
+
StartBridgeOrphanReconciliation(provider, user, { enabled: true, sweepIntervalMs: 120_000 });
|
|
247
|
+
await vi.advanceTimersByTimeAsync(6 * 60_000);
|
|
248
|
+
|
|
249
|
+
expect(reconcile).toHaveBeenCalledTimes(4); // now + every 2 minutes
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
it('disabled: reconciles once now and never again', async () => {
|
|
253
|
+
vi.useFakeTimers();
|
|
254
|
+
const reconcile = vi.spyOn(AIBridgeEngine.Instance, 'ReconcileOrphans').mockResolvedValue(0);
|
|
255
|
+
const { provider } = bridgeTable([]);
|
|
256
|
+
|
|
257
|
+
StartBridgeOrphanReconciliation(provider, user, { enabled: false, sweepIntervalMs: 600_000 });
|
|
258
|
+
await vi.advanceTimersByTimeAsync(HOUR);
|
|
259
|
+
|
|
260
|
+
expect(reconcile).toHaveBeenCalledTimes(1);
|
|
261
|
+
expect(reconcile.mock.calls[0][0]).toBe(user);
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
it('with no settings keeps the shipped 10-minute repeat', async () => {
|
|
265
|
+
vi.useFakeTimers();
|
|
266
|
+
const reconcile = vi.spyOn(AIBridgeEngine.Instance, 'ReconcileOrphans').mockResolvedValue(0);
|
|
267
|
+
const { provider } = bridgeTable([]);
|
|
268
|
+
|
|
269
|
+
StartBridgeOrphanReconciliation(provider, user);
|
|
270
|
+
await vi.advanceTimersByTimeAsync(HOUR);
|
|
271
|
+
|
|
272
|
+
expect(reconcile).toHaveBeenCalledTimes(7);
|
|
273
|
+
});
|
|
274
|
+
});
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The apply's declared-but-inactive-row warnings have to survive the path that PREFETCHES its schema.
|
|
3
|
+
*
|
|
4
|
+
* The collection used to live inside the resolver's schema REBUILD, so the batch apply lost it twice:
|
|
5
|
+
* its own rebuild passed no collector, and handing the result down to the shared builder took the
|
|
6
|
+
* `prefetchedSourceSchema` branch, which skips the rebuild entirely. An operator whose selected table
|
|
7
|
+
* came back without a column therefore had nothing to read on the batch path while the single path
|
|
8
|
+
* explained it.
|
|
9
|
+
*
|
|
10
|
+
* The warnings never depended on the rebuild — they are the catalog's own `Status` rows — so the
|
|
11
|
+
* gathering is a function of (catalog, integration, requested names). These tests pin it against a
|
|
12
|
+
* literal catalog, which is also the only way to test it at all: the resolver imports schema-builder
|
|
13
|
+
* and schema-engine and cannot be loaded in a unit test.
|
|
14
|
+
*/
|
|
15
|
+
import { describe, it, expect } from 'vitest';
|
|
16
|
+
import { readFileSync } from 'node:fs';
|
|
17
|
+
import { join } from 'node:path';
|
|
18
|
+
import { CollectInactiveRowWarnings } from '../integration/InactiveRowWarnings.js';
|
|
19
|
+
import type { CatalogStatusReader } from '../integration/InactiveRowWarnings.js';
|
|
20
|
+
|
|
21
|
+
type Row = { ID: string; Name: string; Status: string | null };
|
|
22
|
+
|
|
23
|
+
/** A catalog of objects, each with its fields. `Status` is the whole question. */
|
|
24
|
+
function catalog(objects: Array<Row & { Fields: Array<{ Name: string; Status: string | null }> }>): CatalogStatusReader {
|
|
25
|
+
return {
|
|
26
|
+
GetActiveIntegrationObjects: () => objects.filter(o => o.Status === 'Active'),
|
|
27
|
+
GetIntegrationObjectsByIntegrationID: () => objects,
|
|
28
|
+
GetIntegrationObjectFields: (objectID: string) => objects.find(o => o.ID === objectID)?.Fields ?? [],
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const contacts = {
|
|
33
|
+
ID: 'io-1', Name: 'Contacts', Status: 'Active',
|
|
34
|
+
Fields: [
|
|
35
|
+
{ Name: 'ID', Status: 'Active' },
|
|
36
|
+
{ Name: 'MiddleName', Status: 'Disabled' },
|
|
37
|
+
],
|
|
38
|
+
};
|
|
39
|
+
const invoices = { ID: 'io-2', Name: 'Invoices', Status: 'Disabled', Fields: [{ Name: 'ID', Status: 'Active' }] };
|
|
40
|
+
|
|
41
|
+
describe('CollectInactiveRowWarnings', () => {
|
|
42
|
+
it('names a deactivated FIELD of an object the apply does materialize', () => {
|
|
43
|
+
const warnings = CollectInactiveRowWarnings(catalog([contacts]), 'int-1', ['Contacts']);
|
|
44
|
+
expect(warnings).toHaveLength(1);
|
|
45
|
+
expect(warnings[0]).toContain('Contacts:');
|
|
46
|
+
expect(warnings[0]).toContain('MiddleName (Disabled)');
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it('reports a REQUESTED object that is not Active — the caller named it and got nothing', () => {
|
|
50
|
+
const warnings = CollectInactiveRowWarnings(catalog([contacts, invoices]), 'int-1', ['Contacts', 'Invoices']);
|
|
51
|
+
expect(warnings.some(w => w.includes('Invoices (Disabled)'))).toBe(true);
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it('matches requested names case-insensitively, the way the apply resolves them', () => {
|
|
55
|
+
const warnings = CollectInactiveRowWarnings(catalog([contacts]), 'int-1', ['contacts']);
|
|
56
|
+
expect(warnings[0]).toContain('MiddleName (Disabled)');
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it('reads fields ONLY for the objects in scope', () => {
|
|
60
|
+
// An out-of-scope object's own dropped fields are not this apply's business, and on a large
|
|
61
|
+
// catalog reporting them would bury the one line that matters.
|
|
62
|
+
const other = {
|
|
63
|
+
ID: 'io-3', Name: 'Events', Status: 'Active',
|
|
64
|
+
Fields: [{ Name: 'Venue', Status: 'Disabled' }],
|
|
65
|
+
};
|
|
66
|
+
const warnings = CollectInactiveRowWarnings(catalog([contacts, other]), 'int-1', ['Contacts']);
|
|
67
|
+
expect(warnings.join(' ')).not.toContain('Venue');
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it('stays quiet about deactivated OBJECTS when nothing was requested by name', () => {
|
|
71
|
+
// The unfiltered apply. Hundreds of deactivated objects are the normal state of a large
|
|
72
|
+
// catalog; the field warnings for what IS materialized still come through.
|
|
73
|
+
const warnings = CollectInactiveRowWarnings(catalog([contacts, invoices]), 'int-1', []);
|
|
74
|
+
expect(warnings.join(' ')).not.toContain('Invoices');
|
|
75
|
+
expect(warnings.join(' ')).toContain('MiddleName (Disabled)');
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
it('says nothing when everything in scope is Active', () => {
|
|
79
|
+
const clean = {
|
|
80
|
+
ID: 'io-9', Name: 'Clean', Status: 'Active',
|
|
81
|
+
Fields: [{ Name: 'ID', Status: 'Active' }],
|
|
82
|
+
};
|
|
83
|
+
expect(CollectInactiveRowWarnings(catalog([clean]), 'int-1', ['Clean'])).toEqual([]);
|
|
84
|
+
});
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Under a per-connection catalog the engine answers [] for a connection's fields during an apply, so
|
|
89
|
+
* every caller has to hand the helper the fields it loaded itself, or field-level warnings vanish.
|
|
90
|
+
* Read from source: the callers are resolver mutations that need a provider and a pipeline to run.
|
|
91
|
+
*/
|
|
92
|
+
describe('every inactive-row warning caller passes the connection\'s own fields', () => {
|
|
93
|
+
const SRC = readFileSync(join(__dirname, '..', 'resolvers', 'IntegrationDiscoveryResolver.ts'), 'utf-8');
|
|
94
|
+
const calls = [...SRC.matchAll(/this\.collectInactiveRowWarnings\(([\s\S]*?)\)\)?;/g)].map(m => m[1]);
|
|
95
|
+
|
|
96
|
+
it('finds the callers', () => {
|
|
97
|
+
expect(calls.length).toBeGreaterThanOrEqual(3);
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
it('passes scopedFields at every call', () => {
|
|
101
|
+
for (const args of calls) expect(args).toMatch(/scopedFields\s*,?\s*$/);
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
it('loads the fields BEFORE the batch branch, so the prefetched path has them too', () => {
|
|
105
|
+
const at = SRC.indexOf('if (prefetchedSourceSchema) {');
|
|
106
|
+
const load = SRC.lastIndexOf('const scopedFields = await LoadScopedFieldsForBuild(', at);
|
|
107
|
+
expect(at).toBeGreaterThan(-1);
|
|
108
|
+
expect(load).toBeGreaterThan(-1);
|
|
109
|
+
expect(at - load).toBeLessThan(400);
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
it('refreshes the catalog caches through the provider the mutation already holds', () => {
|
|
113
|
+
expect(SRC).not.toMatch(/refreshIntegrationCatalogCaches\(user\);/);
|
|
114
|
+
});
|
|
115
|
+
});
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* everything.txt C6: pausing a connection must actually pause it — and resuming must put back
|
|
3
|
+
* what pause took, not "everything".
|
|
4
|
+
*
|
|
5
|
+
* Each test below pins a way this went wrong or would go wrong. The two that matter most are
|
|
6
|
+
* `DecidePauseWrite` (a second pause must not erase the first pause's record) and the two guards
|
|
7
|
+
* in `DecideSchedulesToResume` (never resurrect a schedule the operator turned off themselves).
|
|
8
|
+
*/
|
|
9
|
+
import { describe, it, expect } from 'vitest';
|
|
10
|
+
import {
|
|
11
|
+
DecideSchedulesToPause,
|
|
12
|
+
DecidePauseWrite,
|
|
13
|
+
DecideSchedulesToResume,
|
|
14
|
+
ReadPausedSchedules,
|
|
15
|
+
WritePausedSchedules,
|
|
16
|
+
DescribeCancelScope,
|
|
17
|
+
DescribeCancelOutcome,
|
|
18
|
+
DescribePauseOutcome,
|
|
19
|
+
PAUSED_SCHEDULES_KEY,
|
|
20
|
+
type ScheduleJobState,
|
|
21
|
+
type PausedScheduleRecord,
|
|
22
|
+
} from '../integration/ConnectionPause.js';
|
|
23
|
+
|
|
24
|
+
const job = (ID: string, Status: string | null, Kind: 'sync' | 'discovery' = 'sync'): ScheduleJobState =>
|
|
25
|
+
({ ID, Status, Kind });
|
|
26
|
+
|
|
27
|
+
describe('DecideSchedulesToPause', () => {
|
|
28
|
+
it('moves only the Active jobs', () => {
|
|
29
|
+
const out = DecideSchedulesToPause([job('a', 'Active'), job('b', 'Paused', 'discovery'), job('c', 'Active', 'discovery')]);
|
|
30
|
+
expect(out.map(r => r.ID)).toEqual(['a', 'c']);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it('leaves an already-Paused job out of the record, so resume cannot turn it on', () => {
|
|
34
|
+
// The whole point: 'b' was off before the pause. If it appeared here, resume would
|
|
35
|
+
// silently start a schedule the operator had deliberately stopped.
|
|
36
|
+
const out = DecideSchedulesToPause([job('b', 'Paused')]);
|
|
37
|
+
expect(out).toEqual([]);
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
it('ignores statuses that are neither Active nor Paused', () => {
|
|
41
|
+
expect(DecideSchedulesToPause([job('x', 'Disabled'), job('y', null)])).toEqual([]);
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
it('carries the kind through for the operator message', () => {
|
|
45
|
+
expect(DecideSchedulesToPause([job('a', 'Active', 'discovery')])).toEqual([{ ID: 'a', Kind: 'discovery' }]);
|
|
46
|
+
});
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
describe('DecidePauseWrite — the second pause must not erase the first', () => {
|
|
50
|
+
it('writes the record on a first pause', () => {
|
|
51
|
+
expect(DecidePauseWrite([], [{ ID: 'a', Kind: 'sync' }])).toEqual([{ ID: 'a', Kind: 'sync' }]);
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it('REFUSES to overwrite a stored record with the empty set a re-pause produces', () => {
|
|
55
|
+
// Deactivate an already-deactivated connection: every job is Paused, so nothing is
|
|
56
|
+
// "moved" and newlyPaused is []. Writing that would lose the record and strand both
|
|
57
|
+
// schedules off forever, with no error anywhere.
|
|
58
|
+
const stored: PausedScheduleRecord[] = [{ ID: 'a', Kind: 'sync' }, { ID: 'b', Kind: 'discovery' }];
|
|
59
|
+
expect(DecidePauseWrite(stored, [])).toBeNull();
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it('adds what a re-pause DID move to the stored record, keeping the stored entries as they were', () => {
|
|
63
|
+
// A schedule created or switched back on while paused, or a record a resume could not
|
|
64
|
+
// clear. Dropping the newly paused job would leave it paused with nothing to restore it.
|
|
65
|
+
const stored: PausedScheduleRecord[] = [{ ID: 'a', Kind: 'sync' }];
|
|
66
|
+
expect(DecidePauseWrite(stored, [{ ID: 'c', Kind: 'discovery' }])).toEqual([
|
|
67
|
+
{ ID: 'a', Kind: 'sync' },
|
|
68
|
+
{ ID: 'c', Kind: 'discovery' },
|
|
69
|
+
]);
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it('writes nothing when everything the re-pause moved is already recorded, whatever the id casing', () => {
|
|
73
|
+
const stored: PausedScheduleRecord[] = [{ ID: 'abc-1', Kind: 'sync' }];
|
|
74
|
+
expect(DecidePauseWrite(stored, [{ ID: 'ABC-1', Kind: 'sync' }])).toBeNull();
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it('writes nothing when there is neither a stored record nor anything moved', () => {
|
|
78
|
+
expect(DecidePauseWrite([], [])).toBeNull();
|
|
79
|
+
});
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
describe('DecideSchedulesToResume', () => {
|
|
83
|
+
it('restores exactly the recorded jobs', () => {
|
|
84
|
+
const stored: PausedScheduleRecord[] = [{ ID: 'a', Kind: 'sync' }];
|
|
85
|
+
expect(DecideSchedulesToResume(stored, [job('a', 'Paused'), job('b', 'Paused')])).toEqual(['a']);
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
it('never touches a job that was NOT recorded, however it looks now', () => {
|
|
89
|
+
// 'b' is Paused and looks identical to 'a'. Only the record distinguishes them.
|
|
90
|
+
const out = DecideSchedulesToResume([{ ID: 'a', Kind: 'sync' }], [job('a', 'Paused'), job('b', 'Paused')]);
|
|
91
|
+
expect(out).not.toContain('b');
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
it('does not resurrect a recorded job that has since been deleted', () => {
|
|
95
|
+
expect(DecideSchedulesToResume([{ ID: 'gone', Kind: 'sync' }], [job('a', 'Paused')])).toEqual([]);
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
it('does not rewrite a recorded job someone already set back to Active', () => {
|
|
99
|
+
expect(DecideSchedulesToResume([{ ID: 'a', Kind: 'sync' }], [job('a', 'Active')])).toEqual([]);
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
it('returns nothing when the record is empty', () => {
|
|
103
|
+
expect(DecideSchedulesToResume([], [job('a', 'Paused')])).toEqual([]);
|
|
104
|
+
});
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
describe('Configuration round-trip', () => {
|
|
108
|
+
it('preserves unrelated keys when writing', () => {
|
|
109
|
+
const cfg = JSON.stringify({ discoveryMaxRecords: 50, catalogSource: 'perConnection' });
|
|
110
|
+
const out = JSON.parse(WritePausedSchedules(cfg, [{ ID: 'a', Kind: 'sync' }]));
|
|
111
|
+
expect(out.discoveryMaxRecords).toBe(50);
|
|
112
|
+
expect(out.catalogSource).toBe('perConnection');
|
|
113
|
+
expect(out[PAUSED_SCHEDULES_KEY]).toEqual([{ ID: 'a', Kind: 'sync' }]);
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
it('removes the key entirely when the record is cleared, rather than leaving []', () => {
|
|
117
|
+
const cfg = WritePausedSchedules(JSON.stringify({ a: 1 }), [{ ID: 'x', Kind: 'sync' }]);
|
|
118
|
+
const cleared = JSON.parse(WritePausedSchedules(cfg, []));
|
|
119
|
+
expect(PAUSED_SCHEDULES_KEY in cleared).toBe(false);
|
|
120
|
+
expect(cleared.a).toBe(1);
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
it('round-trips', () => {
|
|
124
|
+
const rec: PausedScheduleRecord[] = [{ ID: 'a', Kind: 'sync' }, { ID: 'b', Kind: 'discovery' }];
|
|
125
|
+
expect(ReadPausedSchedules(WritePausedSchedules(null, rec))).toEqual(rec);
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
it('reads nothing from null, junk, an array, or a non-array value', () => {
|
|
129
|
+
expect(ReadPausedSchedules(null)).toEqual([]);
|
|
130
|
+
expect(ReadPausedSchedules('not json {')).toEqual([]);
|
|
131
|
+
expect(ReadPausedSchedules('[1,2,3]')).toEqual([]);
|
|
132
|
+
expect(ReadPausedSchedules(JSON.stringify({ [PAUSED_SCHEDULES_KEY]: 'nope' }))).toEqual([]);
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
it('drops malformed entries instead of yielding an id-less record', () => {
|
|
136
|
+
// An entry with no ID would produce a restore target of undefined.
|
|
137
|
+
const cfg = JSON.stringify({ [PAUSED_SCHEDULES_KEY]: [{ Kind: 'sync' }, null, 7, { ID: '', Kind: 'sync' }, { ID: 'ok' }] });
|
|
138
|
+
expect(ReadPausedSchedules(cfg)).toEqual([{ ID: 'ok', Kind: 'sync' }]);
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
it('replaces, rather than refuses, when Configuration is unparseable', () => {
|
|
142
|
+
const out = JSON.parse(WritePausedSchedules('{broken', [{ ID: 'a', Kind: 'sync' }]));
|
|
143
|
+
expect(out[PAUSED_SCHEDULES_KEY]).toEqual([{ ID: 'a', Kind: 'sync' }]);
|
|
144
|
+
});
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
describe('DescribeCancelScope — reports what is TRUE, not what is reassuring', () => {
|
|
148
|
+
it('says none when nothing is live', () => {
|
|
149
|
+
expect(DescribeCancelScope({ liveRunPresent: false, durableRequestRecorded: false, runningInThisProcess: true }))
|
|
150
|
+
.toBe('none');
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
it('prefers durable when the request was recorded', () => {
|
|
154
|
+
expect(DescribeCancelScope({ liveRunPresent: true, durableRequestRecorded: true, runningInThisProcess: false }))
|
|
155
|
+
.toBe('durable');
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
it('falls back to this-process when only the in-process registry knows', () => {
|
|
159
|
+
expect(DescribeCancelScope({ liveRunPresent: true, durableRequestRecorded: false, runningInThisProcess: true }))
|
|
160
|
+
.toBe('this-process');
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
it('admits unknown when a run is live and neither signal reached it', () => {
|
|
164
|
+
// The tenant has no CancelRequestedAt column and the owner is another process.
|
|
165
|
+
expect(DescribeCancelScope({ liveRunPresent: true, durableRequestRecorded: false, runningInThisProcess: false }))
|
|
166
|
+
.toBe('unknown');
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
it('never promises a stop it cannot deliver', () => {
|
|
170
|
+
expect(DescribeCancelOutcome('unknown')).not.toMatch(/will stop/i);
|
|
171
|
+
expect(DescribeCancelOutcome('unknown')).toMatch(/no new sync will start/i);
|
|
172
|
+
expect(DescribeCancelOutcome('durable')).toMatch(/will stop after the current batch/i);
|
|
173
|
+
expect(DescribeCancelOutcome('none')).toBe('');
|
|
174
|
+
});
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
describe('DescribePauseOutcome', () => {
|
|
178
|
+
it('names both schedules when both moved', () => {
|
|
179
|
+
const msg = DescribePauseOutcome([{ ID: 'a', Kind: 'sync' }, { ID: 'b', Kind: 'discovery' }], false);
|
|
180
|
+
expect(msg).toMatch(/data sync and schema refresh/);
|
|
181
|
+
expect(msg).toMatch(/schedules\./);
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
it('is singular for one', () => {
|
|
185
|
+
expect(DescribePauseOutcome([{ ID: 'a', Kind: 'discovery' }], false)).toBe('Paused the schema refresh schedule.');
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
it('distinguishes "already paused" from "nothing to pause"', () => {
|
|
189
|
+
expect(DescribePauseOutcome([], true)).toMatch(/already paused/i);
|
|
190
|
+
expect(DescribePauseOutcome([], false)).toMatch(/No active schedules/i);
|
|
191
|
+
});
|
|
192
|
+
});
|