@memberjunction/server 6.2.0-edge.0 → 6.2.0-edge.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/.tsbuildinfo +1 -1
- package/dist/agentSessions/ReturningVisitorRecap.d.ts +2 -0
- package/dist/agentSessions/ReturningVisitorRecap.d.ts.map +1 -1
- package/dist/agentSessions/ReturningVisitorRecap.js +5 -1
- package/dist/agentSessions/ReturningVisitorRecap.js.map +1 -1
- package/dist/agentSessions/SessionManager.js +2 -2
- package/dist/agentSessions/remoteBrowserGoalEngine.d.ts +8 -0
- package/dist/agentSessions/remoteBrowserGoalEngine.d.ts.map +1 -1
- package/dist/agentSessions/remoteBrowserGoalEngine.js +21 -5
- package/dist/agentSessions/remoteBrowserGoalEngine.js.map +1 -1
- package/dist/auth/AuthProviderCatalogRouter.d.ts +2 -0
- package/dist/auth/AuthProviderCatalogRouter.d.ts.map +1 -1
- package/dist/auth/AuthProviderCatalogRouter.js +5 -1
- package/dist/auth/AuthProviderCatalogRouter.js.map +1 -1
- package/dist/auth/index.d.ts +20 -1
- package/dist/auth/index.d.ts.map +1 -1
- package/dist/auth/index.js +20 -10
- package/dist/auth/index.js.map +1 -1
- package/dist/auth/initializeProviders.d.ts +8 -0
- package/dist/auth/initializeProviders.d.ts.map +1 -1
- package/dist/auth/initializeProviders.js +21 -5
- package/dist/auth/initializeProviders.js.map +1 -1
- package/dist/auth/magicLink/MagicLinkRouter.d.ts +9 -0
- package/dist/auth/magicLink/MagicLinkRouter.d.ts.map +1 -1
- package/dist/auth/magicLink/MagicLinkRouter.js +22 -10
- package/dist/auth/magicLink/MagicLinkRouter.js.map +1 -1
- package/dist/auth/magicLink/MagicLinkService.d.ts +17 -15
- package/dist/auth/magicLink/MagicLinkService.d.ts.map +1 -1
- package/dist/auth/magicLink/MagicLinkService.js +65 -51
- package/dist/auth/magicLink/MagicLinkService.js.map +1 -1
- package/dist/auth/magicLink/index.d.ts +2 -2
- package/dist/auth/magicLink/index.d.ts.map +1 -1
- package/dist/auth/magicLink/index.js +2 -2
- package/dist/auth/magicLink/index.js.map +1 -1
- package/dist/auth/magicLink/magicLinkCore.d.ts +54 -33
- package/dist/auth/magicLink/magicLinkCore.d.ts.map +1 -1
- package/dist/auth/magicLink/magicLinkCore.js +61 -61
- package/dist/auth/magicLink/magicLinkCore.js.map +1 -1
- package/dist/auth/magicLink/redeemLanding.d.ts +4 -0
- package/dist/auth/magicLink/redeemLanding.d.ts.map +1 -1
- package/dist/auth/magicLink/redeemLanding.js +12 -4
- package/dist/auth/magicLink/redeemLanding.js.map +1 -1
- package/dist/auth/magicLink/types.d.ts +4 -4
- package/dist/auth/principals.d.ts +2 -0
- package/dist/auth/principals.d.ts.map +1 -1
- package/dist/auth/principals.js +6 -2
- package/dist/auth/principals.js.map +1 -1
- package/dist/cache.d.ts +2 -0
- package/dist/cache.d.ts.map +1 -1
- package/dist/cache.js +3 -1
- package/dist/cache.js.map +1 -1
- package/dist/config.d.ts +346 -2
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +34 -4
- package/dist/config.js.map +1 -1
- package/dist/context.d.ts +20 -0
- package/dist/context.d.ts.map +1 -1
- package/dist/context.js +27 -19
- package/dist/context.js.map +1 -1
- package/dist/directives/Public.d.ts +2 -0
- package/dist/directives/Public.d.ts.map +1 -1
- package/dist/directives/Public.js +3 -1
- package/dist/directives/Public.js.map +1 -1
- package/dist/directives/RequireSystemUser.d.ts +2 -0
- package/dist/directives/RequireSystemUser.d.ts.map +1 -1
- package/dist/directives/RequireSystemUser.js +3 -1
- package/dist/directives/RequireSystemUser.js.map +1 -1
- package/dist/generated/graphql-schemas/__mj.d.ts +1021 -0
- package/dist/generated/graphql-schemas/__mj.d.ts.map +1 -1
- package/dist/generated/graphql-schemas/__mj.js +7368 -2145
- package/dist/generated/graphql-schemas/__mj.js.map +1 -1
- package/dist/generic/CacheInvalidationResolver.d.ts +94 -4
- package/dist/generic/CacheInvalidationResolver.d.ts.map +1 -1
- package/dist/generic/CacheInvalidationResolver.js +122 -3
- package/dist/generic/CacheInvalidationResolver.js.map +1 -1
- package/dist/generic/DeleteOptionsInput.d.ts +8 -1
- package/dist/generic/DeleteOptionsInput.d.ts.map +1 -1
- package/dist/generic/DeleteOptionsInput.js +1 -1
- package/dist/generic/DeleteOptionsInput.js.map +1 -1
- package/dist/generic/FireAndForgetHeartbeat.d.ts +18 -2
- package/dist/generic/FireAndForgetHeartbeat.d.ts.map +1 -1
- package/dist/generic/FireAndForgetHeartbeat.js +24 -6
- package/dist/generic/FireAndForgetHeartbeat.js.map +1 -1
- package/dist/generic/OrphanedConversationDetailReconciler.d.ts +31 -0
- package/dist/generic/OrphanedConversationDetailReconciler.d.ts.map +1 -0
- package/dist/generic/OrphanedConversationDetailReconciler.js +215 -0
- package/dist/generic/OrphanedConversationDetailReconciler.js.map +1 -0
- package/dist/generic/PushStatusResolver.d.ts +48 -1
- package/dist/generic/PushStatusResolver.d.ts.map +1 -1
- package/dist/generic/PushStatusResolver.js +95 -4
- package/dist/generic/PushStatusResolver.js.map +1 -1
- package/dist/generic/ResolverBase.d.ts +18 -2
- package/dist/generic/ResolverBase.d.ts.map +1 -1
- package/dist/generic/ResolverBase.js +64 -55
- package/dist/generic/ResolverBase.js.map +1 -1
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +120 -11
- package/dist/index.js.map +1 -1
- package/dist/integration/CustomColumnPromoter.d.ts +2 -0
- package/dist/integration/CustomColumnPromoter.d.ts.map +1 -1
- package/dist/integration/CustomColumnPromoter.js +5 -1
- package/dist/integration/CustomColumnPromoter.js.map +1 -1
- package/dist/integration/EntityMapLifecycle.d.ts +4 -0
- package/dist/integration/EntityMapLifecycle.d.ts.map +1 -1
- package/dist/integration/EntityMapLifecycle.js +10 -2
- package/dist/integration/EntityMapLifecycle.js.map +1 -1
- package/dist/logging/NoLog.d.ts +4 -0
- package/dist/logging/NoLog.d.ts.map +1 -1
- package/dist/logging/NoLog.js +10 -2
- package/dist/logging/NoLog.js.map +1 -1
- package/dist/logging/StartupLogger.d.ts +2 -0
- package/dist/logging/StartupLogger.d.ts.map +1 -1
- package/dist/logging/StartupLogger.js +5 -1
- package/dist/logging/StartupLogger.js.map +1 -1
- package/dist/logging/bootAudit.d.ts +11 -7
- package/dist/logging/bootAudit.d.ts.map +1 -1
- package/dist/logging/bootAudit.js +15 -7
- package/dist/logging/bootAudit.js.map +1 -1
- package/dist/logging/boundaryLogPayload.d.ts +4 -0
- package/dist/logging/boundaryLogPayload.d.ts.map +1 -1
- package/dist/logging/boundaryLogPayload.js +5 -1
- package/dist/logging/boundaryLogPayload.js.map +1 -1
- package/dist/logging/secretRedactor.d.ts +2 -0
- package/dist/logging/secretRedactor.d.ts.map +1 -1
- package/dist/logging/secretRedactor.js +8 -4
- package/dist/logging/secretRedactor.js.map +1 -1
- package/dist/logging/shortenForLog.d.ts +2 -0
- package/dist/logging/shortenForLog.d.ts.map +1 -1
- package/dist/logging/shortenForLog.js +7 -3
- package/dist/logging/shortenForLog.js.map +1 -1
- package/dist/logging/variablesLoggingMiddleware.d.ts +2 -0
- package/dist/logging/variablesLoggingMiddleware.d.ts.map +1 -1
- package/dist/logging/variablesLoggingMiddleware.js +8 -6
- package/dist/logging/variablesLoggingMiddleware.js.map +1 -1
- package/dist/metadataRefreshSignal.d.ts +19 -0
- package/dist/metadataRefreshSignal.d.ts.map +1 -0
- package/dist/metadataRefreshSignal.js +45 -0
- package/dist/metadataRefreshSignal.js.map +1 -0
- package/dist/middleware/MJTenantFilterMiddleware.js +4 -4
- package/dist/multiTenancy/index.d.ts +8 -0
- package/dist/multiTenancy/index.d.ts.map +1 -1
- package/dist/multiTenancy/index.js +21 -5
- package/dist/multiTenancy/index.js.map +1 -1
- package/dist/realtimeWidget/WidgetRouter.d.ts +5 -0
- package/dist/realtimeWidget/WidgetRouter.d.ts.map +1 -1
- package/dist/realtimeWidget/WidgetRouter.js +14 -10
- package/dist/realtimeWidget/WidgetRouter.js.map +1 -1
- package/dist/realtimeWidget/WidgetSessionService.d.ts +6 -6
- package/dist/realtimeWidget/WidgetSessionService.js +27 -27
- package/dist/realtimeWidget/host-identity.d.ts +5 -1
- package/dist/realtimeWidget/host-identity.d.ts.map +1 -1
- package/dist/realtimeWidget/host-identity.js +11 -3
- package/dist/realtimeWidget/host-identity.js.map +1 -1
- package/dist/realtimeWidget/index.d.ts +2 -2
- package/dist/realtimeWidget/index.d.ts.map +1 -1
- package/dist/realtimeWidget/index.js +2 -2
- package/dist/realtimeWidget/index.js.map +1 -1
- package/dist/realtimeWidget/visitorIdentity.d.ts +21 -1
- package/dist/realtimeWidget/visitorIdentity.d.ts.map +1 -1
- package/dist/realtimeWidget/visitorIdentity.js +16 -4
- package/dist/realtimeWidget/visitorIdentity.js.map +1 -1
- package/dist/realtimeWidget/widgetCore.d.ts +48 -0
- package/dist/realtimeWidget/widgetCore.d.ts.map +1 -1
- package/dist/realtimeWidget/widgetCore.js +38 -10
- package/dist/realtimeWidget/widgetCore.js.map +1 -1
- package/dist/realtimeWidget/widgetGuestElevation.d.ts +10 -6
- package/dist/realtimeWidget/widgetGuestElevation.d.ts.map +1 -1
- package/dist/realtimeWidget/widgetGuestElevation.js +14 -6
- package/dist/realtimeWidget/widgetGuestElevation.js.map +1 -1
- package/dist/resolvers/AdhocQueryResolver.js +3 -3
- package/dist/resolvers/AgentRunStatusPublisher.d.ts +55 -0
- package/dist/resolvers/AgentRunStatusPublisher.d.ts.map +1 -0
- package/dist/resolvers/AgentRunStatusPublisher.js +141 -0
- package/dist/resolvers/AgentRunStatusPublisher.js.map +1 -0
- package/dist/resolvers/ConversationRunEventsResolver.d.ts +90 -0
- package/dist/resolvers/ConversationRunEventsResolver.d.ts.map +1 -0
- package/dist/resolvers/ConversationRunEventsResolver.js +283 -0
- package/dist/resolvers/ConversationRunEventsResolver.js.map +1 -0
- package/dist/resolvers/DuplicateEntryCheckResolver.d.ts +98 -0
- package/dist/resolvers/DuplicateEntryCheckResolver.d.ts.map +1 -0
- package/dist/resolvers/DuplicateEntryCheckResolver.js +227 -0
- package/dist/resolvers/DuplicateEntryCheckResolver.js.map +1 -0
- package/dist/resolvers/FetchEntityVectorsResolver.d.ts.map +1 -1
- package/dist/resolvers/FetchEntityVectorsResolver.js +2 -1
- package/dist/resolvers/FetchEntityVectorsResolver.js.map +1 -1
- package/dist/resolvers/FileResolver.js +3 -3
- package/dist/resolvers/GeoResolver.d.ts +2 -2
- package/dist/resolvers/GeoResolver.js +4 -4
- package/dist/resolvers/GetDataResolver.d.ts +14 -0
- package/dist/resolvers/GetDataResolver.d.ts.map +1 -1
- package/dist/resolvers/GetDataResolver.js +43 -15
- package/dist/resolvers/GetDataResolver.js.map +1 -1
- package/dist/resolvers/IntegrationDiscoveryResolver.js +2 -2
- package/dist/resolvers/ListOperationsResolver.d.ts +20 -0
- package/dist/resolvers/ListOperationsResolver.d.ts.map +1 -1
- package/dist/resolvers/ListOperationsResolver.js +68 -28
- package/dist/resolvers/ListOperationsResolver.js.map +1 -1
- package/dist/resolvers/MCPResolver.d.ts +11 -1
- package/dist/resolvers/MCPResolver.d.ts.map +1 -1
- package/dist/resolvers/MCPResolver.js +7 -3
- package/dist/resolvers/MCPResolver.js.map +1 -1
- package/dist/resolvers/MergeRecordsResolver.d.ts +7 -0
- package/dist/resolvers/MergeRecordsResolver.d.ts.map +1 -1
- package/dist/resolvers/MergeRecordsResolver.js +16 -2
- package/dist/resolvers/MergeRecordsResolver.js.map +1 -1
- package/dist/resolvers/RealtimeBridgeResolver.js +3 -3
- package/dist/resolvers/RealtimeClientSessionResolver.d.ts +44 -6
- package/dist/resolvers/RealtimeClientSessionResolver.d.ts.map +1 -1
- package/dist/resolvers/RealtimeClientSessionResolver.js +93 -37
- package/dist/resolvers/RealtimeClientSessionResolver.js.map +1 -1
- package/dist/resolvers/RemoteBrowserActionResolver.js +5 -5
- package/dist/resolvers/RunAIAgentResolver.d.ts +8 -18
- package/dist/resolvers/RunAIAgentResolver.d.ts.map +1 -1
- package/dist/resolvers/RunAIAgentResolver.js +85 -184
- package/dist/resolvers/RunAIAgentResolver.js.map +1 -1
- package/dist/resolvers/RunAIPromptResolver.d.ts +1 -1
- package/dist/resolvers/RunAIPromptResolver.js +2 -2
- package/dist/resolvers/RunDecisionResolver.d.ts +149 -0
- package/dist/resolvers/RunDecisionResolver.d.ts.map +1 -0
- package/dist/resolvers/RunDecisionResolver.js +396 -0
- package/dist/resolvers/RunDecisionResolver.js.map +1 -0
- package/dist/resolvers/RunTestResolver.js +5 -5
- package/dist/resolvers/SqlLoggingConfigResolver.d.ts +8 -4
- package/dist/resolvers/SqlLoggingConfigResolver.d.ts.map +1 -1
- package/dist/resolvers/SqlLoggingConfigResolver.js +5 -5
- package/dist/resolvers/TaskGraphFrameResolver.d.ts +8 -1
- package/dist/resolvers/TaskGraphFrameResolver.d.ts.map +1 -1
- package/dist/resolvers/TaskGraphFrameResolver.js +6 -2
- package/dist/resolvers/TaskGraphFrameResolver.js.map +1 -1
- package/dist/resolvers/VectorizeEntityResolver.d.ts +1 -1
- package/dist/resolvers/VectorizeEntityResolver.d.ts.map +1 -1
- package/dist/resolvers/VectorizeEntityResolver.js +14 -10
- package/dist/resolvers/VectorizeEntityResolver.js.map +1 -1
- package/dist/resolvers/adhoc-query-helpers.d.ts +6 -0
- package/dist/resolvers/adhoc-query-helpers.d.ts.map +1 -1
- package/dist/resolvers/adhoc-query-helpers.js +10 -2
- package/dist/resolvers/adhoc-query-helpers.js.map +1 -1
- package/dist/resolvers/artifact-routing.d.ts +8 -8
- package/dist/resolvers/artifact-routing.js +2 -2
- package/dist/resolvers/artifact-routing.js.map +1 -1
- package/dist/resolvers/meetingRecordingRegistration.d.ts +10 -2
- package/dist/resolvers/meetingRecordingRegistration.d.ts.map +1 -1
- package/dist/resolvers/meetingRecordingRegistration.js +24 -8
- package/dist/resolvers/meetingRecordingRegistration.js.map +1 -1
- package/dist/resolvers/peaksSidecar.d.ts +4 -0
- package/dist/resolvers/peaksSidecar.d.ts.map +1 -1
- package/dist/resolvers/peaksSidecar.js +10 -2
- package/dist/resolvers/peaksSidecar.js.map +1 -1
- package/dist/rest/EntityCRUDHandler.d.ts +28 -0
- package/dist/rest/EntityCRUDHandler.d.ts.map +1 -1
- package/dist/rest/EntityCRUDHandler.js +20 -4
- package/dist/rest/EntityCRUDHandler.js.map +1 -1
- package/dist/rest/MediaStreamHandler.d.ts +2 -0
- package/dist/rest/MediaStreamHandler.d.ts.map +1 -1
- package/dist/rest/MediaStreamHandler.js +10 -6
- package/dist/rest/MediaStreamHandler.js.map +1 -1
- package/dist/rest/OAuthCallbackHandler.d.ts +9 -0
- package/dist/rest/OAuthCallbackHandler.d.ts.map +1 -1
- package/dist/rest/OAuthCallbackHandler.js +15 -3
- package/dist/rest/OAuthCallbackHandler.js.map +1 -1
- package/dist/rest/RESTEndpointHandler.d.ts +6 -4
- package/dist/rest/RESTEndpointHandler.d.ts.map +1 -1
- package/dist/rest/RESTEndpointHandler.js +15 -11
- package/dist/rest/RESTEndpointHandler.js.map +1 -1
- package/dist/rest/RealtimeSdpBrokerHandler.d.ts +5 -1
- package/dist/rest/RealtimeSdpBrokerHandler.d.ts.map +1 -1
- package/dist/rest/RealtimeSdpBrokerHandler.js +12 -4
- package/dist/rest/RealtimeSdpBrokerHandler.js.map +1 -1
- package/dist/rest/SignatureWebhookHandler.d.ts +2 -0
- package/dist/rest/SignatureWebhookHandler.d.ts.map +1 -1
- package/dist/rest/SignatureWebhookHandler.js +5 -1
- package/dist/rest/SignatureWebhookHandler.js.map +1 -1
- package/dist/rest/UploadTokenManager.d.ts +24 -8
- package/dist/rest/UploadTokenManager.d.ts.map +1 -1
- package/dist/rest/UploadTokenManager.js +53 -21
- package/dist/rest/UploadTokenManager.js.map +1 -1
- package/dist/rest/ViewOperationsHandler.d.ts +20 -0
- package/dist/rest/ViewOperationsHandler.d.ts.map +1 -1
- package/dist/rest/ViewOperationsHandler.js +20 -4
- package/dist/rest/ViewOperationsHandler.js.map +1 -1
- package/dist/rest/mediaRange.d.ts +4 -0
- package/dist/rest/mediaRange.d.ts.map +1 -1
- package/dist/rest/mediaRange.js +10 -2
- package/dist/rest/mediaRange.js.map +1 -1
- package/dist/rest/setupRESTEndpoints.d.ts +4 -0
- package/dist/rest/setupRESTEndpoints.d.ts.map +1 -1
- package/dist/rest/setupRESTEndpoints.js +13 -7
- package/dist/rest/setupRESTEndpoints.js.map +1 -1
- package/dist/serverExtensionReservedRoots.d.ts +2 -0
- package/dist/serverExtensionReservedRoots.d.ts.map +1 -1
- package/dist/serverExtensionReservedRoots.js +5 -1
- package/dist/serverExtensionReservedRoots.js.map +1 -1
- package/dist/services/TaskGraphActionRunner.d.ts +13 -8
- package/dist/services/TaskGraphActionRunner.d.ts.map +1 -1
- package/dist/services/TaskGraphActionRunner.js +16 -15
- package/dist/services/TaskGraphActionRunner.js.map +1 -1
- package/dist/types.d.ts +2 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +7 -3
- package/dist/types.js.map +1 -1
- package/dist/util.d.ts +6 -0
- package/dist/util.d.ts.map +1 -1
- package/dist/util.js +13 -5
- package/dist/util.js.map +1 -1
- package/package.json +94 -92
- package/src/__tests__/AgentRunStatusPublisher.test.ts +175 -0
- package/src/__tests__/DeleteOptionsInput.wireShape.test.ts +62 -0
- package/src/__tests__/DuplicateEntryCheckResolver.test.ts +300 -0
- package/src/__tests__/FileResolverPeaks.test.ts +13 -13
- package/src/__tests__/ListOperationsResolver.test.ts +17 -17
- package/src/__tests__/MediaStreamHandler.test.ts +28 -28
- package/src/__tests__/NoLog.test.ts +11 -11
- package/src/__tests__/PrimaryKeyAlwaysMapped.test.ts +10 -10
- package/src/__tests__/RESTEndpointHandler.test.ts +14 -14
- package/src/__tests__/RealtimeBridgeResolver.test.ts +4 -2
- package/src/__tests__/RealtimeClientSessionResolver.test.ts +216 -2
- package/src/__tests__/RealtimeSdpBrokerHandler.test.ts +6 -6
- package/src/__tests__/RefreshAdoptsNewColumns.test.ts +8 -8
- package/src/__tests__/RemoteBrowserActionRecovery.test.ts +7 -1
- package/src/__tests__/RemoteBrowserAudioStream.test.ts +7 -1
- package/src/__tests__/RemoteBrowserGoalEngine.test.ts +18 -18
- package/src/__tests__/RemoteBrowserSnapshot.test.ts +7 -1
- package/src/__tests__/ResolverBase.clientOldValues.test.ts +52 -0
- package/src/__tests__/RunAIAgentResolver.historyFrom.test.ts +398 -0
- package/src/__tests__/RunAIAgentResolver.streaming.test.ts +18 -27
- package/src/__tests__/RunDecisionResolver.test.ts +699 -0
- package/src/__tests__/TaskGraphActionRunner.durablePayload.test.ts +131 -0
- package/src/__tests__/TaskGraphFrameResolver.test.ts +2 -2
- package/src/__tests__/VectorizeEntityResolver.test.ts +106 -0
- package/src/__tests__/WidgetSessionService.test.ts +8 -4
- package/src/__tests__/adhoc-query-helpers.test.ts +21 -21
- package/src/__tests__/bootAudit.test.ts +11 -11
- package/src/__tests__/boundaryLogPayload.test.ts +6 -6
- package/src/__tests__/cacheInvalidationRecordData.test.ts +177 -0
- package/src/__tests__/conversationRunEvents.test.ts +399 -0
- package/src/__tests__/databaseAbstraction.test.ts +12 -6
- package/src/__tests__/getDataTokenRedaction.test.ts +7 -7
- package/src/__tests__/initializeProviders.refresh.test.ts +4 -4
- package/src/__tests__/magicLink.test.ts +73 -134
- package/src/__tests__/magicLinkService.consume.test.ts +224 -0
- package/src/__tests__/meetingRecordingRegistration.test.ts +12 -12
- package/src/__tests__/metadataRefreshSignal.test.ts +78 -0
- package/src/__tests__/multiTenancy.security.test.ts +40 -40
- package/src/__tests__/multiTenancy.test.ts +18 -18
- package/src/__tests__/newUsers.test.ts +24 -23
- package/src/__tests__/orphanedConversationDetailReconciler.test.ts +283 -0
- package/src/__tests__/principals.test.ts +22 -22
- package/src/__tests__/pushStatusFanOut.test.ts +206 -0
- package/src/__tests__/pushStatusResolver.filter.test.ts +8 -8
- package/src/__tests__/secretRedactor.failOpen.test.ts +12 -12
- package/src/__tests__/secretRedactor.test.ts +12 -12
- package/src/__tests__/serverExtensionReservedRoots.test.ts +7 -7
- package/src/__tests__/subscriptionRedaction.test.ts +5 -5
- package/src/__tests__/unifiedAuth.test.ts +15 -9
- package/src/__tests__/visitorIdentity.resolve.test.ts +6 -6
- package/src/__tests__/widget.test.ts +45 -45
- package/src/__tests__/widgetGuestElevation.test.ts +7 -7
- package/src/__tests__/widgetHostIdentity.test.ts +14 -14
- package/src/agentSessions/ReturningVisitorRecap.ts +12 -1
- package/src/agentSessions/SessionManager.ts +2 -2
- package/src/agentSessions/remoteBrowserGoalEngine.ts +30 -5
- package/src/auth/AuthProviderCatalogRouter.ts +6 -1
- package/src/auth/__tests__/backward-compatibility.test.ts +4 -3
- package/src/auth/index.ts +43 -28
- package/src/auth/initializeProviders.ts +25 -5
- package/src/auth/magicLink/MagicLinkRouter.ts +28 -10
- package/src/auth/magicLink/MagicLinkService.ts +71 -52
- package/src/auth/magicLink/index.ts +7 -7
- package/src/auth/magicLink/magicLinkCore.ts +96 -64
- package/src/auth/magicLink/redeemLanding.ts +14 -4
- package/src/auth/magicLink/types.ts +54 -54
- package/src/auth/newUsers.ts +1 -1
- package/src/auth/principals.ts +11 -2
- package/src/cache.ts +4 -1
- package/src/config.ts +37 -4
- package/src/context.ts +37 -24
- package/src/directives/Public.ts +4 -1
- package/src/directives/RequireSystemUser.ts +4 -1
- package/src/generated/graphql-schemas/__mj.ts +3674 -4
- package/src/generic/CacheInvalidationResolver.ts +148 -4
- package/src/generic/DeleteOptionsInput.ts +9 -2
- package/src/generic/FireAndForgetHeartbeat.ts +35 -16
- package/src/generic/OrphanedConversationDetailReconciler.ts +267 -0
- package/src/generic/PushStatusResolver.ts +118 -8
- package/src/generic/ResolverBase.ts +65 -57
- package/src/generic/RunViewResolver.ts +22 -22
- package/src/generic/__tests__/FireAndForgetHeartbeat.test.ts +7 -7
- package/src/index.ts +137 -11
- package/src/integration/CustomColumnPromoter.ts +6 -1
- package/src/integration/EntityMapLifecycle.ts +20 -2
- package/src/logging/NoLog.ts +16 -2
- package/src/logging/StartupLogger.ts +6 -1
- package/src/logging/bootAudit.ts +26 -16
- package/src/logging/boundaryLogPayload.ts +8 -1
- package/src/logging/secretRedactor.ts +14 -9
- package/src/logging/shortenForLog.ts +8 -3
- package/src/logging/variablesLoggingMiddleware.ts +9 -6
- package/src/metadataRefreshSignal.ts +45 -0
- package/src/middleware/MJTenantFilterMiddleware.ts +4 -4
- package/src/multiTenancy/index.ts +29 -5
- package/src/realtimeWidget/WidgetRouter.ts +15 -10
- package/src/realtimeWidget/WidgetSessionService.ts +76 -76
- package/src/realtimeWidget/host-identity.ts +24 -10
- package/src/realtimeWidget/index.ts +3 -3
- package/src/realtimeWidget/visitorIdentity.ts +39 -8
- package/src/realtimeWidget/widgetCore.ts +75 -10
- package/src/realtimeWidget/widgetGuestElevation.ts +25 -12
- package/src/resolvers/AdhocQueryResolver.ts +3 -3
- package/src/resolvers/AgentRunStatusPublisher.ts +175 -0
- package/src/resolvers/AgentSessionResolver.ts +3 -3
- package/src/resolvers/ColorResolver.ts +6 -6
- package/src/resolvers/ComponentRegistryResolver.ts +36 -36
- package/src/resolvers/ConversationRunEventsResolver.ts +308 -0
- package/src/resolvers/DuplicateEntryCheckResolver.ts +232 -0
- package/src/resolvers/EntityCommunicationsResolver.ts +4 -4
- package/src/resolvers/ExecuteRemoteOperationResolver.ts +9 -9
- package/src/resolvers/FetchEntityVectorsResolver.ts +2 -1
- package/src/resolvers/FileResolver.ts +42 -42
- package/src/resolvers/GeoResolver.ts +4 -4
- package/src/resolvers/GetDataResolver.ts +50 -15
- package/src/resolvers/IntegrationDiscoveryResolver.ts +2 -2
- package/src/resolvers/ListOperationsResolver.ts +78 -28
- package/src/resolvers/MCPResolver.ts +30 -15
- package/src/resolvers/MergeRecordsResolver.ts +14 -2
- package/src/resolvers/QueryResolver.ts +13 -13
- package/src/resolvers/RealtimeBridgeResolver.ts +3 -3
- package/src/resolvers/RealtimeClientSessionResolver.ts +96 -38
- package/src/resolvers/RemoteBrowserActionResolver.ts +5 -5
- package/src/resolvers/RunAIAgentResolver.ts +134 -239
- package/src/resolvers/RunAIPromptResolver.ts +22 -22
- package/src/resolvers/RunDecisionResolver.ts +435 -0
- package/src/resolvers/RunTemplateResolver.ts +4 -4
- package/src/resolvers/RunTestResolver.ts +25 -25
- package/src/resolvers/SqlLoggingConfigResolver.ts +56 -56
- package/src/resolvers/TaskGraphFrameResolver.ts +37 -28
- package/src/resolvers/TelemetryResolver.ts +79 -79
- package/src/resolvers/VectorizeEntityResolver.ts +16 -11
- package/src/resolvers/__tests__/GetDataResolver.tokens.test.ts +40 -38
- package/src/resolvers/__tests__/MergeRecordsResolver.test.ts +93 -0
- package/src/resolvers/__tests__/artifact-routing.test.ts +23 -23
- package/src/resolvers/adhoc-query-helpers.ts +19 -2
- package/src/resolvers/artifact-routing.ts +19 -19
- package/src/resolvers/meetingRecordingRegistration.ts +44 -10
- package/src/resolvers/peaksSidecar.ts +12 -2
- package/src/rest/EntityCRUDHandler.ts +24 -4
- package/src/rest/MediaStreamHandler.ts +11 -6
- package/src/rest/OAuthCallbackHandler.ts +25 -7
- package/src/rest/RESTEndpointHandler.ts +20 -15
- package/src/rest/RealtimeSdpBrokerHandler.ts +14 -4
- package/src/rest/SignatureWebhookHandler.ts +6 -1
- package/src/rest/UploadTokenManager.ts +66 -30
- package/src/rest/ViewOperationsHandler.ts +24 -4
- package/src/rest/mediaRange.ts +16 -6
- package/src/rest/setupRESTEndpoints.ts +24 -12
- package/src/serverExtensionReservedRoots.ts +6 -1
- package/src/services/TaskGraphActionRunner.ts +16 -15
- package/src/types.ts +21 -16
- package/src/util.ts +16 -6
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Arg, Field, ID, ObjectType, PubSubEngine, Resolver, ResolverFilterData, Root, Subscription } from 'type-graphql';
|
|
2
|
-
import { UUIDsEqual } from '@memberjunction/global';
|
|
2
|
+
import { MJGlobal, UUIDsEqual } from '@memberjunction/global';
|
|
3
3
|
import { UserPayload } from '../types.js';
|
|
4
4
|
|
|
5
5
|
export const PUSH_STATUS_UPDATES_TOPIC = 'PUSH_STATUS_UPDATES';
|
|
@@ -7,13 +7,13 @@ export const PUSH_STATUS_UPDATES_TOPIC = 'PUSH_STATUS_UPDATES';
|
|
|
7
7
|
@ObjectType()
|
|
8
8
|
export class PushStatusNotification {
|
|
9
9
|
@Field(() => String, { nullable: true })
|
|
10
|
-
message?: string;
|
|
10
|
+
message?: string; // case-violation-ok-legacy-back-compat: the property name is the GraphQL schema field name — renaming it breaks every client query
|
|
11
11
|
|
|
12
12
|
@Field((_type) => Date)
|
|
13
|
-
date!: Date;
|
|
13
|
+
date!: Date; // case-violation-ok-legacy-back-compat: the property name is the GraphQL schema field name — renaming it breaks every client query
|
|
14
14
|
|
|
15
15
|
@Field((_type) => ID)
|
|
16
|
-
sessionId!: string;
|
|
16
|
+
sessionId!: string; // case-violation-ok-legacy-back-compat: the property name is the GraphQL schema field name — renaming it breaks every client query
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
/**
|
|
@@ -32,6 +32,89 @@ export interface PushStatusNotificationPayload {
|
|
|
32
32
|
sessionId: string;
|
|
33
33
|
/** Authenticated user the update belongs to. Server-side filter key; never sent to the client. */
|
|
34
34
|
ownerUserId: string;
|
|
35
|
+
/**
|
|
36
|
+
* `MJGlobal.ProcessUUID` of the instance that first published this update. Used only for
|
|
37
|
+
* cross-instance echo suppression: an instance that receives its own message back from the
|
|
38
|
+
* message bus must drop it, or every push is delivered twice.
|
|
39
|
+
*/
|
|
40
|
+
SourceServerId?: string;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/** Receives every locally-published status update, for fan-out to other server instances. */
|
|
44
|
+
export type PushStatusPublishHook = (payload: PushStatusNotificationPayload) => void;
|
|
45
|
+
|
|
46
|
+
let _publishHook: PushStatusPublishHook | undefined;
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Register (or clear, with no arg) the cross-instance fan-out hook.
|
|
50
|
+
*
|
|
51
|
+
* Inversion of control, deliberately: this module must not know that Redis exists. The server
|
|
52
|
+
* registers the hook at startup only when a message bus is configured, so a single-instance
|
|
53
|
+
* deployment behaves exactly as before.
|
|
54
|
+
*/
|
|
55
|
+
export function SetPushStatusPublishHook(hook?: PushStatusPublishHook): void {
|
|
56
|
+
_publishHook = hook;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Validate an update received from the cross-instance message bus.
|
|
61
|
+
*
|
|
62
|
+
* The bus carries messages from other processes, so every field is type-checked before the update
|
|
63
|
+
* is republished onto the local topic, where {@link StatusUpdatesFilter} compares `ownerUserId`
|
|
64
|
+
* with the connection's user.
|
|
65
|
+
*
|
|
66
|
+
* @param raw The JSON text taken off the bus.
|
|
67
|
+
* @param localServerId This process's `MJGlobal.ProcessUUID`, used to drop our own echo.
|
|
68
|
+
* @returns The update to republish, or `null` to drop it.
|
|
69
|
+
*/
|
|
70
|
+
export function ParseReplicatedStatusUpdate(raw: string, localServerId: string): PushStatusNotificationPayload | null {
|
|
71
|
+
let parsed: unknown;
|
|
72
|
+
try {
|
|
73
|
+
parsed = JSON.parse(raw);
|
|
74
|
+
} catch {
|
|
75
|
+
return null;
|
|
76
|
+
}
|
|
77
|
+
if (typeof parsed !== 'object' || parsed === null) {
|
|
78
|
+
return null;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const { sessionId, ownerUserId, message, SourceServerId } = parsed as Record<string, unknown>;
|
|
82
|
+
const source = typeof SourceServerId === 'string' ? SourceServerId : undefined;
|
|
83
|
+
const body = typeof message === 'string' ? message : undefined;
|
|
84
|
+
if ((SourceServerId != null && source === undefined) || (message != null && body === undefined)) {
|
|
85
|
+
return null; // a present field of the wrong type
|
|
86
|
+
}
|
|
87
|
+
if (source === localServerId) {
|
|
88
|
+
return null; // our own message, echoed back
|
|
89
|
+
}
|
|
90
|
+
if (typeof sessionId !== 'string' || !sessionId || typeof ownerUserId !== 'string' || !ownerUserId) {
|
|
91
|
+
return null; // fail closed: an update with no valid identity can never be routed safely
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
return { sessionId, ownerUserId, message: body, SourceServerId: source };
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Whether an update is worth sending to other server instances.
|
|
99
|
+
*
|
|
100
|
+
* Streaming content is excluded. It arrives at hundreds of messages per second and each one carries
|
|
101
|
+
* a full serialized agent run, so replicating it would cost far more bandwidth than the delivery it
|
|
102
|
+
* buys — and a token delta is worthless to a client that has already missed the ones before it.
|
|
103
|
+
* Progress and completion are low-rate and each is individually meaningful, which is exactly the
|
|
104
|
+
* kind of message that must survive landing on the wrong replica.
|
|
105
|
+
*/
|
|
106
|
+
export function ShouldReplicateStatusUpdate(message?: string): boolean {
|
|
107
|
+
if (!message) {
|
|
108
|
+
return false;
|
|
109
|
+
}
|
|
110
|
+
try {
|
|
111
|
+
const parsed: unknown = JSON.parse(message);
|
|
112
|
+
const type = (parsed as { type?: unknown })?.type;
|
|
113
|
+
return type !== 'StreamingContent';
|
|
114
|
+
} catch {
|
|
115
|
+
// A plain-string message (not the resolvers' JSON envelope) is rare and low-rate. Replicate it.
|
|
116
|
+
return true;
|
|
117
|
+
}
|
|
35
118
|
}
|
|
36
119
|
|
|
37
120
|
interface PushStatusNotificationArgs {
|
|
@@ -55,13 +138,31 @@ export interface StatusUpdateParams {
|
|
|
55
138
|
* payload shape and the required-identity guarantee live in exactly one place. Adding a field to
|
|
56
139
|
* the push is a one-line change here; a new publisher physically cannot omit `ownerUserId`.
|
|
57
140
|
*/
|
|
58
|
-
export function
|
|
141
|
+
export function PublishStatusUpdate(pubSub: PubSubEngine, params: StatusUpdateParams): void {
|
|
59
142
|
const payload: PushStatusNotificationPayload = {
|
|
60
143
|
sessionId: params.sessionId,
|
|
61
144
|
ownerUserId: params.ownerUserId,
|
|
62
145
|
message: params.message,
|
|
146
|
+
SourceServerId: MJGlobal.Instance.ProcessUUID,
|
|
63
147
|
};
|
|
64
148
|
pubSub.publish(PUSH_STATUS_UPDATES_TOPIC, payload);
|
|
149
|
+
|
|
150
|
+
// Fan out to other instances. Only reached by updates originating HERE: a message arriving from
|
|
151
|
+
// the bus is republished straight onto the local topic and never comes back through this
|
|
152
|
+
// function, so there is no loop to break beyond the SourceServerId check on the receiving side.
|
|
153
|
+
if (_publishHook && ShouldReplicateStatusUpdate(params.message)) {
|
|
154
|
+
try {
|
|
155
|
+
_publishHook(payload);
|
|
156
|
+
} catch {
|
|
157
|
+
// Fan-out is an optimization over the durable tail query. A message bus that is down must
|
|
158
|
+
// never break local delivery, which is the path that works for the common case.
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/** @deprecated Use {@link PublishStatusUpdate}. */
|
|
164
|
+
export function publishStatusUpdate(pubSub: PubSubEngine, params: StatusUpdateParams): void {
|
|
165
|
+
return PublishStatusUpdate(pubSub, params);
|
|
65
166
|
}
|
|
66
167
|
|
|
67
168
|
/** Minimal shape of the subscription's connection context needed by the filter. */
|
|
@@ -82,7 +183,7 @@ export interface StatusUpdatesFilterContext {
|
|
|
82
183
|
* `sessionId` is no longer sufficient. Fails CLOSED — a missing owner or connection identity never
|
|
83
184
|
* matches.
|
|
84
185
|
*/
|
|
85
|
-
export function
|
|
186
|
+
export function StatusUpdatesFilter(data: {
|
|
86
187
|
payload: PushStatusNotificationPayload;
|
|
87
188
|
args: PushStatusNotificationArgs;
|
|
88
189
|
context: StatusUpdatesFilterContext | undefined;
|
|
@@ -98,14 +199,23 @@ export function statusUpdatesFilter(data: {
|
|
|
98
199
|
return UUIDsEqual(payload.ownerUserId, connectionUserId);
|
|
99
200
|
}
|
|
100
201
|
|
|
202
|
+
/** @deprecated Use {@link StatusUpdatesFilter}. */
|
|
203
|
+
export function statusUpdatesFilter(data: {
|
|
204
|
+
payload: PushStatusNotificationPayload;
|
|
205
|
+
args: PushStatusNotificationArgs;
|
|
206
|
+
context: StatusUpdatesFilterContext | undefined;
|
|
207
|
+
}): boolean {
|
|
208
|
+
return StatusUpdatesFilter(data);
|
|
209
|
+
}
|
|
210
|
+
|
|
101
211
|
@Resolver()
|
|
102
212
|
export class PushStatusResolver {
|
|
103
213
|
@Subscription(() => PushStatusNotification, {
|
|
104
214
|
topics: PUSH_STATUS_UPDATES_TOPIC,
|
|
105
215
|
filter: (data: ResolverFilterData<PushStatusNotificationPayload, PushStatusNotificationArgs, StatusUpdatesFilterContext>) =>
|
|
106
|
-
|
|
216
|
+
StatusUpdatesFilter(data),
|
|
107
217
|
})
|
|
108
|
-
statusUpdates(
|
|
218
|
+
statusUpdates( // case-violation-ok-legacy-back-compat: the property name is the GraphQL schema field name — renaming it breaks every client query
|
|
109
219
|
@Root() { message }: PushStatusNotificationPayload,
|
|
110
220
|
@Arg('sessionId', () => String) sessionId: string
|
|
111
221
|
): PushStatusNotification {
|
|
@@ -4,6 +4,7 @@ import {
|
|
|
4
4
|
BaseEntityEvent,
|
|
5
5
|
CompositeKey,
|
|
6
6
|
DatabaseProviderBase,
|
|
7
|
+
EntityFieldInfo,
|
|
7
8
|
EntityFieldTSType,
|
|
8
9
|
EntityInfo,
|
|
9
10
|
EntityPermissionType,
|
|
@@ -40,8 +41,8 @@ import { MJEvent, MJEventType, MJGlobal, ENCRYPTED_SENTINEL, EscapeSQLString, Is
|
|
|
40
41
|
import { SQLParser } from '@memberjunction/sql-parser';
|
|
41
42
|
import { PostgreSQLDialect, SQLServerDialect, type SQLParserDialect } from '@memberjunction/sql-dialect';
|
|
42
43
|
import { EncryptionEngine } from '@memberjunction/encryption';
|
|
43
|
-
import { PUSH_STATUS_UPDATES_TOPIC,
|
|
44
|
-
import { CACHE_INVALIDATION_TOPIC } from './CacheInvalidationResolver.js';
|
|
44
|
+
import { PUSH_STATUS_UPDATES_TOPIC, PublishStatusUpdate } from './PushStatusResolver.js';
|
|
45
|
+
import { CACHE_INVALIDATION_TOPIC, MayBroadcastRecordData } from './CacheInvalidationResolver.js';
|
|
45
46
|
import { PubSubManager } from './PubSubManager.js';
|
|
46
47
|
import { FieldMapper } from '@memberjunction/graphql-dataprovider';
|
|
47
48
|
import { Subscription } from 'rxjs';
|
|
@@ -51,7 +52,7 @@ export class ResolverBase {
|
|
|
51
52
|
private static _cloudeventsHeaders = process.env.CLOUDEVENTS_HTTP_HEADERS ? JSON.parse(process.env.CLOUDEVENTS_HTTP_HEADERS) : {};
|
|
52
53
|
|
|
53
54
|
private static _eventSubscriptionKey: string = '___MJServer___ResolverBase___EventSubscriptions';
|
|
54
|
-
private get
|
|
55
|
+
private get eventSubscriptions(): Map<string, Subscription> {
|
|
55
56
|
// here we use the global object store instead of a static member becuase in some cases based on import code paths/bundling/etc, the static member
|
|
56
57
|
// could actually be duplicated and we'd end up with multiple instances of the same map, which would be bad.
|
|
57
58
|
const g = MJGlobal.Instance.GetGlobalObjectStore();
|
|
@@ -1450,16 +1451,24 @@ export class ResolverBase {
|
|
|
1450
1451
|
* Publishes a CACHE_INVALIDATION event to connected browser clients after a successful
|
|
1451
1452
|
* entity save or delete. Includes the originSessionId so the originating browser can
|
|
1452
1453
|
* skip redundant re-fetches (it already handled the event locally).
|
|
1454
|
+
*
|
|
1455
|
+
* The row itself rides along ONLY for entities opted in via
|
|
1456
|
+
* `cacheSettings.recordDataBroadcastEntities` — this event reaches every connected client
|
|
1457
|
+
* unfiltered, so the row would otherwise be readable by sessions that could not read the record.
|
|
1453
1458
|
*/
|
|
1454
1459
|
protected PublishCacheInvalidation(entityObject: BaseEntity, action: 'save' | 'delete', userPayload: UserPayload): void {
|
|
1460
|
+
const entityName = entityObject.EntityInfo.Name;
|
|
1455
1461
|
PubSubManager.Instance.Publish(CACHE_INVALIDATION_TOPIC, {
|
|
1456
|
-
entityName
|
|
1462
|
+
entityName,
|
|
1457
1463
|
primaryKeyValues: JSON.stringify(entityObject.PrimaryKey.KeyValuePairs),
|
|
1458
1464
|
action,
|
|
1459
1465
|
sourceServerId: MJGlobal.Instance.ProcessUUID,
|
|
1460
1466
|
timestamp: new Date(),
|
|
1461
1467
|
originSessionId: userPayload?.sessionId || null,
|
|
1462
|
-
recordData:
|
|
1468
|
+
recordData:
|
|
1469
|
+
action === 'save' && MayBroadcastRecordData(entityName)
|
|
1470
|
+
? JSON.stringify(entityObject.GetAll())
|
|
1471
|
+
: undefined,
|
|
1463
1472
|
});
|
|
1464
1473
|
}
|
|
1465
1474
|
|
|
@@ -1472,7 +1481,7 @@ export class ResolverBase {
|
|
|
1472
1481
|
* `publishStatusUpdate()` function directly with an explicit `ownerUserId`.
|
|
1473
1482
|
*/
|
|
1474
1483
|
protected PublishStatusUpdate(pubSub: PubSubEngine, sessionId: string, message: string | undefined, userPayload: UserPayload): void {
|
|
1475
|
-
|
|
1484
|
+
PublishStatusUpdate(pubSub, {
|
|
1476
1485
|
sessionId,
|
|
1477
1486
|
ownerUserId: userPayload?.userRecord?.ID ?? '',
|
|
1478
1487
|
message,
|
|
@@ -1485,7 +1494,7 @@ export class ResolverBase {
|
|
|
1485
1494
|
// cause issues with multiple messages for the same event.
|
|
1486
1495
|
const uniqueKey = entityObject.EntityInfo.Name;
|
|
1487
1496
|
|
|
1488
|
-
if (!this.
|
|
1497
|
+
if (!this.eventSubscriptions.has(uniqueKey)) {
|
|
1489
1498
|
// listen for events from the entityObject in case it is a long running task and we can push messages back to the client via pubSub
|
|
1490
1499
|
LogDebug(`ResolverBase.ListenForEntityMessages: About to call MJGlobal.Instance.GetEventListener() to get the event listener subscription for ${uniqueKey}`);
|
|
1491
1500
|
const theSub = MJGlobal.Instance.GetEventListener(false).subscribe(async (event: MJEvent) => {
|
|
@@ -1518,7 +1527,7 @@ export class ResolverBase {
|
|
|
1518
1527
|
}
|
|
1519
1528
|
}
|
|
1520
1529
|
});
|
|
1521
|
-
this.
|
|
1530
|
+
this.eventSubscriptions.set(uniqueKey, theSub);
|
|
1522
1531
|
}
|
|
1523
1532
|
}
|
|
1524
1533
|
|
|
@@ -1684,7 +1693,7 @@ export class ResolverBase {
|
|
|
1684
1693
|
await this.TestAndSetClientOldValuesToDBValues(input, clientNewValues, entityObject, userInfo);
|
|
1685
1694
|
} else {
|
|
1686
1695
|
// no OldValues, so we can just set the new values from input
|
|
1687
|
-
entityObject.SetMany(
|
|
1696
|
+
entityObject.SetMany(clientNewValues);
|
|
1688
1697
|
}
|
|
1689
1698
|
} else {
|
|
1690
1699
|
// Use a generic message to avoid leaking whether a record exists — distinguishing
|
|
@@ -1696,8 +1705,11 @@ export class ResolverBase {
|
|
|
1696
1705
|
} else {
|
|
1697
1706
|
// we get here if we are NOT tracking changes and we DO have OldValues, so we can load from them
|
|
1698
1707
|
const oldValues = {};
|
|
1699
|
-
// for each item in the oldValues array, add it to the oldValues object
|
|
1700
|
-
input.OldValues___?.forEach((item) =>
|
|
1708
|
+
// for each item in the oldValues array, add it to the oldValues object, typed like the field
|
|
1709
|
+
input.OldValues___?.forEach((item) => {
|
|
1710
|
+
const field = entityObject.EntityInfo.Fields.find((f) => f.CodeName === item.Key);
|
|
1711
|
+
oldValues[item.Key] = field ? this.ClientOldValueToFieldValue(field, item.Value) : item.Value;
|
|
1712
|
+
});
|
|
1701
1713
|
|
|
1702
1714
|
// 1) load the old values, this will be the initial state of the object
|
|
1703
1715
|
await entityObject.LoadFromData(oldValues);
|
|
@@ -1764,6 +1776,11 @@ export class ResolverBase {
|
|
|
1764
1776
|
*
|
|
1765
1777
|
* Ordered so the boolean flag is evaluated last: the extra load lands only on entities that have
|
|
1766
1778
|
* the feature switched on, which is almost none of them.
|
|
1779
|
+
*
|
|
1780
|
+
* `MJ: Record Changes` always loads from the database: its server class allows an update only when
|
|
1781
|
+
* Comments is the one dirty field, and "dirty" compared against client-supplied OldValues lets a
|
|
1782
|
+
* caller pin forged audit columns as both old and new values. It doesn't track its own changes, so
|
|
1783
|
+
* nothing else forces the load.
|
|
1767
1784
|
*/
|
|
1768
1785
|
protected MustLoadTruthFromDatabase(
|
|
1769
1786
|
entityInfo: EntityInfo,
|
|
@@ -1773,6 +1790,7 @@ export class ResolverBase {
|
|
|
1773
1790
|
): boolean {
|
|
1774
1791
|
return (
|
|
1775
1792
|
entityInfo.TrackRecordChanges ||
|
|
1793
|
+
entityInfo.Name.trim().toLowerCase() === 'mj: record changes' ||
|
|
1776
1794
|
!input.OldValues___ ||
|
|
1777
1795
|
hasDeniedReadFields ||
|
|
1778
1796
|
hasNarrowedAuditPayload ||
|
|
@@ -1909,6 +1927,41 @@ export class ResolverBase {
|
|
|
1909
1927
|
return true;
|
|
1910
1928
|
}
|
|
1911
1929
|
|
|
1930
|
+
/**
|
|
1931
|
+
* Converts one client-sent old value (always a string on the wire; dates as epoch milliseconds,
|
|
1932
|
+
* the GraphQL Timestamp form) to the field's TypeScript type, so it compares equal to the stored
|
|
1933
|
+
* value. Both the OldValues comparison and the load-from-OldValues path use it: without it a date
|
|
1934
|
+
* old value becomes an Invalid Date and an unchanged date field reads as edited.
|
|
1935
|
+
*/
|
|
1936
|
+
protected ClientOldValueToFieldValue(field: EntityFieldInfo | undefined, raw: unknown): string | number | boolean | Date | null {
|
|
1937
|
+
let val: unknown = raw;
|
|
1938
|
+
if ((val === null || val === undefined) && field && field.DefaultValue !== null && field.DefaultValue !== undefined && !field.AllowsNull)
|
|
1939
|
+
val = field.DefaultValue; // set default value as the field was never set and it does NOT allow nulls
|
|
1940
|
+
if (val === undefined) val = null;
|
|
1941
|
+
// A null old value stays null, except a boolean, which the comparison has always read as false.
|
|
1942
|
+
if (field?.TSType === EntityFieldTSType.Boolean && val === null) return false;
|
|
1943
|
+
if (val === null) return null;
|
|
1944
|
+
if (!field) {
|
|
1945
|
+
// No field metadata to convert by: pass scalars through, stringify anything else.
|
|
1946
|
+
return typeof val === 'string' || typeof val === 'number' || typeof val === 'boolean' || val instanceof Date ? val : String(val);
|
|
1947
|
+
}
|
|
1948
|
+
|
|
1949
|
+
const text = String(val);
|
|
1950
|
+
switch (field.TSType) {
|
|
1951
|
+
case EntityFieldTSType.Number: {
|
|
1952
|
+
const integer = ['int', 'smallint', 'bigint', 'tinyint'].includes((field.Type as string).toLowerCase());
|
|
1953
|
+
return integer ? parseInt(text) : parseFloat(text);
|
|
1954
|
+
}
|
|
1955
|
+
case EntityFieldTSType.Boolean:
|
|
1956
|
+
return !(text === 'false' || text === '0' || parseInt(text) === 0);
|
|
1957
|
+
case EntityFieldTSType.Date:
|
|
1958
|
+
// Epoch milliseconds (the GraphQL Timestamp form) arrive as a numeric string.
|
|
1959
|
+
return new Date(text.trim() !== '' && !isNaN(Number(text)) ? parseInt(text) : text);
|
|
1960
|
+
default:
|
|
1961
|
+
return text; // already a string
|
|
1962
|
+
}
|
|
1963
|
+
}
|
|
1964
|
+
|
|
1912
1965
|
/**
|
|
1913
1966
|
* This routine compares the OldValues property in the input object to the values in the DB that we just loaded. If there are differences, we need to check to see if the client
|
|
1914
1967
|
* is trying to update any of those fields (e.g. overlap). If there is overlap, we throw an error. If there is no overlap, we can proceed with the update even if the DB Values
|
|
@@ -1923,52 +1976,7 @@ export class ResolverBase {
|
|
|
1923
1976
|
input.OldValues___.forEach((item) => {
|
|
1924
1977
|
// we need to do a quick transform on the values to make sure they match the TS Type for the given field because item.Value will always be a string
|
|
1925
1978
|
const field = entityObject.EntityInfo.Fields.find((f) => f.CodeName === item.Key);
|
|
1926
|
-
|
|
1927
|
-
if ((val === null || val === undefined) && field.DefaultValue !== null && field.DefaultValue !== undefined && !field.AllowsNull)
|
|
1928
|
-
val = field.DefaultValue; // set default value as the field was never set and it does NOT allow nulls
|
|
1929
|
-
|
|
1930
|
-
if (field) {
|
|
1931
|
-
switch (field.TSType) {
|
|
1932
|
-
case EntityFieldTSType.Number:
|
|
1933
|
-
if (val == null && val == undefined) {
|
|
1934
|
-
val = null;
|
|
1935
|
-
}
|
|
1936
|
-
else {
|
|
1937
|
-
let typeLowered = (field.Type as string).toLowerCase();
|
|
1938
|
-
|
|
1939
|
-
switch (typeLowered) {
|
|
1940
|
-
case 'int':
|
|
1941
|
-
case 'smallint':
|
|
1942
|
-
case 'bigint':
|
|
1943
|
-
case 'tinyint':
|
|
1944
|
-
val = parseInt(val);
|
|
1945
|
-
break;
|
|
1946
|
-
case 'money':
|
|
1947
|
-
case 'smallmoney':
|
|
1948
|
-
case 'decimal':
|
|
1949
|
-
case 'numeric':
|
|
1950
|
-
case 'float':
|
|
1951
|
-
val = parseFloat(val);
|
|
1952
|
-
break;
|
|
1953
|
-
default:
|
|
1954
|
-
val = parseFloat(val);
|
|
1955
|
-
break;
|
|
1956
|
-
}
|
|
1957
|
-
}
|
|
1958
|
-
break;
|
|
1959
|
-
case EntityFieldTSType.Boolean:
|
|
1960
|
-
val = val === null || val === undefined || val === 'false' || val === '0' || parseInt(val) === 0 ? false : true;
|
|
1961
|
-
break;
|
|
1962
|
-
case EntityFieldTSType.Date:
|
|
1963
|
-
// first, if val is a string and it is actually a number (milliseconds since epoch), convert it to a number.
|
|
1964
|
-
if (val !== null && val !== undefined && val.toString().trim() !== '' && !isNaN(val)) val = parseInt(val);
|
|
1965
|
-
|
|
1966
|
-
val = val !== null && val !== undefined ? new Date(val) : null;
|
|
1967
|
-
break;
|
|
1968
|
-
default:
|
|
1969
|
-
break; // already a string
|
|
1970
|
-
}
|
|
1971
|
-
}
|
|
1979
|
+
const val = this.ClientOldValueToFieldValue(field, item.Value);
|
|
1972
1980
|
clientOldValues[item.Key] = val;
|
|
1973
1981
|
});
|
|
1974
1982
|
|
|
@@ -45,13 +45,13 @@ export class AggregateExpressionInput {
|
|
|
45
45
|
@Field(() => String, {
|
|
46
46
|
description: 'SQL expression for the aggregate (e.g., "SUM(OrderTotal)", "COUNT(*)", "AVG(Price)")'
|
|
47
47
|
})
|
|
48
|
-
expression: string;
|
|
48
|
+
expression: string; // case-violation-ok-legacy-back-compat: the property name is the GraphQL schema field name — renaming it breaks every client query
|
|
49
49
|
|
|
50
50
|
@Field(() => String, {
|
|
51
51
|
nullable: true,
|
|
52
52
|
description: 'Optional alias for the result (used in error messages and debugging)'
|
|
53
53
|
})
|
|
54
|
-
alias?: string;
|
|
54
|
+
alias?: string; // case-violation-ok-legacy-back-compat: the property name is the GraphQL schema field name — renaming it breaks every client query
|
|
55
55
|
}
|
|
56
56
|
|
|
57
57
|
/**
|
|
@@ -60,22 +60,22 @@ export class AggregateExpressionInput {
|
|
|
60
60
|
@ObjectType()
|
|
61
61
|
export class AggregateResultOutput {
|
|
62
62
|
@Field(() => String, { description: 'The expression that was calculated' })
|
|
63
|
-
expression: string;
|
|
63
|
+
expression: string; // case-violation-ok-legacy-back-compat: the property name is the GraphQL schema field name — renaming it breaks every client query
|
|
64
64
|
|
|
65
65
|
@Field(() => String, { description: 'The alias (or expression if no alias provided)' })
|
|
66
|
-
alias: string;
|
|
66
|
+
alias: string; // case-violation-ok-legacy-back-compat: the property name is the GraphQL schema field name — renaming it breaks every client query
|
|
67
67
|
|
|
68
68
|
@Field(() => String, {
|
|
69
69
|
nullable: true,
|
|
70
70
|
description: 'The calculated value as a JSON string (preserves type information)'
|
|
71
71
|
})
|
|
72
|
-
value?: string;
|
|
72
|
+
value?: string; // case-violation-ok-legacy-back-compat: the property name is the GraphQL schema field name — renaming it breaks every client query
|
|
73
73
|
|
|
74
74
|
@Field(() => String, {
|
|
75
75
|
nullable: true,
|
|
76
76
|
description: 'Error message if calculation failed'
|
|
77
77
|
})
|
|
78
|
-
error?: string;
|
|
78
|
+
error?: string; // case-violation-ok-legacy-back-compat: the property name is the GraphQL schema field name — renaming it breaks every client query
|
|
79
79
|
}
|
|
80
80
|
|
|
81
81
|
//****************************************************************************
|
|
@@ -573,22 +573,22 @@ export class RunViewGenericInput {
|
|
|
573
573
|
@InputType()
|
|
574
574
|
export class RunViewCacheStatusInput {
|
|
575
575
|
@Field(() => String, { description: 'The maximum __mj_UpdatedAt value from cached results' })
|
|
576
|
-
maxUpdatedAt: string;
|
|
576
|
+
maxUpdatedAt: string; // case-violation-ok-legacy-back-compat: the property name is the GraphQL schema field name — renaming it breaks every client query
|
|
577
577
|
|
|
578
578
|
@Field(() => Int, { description: 'The number of rows in cached results' })
|
|
579
|
-
rowCount: number;
|
|
579
|
+
rowCount: number; // case-violation-ok-legacy-back-compat: the property name is the GraphQL schema field name — renaming it breaks every client query
|
|
580
580
|
}
|
|
581
581
|
|
|
582
582
|
@InputType()
|
|
583
583
|
export class RunViewWithCacheCheckInput {
|
|
584
584
|
@Field(() => RunDynamicViewInput, { description: 'The RunView parameters' })
|
|
585
|
-
params: RunDynamicViewInput;
|
|
585
|
+
params: RunDynamicViewInput; // case-violation-ok-legacy-back-compat: the property name is the GraphQL schema field name — renaming it breaks every client query
|
|
586
586
|
|
|
587
587
|
@Field(() => RunViewCacheStatusInput, {
|
|
588
588
|
nullable: true,
|
|
589
589
|
description: 'Optional cache status - if provided, server will check if cache is current'
|
|
590
590
|
})
|
|
591
|
-
cacheStatus?: RunViewCacheStatusInput;
|
|
591
|
+
cacheStatus?: RunViewCacheStatusInput; // case-violation-ok-legacy-back-compat: the property name is the GraphQL schema field name — renaming it breaks every client query
|
|
592
592
|
}
|
|
593
593
|
|
|
594
594
|
@ObjectType()
|
|
@@ -596,21 +596,21 @@ export class DifferentialDataOutput {
|
|
|
596
596
|
@Field(() => [RunViewGenericResultRow], {
|
|
597
597
|
description: 'Records that have been created or updated since the client\'s maxUpdatedAt'
|
|
598
598
|
})
|
|
599
|
-
updatedRows: RunViewGenericResultRow[];
|
|
599
|
+
updatedRows: RunViewGenericResultRow[]; // case-violation-ok-legacy-back-compat: the property name is the GraphQL schema field name — renaming it breaks every client query
|
|
600
600
|
|
|
601
601
|
@Field(() => [String], {
|
|
602
602
|
description: 'Primary key values (as concatenated strings) of records that have been deleted'
|
|
603
603
|
})
|
|
604
|
-
deletedRecordIDs: string[];
|
|
604
|
+
deletedRecordIDs: string[]; // case-violation-ok-legacy-back-compat: the property name is the GraphQL schema field name — renaming it breaks every client query
|
|
605
605
|
}
|
|
606
606
|
|
|
607
607
|
@ObjectType()
|
|
608
608
|
export class RunViewWithCacheCheckResultOutput {
|
|
609
609
|
@Field(() => Int, { description: 'The index of this view in the batch request' })
|
|
610
|
-
viewIndex: number;
|
|
610
|
+
viewIndex: number; // case-violation-ok-legacy-back-compat: the property name is the GraphQL schema field name — renaming it breaks every client query
|
|
611
611
|
|
|
612
612
|
@Field(() => String, { description: "'current', 'differential', 'stale', or 'error'" })
|
|
613
|
-
status: string;
|
|
613
|
+
status: string; // case-violation-ok-legacy-back-compat: the property name is the GraphQL schema field name — renaming it breaks every client query
|
|
614
614
|
|
|
615
615
|
@Field(() => [RunViewGenericResultRow], {
|
|
616
616
|
nullable: true,
|
|
@@ -622,34 +622,34 @@ export class RunViewWithCacheCheckResultOutput {
|
|
|
622
622
|
nullable: true,
|
|
623
623
|
description: 'Differential update data - only populated when status is differential'
|
|
624
624
|
})
|
|
625
|
-
differentialData?: DifferentialDataOutput;
|
|
625
|
+
differentialData?: DifferentialDataOutput; // case-violation-ok-legacy-back-compat: the property name is the GraphQL schema field name — renaming it breaks every client query
|
|
626
626
|
|
|
627
627
|
@Field(() => String, { nullable: true, description: 'Max __mj_UpdatedAt from results when stale or differential' })
|
|
628
|
-
maxUpdatedAt?: string;
|
|
628
|
+
maxUpdatedAt?: string; // case-violation-ok-legacy-back-compat: the property name is the GraphQL schema field name — renaming it breaks every client query
|
|
629
629
|
|
|
630
630
|
@Field(() => Int, { nullable: true, description: 'Row count of results when stale or differential (total after applying delta)' })
|
|
631
|
-
rowCount?: number;
|
|
631
|
+
rowCount?: number; // case-violation-ok-legacy-back-compat: the property name is the GraphQL schema field name — renaming it breaks every client query
|
|
632
632
|
|
|
633
633
|
@Field(() => String, { nullable: true, description: 'Error message if status is error' })
|
|
634
|
-
errorMessage?: string;
|
|
634
|
+
errorMessage?: string; // case-violation-ok-legacy-back-compat: the property name is the GraphQL schema field name — renaming it breaks every client query
|
|
635
635
|
|
|
636
636
|
@Field(() => [AggregateResultOutput], {
|
|
637
637
|
nullable: true,
|
|
638
638
|
description: 'Aggregate results when status is stale and aggregates were requested (B40 — previously never marshalled, so CacheLocal callers got no aggregates at all)'
|
|
639
639
|
})
|
|
640
|
-
aggregateResults?: AggregateResultOutput[];
|
|
640
|
+
aggregateResults?: AggregateResultOutput[]; // case-violation-ok-legacy-back-compat: the property name is the GraphQL schema field name — renaming it breaks every client query
|
|
641
641
|
}
|
|
642
642
|
|
|
643
643
|
@ObjectType()
|
|
644
644
|
export class RunViewsWithCacheCheckOutput {
|
|
645
645
|
@Field(() => Boolean, { description: 'Whether the overall operation succeeded' })
|
|
646
|
-
success: boolean;
|
|
646
|
+
success: boolean; // case-violation-ok-legacy-back-compat: the property name is the GraphQL schema field name — renaming it breaks every client query
|
|
647
647
|
|
|
648
648
|
@Field(() => [RunViewWithCacheCheckResultOutput], { description: 'Results for each view in the batch' })
|
|
649
|
-
results: RunViewWithCacheCheckResultOutput[];
|
|
649
|
+
results: RunViewWithCacheCheckResultOutput[]; // case-violation-ok-legacy-back-compat: the property name is the GraphQL schema field name — renaming it breaks every client query
|
|
650
650
|
|
|
651
651
|
@Field(() => String, { nullable: true, description: 'Overall error message if success is false' })
|
|
652
|
-
errorMessage?: string;
|
|
652
|
+
errorMessage?: string; // case-violation-ok-legacy-back-compat: the property name is the GraphQL schema field name — renaming it breaks every client query
|
|
653
653
|
}
|
|
654
654
|
|
|
655
655
|
//****************************************************************************
|
|
@@ -2,7 +2,7 @@ import 'reflect-metadata';
|
|
|
2
2
|
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
|
3
3
|
import { PubSubEngine } from 'type-graphql';
|
|
4
4
|
import {
|
|
5
|
-
|
|
5
|
+
StartLivenessPulse,
|
|
6
6
|
DEFAULT_PULSE_INTERVAL_MS,
|
|
7
7
|
HEARTBEAT_MESSAGE_TYPE,
|
|
8
8
|
} from '../FireAndForgetHeartbeat';
|
|
@@ -31,14 +31,14 @@ describe('startLivenessPulse', () => {
|
|
|
31
31
|
|
|
32
32
|
it('does not publish before the first interval elapses', () => {
|
|
33
33
|
const pubSub = makePubSub();
|
|
34
|
-
|
|
34
|
+
StartLivenessPulse({ pubSub, sessionId: 's1', resolver: 'TestResolver' });
|
|
35
35
|
vi.advanceTimersByTime(DEFAULT_PULSE_INTERVAL_MS - 1);
|
|
36
36
|
expect(pubSub.publish).not.toHaveBeenCalled();
|
|
37
37
|
});
|
|
38
38
|
|
|
39
39
|
it('publishes a heartbeat on the push-status topic each interval', () => {
|
|
40
40
|
const pubSub = makePubSub();
|
|
41
|
-
|
|
41
|
+
StartLivenessPulse({ pubSub, sessionId: 's1', resolver: 'TestResolver', intervalMs: 1000 });
|
|
42
42
|
|
|
43
43
|
vi.advanceTimersByTime(3000);
|
|
44
44
|
|
|
@@ -52,7 +52,7 @@ describe('startLivenessPulse', () => {
|
|
|
52
52
|
|
|
53
53
|
it('includes the enriched status snapshot from readStatus', () => {
|
|
54
54
|
const pubSub = makePubSub();
|
|
55
|
-
|
|
55
|
+
StartLivenessPulse({
|
|
56
56
|
pubSub,
|
|
57
57
|
sessionId: 's1',
|
|
58
58
|
resolver: 'RunAIAgentResolver',
|
|
@@ -68,7 +68,7 @@ describe('startLivenessPulse', () => {
|
|
|
68
68
|
|
|
69
69
|
it('publishes an empty data object when readStatus throws (loop survives)', () => {
|
|
70
70
|
const pubSub = makePubSub();
|
|
71
|
-
|
|
71
|
+
StartLivenessPulse({
|
|
72
72
|
pubSub,
|
|
73
73
|
sessionId: 's1',
|
|
74
74
|
resolver: 'RunAIAgentResolver',
|
|
@@ -87,12 +87,12 @@ describe('startLivenessPulse', () => {
|
|
|
87
87
|
|
|
88
88
|
it('stops publishing after stop() is called', () => {
|
|
89
89
|
const pubSub = makePubSub();
|
|
90
|
-
const handle =
|
|
90
|
+
const handle = StartLivenessPulse({ pubSub, sessionId: 's1', resolver: 'TestResolver', intervalMs: 1000 });
|
|
91
91
|
|
|
92
92
|
vi.advanceTimersByTime(2000);
|
|
93
93
|
expect(pubSub.publish).toHaveBeenCalledTimes(2);
|
|
94
94
|
|
|
95
|
-
handle.
|
|
95
|
+
handle.Stop();
|
|
96
96
|
vi.advanceTimersByTime(5000);
|
|
97
97
|
expect(pubSub.publish).toHaveBeenCalledTimes(2);
|
|
98
98
|
});
|