@memberjunction/server 6.2.0-edge.5 → 6.2.0-edge.6
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 +27 -1
- package/dist/.tsbuildinfo +1 -1
- package/dist/agentSessions/SessionJanitor.d.ts +3 -3
- package/dist/agentSessions/SessionJanitor.js +3 -3
- package/dist/agentSessions/SessionManager.d.ts +15 -5
- package/dist/agentSessions/SessionManager.d.ts.map +1 -1
- package/dist/agentSessions/SessionManager.js +34 -12
- package/dist/agentSessions/SessionManager.js.map +1 -1
- package/dist/auth/AuthProviderCatalogRouter.d.ts.map +1 -1
- package/dist/auth/AuthProviderCatalogRouter.js +2 -0
- package/dist/auth/AuthProviderCatalogRouter.js.map +1 -1
- package/dist/auth/index.d.ts.map +1 -1
- package/dist/auth/index.js +51 -10
- package/dist/auth/index.js.map +1 -1
- package/dist/auth/initializeProviders.d.ts +10 -0
- package/dist/auth/initializeProviders.d.ts.map +1 -1
- package/dist/auth/initializeProviders.js +37 -1
- package/dist/auth/initializeProviders.js.map +1 -1
- package/dist/auth/localDevAuth.d.ts +79 -0
- package/dist/auth/localDevAuth.d.ts.map +1 -0
- package/dist/auth/localDevAuth.js +439 -0
- package/dist/auth/localDevAuth.js.map +1 -0
- package/dist/auth/magicLink/MagicLinkKeys.d.ts +2 -2
- package/dist/auth/magicLink/MagicLinkKeys.d.ts.map +1 -1
- package/dist/auth/magicLink/MagicLinkKeys.js +1 -1
- package/dist/auth/magicLink/MagicLinkKeys.js.map +1 -1
- package/dist/auth/magicLink/types.d.ts +12 -0
- package/dist/auth/magicLink/types.d.ts.map +1 -1
- package/dist/auth/sessionUserClone.d.ts +1 -1
- package/dist/auth/sessionUserClone.d.ts.map +1 -1
- package/dist/auth/sessionUserClone.js +3 -2
- package/dist/auth/sessionUserClone.js.map +1 -1
- package/dist/broadcast/BaseCacheInvalidationBroadcastProvider.d.ts +292 -0
- package/dist/broadcast/BaseCacheInvalidationBroadcastProvider.d.ts.map +1 -0
- package/dist/broadcast/BaseCacheInvalidationBroadcastProvider.js +442 -0
- package/dist/broadcast/BaseCacheInvalidationBroadcastProvider.js.map +1 -0
- package/dist/broadcast/index.d.ts +6 -0
- package/dist/broadcast/index.d.ts.map +1 -0
- package/dist/broadcast/index.js +6 -0
- package/dist/broadcast/index.js.map +1 -0
- package/dist/broadcast/providers/AWSIoTCoreBroadcastProvider.d.ts +43 -0
- package/dist/broadcast/providers/AWSIoTCoreBroadcastProvider.d.ts.map +1 -0
- package/dist/broadcast/providers/AWSIoTCoreBroadcastProvider.js +144 -0
- package/dist/broadcast/providers/AWSIoTCoreBroadcastProvider.js.map +1 -0
- package/dist/broadcast/providers/AzureWebPubSubBroadcastProvider.d.ts +46 -0
- package/dist/broadcast/providers/AzureWebPubSubBroadcastProvider.d.ts.map +1 -0
- package/dist/broadcast/providers/AzureWebPubSubBroadcastProvider.js +149 -0
- package/dist/broadcast/providers/AzureWebPubSubBroadcastProvider.js.map +1 -0
- package/dist/broadcast/providers/GCPPubSubBroadcastProvider.d.ts +49 -0
- package/dist/broadcast/providers/GCPPubSubBroadcastProvider.d.ts.map +1 -0
- package/dist/broadcast/providers/GCPPubSubBroadcastProvider.js +131 -0
- package/dist/broadcast/providers/GCPPubSubBroadcastProvider.js.map +1 -0
- package/dist/broadcast/providers/SequencedRoleBucketBroadcastProvider.d.ts +24 -0
- package/dist/broadcast/providers/SequencedRoleBucketBroadcastProvider.d.ts.map +1 -0
- package/dist/broadcast/providers/SequencedRoleBucketBroadcastProvider.js +51 -0
- package/dist/broadcast/providers/SequencedRoleBucketBroadcastProvider.js.map +1 -0
- package/dist/cache.d.ts +3 -2
- package/dist/cache.d.ts.map +1 -1
- package/dist/cache.js.map +1 -1
- package/dist/config.d.ts +496 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +61 -0
- package/dist/config.js.map +1 -1
- package/dist/context.d.ts +3 -1
- package/dist/context.d.ts.map +1 -1
- package/dist/context.js +29 -8
- package/dist/context.js.map +1 -1
- package/dist/coreRoutes.d.ts.map +1 -1
- package/dist/coreRoutes.js +14 -0
- package/dist/coreRoutes.js.map +1 -1
- package/dist/generated/graphql-schemas/__mj.d.ts +635 -14
- package/dist/generated/graphql-schemas/__mj.d.ts.map +1 -1
- package/dist/generated/graphql-schemas/__mj.js +3366 -418
- package/dist/generated/graphql-schemas/__mj.js.map +1 -1
- package/dist/generic/CacheInvalidationResolver.d.ts +50 -30
- package/dist/generic/CacheInvalidationResolver.d.ts.map +1 -1
- package/dist/generic/CacheInvalidationResolver.js +154 -79
- package/dist/generic/CacheInvalidationResolver.js.map +1 -1
- package/dist/generic/OrphanedConversationDetailReconciler.d.ts.map +1 -1
- package/dist/generic/OrphanedConversationDetailReconciler.js +2 -0
- package/dist/generic/OrphanedConversationDetailReconciler.js.map +1 -1
- package/dist/generic/PubSubManager.d.ts +10 -0
- package/dist/generic/PubSubManager.d.ts.map +1 -1
- package/dist/generic/PubSubManager.js +44 -1
- package/dist/generic/PubSubManager.js.map +1 -1
- package/dist/generic/PushStatusResolver.d.ts +39 -6
- package/dist/generic/PushStatusResolver.d.ts.map +1 -1
- package/dist/generic/PushStatusResolver.js +93 -11
- package/dist/generic/PushStatusResolver.js.map +1 -1
- package/dist/generic/ResolverBase.d.ts +65 -25
- package/dist/generic/ResolverBase.d.ts.map +1 -1
- package/dist/generic/ResolverBase.js +201 -101
- package/dist/generic/ResolverBase.js.map +1 -1
- package/dist/generic/RunViewResolver.d.ts +11 -3
- package/dist/generic/RunViewResolver.d.ts.map +1 -1
- package/dist/generic/RunViewResolver.js +79 -13
- package/dist/generic/RunViewResolver.js.map +1 -1
- package/dist/generic/actionOutputFieldSecurity.d.ts +26 -0
- package/dist/generic/actionOutputFieldSecurity.d.ts.map +1 -0
- package/dist/generic/actionOutputFieldSecurity.js +47 -0
- package/dist/generic/actionOutputFieldSecurity.js.map +1 -0
- package/dist/generic/fieldSecurityTransport.d.ts +30 -0
- package/dist/generic/fieldSecurityTransport.d.ts.map +1 -0
- package/dist/generic/fieldSecurityTransport.js +30 -0
- package/dist/generic/fieldSecurityTransport.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +122 -66
- package/dist/index.js.map +1 -1
- package/dist/integration/CustomColumnPromoter.d.ts.map +1 -1
- package/dist/integration/CustomColumnPromoter.js +6 -1
- package/dist/integration/CustomColumnPromoter.js.map +1 -1
- package/dist/logging/variablesLoggingMiddleware.d.ts +12 -0
- package/dist/logging/variablesLoggingMiddleware.d.ts.map +1 -1
- package/dist/logging/variablesLoggingMiddleware.js +50 -1
- package/dist/logging/variablesLoggingMiddleware.js.map +1 -1
- package/dist/realtimeSessions/SessionVerificationPortsBase.d.ts.map +1 -1
- package/dist/realtimeSessions/SessionVerificationPortsBase.js +1 -0
- package/dist/realtimeSessions/SessionVerificationPortsBase.js.map +1 -1
- package/dist/realtimeSessions/ownedRealtimeSession.d.ts +8 -6
- package/dist/realtimeSessions/ownedRealtimeSession.d.ts.map +1 -1
- package/dist/realtimeSessions/ownedRealtimeSession.js +15 -6
- package/dist/realtimeSessions/ownedRealtimeSession.js.map +1 -1
- package/dist/realtimeSessions/sessionAccess.d.ts +88 -11
- package/dist/realtimeSessions/sessionAccess.d.ts.map +1 -1
- package/dist/realtimeSessions/sessionAccess.js +125 -22
- package/dist/realtimeSessions/sessionAccess.js.map +1 -1
- package/dist/realtimeSessions/sessionConfigStamps.js +1 -1
- package/dist/realtimeSessions/sessionConfigStamps.js.map +1 -1
- package/dist/realtimeSessions/subscriptionAuthorization.d.ts.map +1 -1
- package/dist/realtimeSessions/subscriptionAuthorization.js +5 -2
- package/dist/realtimeSessions/subscriptionAuthorization.js.map +1 -1
- package/dist/realtimeSessions/verificationWorkflow.d.ts +2 -0
- package/dist/realtimeSessions/verificationWorkflow.d.ts.map +1 -1
- package/dist/realtimeSessions/verificationWorkflow.js +1 -1
- package/dist/realtimeSessions/verificationWorkflow.js.map +1 -1
- package/dist/realtimeWidget/WidgetRouter.d.ts.map +1 -1
- package/dist/realtimeWidget/WidgetRouter.js +19 -3
- package/dist/realtimeWidget/WidgetRouter.js.map +1 -1
- package/dist/realtimeWidget/WidgetSessionService.d.ts +67 -7
- package/dist/realtimeWidget/WidgetSessionService.d.ts.map +1 -1
- package/dist/realtimeWidget/WidgetSessionService.js +201 -19
- package/dist/realtimeWidget/WidgetSessionService.js.map +1 -1
- package/dist/realtimeWidget/host-identity.d.ts +1 -1
- package/dist/realtimeWidget/host-identity.js +1 -1
- package/dist/realtimeWidget/widgetCore.d.ts +20 -1
- package/dist/realtimeWidget/widgetCore.d.ts.map +1 -1
- package/dist/realtimeWidget/widgetCore.js +17 -1
- package/dist/realtimeWidget/widgetCore.js.map +1 -1
- package/dist/realtimeWidget/widgetGuestElevation.d.ts +1 -1
- package/dist/realtimeWidget/widgetGuestElevation.d.ts.map +1 -1
- package/dist/realtimeWidget/widgetGuestElevation.js +2 -1
- package/dist/realtimeWidget/widgetGuestElevation.js.map +1 -1
- package/dist/resolvers/ActionResolver.d.ts +19 -1
- package/dist/resolvers/ActionResolver.d.ts.map +1 -1
- package/dist/resolvers/ActionResolver.js +63 -12
- package/dist/resolvers/ActionResolver.js.map +1 -1
- package/dist/resolvers/AdhocQueryResolver.d.ts.map +1 -1
- package/dist/resolvers/AdhocQueryResolver.js +1 -0
- package/dist/resolvers/AdhocQueryResolver.js.map +1 -1
- package/dist/resolvers/AgentRunStatusPublisher.d.ts +18 -4
- package/dist/resolvers/AgentRunStatusPublisher.d.ts.map +1 -1
- package/dist/resolvers/AgentRunStatusPublisher.js +32 -8
- package/dist/resolvers/AgentRunStatusPublisher.js.map +1 -1
- package/dist/resolvers/AgentSessionResolver.d.ts +10 -4
- package/dist/resolvers/AgentSessionResolver.d.ts.map +1 -1
- package/dist/resolvers/AgentSessionResolver.js +40 -17
- package/dist/resolvers/AgentSessionResolver.js.map +1 -1
- package/dist/resolvers/EntityCommunicationsResolver.d.ts +13 -1
- package/dist/resolvers/EntityCommunicationsResolver.d.ts.map +1 -1
- package/dist/resolvers/EntityCommunicationsResolver.js +50 -14
- package/dist/resolvers/EntityCommunicationsResolver.js.map +1 -1
- package/dist/resolvers/FetchEntityVectorsResolver.d.ts +28 -0
- package/dist/resolvers/FetchEntityVectorsResolver.d.ts.map +1 -1
- package/dist/resolvers/FetchEntityVectorsResolver.js +117 -21
- package/dist/resolvers/FetchEntityVectorsResolver.js.map +1 -1
- package/dist/resolvers/FileCategoryResolver.d.ts.map +1 -1
- package/dist/resolvers/FileCategoryResolver.js +1 -0
- package/dist/resolvers/FileCategoryResolver.js.map +1 -1
- package/dist/resolvers/FileResolver.d.ts +5 -0
- package/dist/resolvers/FileResolver.d.ts.map +1 -1
- package/dist/resolvers/FileResolver.js +13 -4
- package/dist/resolvers/FileResolver.js.map +1 -1
- package/dist/resolvers/IntegrationDiscoveryResolver.d.ts.map +1 -1
- package/dist/resolvers/IntegrationDiscoveryResolver.js +45 -7
- package/dist/resolvers/IntegrationDiscoveryResolver.js.map +1 -1
- package/dist/resolvers/MergeRecordsResolver.d.ts +8 -1
- package/dist/resolvers/MergeRecordsResolver.d.ts.map +1 -1
- package/dist/resolvers/MergeRecordsResolver.js +31 -3
- package/dist/resolvers/MergeRecordsResolver.js.map +1 -1
- package/dist/resolvers/PipelineProgressResolver.d.ts +6 -0
- package/dist/resolvers/PipelineProgressResolver.d.ts.map +1 -1
- package/dist/resolvers/PipelineProgressResolver.js +5 -3
- package/dist/resolvers/PipelineProgressResolver.js.map +1 -1
- package/dist/resolvers/QueryResolver.d.ts.map +1 -1
- package/dist/resolvers/QueryResolver.js +20 -6
- package/dist/resolvers/QueryResolver.js.map +1 -1
- package/dist/resolvers/QuerySystemUserResolver.d.ts +2 -0
- package/dist/resolvers/QuerySystemUserResolver.d.ts.map +1 -1
- package/dist/resolvers/QuerySystemUserResolver.js +15 -6
- package/dist/resolvers/QuerySystemUserResolver.js.map +1 -1
- package/dist/resolvers/RSUResolver.d.ts.map +1 -1
- package/dist/resolvers/RSUResolver.js +12 -0
- package/dist/resolvers/RSUResolver.js.map +1 -1
- package/dist/resolvers/RealtimeClientSessionResolver.d.ts +16 -2
- package/dist/resolvers/RealtimeClientSessionResolver.d.ts.map +1 -1
- package/dist/resolvers/RealtimeClientSessionResolver.js +82 -28
- package/dist/resolvers/RealtimeClientSessionResolver.js.map +1 -1
- package/dist/resolvers/RemoteBrowserActionResolver.d.ts.map +1 -1
- package/dist/resolvers/RemoteBrowserActionResolver.js +11 -9
- package/dist/resolvers/RemoteBrowserActionResolver.js.map +1 -1
- package/dist/resolvers/RunAIAgentResolver.d.ts +56 -8
- package/dist/resolvers/RunAIAgentResolver.d.ts.map +1 -1
- package/dist/resolvers/RunAIAgentResolver.js +209 -64
- package/dist/resolvers/RunAIAgentResolver.js.map +1 -1
- package/dist/resolvers/RunTemplateResolver.d.ts.map +1 -1
- package/dist/resolvers/RunTemplateResolver.js +4 -4
- package/dist/resolvers/RunTemplateResolver.js.map +1 -1
- package/dist/resolvers/RunTestResolver.d.ts.map +1 -1
- package/dist/resolvers/RunTestResolver.js +3 -1
- package/dist/resolvers/RunTestResolver.js.map +1 -1
- package/dist/resolvers/SyncDataResolver.d.ts.map +1 -1
- package/dist/resolvers/SyncDataResolver.js +4 -0
- package/dist/resolvers/SyncDataResolver.js.map +1 -1
- package/dist/resolvers/TestQuerySQLResolver.d.ts.map +1 -1
- package/dist/resolvers/TestQuerySQLResolver.js +1 -0
- package/dist/resolvers/TestQuerySQLResolver.js.map +1 -1
- package/dist/resolvers/TransactionGroupResolver.d.ts +17 -2
- package/dist/resolvers/TransactionGroupResolver.d.ts.map +1 -1
- package/dist/resolvers/TransactionGroupResolver.js +60 -15
- package/dist/resolvers/TransactionGroupResolver.js.map +1 -1
- package/dist/resolvers/UserViewResolver.d.ts +1 -1
- package/dist/resolvers/UserViewResolver.d.ts.map +1 -1
- package/dist/resolvers/UserViewResolver.js +2 -1
- package/dist/resolvers/UserViewResolver.js.map +1 -1
- package/dist/rest/EntityCRUDHandler.d.ts +6 -0
- package/dist/rest/EntityCRUDHandler.d.ts.map +1 -1
- package/dist/rest/EntityCRUDHandler.js +19 -5
- package/dist/rest/EntityCRUDHandler.js.map +1 -1
- package/dist/rest/RESTEndpointHandler.d.ts.map +1 -1
- package/dist/rest/RESTEndpointHandler.js +8 -5
- package/dist/rest/RESTEndpointHandler.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 +7 -7
- package/dist/rest/RealtimeRecordingHandler.js.map +1 -1
- package/dist/rest/ViewOperationsHandler.d.ts +6 -0
- package/dist/rest/ViewOperationsHandler.d.ts.map +1 -1
- package/dist/rest/ViewOperationsHandler.js +22 -5
- package/dist/rest/ViewOperationsHandler.js.map +1 -1
- package/dist/serverRoleConfigUnits.d.ts +29 -0
- package/dist/serverRoleConfigUnits.d.ts.map +1 -0
- package/dist/serverRoleConfigUnits.js +33 -0
- package/dist/serverRoleConfigUnits.js.map +1 -0
- package/dist/services/TaskGraphContinuationDeliverer.d.ts +5 -0
- package/dist/services/TaskGraphContinuationDeliverer.d.ts.map +1 -1
- package/dist/services/TaskGraphContinuationDeliverer.js +42 -15
- package/dist/services/TaskGraphContinuationDeliverer.js.map +1 -1
- package/dist/types.d.ts +23 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/package.json +98 -98
- package/src/__tests__/ActionResolver.fieldSecurity.test.ts +237 -0
- package/src/__tests__/AdhocQueryResolver.dialect.test.ts +137 -0
- package/src/__tests__/AgentRunStatusPublisher.test.ts +49 -0
- package/src/__tests__/AgentSessionResolver.ownership.test.ts +137 -0
- package/src/__tests__/CustomColumnPromoterOnePass.test.ts +15 -1
- package/src/__tests__/EntityCommunicationsResolver.actingUser.test.ts +151 -0
- package/src/__tests__/EvolutionActsOnWhatItDetects.test.ts +113 -0
- package/src/__tests__/FetchEntityVectorsResolver.test.ts +206 -3
- package/src/__tests__/FileResolver.fieldSecurity.test.ts +82 -0
- package/src/__tests__/PipelineProgressResolver.filter.test.ts +58 -0
- package/src/__tests__/QuerySystemUserResolver.fieldSecurity.test.ts +49 -0
- package/src/__tests__/RESTEndpointHandler.test.ts +25 -0
- package/src/__tests__/RealtimeClientSessionResolver.configTamper.test.ts +23 -3
- package/src/__tests__/RealtimeClientSessionResolver.test.ts +324 -46
- package/src/__tests__/RemoteBrowserActionResolver.ownership.test.ts +98 -0
- package/src/__tests__/ResolverBase.clientOldValues.test.ts +37 -4
- package/src/__tests__/ResolverBase.deleteReturnProjection.test.ts +52 -0
- package/src/__tests__/ResolverBase.entityMessageRelay.test.ts +176 -0
- package/src/__tests__/ResolverBase.includeBinaryFields.test.ts +23 -1
- package/src/__tests__/ResolverBase.publishStatusUpdate.test.ts +68 -0
- package/src/__tests__/ResolverBase.userViewCacheAndDedup.test.ts +130 -0
- package/src/__tests__/RunAIAgentResolver.branch.test.ts +68 -13
- package/src/__tests__/RunAIAgentResolver.conversationWriter.test.ts +210 -0
- package/src/__tests__/RunAIAgentResolver.fireAndForgetCompletion.test.ts +209 -0
- package/src/__tests__/RunAIAgentResolver.historyFrom.test.ts +17 -27
- package/src/__tests__/RunAIAgentResolver.runDataGuard.test.ts +8 -2
- package/src/__tests__/RunAIAgentResolver.serverReply.test.ts +358 -0
- package/src/__tests__/RunAIAgentResolver.stoppedPredecessor.test.ts +4 -1
- package/src/__tests__/RunTemplateResolver.test.ts +35 -2
- package/src/__tests__/RunViewResolver.cacheCheckForwarding.test.ts +11 -0
- package/src/__tests__/SessionManager.test.ts +95 -0
- package/src/__tests__/TaskGraphContinuationDeliverer.test.ts +114 -3
- package/src/__tests__/TransactionGroupResolver.fieldSecurity.test.ts +199 -0
- package/src/__tests__/UserViewResolver.updateWhereClause.test.ts +15 -0
- package/src/__tests__/ViewOperationsHandler.test.ts +7 -2
- package/src/__tests__/WidgetSessionService.test.ts +583 -4
- package/src/__tests__/apiRootBoot.test.ts +1 -1
- package/src/__tests__/buildMagicLinkSessionUser.test.ts +96 -0
- package/src/__tests__/cacheBroadcastProviders.test.ts +403 -0
- package/src/__tests__/cacheInvalidationRecordData.test.ts +45 -0
- package/src/__tests__/fieldSecurityFixtures.ts +91 -0
- package/src/__tests__/globalCors.test.ts +3 -3
- package/src/__tests__/localDevAuth.test.ts +196 -0
- package/src/__tests__/orphanedConversationDetailReconciler.test.ts +10 -0
- package/src/__tests__/pushStatusResolver.filter.test.ts +57 -1
- package/src/__tests__/realtimeRecordingHandler.test.ts +19 -2
- package/src/__tests__/realtimeSessions/RealtimeSessionEventsResolver.test.ts +29 -1
- package/src/__tests__/realtimeSessions/ownedRealtimeSession.anonymous.test.ts +31 -0
- package/src/__tests__/realtimeSessions/ownedRealtimeSession.namedScoped.test.ts +37 -0
- package/src/__tests__/realtimeSessions/sessionAccess.test.ts +146 -7
- package/src/__tests__/realtimeSessions/sessionConfigStamps.test.ts +1 -1
- package/src/__tests__/realtimeSessions/verificationWorkflow.test.ts +25 -4
- package/src/__tests__/resolverBase.cloudEventChangedFields.test.ts +157 -0
- package/src/__tests__/resolverBase.fieldSecurityTransport.test.ts +222 -0
- package/src/__tests__/resolverBase.fls.test.ts +146 -97
- package/src/__tests__/restTransport.fieldSecurity.test.ts +148 -0
- package/src/__tests__/runParamsTimeZone.test.ts +179 -0
- package/src/__tests__/runStreams.fieldSecurity.test.ts +100 -0
- package/src/__tests__/runViewProperties.test.ts +114 -0
- package/src/__tests__/scalabilityBenchmarks.test.ts +765 -0
- package/src/__tests__/scalabilityDatabaseIntegration.test.ts +207 -0
- package/src/__tests__/sessionUserClone.test.ts +5 -1
- package/src/__tests__/startupSweepOrder.test.ts +40 -0
- package/src/__tests__/unifiedAuth.test.ts +28 -0
- package/src/__tests__/widgetGuestElevation.test.ts +2 -0
- package/src/agentSessions/SessionJanitor.ts +3 -3
- package/src/agentSessions/SessionManager.ts +35 -12
- package/src/auth/AuthProviderCatalogRouter.ts +3 -0
- package/src/auth/index.ts +56 -12
- package/src/auth/initializeProviders.ts +43 -1
- package/src/auth/localDevAuth.ts +528 -0
- package/src/auth/magicLink/MagicLinkKeys.ts +2 -2
- package/src/auth/magicLink/types.ts +10 -0
- package/src/auth/sessionUserClone.ts +3 -2
- package/src/broadcast/BaseCacheInvalidationBroadcastProvider.ts +674 -0
- package/src/broadcast/index.ts +5 -0
- package/src/broadcast/providers/AWSIoTCoreBroadcastProvider.ts +180 -0
- package/src/broadcast/providers/AzureWebPubSubBroadcastProvider.ts +182 -0
- package/src/broadcast/providers/GCPPubSubBroadcastProvider.ts +169 -0
- package/src/broadcast/providers/SequencedRoleBucketBroadcastProvider.ts +57 -0
- package/src/cache.ts +2 -1
- package/src/config.ts +69 -0
- package/src/context.ts +32 -8
- package/src/coreRoutes.ts +15 -0
- package/src/generated/graphql-schemas/__mj.ts +2239 -102
- package/src/generic/CacheInvalidationResolver.ts +135 -78
- package/src/generic/OrphanedConversationDetailReconciler.ts +4 -2
- package/src/generic/PubSubManager.ts +54 -1
- package/src/generic/PushStatusResolver.ts +108 -11
- package/src/generic/ResolverBase.ts +237 -107
- package/src/generic/RunViewResolver.ts +80 -15
- package/src/generic/actionOutputFieldSecurity.ts +75 -0
- package/src/generic/fieldSecurityTransport.ts +62 -0
- package/src/index.ts +127 -68
- package/src/integration/CustomColumnPromoter.ts +6 -1
- package/src/logging/variablesLoggingMiddleware.ts +59 -3
- package/src/realtimeSessions/README.md +7 -3
- package/src/realtimeSessions/SessionVerificationPortsBase.ts +1 -0
- package/src/realtimeSessions/ownedRealtimeSession.ts +20 -6
- package/src/realtimeSessions/sessionAccess.ts +181 -25
- package/src/realtimeSessions/sessionConfigStamps.ts +1 -1
- package/src/realtimeSessions/subscriptionAuthorization.ts +4 -2
- package/src/realtimeSessions/verificationWorkflow.ts +3 -1
- package/src/realtimeWidget/WidgetRouter.ts +22 -4
- package/src/realtimeWidget/WidgetSessionService.ts +252 -17
- package/src/realtimeWidget/host-identity.ts +1 -1
- package/src/realtimeWidget/widgetCore.ts +30 -1
- package/src/realtimeWidget/widgetGuestElevation.ts +2 -1
- package/src/resolvers/ActionResolver.ts +78 -15
- package/src/resolvers/AdhocQueryResolver.ts +1 -0
- package/src/resolvers/AgentRunStatusPublisher.ts +36 -8
- package/src/resolvers/AgentSessionResolver.ts +46 -18
- package/src/resolvers/EntityCommunicationsResolver.ts +56 -15
- package/src/resolvers/FetchEntityVectorsResolver.ts +132 -21
- package/src/resolvers/FileCategoryResolver.ts +1 -0
- package/src/resolvers/FileResolver.ts +14 -4
- package/src/resolvers/IntegrationDiscoveryResolver.ts +49 -7
- package/src/resolvers/MergeRecordsResolver.ts +23 -2
- package/src/resolvers/PipelineProgressResolver.ts +12 -3
- package/src/resolvers/QueryResolver.ts +20 -6
- package/src/resolvers/QuerySystemUserResolver.ts +15 -6
- package/src/resolvers/RSUResolver.ts +12 -0
- package/src/resolvers/RealtimeClientSessionResolver.ts +104 -33
- package/src/resolvers/RemoteBrowserActionResolver.ts +15 -10
- package/src/resolvers/RunAIAgentResolver.ts +258 -82
- package/src/resolvers/RunTemplateResolver.ts +6 -4
- package/src/resolvers/RunTestResolver.ts +3 -1
- package/src/resolvers/SyncDataResolver.ts +4 -0
- package/src/resolvers/TestQuerySQLResolver.ts +1 -0
- package/src/resolvers/TransactionGroupResolver.ts +66 -15
- package/src/resolvers/UserViewResolver.ts +2 -1
- package/src/resolvers/__tests__/MergeRecordsResolver.test.ts +43 -0
- package/src/rest/EntityCRUDHandler.ts +22 -5
- package/src/rest/RESTEndpointHandler.ts +8 -5
- package/src/rest/RealtimeRecordingHandler.ts +13 -10
- package/src/rest/ViewOperationsHandler.ts +24 -6
- package/src/serverRoleConfigUnits.ts +48 -0
- package/src/services/TaskGraphContinuationDeliverer.ts +52 -20
- package/src/types.ts +23 -1
|
@@ -26,15 +26,18 @@ vi.mock('@memberjunction/core', () => ({
|
|
|
26
26
|
},
|
|
27
27
|
}));
|
|
28
28
|
|
|
29
|
+
const { OWNER, SUBMITTER } = vi.hoisted(() => ({ OWNER: { ID: 'owner-1' }, SUBMITTER: { ID: 'submitter-1' } }));
|
|
29
30
|
vi.mock('@memberjunction/generic-database-provider', () => ({
|
|
30
31
|
UserCache: {
|
|
31
|
-
Instance: { Users: [{ ID: 'owner-user-99', Email: 'owner@example.com' }] },
|
|
32
|
-
Users: [{ ID: 'owner-user-99', Email: 'owner@example.com' }],
|
|
32
|
+
Instance: { Users: [OWNER, SUBMITTER, { ID: 'owner-user-99', Email: 'owner@example.com' }] },
|
|
33
|
+
Users: [OWNER, SUBMITTER, { ID: 'owner-user-99', Email: 'owner@example.com' }],
|
|
33
34
|
},
|
|
34
35
|
}));
|
|
35
36
|
|
|
36
37
|
vi.mock('@memberjunction/core-entities', () => ({
|
|
37
38
|
MJConversationDetailEntity: class {},
|
|
39
|
+
MJConversationDetailEntityExtended: class {},
|
|
40
|
+
MJConversationEntity: class {},
|
|
38
41
|
MJAIAgentRunEntity: class {},
|
|
39
42
|
}));
|
|
40
43
|
|
|
@@ -56,15 +59,19 @@ import type { PubSubEngine } from 'type-graphql';
|
|
|
56
59
|
/** A conversation-detail row that can be loaded (the source) and saved (the reply). */
|
|
57
60
|
function detailRow(over: Partial<{ loads: boolean; saves: boolean; conversationID: string; branchID: string | null }> = {}) {
|
|
58
61
|
const { loads = true, saves = true, conversationID = 'conv-1', branchID = null } = over;
|
|
62
|
+
let marked = false;
|
|
59
63
|
return {
|
|
60
64
|
ID: 'reply-detail-1',
|
|
61
65
|
ConversationID: conversationID,
|
|
62
66
|
BranchID: branchID as string | null,
|
|
67
|
+
UserID: 'owner-1',
|
|
63
68
|
Role: '', Status: '', HiddenToUser: true, Message: '',
|
|
64
69
|
AgentID: null as string | null,
|
|
65
70
|
NewRecord: vi.fn(),
|
|
71
|
+
MarkServerAuthoredReply: vi.fn(() => { marked = true; }),
|
|
66
72
|
Load: vi.fn().mockResolvedValue(loads),
|
|
67
|
-
|
|
73
|
+
// Like MJConversationDetailEntityExtended, refuses to save an unmarked AI reply.
|
|
74
|
+
Save: vi.fn().mockImplementation(async () => saves && marked),
|
|
68
75
|
LatestResult: { CompleteMessage: 'FK violation' },
|
|
69
76
|
};
|
|
70
77
|
}
|
|
@@ -88,6 +95,8 @@ function harness(over: Parameters<typeof detailRow>[0] = {}) {
|
|
|
88
95
|
{ ID: 'user-1' } as UserInfo,
|
|
89
96
|
pubSub,
|
|
90
97
|
);
|
|
98
|
+
// Rows in order: the source detail (Load), the reply (Save). The reply starts on a 'stale' fork, so a test sees
|
|
99
|
+
// the deliverer set it.
|
|
91
100
|
return { deliverer, providerFactory, provider, rows, pubSub, reply: () => rows[1] };
|
|
92
101
|
}
|
|
93
102
|
|
|
@@ -96,6 +105,7 @@ const params = (over: Partial<TaskContinuationParams> = {}): TaskContinuationPar
|
|
|
96
105
|
WorkflowName: 'Weekly digest',
|
|
97
106
|
ConversationDetailID: 'detail-1',
|
|
98
107
|
SubmittedByAgentRunID: 'run-1',
|
|
108
|
+
SubmittedByUserID: 'submitter-1',
|
|
99
109
|
ReinvokeDepth: 0,
|
|
100
110
|
Tasks: [
|
|
101
111
|
{ TaskID: 't1', Name: 'Gather', Status: 'Complete', Summary: '412 rows' },
|
|
@@ -132,6 +142,51 @@ describe('posting the outcome', () => {
|
|
|
132
142
|
expect(h.pubSub.publish).toHaveBeenCalledTimes(2);
|
|
133
143
|
});
|
|
134
144
|
|
|
145
|
+
it("writes it as the graph's submitter, not the conversation's owner, marked as the server's reply", async () => {
|
|
146
|
+
// TaskGraph.Submit takes any conversation detail ID. Written as the owner, the gate would pass for a
|
|
147
|
+
// conversation the submitter cannot reach; written as the submitter, the gate checks their access.
|
|
148
|
+
const h = harness();
|
|
149
|
+
await h.deliverer.PostMessage(params());
|
|
150
|
+
|
|
151
|
+
expect(h.provider.GetEntityObject.mock.calls[0]).toEqual(['MJ: Conversation Details', SUBMITTER]);
|
|
152
|
+
expect(h.provider.GetEntityObject.mock.calls[1]).toEqual(['MJ: Conversation Details', SUBMITTER]);
|
|
153
|
+
expect(h.provider.GetEntityObject.mock.calls.some((call) => call[1] === OWNER)).toBe(false);
|
|
154
|
+
expect(h.rows[0].MarkServerAuthoredReply).toHaveBeenCalledOnce();
|
|
155
|
+
expect(h.reply().MarkServerAuthoredReply).toHaveBeenCalledOnce();
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
it("updates the originating message in-place as the graph's submitter, marked as the server's reply, when IsLatest is true", async () => {
|
|
159
|
+
const { RunView } = await import('@memberjunction/core');
|
|
160
|
+
vi.mocked(RunView.FromMetadataProvider).mockReturnValue({
|
|
161
|
+
RunView: vi.fn().mockImplementation(async (rvParams: { EntityName: string }) => {
|
|
162
|
+
if (rvParams.EntityName === 'MJ: Conversation Details') {
|
|
163
|
+
return { Success: true, Results: [{ ID: 'detail-1' }] };
|
|
164
|
+
}
|
|
165
|
+
return { Success: false, Results: [] };
|
|
166
|
+
}),
|
|
167
|
+
} as never);
|
|
168
|
+
|
|
169
|
+
const h = harness();
|
|
170
|
+
await h.deliverer.PostMessage(params());
|
|
171
|
+
|
|
172
|
+
expect(h.rows.length).toBe(1);
|
|
173
|
+
expect(h.provider.GetEntityObject.mock.calls[0]).toEqual(['MJ: Conversation Details', SUBMITTER]);
|
|
174
|
+
expect(h.rows[0].MarkServerAuthoredReply).toHaveBeenCalledOnce();
|
|
175
|
+
expect(h.rows[0].Save).toHaveBeenCalledOnce();
|
|
176
|
+
expect(h.rows[0].Status).toBe('Complete');
|
|
177
|
+
expect(h.rows[0].Message).toContain('Weekly digest');
|
|
178
|
+
expect(h.pubSub.publish).toHaveBeenCalledTimes(1);
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
it("falls back to the deliverer's own user when the graph records no submitter it knows", async () => {
|
|
182
|
+
for (const submitter of [null, 'stranger-1']) {
|
|
183
|
+
const h = harness();
|
|
184
|
+
await h.deliverer.PostMessage(params({ SubmittedByUserID: submitter }));
|
|
185
|
+
expect(h.provider.GetEntityObject.mock.calls[0]).toEqual(['MJ: Conversation Details', { ID: 'user-1' }]);
|
|
186
|
+
expect(h.provider.GetEntityObject.mock.calls[1]).toEqual(['MJ: Conversation Details', { ID: 'user-1' }]);
|
|
187
|
+
}
|
|
188
|
+
});
|
|
189
|
+
|
|
135
190
|
it('names the workflow and carries the roll-up', async () => {
|
|
136
191
|
const h = harness();
|
|
137
192
|
await h.deliverer.PostMessage(params());
|
|
@@ -409,6 +464,8 @@ describe('Reinvoke — on the fork of the message that started the graph', () =>
|
|
|
409
464
|
// Only the originating detail was loaded and updated in-place — no second detail row created!
|
|
410
465
|
expect(createdDetails.length).toBe(1);
|
|
411
466
|
expect(originDetail.NewRecord).not.toHaveBeenCalled();
|
|
467
|
+
expect(originDetail.MarkServerAuthoredReply).toHaveBeenCalledOnce();
|
|
468
|
+
expect(originDetail.Save).toHaveBeenCalledOnce();
|
|
412
469
|
expect(originDetail.Message).toBe('Synthesized workflow outcome');
|
|
413
470
|
expect(originDetail.Status).toBe('Complete');
|
|
414
471
|
expect(runAgent.mock.calls[0][0].conversationDetailId).toBe('detail-1');
|
|
@@ -460,8 +517,11 @@ describe('Reinvoke — on the fork of the message that started the graph', () =>
|
|
|
460
517
|
await deliverer.Reinvoke(params());
|
|
461
518
|
|
|
462
519
|
expect(replyDetail.NewRecord).toHaveBeenCalledTimes(1);
|
|
520
|
+
expect(replyDetail.MarkServerAuthoredReply).toHaveBeenCalledOnce();
|
|
463
521
|
expect(replyDetail.ParentID).toBe('detail-1');
|
|
464
522
|
expect(replyDetail.Message).toBe('Synthesized workflow outcome');
|
|
523
|
+
expect(originDetail.MarkServerAuthoredReply).toHaveBeenCalledOnce();
|
|
524
|
+
expect(originDetail.Save).toHaveBeenCalledOnce();
|
|
465
525
|
expect(originDetail.Message).toContain('*(✅ **Weekly digest** completed — synthesized results posted below.)*');
|
|
466
526
|
expect(originDetail.Message).not.toContain("I'll follow up when it finishes");
|
|
467
527
|
expect(pubSub.publish).toHaveBeenCalledTimes(2);
|
|
@@ -625,6 +685,57 @@ describe('Reinvoke — on the fork of the message that started the graph', () =>
|
|
|
625
685
|
expect(sentContent).toContain('- ⏭️ **Analyze Results** — Skipped: branch condition `output.rowCount > 0` on "Run Query" evaluated to false');
|
|
626
686
|
expect(sentContent).toContain('briefly explaining any conditional steps that were skipped');
|
|
627
687
|
});
|
|
688
|
+
|
|
689
|
+
it('logs an error and does not publish completion when an in-place (IsLatest = true) PostMessage save fails', async () => {
|
|
690
|
+
const { LogError, RunView } = await import('@memberjunction/core');
|
|
691
|
+
vi.mocked(RunView.FromMetadataProvider).mockReturnValue({
|
|
692
|
+
RunView: vi.fn().mockResolvedValue({
|
|
693
|
+
Success: true,
|
|
694
|
+
Results: [{ ID: 'detail-1' }],
|
|
695
|
+
}),
|
|
696
|
+
} as never);
|
|
697
|
+
|
|
698
|
+
const h = harness({ saves: false });
|
|
699
|
+
await h.deliverer.PostMessage(params());
|
|
700
|
+
|
|
701
|
+
expect(h.rows[0].Save).toHaveBeenCalledOnce();
|
|
702
|
+
expect(LogError).toHaveBeenCalledWith(expect.stringContaining('Could not update in-place outcome of "Weekly digest"'));
|
|
703
|
+
expect(h.pubSub.publish).not.toHaveBeenCalled();
|
|
704
|
+
});
|
|
705
|
+
|
|
706
|
+
it('loads the originating detail as SubmittedByUserID in Reinvoke when both run UserIDs are null', async () => {
|
|
707
|
+
const runRow = {
|
|
708
|
+
ID: 'run-1',
|
|
709
|
+
AgentID: 'agent-1',
|
|
710
|
+
ParentRunID: null as string | null,
|
|
711
|
+
UserID: null as string | null,
|
|
712
|
+
Load: vi.fn().mockResolvedValue(true),
|
|
713
|
+
};
|
|
714
|
+
const agentRow = {
|
|
715
|
+
ID: 'agent-1',
|
|
716
|
+
Name: 'Sage',
|
|
717
|
+
Load: vi.fn().mockResolvedValue(true),
|
|
718
|
+
};
|
|
719
|
+
const detail = detailRow();
|
|
720
|
+
const detailUsers: Array<UserInfo | undefined> = [];
|
|
721
|
+
const provider = {
|
|
722
|
+
GetEntityObject: vi.fn().mockImplementation(async (name: string, user?: UserInfo) => {
|
|
723
|
+
if (name === 'MJ: AI Agent Runs') return runRow;
|
|
724
|
+
if (name === 'MJ: AI Agents') return agentRow;
|
|
725
|
+
detailUsers.push(user);
|
|
726
|
+
return detail;
|
|
727
|
+
}),
|
|
728
|
+
};
|
|
729
|
+
const deliverer = new TaskGraphContinuationDeliverer(
|
|
730
|
+
{ CreateProvider: vi.fn().mockResolvedValue(provider) } as never,
|
|
731
|
+
{ ID: 'system-user' } as UserInfo,
|
|
732
|
+
);
|
|
733
|
+
|
|
734
|
+
await deliverer.Reinvoke(params({ SubmittedByUserID: SUBMITTER.ID }));
|
|
735
|
+
|
|
736
|
+
expect(detailUsers[0]).toBe(SUBMITTER);
|
|
737
|
+
expect(runAgent.mock.calls[0][0].contextUser).toBe(SUBMITTER);
|
|
738
|
+
});
|
|
628
739
|
});
|
|
629
740
|
|
|
630
741
|
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Field-level security on the transaction-group transport.
|
|
3
|
+
*
|
|
4
|
+
* Output: every `ResultsJSON` entry is projected for the caller. The update entry is a DELTA,
|
|
5
|
+
* computed by comparing the post-save row with a pre-save snapshot. When the snapshot came from a
|
|
6
|
+
* load narrowed for the caller and the post-save row is full width, every non-null denied value
|
|
7
|
+
* differs from its (absent) snapshot and counts as "changed", so the delta itself carried the
|
|
8
|
+
* values the caller may not read. The entity objects here deliberately return full-width rows
|
|
9
|
+
* to stand in for any object not narrowed at its source.
|
|
10
|
+
*
|
|
11
|
+
* Input: a restricted caller's item payload, and the field a transaction variable names, are
|
|
12
|
+
* refused when they carry a key the caller may not read or a key that names no field, with the
|
|
13
|
+
* same message for both, so the refusal does not reveal which columns exist.
|
|
14
|
+
*/
|
|
15
|
+
import 'reflect-metadata';
|
|
16
|
+
import { describe, it, expect, vi } from 'vitest';
|
|
17
|
+
import { BaseEntityResult, EntityInfo, UserInfo } from '@memberjunction/core';
|
|
18
|
+
|
|
19
|
+
vi.mock('@memberjunction/core', async (importOriginal) => {
|
|
20
|
+
const actual = await importOriginal<typeof import('@memberjunction/core')>();
|
|
21
|
+
return { ...actual, LogError: () => undefined };
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
import {
|
|
25
|
+
TransactionResolver,
|
|
26
|
+
TransactionInputType,
|
|
27
|
+
TransactionOperationType,
|
|
28
|
+
TransactionVariableType,
|
|
29
|
+
} from '../resolvers/TransactionGroupResolver.js';
|
|
30
|
+
import type { AppContext } from '../types.js';
|
|
31
|
+
import { EmployeeEntity, FullWidthEmployeeRow, HRUser, Intern, RefusalFor, SECRET_SALARY } from './fieldSecurityFixtures.js';
|
|
32
|
+
|
|
33
|
+
class FakeTransactionGroup {
|
|
34
|
+
public Enrolled = 0;
|
|
35
|
+
public AddTransaction(): void { this.Enrolled++; }
|
|
36
|
+
public AddVariable(): void { /* variables are not under test here */ }
|
|
37
|
+
public async Submit(): Promise<boolean> { return true; }
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* An entity object that is NOT narrowed at its source: its load returns `loaded`, and after the
|
|
42
|
+
* save it reports the full-width row, as a save's re-read does.
|
|
43
|
+
*/
|
|
44
|
+
class FullWidthEntity {
|
|
45
|
+
public TransactionGroup: FakeTransactionGroup | null = null;
|
|
46
|
+
public LatestResult: BaseEntityResult | null = null;
|
|
47
|
+
public PrimaryKeys = [{ Name: 'ID' }];
|
|
48
|
+
public SetManyCalls: Array<Record<string, unknown>> = [];
|
|
49
|
+
private values: Record<string, unknown>;
|
|
50
|
+
private saved = false;
|
|
51
|
+
|
|
52
|
+
constructor(public readonly EntityInfo: EntityInfo, private readonly loaded: Record<string, unknown>) {
|
|
53
|
+
this.values = { ...loaded };
|
|
54
|
+
}
|
|
55
|
+
public SetMany(values: Record<string, unknown>): void {
|
|
56
|
+
this.SetManyCalls.push(values);
|
|
57
|
+
this.values = { ...this.values, ...values };
|
|
58
|
+
}
|
|
59
|
+
public async InnerLoad(): Promise<boolean> { this.values = { ...this.loaded }; return true; }
|
|
60
|
+
public async GetDataObject(): Promise<Record<string, unknown>> {
|
|
61
|
+
return this.saved ? { ...FullWidthEmployeeRow(), ...this.values, Salary: SECRET_SALARY } : { ...this.values };
|
|
62
|
+
}
|
|
63
|
+
public async GetDataObjectJSON(): Promise<string> { return JSON.stringify(await this.GetDataObject()); }
|
|
64
|
+
public async Save(): Promise<boolean> { return this.write(); }
|
|
65
|
+
public async Delete(): Promise<boolean> { return this.write(); }
|
|
66
|
+
private write(): boolean {
|
|
67
|
+
this.TransactionGroup?.AddTransaction();
|
|
68
|
+
this.saved = true;
|
|
69
|
+
const result = new BaseEntityResult();
|
|
70
|
+
result.Success = true;
|
|
71
|
+
this.LatestResult = result;
|
|
72
|
+
return true;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/** What a load narrowed for an intern holds: no salary keys at all. */
|
|
77
|
+
const NARROW_ROW = { ID: 'emp-1', Name: 'Ada', Notes: 'n' };
|
|
78
|
+
|
|
79
|
+
function buildContext(entities: FullWidthEntity[], user: UserInfo): AppContext {
|
|
80
|
+
let next = 0;
|
|
81
|
+
const provider = {
|
|
82
|
+
CreateTransactionGroup: async () => new FakeTransactionGroup(),
|
|
83
|
+
GetEntityObject: async () => entities[next++],
|
|
84
|
+
Entities: [] as EntityInfo[],
|
|
85
|
+
EntityByID: () => undefined,
|
|
86
|
+
};
|
|
87
|
+
return {
|
|
88
|
+
providers: [{ type: 'Read-Write', provider }],
|
|
89
|
+
userPayload: { userRecord: user, email: user.Email },
|
|
90
|
+
} as unknown as AppContext;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function group(operation: TransactionOperationType, payload: Record<string, unknown>): TransactionInputType {
|
|
94
|
+
return { Items: [{ EntityName: 'Employees', EntityObjectJSON: JSON.stringify(payload), OperationType: operation }], Variables: [] };
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
describe('ExecuteTransactionGroup: output is projected for the caller', () => {
|
|
98
|
+
it('REGRESSION: the update delta never carries a denied value, though the post-save row is full width', async () => {
|
|
99
|
+
const entity = new FullWidthEntity(EmployeeEntity(), NARROW_ROW);
|
|
100
|
+
const result = await new TransactionResolver().ExecuteTransactionGroup(
|
|
101
|
+
group(TransactionOperationType.Update, { ID: 'emp-1', Notes: 'edited' }),
|
|
102
|
+
buildContext([entity], Intern())
|
|
103
|
+
);
|
|
104
|
+
|
|
105
|
+
expect(result.Success).toBe(true);
|
|
106
|
+
const delta = JSON.parse(result.ResultsJSON[0]);
|
|
107
|
+
expect(delta).toMatchObject({ ID: 'emp-1', Notes: 'edited' });
|
|
108
|
+
expect(delta).not.toHaveProperty('Salary');
|
|
109
|
+
expect(delta).not.toHaveProperty('Base Salary');
|
|
110
|
+
expect(result.ResultsJSON[0]).not.toContain(String(SECRET_SALARY));
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
it('REGRESSION: the create result never carries a denied value', async () => {
|
|
114
|
+
const entity = new FullWidthEntity(EmployeeEntity(), {});
|
|
115
|
+
const result = await new TransactionResolver().ExecuteTransactionGroup(
|
|
116
|
+
group(TransactionOperationType.Create, { ID: 'emp-1', Name: 'Ada' }),
|
|
117
|
+
buildContext([entity], Intern())
|
|
118
|
+
);
|
|
119
|
+
|
|
120
|
+
expect(result.ResultsJSON[0]).not.toContain(String(SECRET_SALARY));
|
|
121
|
+
expect(JSON.parse(result.ResultsJSON[0])).toMatchObject({ ID: 'emp-1', Name: 'Ada' });
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
it('REGRESSION: the delete result never carries a denied value', async () => {
|
|
125
|
+
const entity = new FullWidthEntity(EmployeeEntity(), FullWidthEmployeeRow());
|
|
126
|
+
const result = await new TransactionResolver().ExecuteTransactionGroup(
|
|
127
|
+
group(TransactionOperationType.Delete, { ID: 'emp-1' }),
|
|
128
|
+
buildContext([entity], Intern())
|
|
129
|
+
);
|
|
130
|
+
|
|
131
|
+
expect(result.ResultsJSON[0]).not.toContain(String(SECRET_SALARY));
|
|
132
|
+
expect(JSON.parse(result.ResultsJSON[0])).toMatchObject({ ID: 'emp-1', Name: 'Ada' });
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
it('a caller who may read the salary still gets it back', async () => {
|
|
136
|
+
const entity = new FullWidthEntity(EmployeeEntity(), {});
|
|
137
|
+
const result = await new TransactionResolver().ExecuteTransactionGroup(
|
|
138
|
+
group(TransactionOperationType.Create, { ID: 'emp-1', Name: 'Ada', Salary: 1 }),
|
|
139
|
+
buildContext([entity], HRUser())
|
|
140
|
+
);
|
|
141
|
+
|
|
142
|
+
expect(JSON.parse(result.ResultsJSON[0]).Salary).toBe(SECRET_SALARY);
|
|
143
|
+
});
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
describe('ExecuteTransactionGroup: client input is gated for a restricted caller', () => {
|
|
147
|
+
it.each([
|
|
148
|
+
['a denied field', 'Salary'],
|
|
149
|
+
['a key that names no field', 'Bogus'],
|
|
150
|
+
])('REGRESSION: refuses %s with the same message, before anything is applied', async (_label, key) => {
|
|
151
|
+
const entity = new FullWidthEntity(EmployeeEntity(), NARROW_ROW);
|
|
152
|
+
const run = new TransactionResolver().ExecuteTransactionGroup(
|
|
153
|
+
group(TransactionOperationType.Update, { ID: 'emp-1', Notes: 'edited', [key]: 1 }),
|
|
154
|
+
buildContext([entity], Intern())
|
|
155
|
+
);
|
|
156
|
+
|
|
157
|
+
await expect(run).rejects.toThrow(RefusalFor(key));
|
|
158
|
+
expect(entity.SetManyCalls).toHaveLength(0);
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
it('REGRESSION: refuses a transaction variable that names a denied field (it would copy the value elsewhere)', async () => {
|
|
162
|
+
const source = new FullWidthEntity(EmployeeEntity(), NARROW_ROW);
|
|
163
|
+
const target = new FullWidthEntity(EmployeeEntity(), NARROW_ROW);
|
|
164
|
+
const input: TransactionInputType = {
|
|
165
|
+
Items: [
|
|
166
|
+
{ EntityName: 'Employees', EntityObjectJSON: JSON.stringify({ ID: 'emp-1', Notes: 'a' }), OperationType: TransactionOperationType.Update },
|
|
167
|
+
{ EntityName: 'Employees', EntityObjectJSON: JSON.stringify({ ID: 'emp-2', Notes: 'b' }), OperationType: TransactionOperationType.Update },
|
|
168
|
+
],
|
|
169
|
+
Variables: [
|
|
170
|
+
{ Name: 'leak', ItemIndex: 0, FieldName: 'Salary', Type: TransactionVariableType.Define },
|
|
171
|
+
{ Name: 'leak', ItemIndex: 1, FieldName: 'Notes', Type: TransactionVariableType.Use },
|
|
172
|
+
],
|
|
173
|
+
};
|
|
174
|
+
|
|
175
|
+
await expect(new TransactionResolver().ExecuteTransactionGroup(input, buildContext([source, target], Intern())))
|
|
176
|
+
.rejects.toThrow(RefusalFor('Salary'));
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
it('accepts the keys a well-behaved client sends, plus transport keys', async () => {
|
|
180
|
+
const entity = new FullWidthEntity(EmployeeEntity(), NARROW_ROW);
|
|
181
|
+
const result = await new TransactionResolver().ExecuteTransactionGroup(
|
|
182
|
+
group(TransactionOperationType.Update, { ID: 'emp-1', Notes: 'edited', __typename: 'Employee_' }),
|
|
183
|
+
buildContext([entity], Intern())
|
|
184
|
+
);
|
|
185
|
+
|
|
186
|
+
expect(result.Success).toBe(true);
|
|
187
|
+
expect(entity.SetManyCalls).toHaveLength(1);
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
it('does not gate a caller who is denied nothing', async () => {
|
|
191
|
+
const entity = new FullWidthEntity(EmployeeEntity(), FullWidthEmployeeRow());
|
|
192
|
+
const result = await new TransactionResolver().ExecuteTransactionGroup(
|
|
193
|
+
group(TransactionOperationType.Update, { ID: 'emp-1', Salary: 5 }),
|
|
194
|
+
buildContext([entity], HRUser())
|
|
195
|
+
);
|
|
196
|
+
|
|
197
|
+
expect(result.Success).toBe(true);
|
|
198
|
+
});
|
|
199
|
+
});
|
|
@@ -35,6 +35,7 @@ vi.mock('../resolvers/UserResolver.js', () => ({ UserResolver: class {} }));
|
|
|
35
35
|
|
|
36
36
|
import { UserViewResolver } from '../resolvers/UserViewResolver.js';
|
|
37
37
|
import type { AppContext, ProviderInfo, UserPayload } from '../types.js';
|
|
38
|
+
import { EmployeeEntity, FullWidthEmployeeRow, Intern, SECRET_SALARY } from './fieldSecurityFixtures.js';
|
|
38
39
|
|
|
39
40
|
const VIEW_ID = '0F8FAD5B-D9CB-469F-A165-70867728950E';
|
|
40
41
|
const USER = { ID: 'user-1', Email: 'dev@example.com' } as unknown as UserInfo;
|
|
@@ -111,4 +112,18 @@ describe('UserViewResolver.UpdateWhereClause', () => {
|
|
|
111
112
|
const ctx = makeContext([{ provider: makeProvider(view), type: 'Read-Write' }]);
|
|
112
113
|
await expect(new UserViewResolver().UpdateWhereClause(VIEW_ID, ctx)).rejects.toThrow(/Failed to update where clause/);
|
|
113
114
|
});
|
|
115
|
+
|
|
116
|
+
it('REGRESSION: returns the saved view projected for the caller, never a denied value', async () => {
|
|
117
|
+
const { view } = makeView({ loadOk: true });
|
|
118
|
+
const fullWidth = view as FakeView & { EntityInfo?: unknown };
|
|
119
|
+
fullWidth.EntityInfo = EmployeeEntity();
|
|
120
|
+
fullWidth.GetAll = vi.fn(() => FullWidthEmployeeRow());
|
|
121
|
+
const ctx = makeContext([{ provider: makeProvider(view), type: 'Read-Write' }]);
|
|
122
|
+
(ctx.userPayload as { userRecord: unknown }).userRecord = Intern();
|
|
123
|
+
|
|
124
|
+
const result = await new UserViewResolver().UpdateWhereClause(VIEW_ID, ctx);
|
|
125
|
+
|
|
126
|
+
expect(JSON.stringify(result)).not.toContain(String(SECRET_SALARY));
|
|
127
|
+
expect(result).toMatchObject({ ID: 'emp-1', Name: 'Ada' });
|
|
128
|
+
});
|
|
114
129
|
});
|
|
@@ -107,14 +107,19 @@ describe('ViewOperationsHandler.runView', () => {
|
|
|
107
107
|
});
|
|
108
108
|
|
|
109
109
|
describe('parameter sanitization', () => {
|
|
110
|
-
it('defaults ResultType to "simple" when unset
|
|
110
|
+
it('defaults ResultType to "simple" when unset, forces "simple" over "entity_object", and keeps "count_only"', async () => {
|
|
111
111
|
const defaulted: RunViewParams = { EntityName: 'Customers' };
|
|
112
112
|
await ViewOperationsHandler.runView(defaulted, makeUser());
|
|
113
113
|
expect((mockRunViewFn.mock.calls[0][0] as RunViewParams).ResultType).toBe('simple');
|
|
114
114
|
|
|
115
|
+
// Entity objects cannot cross the wire: serializing one sends its internal state, not its fields.
|
|
115
116
|
const explicit: RunViewParams = { EntityName: 'Customers', ResultType: 'entity_object' };
|
|
116
117
|
await ViewOperationsHandler.runView(explicit, makeUser());
|
|
117
|
-
expect((mockRunViewFn.mock.calls[1][0] as RunViewParams).ResultType).toBe('
|
|
118
|
+
expect((mockRunViewFn.mock.calls[1][0] as RunViewParams).ResultType).toBe('simple');
|
|
119
|
+
|
|
120
|
+
const countOnly: RunViewParams = { EntityName: 'Customers', ResultType: 'count_only' };
|
|
121
|
+
await ViewOperationsHandler.runView(countOnly, makeUser());
|
|
122
|
+
expect((mockRunViewFn.mock.calls[2][0] as RunViewParams).ResultType).toBe('count_only');
|
|
118
123
|
});
|
|
119
124
|
|
|
120
125
|
it('splits comma-separated Fields strings and coerces numeric strings for MaxRows/StartRow', async () => {
|