@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
package/src/config.ts
CHANGED
|
@@ -5,6 +5,7 @@ import { LogError, LogStatus, LogStatusEx } from '@memberjunction/core';
|
|
|
5
5
|
import { mergeConfigs, parseBooleanEnv } from '@memberjunction/config';
|
|
6
6
|
import { TelemetryEnabledDefault } from './telemetryConfigUnits.js';
|
|
7
7
|
import { RealtimeEnabledDefault } from './realtimeConfigUnits.js';
|
|
8
|
+
import { ResolveServerRole, type ServerRole } from './serverRoleConfigUnits.js';
|
|
8
9
|
|
|
9
10
|
const explorer = cosmiconfigSync('mj', { searchStrategy: 'global' });
|
|
10
11
|
|
|
@@ -316,6 +317,45 @@ const cacheSettingsSchema = z.object({
|
|
|
316
317
|
* for it. 0 disables the turn-taking. Default: 30.
|
|
317
318
|
*/
|
|
318
319
|
startupWarmupLeaseSeconds: z.number().int().nonnegative().optional().default(30),
|
|
320
|
+
/**
|
|
321
|
+
* Pluggable cache invalidation broadcast topology configuration.
|
|
322
|
+
* Defaults to `'SequencedRoleBucket'` (Topology 1 Monotonic Sequence Ring Buffer + Topology 2
|
|
323
|
+
* Role-Fingerprint $O(\text{Roles})$ evaluation & shaping memoizer).
|
|
324
|
+
*/
|
|
325
|
+
broadcast: z
|
|
326
|
+
.object({
|
|
327
|
+
provider: z.string().optional().default('SequencedRoleBucket'),
|
|
328
|
+
ringBufferSize: z.number().int().positive().optional().default(5000),
|
|
329
|
+
excludedEntities: z.array(z.string()).optional().default([]),
|
|
330
|
+
azureWebPubSub: z
|
|
331
|
+
.object({
|
|
332
|
+
endpoint: z.string().optional(),
|
|
333
|
+
hubName: z.string().optional(),
|
|
334
|
+
accessKey: z.string().optional(),
|
|
335
|
+
tokenTtlMinutes: z.number().positive().optional(),
|
|
336
|
+
})
|
|
337
|
+
.optional(),
|
|
338
|
+
awsIoTCore: z
|
|
339
|
+
.object({
|
|
340
|
+
endpoint: z.string().optional(),
|
|
341
|
+
region: z.string().optional(),
|
|
342
|
+
topicPrefix: z.string().optional(),
|
|
343
|
+
accessKeyId: z.string().optional(),
|
|
344
|
+
secretAccessKey: z.string().optional(),
|
|
345
|
+
tokenTtlSeconds: z.number().positive().optional(),
|
|
346
|
+
})
|
|
347
|
+
.optional(),
|
|
348
|
+
gcpPubSub: z
|
|
349
|
+
.object({
|
|
350
|
+
projectId: z.string().optional(),
|
|
351
|
+
topicPrefix: z.string().optional(),
|
|
352
|
+
gatewayUrl: z.string().optional(),
|
|
353
|
+
signingSecret: z.string().optional(),
|
|
354
|
+
tokenTtlSeconds: z.number().positive().optional(),
|
|
355
|
+
})
|
|
356
|
+
.optional(),
|
|
357
|
+
})
|
|
358
|
+
.optional(),
|
|
319
359
|
});
|
|
320
360
|
|
|
321
361
|
const loggingSettingsSchema = z.object({
|
|
@@ -843,9 +883,17 @@ const realtimeSchema = z.object({
|
|
|
843
883
|
recordingAssemblyGraceSeconds: z.coerce.number().positive().optional().default(90),
|
|
844
884
|
}).passthrough();
|
|
845
885
|
|
|
886
|
+
const devAuthSchema = z.object({
|
|
887
|
+
/** Master switch for local-only development JWT auth (`MJ_DEV_AUTH_ENABLED=true`). Forbidden when NODE_ENV=production. */
|
|
888
|
+
enabled: zodBooleanWithTransforms().default(false),
|
|
889
|
+
/** Optional default email for `/auth/dev-login` when no email is specified in the request body. */
|
|
890
|
+
defaultEmail: z.string().optional(),
|
|
891
|
+
}).passthrough();
|
|
892
|
+
|
|
846
893
|
const configInfoSchema = z.object({
|
|
847
894
|
userHandling: userHandlingInfoSchema,
|
|
848
895
|
magicLink: magicLinkSchema.optional().default({}),
|
|
896
|
+
devAuth: devAuthSchema.optional().default({}),
|
|
849
897
|
widget: widgetSchema.optional().default({}),
|
|
850
898
|
telephony: telephonySchema.optional().default({}),
|
|
851
899
|
realtime: realtimeSchema.optional().default({}),
|
|
@@ -905,8 +953,18 @@ const configInfoSchema = z.object({
|
|
|
905
953
|
.transform((val) => z.record(z.string()).parse(JSON.parse(val)))
|
|
906
954
|
.optional(),
|
|
907
955
|
mjCoreSchema: z.string(),
|
|
956
|
+
/**
|
|
957
|
+
* Server execution role for horizontal scaling:
|
|
958
|
+
* - `'all'` (default): runs both interactive HTTP/GraphQL/WebSocket handlers and background worker loops.
|
|
959
|
+
* - `'api'`: runs interactive HTTP/GraphQL/WebSocket handlers only; background worker loops are skipped.
|
|
960
|
+
* - `'worker'`: runs background worker loops (`ScheduledJobsService`, `IntegrationSyncWorkerService`,
|
|
961
|
+
* `StartTaskGraphDispatcher`, `StartWorkQueueHost`, `SessionJanitor`, reconcilers).
|
|
962
|
+
*/
|
|
963
|
+
serverRole: z.enum(['all', 'api', 'worker']).optional().default('all'),
|
|
908
964
|
});
|
|
909
965
|
|
|
966
|
+
export type { ServerRole };
|
|
967
|
+
|
|
910
968
|
export type UserHandlingInfo = z.infer<typeof userHandlingInfoSchema>;
|
|
911
969
|
export type MagicLinkConfig = z.infer<typeof magicLinkSchema>;
|
|
912
970
|
export type WidgetConfig = z.infer<typeof widgetSchema>;
|
|
@@ -976,6 +1034,7 @@ export const DEFAULT_SERVER_CONFIG: Partial<ConfigInfo> = {
|
|
|
976
1034
|
enableIntrospection: process.env.ENABLE_INTROSPECTION === 'true',
|
|
977
1035
|
apiKey: process.env.MJ_API_KEY,
|
|
978
1036
|
websiteRunFromPackage: process.env.WEBSITE_RUN_FROM_PACKAGE ? parseInt(process.env.WEBSITE_RUN_FROM_PACKAGE, 10) : undefined,
|
|
1037
|
+
serverRole: ResolveServerRole(process.env.MJ_SERVER_ROLE),
|
|
979
1038
|
|
|
980
1039
|
// User handling defaults
|
|
981
1040
|
userHandling: {
|
|
@@ -1187,6 +1246,16 @@ export function LoadConfig() {
|
|
|
1187
1246
|
LogError('Error parsing config file', null, JSON.stringify(configParsing.error.issues, null, 2));
|
|
1188
1247
|
throw new Error('Configuration validation failed');
|
|
1189
1248
|
}
|
|
1249
|
+
|
|
1250
|
+
const rawDevAuthEnv = process.env.MJ_DEV_AUTH_ENABLED?.trim().toLowerCase();
|
|
1251
|
+
const isDevAuthEnabled =
|
|
1252
|
+
rawDevAuthEnv === 'true' || rawDevAuthEnv === '1' || configParsing.data.devAuth?.enabled === true;
|
|
1253
|
+
if (isDevAuthEnabled && process.env.NODE_ENV?.trim().toLowerCase() === 'production') {
|
|
1254
|
+
throw new Error(
|
|
1255
|
+
'[SECURITY] Local dev auth (MJ_DEV_AUTH_ENABLED) is strictly forbidden when NODE_ENV=production. Refusing to start.'
|
|
1256
|
+
);
|
|
1257
|
+
}
|
|
1258
|
+
|
|
1190
1259
|
return configParsing.data;
|
|
1191
1260
|
}
|
|
1192
1261
|
|
package/src/context.ts
CHANGED
|
@@ -21,7 +21,7 @@ import e from 'express';
|
|
|
21
21
|
import type { RequestHandler, Request, Response, NextFunction } from 'express';
|
|
22
22
|
import { DatabaseProviderBase, UserInfo, type IMetadataProvider, type MagicLinkScope, type ReturningVisitorContext, type WidgetGuestContext } from '@memberjunction/core';
|
|
23
23
|
import { SQLServerDataProvider, SQLServerProviderConfigData } from '@memberjunction/sqlserver-dataprovider';
|
|
24
|
-
import { Metadata } from '@memberjunction/core';
|
|
24
|
+
import { Metadata, IsValidTimeZone, TIME_ZONE_HEADER } from '@memberjunction/core';
|
|
25
25
|
import { IdentityClaimEngineServer } from '@memberjunction/core-entities-server';
|
|
26
26
|
import { UUIDsEqual } from '@memberjunction/global';
|
|
27
27
|
import { UserCache, resolveDbPlatformFromEnv } from '@memberjunction/generic-database-provider';
|
|
@@ -204,12 +204,17 @@ async function auditLoginFailure(
|
|
|
204
204
|
// userRecord may be a SHARED cached instance (e.g. multiple recruiters on one per-company
|
|
205
205
|
// link) — mutating it would leak one session's scope to another. Non-magic-link sessions,
|
|
206
206
|
// and named sessions with no resource scope, are returned untouched.
|
|
207
|
-
function
|
|
207
|
+
export function BuildMagicLinkSessionUser(userRecord: UserInfo, payload: jwt.JwtPayload): UserInfo {
|
|
208
208
|
if (payload['mj_magic_link'] !== true) {
|
|
209
209
|
return userRecord;
|
|
210
210
|
}
|
|
211
211
|
const md = Metadata.Provider; // global-provider-ok: server-side magic-link session built under the server's single default provider
|
|
212
212
|
const isAnon = payload['mj_anon'] === true;
|
|
213
|
+
const rawSid = payload['mj_sid'];
|
|
214
|
+
const mjSid = typeof rawSid === 'string' ? rawSid.trim() : '';
|
|
215
|
+
if (isAnon && !mjSid) {
|
|
216
|
+
throw new AuthenticationError('Anonymous magic-link session token is missing required mj_sid claim');
|
|
217
|
+
}
|
|
213
218
|
const scopes = payload['mj_scopes'];
|
|
214
219
|
|
|
215
220
|
// Per-session resource scope (single-resource share; multi-resource union is a follow-on).
|
|
@@ -279,6 +284,7 @@ function buildMagicLinkSessionUser(userRecord: UserInfo, payload: jwt.JwtPayload
|
|
|
279
284
|
// Mark the session so the CurrentUser field resolver serves these synthesized roles
|
|
280
285
|
// (the shared Anonymous principal holds none in the DB). See UserInfo.IsMagicLinkAnonymous.
|
|
281
286
|
sessionUser.IsMagicLinkAnonymous = true;
|
|
287
|
+
sessionUser.AnonymousVisitorID = mjSid;
|
|
282
288
|
}
|
|
283
289
|
return sessionUser;
|
|
284
290
|
}
|
|
@@ -420,7 +426,7 @@ export const GetUserPayload = async (
|
|
|
420
426
|
// very often the SHARED UserCache instance, and stamping it in place
|
|
421
427
|
// leaks one session's row-filter bindings / acting context to every
|
|
422
428
|
// concurrent session of the same user (same pattern as
|
|
423
|
-
//
|
|
429
|
+
// BuildMagicLinkSessionUser — see sessionUserClone.ts).
|
|
424
430
|
const userRecord = CloneUserForSessionContext(resolvedUser);
|
|
425
431
|
|
|
426
432
|
// Row-filter bindings for this key, rebuilt PER REQUEST from the
|
|
@@ -486,7 +492,11 @@ export const GetUserPayload = async (
|
|
|
486
492
|
throw new AuthenticationError('Missing token');
|
|
487
493
|
}
|
|
488
494
|
|
|
489
|
-
const
|
|
495
|
+
const cachedAuth = AuthCache.get(token);
|
|
496
|
+
const payload =
|
|
497
|
+
cachedAuth && typeof cachedAuth === 'object'
|
|
498
|
+
? cachedAuth
|
|
499
|
+
: jwt.decode(token);
|
|
490
500
|
if (!payload || typeof payload === 'string') {
|
|
491
501
|
throw new AuthenticationError('Invalid token payload');
|
|
492
502
|
}
|
|
@@ -498,7 +508,7 @@ export const GetUserPayload = async (
|
|
|
498
508
|
throw new TokenExpiredError(expiryDate);
|
|
499
509
|
}
|
|
500
510
|
|
|
501
|
-
if (!
|
|
511
|
+
if (!cachedAuth) {
|
|
502
512
|
const issuer = payload.iss;
|
|
503
513
|
if (!issuer) {
|
|
504
514
|
console.warn('No issuer claim on token');
|
|
@@ -513,7 +523,7 @@ export const GetUserPayload = async (
|
|
|
513
523
|
}
|
|
514
524
|
|
|
515
525
|
await verifyAsync(issuer, token);
|
|
516
|
-
AuthCache.set(token,
|
|
526
|
+
AuthCache.set(token, payload);
|
|
517
527
|
}
|
|
518
528
|
|
|
519
529
|
// Use provider to extract user information
|
|
@@ -540,7 +550,7 @@ export const GetUserPayload = async (
|
|
|
540
550
|
// Anonymous principal with an in-memory UserInfo carrying the claimed role(s), so
|
|
541
551
|
// the session enforces exactly the link's granted scope (no DB accretion). No-op
|
|
542
552
|
// for everything else.
|
|
543
|
-
const sessionUser =
|
|
553
|
+
const sessionUser = BuildMagicLinkSessionUser(userRecord, payload);
|
|
544
554
|
|
|
545
555
|
// Session-established audit — once per issued token (deduped), fire-and-forget so
|
|
546
556
|
// it never adds latency to the request. The dedup mark is set synchronously here.
|
|
@@ -626,6 +636,15 @@ function parseRequestHostname(origin: string | undefined): string | undefined {
|
|
|
626
636
|
}
|
|
627
637
|
}
|
|
628
638
|
|
|
639
|
+
/**
|
|
640
|
+
* The caller's IANA time zone from the `x-mj-timezone` header, or undefined when the header is
|
|
641
|
+
* absent, repeated, or names no zone this runtime knows (callers then use UTC).
|
|
642
|
+
*/
|
|
643
|
+
function readTimeZoneHeader(req: IncomingMessage): string | undefined {
|
|
644
|
+
const value = req.headers[TIME_ZONE_HEADER];
|
|
645
|
+
return typeof value === 'string' && IsValidTimeZone(value) ? value : undefined;
|
|
646
|
+
}
|
|
647
|
+
|
|
629
648
|
function extractAuthInputs(req: IncomingMessage): {
|
|
630
649
|
bearerToken: string;
|
|
631
650
|
sessionId: string;
|
|
@@ -633,6 +652,7 @@ function extractAuthInputs(req: IncomingMessage): {
|
|
|
633
652
|
systemApiKey: string;
|
|
634
653
|
userApiKey: string;
|
|
635
654
|
requestContext: RequestContext;
|
|
655
|
+
timeZone: string | undefined;
|
|
636
656
|
} {
|
|
637
657
|
const sessionIdRaw = req.headers['x-session-id'];
|
|
638
658
|
const requestDomain = parseRequestHostname(req.headers.origin);
|
|
@@ -657,6 +677,7 @@ function extractAuthInputs(req: IncomingMessage): {
|
|
|
657
677
|
systemApiKey,
|
|
658
678
|
userApiKey,
|
|
659
679
|
requestContext,
|
|
680
|
+
timeZone: readTimeZoneHeader(req),
|
|
660
681
|
};
|
|
661
682
|
}
|
|
662
683
|
|
|
@@ -681,7 +702,7 @@ export function CreateUnifiedAuthMiddleware(
|
|
|
681
702
|
}
|
|
682
703
|
|
|
683
704
|
try {
|
|
684
|
-
const { bearerToken, sessionId, requestDomain, systemApiKey, userApiKey, requestContext } =
|
|
705
|
+
const { bearerToken, sessionId, requestDomain, systemApiKey, userApiKey, requestContext, timeZone } =
|
|
685
706
|
extractAuthInputs(req);
|
|
686
707
|
|
|
687
708
|
const userPayload = await GetUserPayload(
|
|
@@ -699,6 +720,9 @@ export function CreateUnifiedAuthMiddleware(
|
|
|
699
720
|
res.status(401).json({ error: 'Invalid token' });
|
|
700
721
|
return;
|
|
701
722
|
}
|
|
723
|
+
if (timeZone) {
|
|
724
|
+
userPayload.TimeZone = timeZone;
|
|
725
|
+
}
|
|
702
726
|
|
|
703
727
|
// Attach to request for downstream consumers
|
|
704
728
|
req.userPayload = userPayload;
|
package/src/coreRoutes.ts
CHANGED
|
@@ -23,6 +23,7 @@ import { CreateMagicLinkHandler, CreateMagicLinkJwksRouter, RegisterMagicLinkAut
|
|
|
23
23
|
import { CreateWidgetHandler, WIDGET_MOUNT_PATH } from './realtimeWidget/index.js';
|
|
24
24
|
import { CreateRealtimeVerifyRouter, REALTIME_VERIFY_MOUNT_PATH, RealtimeSessionVerificationService } from './realtimeSessions/index.js';
|
|
25
25
|
import { CreateAuthProviderCatalogRouter, AUTH_CATALOG_MOUNT_PATH } from './auth/AuthProviderCatalogRouter.js';
|
|
26
|
+
import { AssertLocalDevAuthAllowed, IsLocalDevAuthEnabled, RegisterLocalDevAuthProvider } from './auth/localDevAuth.js';
|
|
26
27
|
import type { StartupLogger } from './logging/StartupLogger.js';
|
|
27
28
|
|
|
28
29
|
type RouteLog = Pick<StartupLogger, 'LogIf'>;
|
|
@@ -63,10 +64,24 @@ export function MountPublicCoreRoutes(app: Application, publicUrl: string, log:
|
|
|
63
64
|
|
|
64
65
|
routers.MagicLink = mountMagicLinkPublic(app, publicUrl, log);
|
|
65
66
|
routers.Widget = mountWidgetPublic(app, publicUrl, log);
|
|
67
|
+
mountLocalDevAuthPublic(app, publicUrl, log);
|
|
66
68
|
mountRealtimeVerify(app, log);
|
|
67
69
|
return routers;
|
|
68
70
|
}
|
|
69
71
|
|
|
72
|
+
function mountLocalDevAuthPublic(app: Application, publicUrl: string, log: RouteLog): void {
|
|
73
|
+
AssertLocalDevAuthAllowed();
|
|
74
|
+
if (!IsLocalDevAuthEnabled()) {
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
RegisterLocalDevAuthProvider(publicUrl);
|
|
78
|
+
if (!configInfo.magicLink?.enabled && !configInfo.widget?.enabled) {
|
|
79
|
+
app.use(ApiRoute(MAGIC_LINK_MOUNT_PATH), cors<cors.CorsRequest>(), CreateMagicLinkJwksRouter());
|
|
80
|
+
log.LogIf('verbose', `[LocalDevAuth] Published RS256 signing key at ${ApiRoute(MAGIC_LINK_MOUNT_PATH)}/jwks.json`);
|
|
81
|
+
}
|
|
82
|
+
log.LogIf('verbose', `[LocalDevAuth] Local dev auth routes enabled at ${ApiRoute(AUTH_CATALOG_MOUNT_PATH)}/dev-login (localhost only)`);
|
|
83
|
+
}
|
|
84
|
+
|
|
70
85
|
function mountOAuthCallback(app: Application, publicUrl: string, log: RouteLog): Router | undefined {
|
|
71
86
|
if (!publicUrl) {
|
|
72
87
|
return undefined;
|