From a45715c80afef266a34c4a5ed8106ef056e4b5e6 Mon Sep 17 00:00:00 2001 From: Adam Wootton <adam@taplytics.com> Date: Wed, 23 Oct 2024 12:12:10 -0400 Subject: [PATCH] disable next events serverside in the ClientSide provider --- sdk/js/src/Client.ts | 27 +++++++++++++------ sdk/js/src/EventQueue.ts | 2 ++ .../InternalDevCycleClientsideProvider.tsx | 16 ++++++++--- sdk/nextjs/src/client/internal/context.ts | 19 +++++++++++-- .../src/client/internal/useDevCycleClient.ts | 5 ++-- sdk/nextjs/src/client/useDevCycleClient.ts | 4 +-- 6 files changed, 55 insertions(+), 18 deletions(-) diff --git a/sdk/js/src/Client.ts b/sdk/js/src/Client.ts index 1f82464f8..4923b1c79 100644 --- a/sdk/js/src/Client.ts +++ b/sdk/js/src/Client.ts @@ -82,7 +82,7 @@ export class DevCycleClient< [key: string]: { [key: string]: DVCVariable<any> } } private store: CacheStore - private eventQueue: EventQueue<Variables, CustomData> + private eventQueue?: EventQueue<Variables, CustomData> private requestConsolidator: ConfigRequestConsolidator eventEmitter: EventEmitter private streamingConnection?: StreamingConnection @@ -125,7 +125,15 @@ export class DevCycleClient< this.sdkKey = sdkKey this.variableDefaultMap = {} - this.eventQueue = new EventQueue(sdkKey, this, options) + + if ( + !( + this.options.disableAutomaticEventLogging && + this.options.disableCustomEventLogging + ) + ) { + this.eventQueue = new EventQueue(sdkKey, this, options) + } this.eventEmitter = new EventEmitter() if (!this.options.disableRealtimeUpdates) { @@ -359,7 +367,7 @@ export class DevCycleClient< try { const variableFromConfig = this.config?.variables?.[variable.key] - this.eventQueue.queueAggregateEvent({ + this.eventQueue?.queueAggregateEvent({ type: variable.isDefaulted ? EventTypes.variableDefaulted : EventTypes.variableEvaluated, @@ -439,7 +447,7 @@ export class DevCycleClient< return this.config?.variables || {} } - void this.eventQueue.flushEvents() + void this.eventQueue?.flushEvents() try { await this.onInitialized @@ -489,7 +497,7 @@ export class DevCycleClient< this.options, ) const promise = new Promise<DVCVariableSet>((resolve, reject) => { - this.eventQueue.flushEvents() + this.eventQueue?.flushEvents() this.onInitialized .then(() => this.store.loadAnonUserId()) @@ -598,7 +606,7 @@ export class DevCycleClient< checkParamDefined('type', event.type) this.onInitialized.then(() => { - this.eventQueue.queueEvent(event) + this.eventQueue?.queueEvent(event) }) } @@ -608,7 +616,10 @@ export class DevCycleClient< * @param callback */ flushEvents(callback?: () => void): Promise<void> { - return this.eventQueue.flushEvents().then(() => callback?.()) + return ( + this.eventQueue?.flushEvents().then(() => callback?.()) ?? + Promise.resolve().then(() => callback?.()) + ) } /** @@ -637,7 +648,7 @@ export class DevCycleClient< this.streamingConnection?.close() - await this.eventQueue.close() + await this.eventQueue?.close() } /** diff --git a/sdk/js/src/EventQueue.ts b/sdk/js/src/EventQueue.ts index 77b44a821..e1145dc37 100644 --- a/sdk/js/src/EventQueue.ts +++ b/sdk/js/src/EventQueue.ts @@ -156,6 +156,7 @@ export class EventQueue< ) return } + console.log('queueEvent', event) this.eventQueue.push(event) } @@ -170,6 +171,7 @@ export class EventQueue< ) return } + console.log('queueAggregateEvent', event) checkParamDefined('type', event.type) checkParamDefined('target', event.target) diff --git a/sdk/nextjs/src/client/internal/InternalDevCycleClientsideProvider.tsx b/sdk/nextjs/src/client/internal/InternalDevCycleClientsideProvider.tsx index 91092731e..061da4e78 100644 --- a/sdk/nextjs/src/client/internal/InternalDevCycleClientsideProvider.tsx +++ b/sdk/nextjs/src/client/internal/InternalDevCycleClientsideProvider.tsx @@ -22,6 +22,8 @@ type DevCycleClientsideProviderProps = { children: React.ReactNode } +const isServer = typeof window === 'undefined' + /** * Component which renders nothing, but runs code to keep client state in sync with server * Also waits for the server's data promise with the `use` hook. This triggers the nearest suspense boundary, @@ -36,7 +38,7 @@ export const SuspendedProviderInitialization = ({ }: Pick< DevCycleClientContext, 'serverDataPromise' | 'userAgent' ->): React.ReactElement => { +>): React.ReactNode => { const serverData = use(serverDataPromise) const [previousContext, setPreviousContext] = useState< DevCycleServerData | undefined @@ -45,14 +47,16 @@ export const SuspendedProviderInitialization = ({ if (previousContext !== serverData) { // change user and config data to match latest server data // if the data has changed since the last invocation - context.client.synchronizeBootstrapData( + // assert this is a DevCycleClient, not a DevCycleNextClient, because it is. We expose a more limited type + // to the end user + (context.client as DevCycleClient).synchronizeBootstrapData( serverData.config, serverData.user, userAgent, ) setPreviousContext(serverData) } - return <></> + return null } export const InternalDevCycleClientsideProvider = ({ @@ -102,6 +106,12 @@ export const InternalDevCycleClientsideProvider = ({ sdkPlatform: 'nextjs', deferInitialization: true, disableConfigCache: true, + ...(isServer + ? { + disableAutomaticEventLogging: true, + disableCustomEventLogging: true, + } + : {}), next: { configRefreshHandler: revalidateConfig, }, diff --git a/sdk/nextjs/src/client/internal/context.ts b/sdk/nextjs/src/client/internal/context.ts index 24ff9b052..20e2109ab 100644 --- a/sdk/nextjs/src/client/internal/context.ts +++ b/sdk/nextjs/src/client/internal/context.ts @@ -1,9 +1,24 @@ 'use client' -import { DevCycleClient } from '@devcycle/js-client-sdk' +import { + DevCycleClient, + DVCCustomDataJSON, + VariableDefinitions, +} from '@devcycle/js-client-sdk' import React from 'react' +export type DevCycleNextClient< + Variables extends VariableDefinitions = VariableDefinitions, + CustomData extends DVCCustomDataJSON = DVCCustomDataJSON, +> = Omit< + DevCycleClient<Variables, CustomData>, + | 'onClientInitialized' + | 'identifyUser' + | 'resetUser' + | 'synchronizeBootstrapData' +> + type ClientProviderContext = { - client: DevCycleClient + client: DevCycleNextClient clientSDKKey: string enableStreaming: boolean serverDataPromise: Promise<unknown> diff --git a/sdk/nextjs/src/client/internal/useDevCycleClient.ts b/sdk/nextjs/src/client/internal/useDevCycleClient.ts index 07de42357..34ce0cd2e 100644 --- a/sdk/nextjs/src/client/internal/useDevCycleClient.ts +++ b/sdk/nextjs/src/client/internal/useDevCycleClient.ts @@ -1,7 +1,6 @@ import { useContext } from 'react' -import { DevCycleProviderContext } from './context' -import { DevCycleClient } from '@devcycle/js-client-sdk' +import { DevCycleNextClient, DevCycleProviderContext } from './context' -export const useDevCycleClient = (): DevCycleClient => { +export const useDevCycleClient = (): DevCycleNextClient => { return useContext(DevCycleProviderContext).client } diff --git a/sdk/nextjs/src/client/useDevCycleClient.ts b/sdk/nextjs/src/client/useDevCycleClient.ts index 4e287720b..1f0da1f90 100644 --- a/sdk/nextjs/src/client/useDevCycleClient.ts +++ b/sdk/nextjs/src/client/useDevCycleClient.ts @@ -1,6 +1,6 @@ +import { DevCycleNextClient } from './internal/context' import { useDevCycleClient as internalUseClient } from './internal/useDevCycleClient' -import { DevCycleClient } from '@devcycle/js-client-sdk' -export const useDevCycleClient = (): DevCycleClient => { +export const useDevCycleClient = (): DevCycleNextClient => { return internalUseClient() }