@devcycle/nextjs-sdk
Version:
The Next.js SDK for DevCycle!
89 lines • 4.52 kB
JavaScript
'use client';
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
import { Suspense, use, useRef } from 'react';
import { initializeDevCycle } from '@devcycle/js-client-sdk';
import { DevCycleProviderContext } from './context';
import { useRouter } from 'next/navigation';
import { invalidateConfig, setDebugUser, removeDebugUser, } from '../../common/actions';
import { useClearUserDebugCookie } from './useClearUserDebugCookie';
const isServer = typeof window === 'undefined';
/**
* keep the clientside instance of the SDK up-to-date with new data coming from the server during realtime updates
* @param serverDataPromise
* @param client
* @param enableStreaming
* @constructor
*/
const SynchronizeClientData = ({ serverDataPromise, client, enableStreaming, }) => {
const serverData = use(serverDataPromise);
const dataRef = useRef(
// when streaming is disabled, we run synchronization on the initial server data in the
// InternalDevCycleClientsideProvider component so we don't need to do it again immediately.
// In streaming mode we want to synchronize on that initial server data since we aren't doing it above
// Therefore set this ref to the initial server data so the below check won't run when not in streaming mode
!enableStreaming ? serverData : null);
const clientRef = useRef(client);
if (dataRef.current !== serverData || clientRef.current !== client) {
dataRef.current = serverData;
clientRef.current = client;
// do this in a timeout to avoid setting React state in components that are subscribed to variables as a
// side effect of the current render, since this causes errors. Instead schedule the update to occur after
// render completes
setTimeout(() => client.synchronizeBootstrapData(serverData.config, serverData.user));
}
return null;
};
export const InternalDevCycleClientsideProvider = ({ context, children, }) => {
const clientRef = useRef();
const router = useRouter();
useClearUserDebugCookie();
const { serverDataPromise, serverData, clientSDKKey, enableStreaming } = context;
const revalidateConfig = async (lastModified) => {
var _a;
if (context.realtimeDelay) {
// wait configured delay before checking for new config
await new Promise((resolve) => setTimeout(resolve, context.realtimeDelay));
}
try {
await invalidateConfig(clientSDKKey, (_a = serverData === null || serverData === void 0 ? void 0 : serverData.user.user_id) !== null && _a !== void 0 ? _a : null);
}
catch {
// do nothing on failure, this is best effort
}
if (context.realtimeDelay) {
// if delay is configured, assume that the server action invalidation won't update any content and just
// call for a full in-place refresh
router.refresh();
}
};
if (!clientRef.current) {
clientRef.current = initializeDevCycle(clientSDKKey, {
...context.options,
sdkPlatform: 'nextjs',
deferInitialization: true,
disableConfigCache: true,
next: {
configRefreshHandler: revalidateConfig,
disableAutomaticEventFlush: isServer,
},
});
// pass server actions as callback in client to be called by web-debugger
clientRef.current.subscribe('debugUserSet', setDebugUser);
clientRef.current.subscribe('debugUserReverted', removeDebugUser);
if (!enableStreaming) {
// we expect that in non-streaming mode, the serverside portion of this provider should have awaited
// the serverDataPromise and passed in the result here
if (!serverData) {
throw new Error('Server data should be available. Please contact DevCycle support.');
}
clientRef.current.synchronizeBootstrapData(serverData.config, serverData.user, serverData.userAgent);
}
}
return (_jsxs(DevCycleProviderContext.Provider, { value: {
client: clientRef.current,
clientSDKKey,
enableStreaming,
serverDataPromise,
}, children: [_jsx(Suspense, { fallback: null, children: _jsx(SynchronizeClientData, { serverDataPromise: serverDataPromise, client: clientRef.current, enableStreaming: enableStreaming }) }), children] }));
};
//# sourceMappingURL=InternalDevCycleClientsideProvider.js.map