@memberjunction/server 6.2.0-edge.6 → 6.2.0-edge.7
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/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 +35 -4
- 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 +228 -0
- package/dist/generated/graphql-schemas/__mj.d.ts.map +1 -1
- package/dist/generated/graphql-schemas/__mj.js +1163 -6
- 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 +321 -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/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +157 -35
- 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/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/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.map +1 -1
- package/dist/resolvers/AutotagPipelineResolver.js +2 -1
- 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/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 +36 -0
- package/dist/resolvers/QuerySystemUserResolver.d.ts.map +1 -1
- package/dist/resolvers/QuerySystemUserResolver.js +174 -87
- 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/meetingRecordingRegistration.d.ts.map +1 -1
- package/dist/resolvers/meetingRecordingRegistration.js +2 -0
- package/dist/resolvers/meetingRecordingRegistration.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/package.json +98 -98
- 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 +323 -26
- 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__/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__/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__/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__/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/config.ts +69 -0
- package/src/context.ts +39 -6
- package/src/coreRoutes.ts +14 -7
- package/src/generated/graphql-schemas/__mj.ts +822 -7
- package/src/generic/OrphanedConversationDetailReconciler.ts +32 -1
- package/src/generic/ResolverBase.ts +414 -143
- package/src/generic/RunViewResolver.ts +96 -49
- package/src/index.ts +201 -35
- 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/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/AgentSessionResolver.ts +6 -2
- package/src/resolvers/AutotagPipelineResolver.ts +2 -1
- package/src/resolvers/ConversationRunEventsResolver.ts +2 -0
- package/src/resolvers/ExecuteRemoteOperationResolver.ts +41 -9
- package/src/resolvers/GetDataResolver.ts +35 -13
- package/src/resolvers/IntegrationDiscoveryResolver.ts +1522 -203
- package/src/resolvers/MCPResolver.ts +15 -2
- package/src/resolvers/QuerySystemUserResolver.ts +197 -98
- 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/__tests__/GetDataResolver.tokens.test.ts +52 -0
- package/src/resolvers/meetingRecordingRegistration.ts +2 -0
- 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/services/TaskOrchestration-Integration.md +0 -188
|
@@ -4,7 +4,8 @@
|
|
|
4
4
|
* Selection removal DISABLES (never deletes — data is kept), re-selection
|
|
5
5
|
* re-enables, and a refresh's new objects arrive disabled-by-default.
|
|
6
6
|
*/
|
|
7
|
-
import { CompositeKey, IMetadataProvider, LogError, RunView, UserInfo } from '@memberjunction/core';
|
|
7
|
+
import { CompositeKey, IMetadataProvider, LogError, RunView, UserInfo, type DatabaseProviderBase } from '@memberjunction/core';
|
|
8
|
+
import { EscapeSQLString } from '@memberjunction/global';
|
|
8
9
|
import type {
|
|
9
10
|
MJCompanyIntegrationEntityMapEntity,
|
|
10
11
|
MJCompanyIntegrationFieldMapEntity,
|
|
@@ -284,9 +285,9 @@ export interface FieldMapReconcilePlan {
|
|
|
284
285
|
* `decideAbsentDeactivations` in the engine.
|
|
285
286
|
*
|
|
286
287
|
* The rules:
|
|
287
|
-
* - **New column → `
|
|
288
|
-
*
|
|
289
|
-
*
|
|
288
|
+
* - **New column → `Inactive` (default).** A column the refresh finds gets a field map, disabled,
|
|
289
|
+
* and the user turns it on. `autoEnableNewColumns: true` adopts it as `Active` instead, for a
|
|
290
|
+
* connection that wants a refresh to bring the source's current shape straight in.
|
|
290
291
|
*
|
|
291
292
|
* This is REFRESH ONLY. A column first seen mid-SYNC is never auto-created — it is captured as a
|
|
292
293
|
* candidate with its statistics and needs acceptance before any DDL runs (see
|
|
@@ -301,7 +302,9 @@ export function DecideFieldMapReconcile(
|
|
|
301
302
|
activeFieldNames: readonly string[],
|
|
302
303
|
existing: readonly ExistingFieldMapRead[],
|
|
303
304
|
mapEnabled: boolean,
|
|
304
|
-
|
|
305
|
+
// Defaults to the CONTRACT (everything.txt): a refresh's new columns arrive disabled and
|
|
306
|
+
// the user turns them on. An omitted argument must not silently adopt.
|
|
307
|
+
autoEnableNewColumns = false,
|
|
305
308
|
): FieldMapReconcilePlan {
|
|
306
309
|
const plan: FieldMapReconcilePlan = { Create: [], Enable: [], Disable: [] };
|
|
307
310
|
const existingByLower = new Map(
|
|
@@ -334,7 +337,227 @@ export function decideFieldMapReconcile(
|
|
|
334
337
|
activeFieldNames: readonly string[],
|
|
335
338
|
existing: readonly ExistingFieldMapRead[],
|
|
336
339
|
mapEnabled: boolean,
|
|
337
|
-
autoEnableNewColumns =
|
|
340
|
+
autoEnableNewColumns = false,
|
|
338
341
|
): FieldMapReconcilePlan {
|
|
339
342
|
return DecideFieldMapReconcile(activeFieldNames, existing, mapEnabled, autoEnableNewColumns);
|
|
340
343
|
}
|
|
344
|
+
|
|
345
|
+
// ── Re-keying: when a connector upgrade changes what identifies a row ────────────────────────
|
|
346
|
+
//
|
|
347
|
+
// A connector release can change an object's primary key — PheedLoop 1.4.6 makes Attendees
|
|
348
|
+
// `code + eventCode` because the object is fetched once per event and an attendee at two events
|
|
349
|
+
// comes back twice with the same `code`.
|
|
350
|
+
//
|
|
351
|
+
// That is not an ordinary schema change. `BaseRESTIntegrationConnector.ToExternalRecord` builds
|
|
352
|
+
// `ExternalID` by joining every IsPrimaryKey field with '|' in Sequence order, so changing the key
|
|
353
|
+
// changes the identity of every row the source will ever send again. Nothing can match a row
|
|
354
|
+
// already stored: the RecordMap is keyed `EntityID|ExternalID`, and the fallback
|
|
355
|
+
// (`MatchEngine.FindByKeyFields`) queries the destination table by key fields that are NULL on
|
|
356
|
+
// rows written before the change. The next sync therefore inserts the whole source ALONGSIDE the
|
|
357
|
+
// rows already there, leaving two copies of everything and nothing to say which is current.
|
|
358
|
+
//
|
|
359
|
+
// So a re-key means the object's stored data has to go, and the source reloaded under the new
|
|
360
|
+
// identity. This is the same reasoning as U10's watermark reset one step further: not just
|
|
361
|
+
// "re-fetch everything", but "re-fetch everything into a table that is not holding rows the
|
|
362
|
+
// re-fetch can never reconcile with".
|
|
363
|
+
|
|
364
|
+
const ENTITY_RECORD_MAPS = 'MJ: Company Integration Record Maps';
|
|
365
|
+
|
|
366
|
+
/** A catalog or entity field, as much of one as the key decision needs. */
|
|
367
|
+
export interface KeyCandidateField {
|
|
368
|
+
Name: string;
|
|
369
|
+
IsPrimaryKey: boolean | null;
|
|
370
|
+
Sequence: number | null;
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
/**
|
|
374
|
+
* The key that decides identity, in the order identity uses.
|
|
375
|
+
*
|
|
376
|
+
* Deliberately NOT `SourceObjectInfo.PrimaryKeyFields`. That is built in
|
|
377
|
+
* `buildSourceSchemaFromPersistedRows` as `fields.filter(f => f.IsPrimaryKey).map(f => f.Name)`
|
|
378
|
+
* over `GetIntegrationObjectFields`, which returns rows in load order with NO Sequence sort — so
|
|
379
|
+
* its order is not the order `ToExternalRecord` joins in, and comparing against it would miss a
|
|
380
|
+
* reorder and could invent one.
|
|
381
|
+
*/
|
|
382
|
+
export function IdentityKeyFields(fields: ReadonlyArray<KeyCandidateField>): string[] {
|
|
383
|
+
return fields
|
|
384
|
+
.filter(f => f.IsPrimaryKey === true)
|
|
385
|
+
.slice()
|
|
386
|
+
.sort((a, b) => (a.Sequence ?? 0) - (b.Sequence ?? 0))
|
|
387
|
+
.map(f => f.Name);
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
/**
|
|
391
|
+
* The key the catalog declares for an object, or `null` when its field rows were not read.
|
|
392
|
+
*
|
|
393
|
+
* An object with no Active field rows is unread, not keyless: a per-connection object whose fields
|
|
394
|
+
* were never warmed answers `[]`, and a continuing object with a built table had columns when it
|
|
395
|
+
* was built. Reporting that as "no key" would make {@link DecideRekeyed} see every keyed object as
|
|
396
|
+
* re-keyed, and the evolution would delete its record maps and rows.
|
|
397
|
+
*/
|
|
398
|
+
export function CatalogIdentityKey(
|
|
399
|
+
fields: ReadonlyArray<KeyCandidateField & { Status: string | null }>,
|
|
400
|
+
): string[] | null {
|
|
401
|
+
const active = fields.filter(f => f.Status === 'Active');
|
|
402
|
+
return active.length === 0 ? null : IdentityKeyFields(active);
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
/**
|
|
406
|
+
* Has the identity changed between the key the tables were BUILT with and the key the catalog
|
|
407
|
+
* now declares?
|
|
408
|
+
*
|
|
409
|
+
* Ordered, not set-based: `code|eventCode` and `eventCode|code` are different identity strings,
|
|
410
|
+
* so a key that keeps its columns and reorders them strands rows exactly like one that gains a
|
|
411
|
+
* column.
|
|
412
|
+
*
|
|
413
|
+
* Returns false when the built key is EMPTY. That is "we could not read it", not "it had no key" —
|
|
414
|
+
* an object genuinely without a key never had stable identities to strand, and treating unknown as
|
|
415
|
+
* empty would re-key every table on a workspace whose entity metadata failed to load.
|
|
416
|
+
*
|
|
417
|
+
* Returns false when the catalog key is `null` — the catalog side could not be read (see
|
|
418
|
+
* {@link CatalogIdentityKey}). An empty ARRAY is different: the catalog was read and declares no
|
|
419
|
+
* key, so the object moves to content-hash identity, and that is a re-key.
|
|
420
|
+
*/
|
|
421
|
+
export function DecideRekeyed(builtKey: readonly string[], catalogKey: readonly string[] | null): boolean {
|
|
422
|
+
if (builtKey.length === 0 || catalogKey === null) return false;
|
|
423
|
+
const norm = (s: string) => (s ?? '').trim().toLowerCase();
|
|
424
|
+
if (builtKey.length !== catalogKey.length) return true;
|
|
425
|
+
return builtKey.some((n, i) => norm(n) !== norm(catalogKey[i]));
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
/** Record maps per chunk. Each chunk is one read and one transaction, so this bounds both. */
|
|
429
|
+
export const REKEY_CLEAR_CHUNK = 500;
|
|
430
|
+
|
|
431
|
+
/**
|
|
432
|
+
* The key values a record map's `EntityRecordID` names, or `null` when it cannot name exactly one
|
|
433
|
+
* row. The inverse of the engine's `composeEntityRecordID`: a single-column key is the value
|
|
434
|
+
* itself; a composite one is '|'-joined in PrimaryKeys order.
|
|
435
|
+
*
|
|
436
|
+
* A composite id that splits into more parts than the key has columns held a value that itself
|
|
437
|
+
* contained '|', and its split would name some other row. An empty part is a null key value. Both
|
|
438
|
+
* are refused rather than matched.
|
|
439
|
+
*/
|
|
440
|
+
function keyValuesOf(entityRecordID: string | null | undefined, keyColumns: number): string[] | null {
|
|
441
|
+
if (!entityRecordID) return null;
|
|
442
|
+
const parts = keyColumns === 1 ? [entityRecordID] : entityRecordID.split('|');
|
|
443
|
+
return parts.length === keyColumns && parts.every(p => p.length > 0) ? parts : null;
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
/**
|
|
447
|
+
* Clear what ties a re-keyed object's stored rows to the source, so the next sync rebuilds it:
|
|
448
|
+
* this connection's rows, and the record maps that point at them.
|
|
449
|
+
*
|
|
450
|
+
* Scoped by the record maps, never the table. Two connections to the same vendor resolve to one
|
|
451
|
+
* schema and therefore one table, and nothing on the table records which connection wrote a row.
|
|
452
|
+
* The connection's record maps are the only thing that does, so they decide which rows go: each
|
|
453
|
+
* map's `EntityRecordID` is the row's key, and the delete names exactly those keys. A row no map
|
|
454
|
+
* of this connection points at is left alone.
|
|
455
|
+
*
|
|
456
|
+
* In chunks of {@link REKEY_CLEAR_CHUNK} maps, read in ID order and resumed after the last ID, so
|
|
457
|
+
* neither the read nor a statement grows with the table. Each chunk deletes its rows and then its
|
|
458
|
+
* maps in ONE transaction: the rows have to go while the maps still say which rows they are, and a
|
|
459
|
+
* failure between the two must not leave maps pointing at deleted rows (the next sync would try to
|
|
460
|
+
* UPDATE them, and fail per record). Set-based, like the engine's `touchLastReconciledAt`, whose
|
|
461
|
+
* `IN` list of dialect-quoted key literals this follows.
|
|
462
|
+
*
|
|
463
|
+
* Returns the object names fully cleared. A failed chunk rolls back, is logged, and leaves its
|
|
464
|
+
* object out. So does a map whose id cannot name one row (see {@link keyValuesOf}): it stays in
|
|
465
|
+
* place, and so does its row. A table that could not be cleared must not look like one that was.
|
|
466
|
+
*/
|
|
467
|
+
export async function ClearRekeyedObjectData(
|
|
468
|
+
targets: ReadonlyArray<{
|
|
469
|
+
ExternalObjectName: string;
|
|
470
|
+
EntityMapID: string;
|
|
471
|
+
EntityID: string;
|
|
472
|
+
SchemaName: string;
|
|
473
|
+
BaseTable: string;
|
|
474
|
+
}>,
|
|
475
|
+
companyIntegrationID: string,
|
|
476
|
+
contextUser: UserInfo,
|
|
477
|
+
provider: DatabaseProviderBase,
|
|
478
|
+
chunkSize = REKEY_CLEAR_CHUNK,
|
|
479
|
+
): Promise<string[]> {
|
|
480
|
+
const cleared: string[] = [];
|
|
481
|
+
if (targets.length === 0) return cleared;
|
|
482
|
+
|
|
483
|
+
// Resolved from metadata, never spelled out. A hardcoded `__mj.CompanyIntegrationRecordMap`
|
|
484
|
+
// would be a second place the schema is declared, and the one place nothing would update.
|
|
485
|
+
const rmEntity = provider.EntityByName(ENTITY_RECORD_MAPS);
|
|
486
|
+
if (!rmEntity?.SchemaName || !rmEntity.BaseTable) {
|
|
487
|
+
LogError(`[EntityMapLifecycle] Cannot clear re-keyed objects: '${ENTITY_RECORD_MAPS}' is not registered`);
|
|
488
|
+
return cleared;
|
|
489
|
+
}
|
|
490
|
+
const d = provider.Dialect;
|
|
491
|
+
const recordMaps = `${d.QuoteIdentifier(rmEntity.SchemaName)}.${d.QuoteIdentifier(rmEntity.BaseTable)}`;
|
|
492
|
+
const literal = (value: string) => d.QuoteStringLiteral(value);
|
|
493
|
+
|
|
494
|
+
for (const t of targets) {
|
|
495
|
+
const key = t.EntityID ? provider.EntityByID(t.EntityID)?.PrimaryKeys ?? [] : [];
|
|
496
|
+
if (!t.SchemaName || !t.BaseTable || key.length === 0) {
|
|
497
|
+
LogError(`[EntityMapLifecycle] Skipping re-key clear for ${t.ExternalObjectName}: no resolved table or key`);
|
|
498
|
+
continue;
|
|
499
|
+
}
|
|
500
|
+
const table = `${d.QuoteIdentifier(t.SchemaName)}.${d.QuoteIdentifier(t.BaseTable)}`;
|
|
501
|
+
const scope = `CompanyIntegrationID='${EscapeSQLString(companyIntegrationID)}' AND EntityID='${EscapeSQLString(t.EntityID)}'`;
|
|
502
|
+
const mapScope = `${d.QuoteIdentifier('CompanyIntegrationID')}=${literal(companyIntegrationID)} ` +
|
|
503
|
+
`AND ${d.QuoteIdentifier('EntityID')}=${literal(t.EntityID)}`;
|
|
504
|
+
let unmatched = 0;
|
|
505
|
+
let after: string | null = null;
|
|
506
|
+
try {
|
|
507
|
+
for (;;) {
|
|
508
|
+
const page = await new RunView().RunView<{ ID: string; EntityRecordID: string }>({
|
|
509
|
+
EntityName: ENTITY_RECORD_MAPS,
|
|
510
|
+
ExtraFilter: after ? `${scope} AND ID > '${EscapeSQLString(after)}'` : scope,
|
|
511
|
+
Fields: ['ID', 'EntityRecordID'],
|
|
512
|
+
OrderBy: 'ID',
|
|
513
|
+
MaxRows: chunkSize,
|
|
514
|
+
ResultType: 'simple',
|
|
515
|
+
BypassCache: true,
|
|
516
|
+
SkipTotalRowCount: true,
|
|
517
|
+
}, contextUser);
|
|
518
|
+
if (!page.Success) throw new Error(`record maps could not be read: ${page.ErrorMessage ?? 'unknown error'}`);
|
|
519
|
+
if (page.Results.length === 0) break;
|
|
520
|
+
after = page.Results[page.Results.length - 1].ID;
|
|
521
|
+
|
|
522
|
+
const matched = page.Results
|
|
523
|
+
.map(m => ({ MapID: m.ID, Values: keyValuesOf(m.EntityRecordID, key.length) }))
|
|
524
|
+
.filter((m): m is { MapID: string; Values: string[] } => m.Values !== null);
|
|
525
|
+
unmatched += page.Results.length - matched.length;
|
|
526
|
+
if (matched.length > 0) {
|
|
527
|
+
const rows = key.length === 1
|
|
528
|
+
? `${d.QuoteIdentifier(key[0].Name)} IN (${matched.map(m => literal(m.Values[0])).join(',')})`
|
|
529
|
+
: matched
|
|
530
|
+
.map(m => `(${key.map((k, i) => `${d.QuoteIdentifier(k.Name)}=${literal(m.Values[i])}`).join(' AND ')})`)
|
|
531
|
+
.join(' OR ');
|
|
532
|
+
await provider.BeginTransaction();
|
|
533
|
+
try {
|
|
534
|
+
await provider.ExecuteSQL(`DELETE FROM ${table} WHERE ${rows}`, undefined, undefined, contextUser);
|
|
535
|
+
await provider.ExecuteSQL(
|
|
536
|
+
`DELETE FROM ${recordMaps} WHERE ${mapScope} AND ${d.QuoteIdentifier('ID')} IN (${matched.map(m => literal(m.MapID)).join(',')})`,
|
|
537
|
+
undefined, undefined, contextUser
|
|
538
|
+
);
|
|
539
|
+
await provider.CommitTransaction();
|
|
540
|
+
} catch (err) {
|
|
541
|
+
await provider.RollbackTransaction();
|
|
542
|
+
throw err;
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
if (page.Results.length < chunkSize) break;
|
|
546
|
+
}
|
|
547
|
+
if (unmatched > 0) {
|
|
548
|
+
LogError(
|
|
549
|
+
`[EntityMapLifecycle] Re-key clear of ${t.ExternalObjectName} left ${unmatched} record map(s) ` +
|
|
550
|
+
`whose id does not name one row of its ${key.length}-column key; those rows were kept`
|
|
551
|
+
);
|
|
552
|
+
continue;
|
|
553
|
+
}
|
|
554
|
+
cleared.push(t.ExternalObjectName);
|
|
555
|
+
} catch (err) {
|
|
556
|
+
LogError(
|
|
557
|
+
`[EntityMapLifecycle] Failed to clear re-keyed object ${t.ExternalObjectName}: ` +
|
|
558
|
+
`${err instanceof Error ? err.message : String(err)}`
|
|
559
|
+
);
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
return cleared;
|
|
563
|
+
}
|
|
@@ -51,6 +51,56 @@ const isActive = (row: NamedStatusRow): boolean => row.Status === 'Active';
|
|
|
51
51
|
* Returns an empty array when everything declared and in scope is Active.
|
|
52
52
|
*/
|
|
53
53
|
export function ComputeInactiveRowWarnings(input: InactiveRowWarningInput): string[] {
|
|
54
|
+
return computeInactiveRowWarnings(input);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* The catalog, reduced to the three reads this warning needs. `IntegrationEngineBase` satisfies it
|
|
59
|
+
* structurally, so the resolver passes its engine instance and a test passes a literal — which is the
|
|
60
|
+
* point: the collection used to live inside the resolver's schema REBUILD, and the one apply path that
|
|
61
|
+
* skips the rebuild (the batch, which prefetches its schema) therefore lost the warnings entirely.
|
|
62
|
+
* They never depended on the rebuild; they are the catalog's own Status rows.
|
|
63
|
+
*/
|
|
64
|
+
export interface CatalogStatusReader {
|
|
65
|
+
/** Active objects of this integration — the ones an apply materializes. */
|
|
66
|
+
GetActiveIntegrationObjects(integrationID: string): ReadonlyArray<{ ID: string; Name: string }>;
|
|
67
|
+
/** EVERY object of this integration, Active or not. */
|
|
68
|
+
GetIntegrationObjectsByIntegrationID(integrationID: string): ReadonlyArray<{ Name: string; Status: string | null }>;
|
|
69
|
+
/** Every field of one object, Active or not. */
|
|
70
|
+
GetIntegrationObjectFields(objectID: string): ReadonlyArray<{ Name: string; Status: string | null }>;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Gathers the rows {@link ComputeInactiveRowWarnings} needs from the catalog and returns its verdict.
|
|
75
|
+
*
|
|
76
|
+
* `requestedNames` empty/undefined means "everything active", which deliberately does not report
|
|
77
|
+
* deactivated OBJECTS — see {@link InactiveRowWarningInput.RequestedNames}.
|
|
78
|
+
*/
|
|
79
|
+
export function CollectInactiveRowWarnings(
|
|
80
|
+
catalog: CatalogStatusReader,
|
|
81
|
+
integrationID: string,
|
|
82
|
+
requestedNames?: string[] | null,
|
|
83
|
+
): string[] {
|
|
84
|
+
const filter = requestedNames && requestedNames.length > 0
|
|
85
|
+
? new Set(requestedNames.map(n => n.toLowerCase()))
|
|
86
|
+
: null;
|
|
87
|
+
// Fields only for the objects this apply materializes: a non-materialized object is reported by
|
|
88
|
+
// the object warning, not by one line per field it also lost.
|
|
89
|
+
const FieldsByObjectName: Record<string, NamedStatusRow[]> = {};
|
|
90
|
+
for (const io of catalog.GetActiveIntegrationObjects(integrationID)) {
|
|
91
|
+
if (filter && !filter.has(io.Name.toLowerCase())) continue;
|
|
92
|
+
FieldsByObjectName[io.Name] = catalog.GetIntegrationObjectFields(io.ID)
|
|
93
|
+
.map(f => ({ Name: f.Name, Status: f.Status }));
|
|
94
|
+
}
|
|
95
|
+
return computeInactiveRowWarnings({
|
|
96
|
+
RequestedNames: requestedNames ?? null,
|
|
97
|
+
AllObjects: catalog.GetIntegrationObjectsByIntegrationID(integrationID)
|
|
98
|
+
.map(io => ({ Name: io.Name, Status: io.Status })),
|
|
99
|
+
FieldsByObjectName,
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function computeInactiveRowWarnings(input: InactiveRowWarningInput): string[] {
|
|
54
104
|
const warnings: string[] = [];
|
|
55
105
|
const requested = input.RequestedNames && input.RequestedNames.length > 0
|
|
56
106
|
? new Set(input.RequestedNames.map(n => n.toLowerCase()))
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A table selection's object ids, resolved to object names — against the CONNECTION's catalog first.
|
|
3
|
+
*
|
|
4
|
+
* Inside a catalog scope, `IntegrationListSourceObjects` hands the picker per-connection
|
|
5
|
+
* (CompanyIntegrationObject) ids. The apply paths looked ids up only in the shared
|
|
6
|
+
* `MJ: Integration Objects` catalog, so an id-only selection matched nothing: every pick was dropped
|
|
7
|
+
* with a LogError, the migration ran with an empty table list, CodeGen and the restart still ran,
|
|
8
|
+
* and the apply reported Success with 0 tables.
|
|
9
|
+
*
|
|
10
|
+
* Three tiers, cheapest first:
|
|
11
|
+
* 1. the scope's loaded catalog, no query — every per-connection object, keyed by its own id AND
|
|
12
|
+
* by the shared IntegrationObjectID it carries, so an id from either catalog resolves;
|
|
13
|
+
* 2. a direct, uncached read of `MJ: Company Integration Objects` for this connection, for ids the
|
|
14
|
+
* loaded catalog does not hold yet (a row written earlier in the same run);
|
|
15
|
+
* 3. the shared catalog, exactly as before — and the only tier outside a catalog scope.
|
|
16
|
+
*
|
|
17
|
+
* Keys are upper-cased ids; values are object names. An id no tier resolves is simply absent: the
|
|
18
|
+
* caller decides whether an empty resolution is fatal (an apply refuses it).
|
|
19
|
+
*/
|
|
20
|
+
import { RunView, type UserInfo } from '@memberjunction/core';
|
|
21
|
+
import { EscapeSQLString } from '@memberjunction/global';
|
|
22
|
+
import {
|
|
23
|
+
IntegrationEngineBase,
|
|
24
|
+
ENTITY_COMPANY_INTEGRATION_OBJECTS,
|
|
25
|
+
type CompanyIntegrationObjectRow,
|
|
26
|
+
} from '@memberjunction/integration-engine-base';
|
|
27
|
+
import { CurrentCatalogCI } from '@memberjunction/integration-engine';
|
|
28
|
+
|
|
29
|
+
/** Ids per per-connection read. Each read is one `IN (…)` list, so this bounds the filter's length. */
|
|
30
|
+
export const OBJECT_ID_LOOKUP_CHUNK = 200;
|
|
31
|
+
|
|
32
|
+
/** What the lookup reads off a per-connection object. */
|
|
33
|
+
type CatalogIdentity = Pick<CompanyIntegrationObjectRow, 'ID' | 'Name' | 'IntegrationObjectID'>;
|
|
34
|
+
|
|
35
|
+
const quote = (value: string): string => `'${EscapeSQLString(value)}'`;
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Resolves object ids to names for the connection in catalog scope (or the shared catalog when
|
|
39
|
+
* there is none). Never throws for a missing id; a failed read leaves its ids unresolved.
|
|
40
|
+
*/
|
|
41
|
+
export async function LookupObjectNamesByID(
|
|
42
|
+
ids: ReadonlyArray<string | null | undefined> | undefined,
|
|
43
|
+
user: UserInfo
|
|
44
|
+
): Promise<Map<string, string>> {
|
|
45
|
+
const map = new Map<string, string>();
|
|
46
|
+
const wanted = [...new Set((ids ?? [])
|
|
47
|
+
.filter((id): id is string => typeof id === 'string' && id.length > 0)
|
|
48
|
+
.map(id => id.toUpperCase()))];
|
|
49
|
+
if (wanted.length === 0) return map;
|
|
50
|
+
|
|
51
|
+
const ciID = CurrentCatalogCI();
|
|
52
|
+
if (ciID) {
|
|
53
|
+
const engine = IntegrationEngineBase.Instance;
|
|
54
|
+
if (engine.HasCompanyIntegrationCatalog(ciID)) {
|
|
55
|
+
// GetCompanyIntegrationObjects presents CompanyIntegrationObjectRow projections typed as
|
|
56
|
+
// the shared entity (asReadOnlyObject); the per-connection columns are on every row.
|
|
57
|
+
const rows = engine.GetCompanyIntegrationObjects(ciID) as unknown as CatalogIdentity[];
|
|
58
|
+
for (const row of rows) {
|
|
59
|
+
if (row.ID) map.set(String(row.ID).toUpperCase(), row.Name);
|
|
60
|
+
if (row.IntegrationObjectID) map.set(String(row.IntegrationObjectID).toUpperCase(), row.Name);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const rv = new RunView();
|
|
66
|
+
let missing = wanted.filter(id => !map.has(id));
|
|
67
|
+
if (missing.length > 0 && ciID) {
|
|
68
|
+
for (let i = 0; i < missing.length; i += OBJECT_ID_LOOKUP_CHUNK) {
|
|
69
|
+
const chunk = missing.slice(i, i + OBJECT_ID_LOOKUP_CHUNK);
|
|
70
|
+
const result = await rv.RunView<{ ID: string; Name: string }>({
|
|
71
|
+
EntityName: ENTITY_COMPANY_INTEGRATION_OBJECTS,
|
|
72
|
+
ExtraFilter: `CompanyIntegrationID=${quote(ciID)} AND ID IN (${chunk.map(quote).join(',')})`,
|
|
73
|
+
ResultType: 'simple',
|
|
74
|
+
Fields: ['ID', 'Name'],
|
|
75
|
+
BypassCache: true,
|
|
76
|
+
IgnoreMaxRows: true,
|
|
77
|
+
}, user);
|
|
78
|
+
if (result.Success) {
|
|
79
|
+
for (const r of result.Results) map.set(String(r.ID).toUpperCase(), r.Name);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
missing = wanted.filter(id => !map.has(id));
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
if (missing.length > 0) {
|
|
86
|
+
const result = await rv.RunView<{ ID: string; Name: string }>({
|
|
87
|
+
EntityName: 'MJ: Integration Objects',
|
|
88
|
+
ExtraFilter: missing.map(id => `ID=${quote(id)}`).join(' OR '),
|
|
89
|
+
ResultType: 'simple',
|
|
90
|
+
Fields: ['ID', 'Name'],
|
|
91
|
+
}, user);
|
|
92
|
+
if (result.Success) {
|
|
93
|
+
for (const r of result.Results) map.set(String(r.ID).toUpperCase(), r.Name);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
return map;
|
|
97
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The post-restart introspection, run against the connection's own catalog with its fields warm.
|
|
3
|
+
*
|
|
4
|
+
* After the RSU restarts the process, the consumer introspects the connector to build the entity
|
|
5
|
+
* and field maps. That runs at process start, outside any request, so nothing has entered a scope
|
|
6
|
+
* for it, and nothing has loaded this connection's catalog either. Two things have to be true for
|
|
7
|
+
* the connector to see the connection's fields:
|
|
8
|
+
*
|
|
9
|
+
* - the scope must be entered ASYNC (`WithCatalogScope`), which loads the connection's objects;
|
|
10
|
+
* a synchronous `RunInCatalogScope` loads nothing, so on a fresh process the reads fall back to
|
|
11
|
+
* the shared catalog and every column this connection's discovery sampled gets no field map;
|
|
12
|
+
* - each object's FIELD rows must be warm. A scope loads objects, not fields: only the sync loop
|
|
13
|
+
* warms fields, so a connector reading them here (`GetCachedFields`) gets per-connection objects
|
|
14
|
+
* with no fields at all, and the consumer creates no field maps.
|
|
15
|
+
*
|
|
16
|
+
* So this enters the scope, warms and pins every per-connection object the pending work names,
|
|
17
|
+
* introspects, and releases the pins whether or not the introspection succeeded.
|
|
18
|
+
*/
|
|
19
|
+
import type { UserInfo } from '@memberjunction/core';
|
|
20
|
+
import { IntegrationEngineBase } from '@memberjunction/integration-engine-base';
|
|
21
|
+
import { UnpinCatalogObjects, WarmCatalogObject, WithCatalogScope } from '@memberjunction/integration-engine';
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Run `introspect` in `companyIntegrationID`'s catalog scope, with the fields of each named object
|
|
25
|
+
* warm. A name the connection has no object of its own for is skipped: it reads the shared catalog,
|
|
26
|
+
* as it always did. A failed warm throws; it is never read as an object with no fields.
|
|
27
|
+
*/
|
|
28
|
+
export async function IntrospectInCatalogScope<T>(
|
|
29
|
+
companyIntegrationID: string,
|
|
30
|
+
sourceObjectNames: readonly string[],
|
|
31
|
+
introspect: () => Promise<T>,
|
|
32
|
+
user: UserInfo,
|
|
33
|
+
): Promise<T> {
|
|
34
|
+
return WithCatalogScope(companyIntegrationID, async () => {
|
|
35
|
+
const engine = IntegrationEngineBase.Instance;
|
|
36
|
+
const pinned: string[] = [];
|
|
37
|
+
try {
|
|
38
|
+
for (const name of sourceObjectNames) {
|
|
39
|
+
const own = engine.GetCompanyIntegrationObject(companyIntegrationID, name);
|
|
40
|
+
if (!own) continue;
|
|
41
|
+
await WarmCatalogObject(own.ID, user);
|
|
42
|
+
pinned.push(own.ID);
|
|
43
|
+
}
|
|
44
|
+
return await introspect();
|
|
45
|
+
} finally {
|
|
46
|
+
UnpinCatalogObjects(pinned);
|
|
47
|
+
}
|
|
48
|
+
}, user);
|
|
49
|
+
}
|