-
Notifications
You must be signed in to change notification settings - Fork 36
Expand file tree
/
Copy pathLDServerSession.ts
More file actions
158 lines (145 loc) · 5.88 KB
/
LDServerSession.ts
File metadata and controls
158 lines (145 loc) · 5.88 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
import { cache } from 'react';
import { LDContext, type LDFlagsStateOptions } from '@launchdarkly/js-server-sdk-common';
import { LDServerSession } from './LDClient';
import { LDServerBaseClient } from './LDServerBaseClient';
// Creates a per-request memoized store — each React render tree (request)
// gets its own isolated instance. The store is populated by createLDServerSession
// and read by useLDServerSession.
const withCache = cache(() => ({ session: null as LDServerSession | null }));
function isServer(): boolean {
return typeof window === 'undefined';
}
/**
* Creates a LaunchDarkly server SDK client that is scoped to a specific context.
*
* @remarks
* **NOTE:** We recommend using the {@link createLDServerSession} function to create your server session
* instead of directly calling this function.
*
* This function is provided to allow the caller to have more control over handling their scoped LD client.
* If using this function, the caller is responsible for managing the lifecycle of the created wrapped client.
*
* @throws {Error} If called in a browser environment. This function must only be called on the
* server. Ensure the module that calls this is not imported from client components.
*
* @example
* ```ts
* // lib/ld-server.ts
* import { init } from '@launchdarkly/node-server-sdk';
* import { createLDServerWrapper } from '@launchdarkly/react-sdk/server';
*
* const ldBaseClient = await init(process.env.LAUNCHDARKLY_SDK_KEY || '');
* export const serverSession = createLDServerWrapper(ldBaseClient, defaultContext);
* ```
*
* @param client Any LaunchDarkly server SDK client that satisfies {@link LDServerBaseClient}.
* @param context The context to bind to this session. Typically resolved from the request
* (e.g. from auth tokens, cookies, or headers).
* @returns An {@link LDServerSession} scoped to the given context.
*/
export function createLDServerWrapper(
client: LDServerBaseClient,
context: LDContext,
): LDServerSession {
if (!isServer()) {
throw new Error(
'createLDServerWrapper must only be called on the server. ' +
'Ensure this module is not imported from client components.',
);
}
if (client.forContext) {
const scoped = client.forContext(context, {
wrapperName: 'react-client-sdk',
wrapperVersion: '0.0.0', // x-release-please-version
});
return {
initialized: () => client.initialized(),
getContext: () => scoped.currentContext(),
boolVariation: scoped.boolVariation,
numberVariation: scoped.numberVariation,
stringVariation: scoped.stringVariation,
jsonVariation: scoped.jsonVariation,
boolVariationDetail: scoped.boolVariationDetail,
numberVariationDetail: scoped.numberVariationDetail,
stringVariationDetail: scoped.stringVariationDetail,
jsonVariationDetail: scoped.jsonVariationDetail,
allFlagsState: scoped.allFlagsState,
};
}
// Fallback for clients without forContext (e.g., edge SDKs)
return {
initialized: () => client.initialized(),
getContext: () => context,
boolVariation: (key, defaultValue) => client.boolVariation(key, context, defaultValue),
numberVariation: (key, defaultValue) => client.numberVariation(key, context, defaultValue),
stringVariation: (key, defaultValue) => client.stringVariation(key, context, defaultValue),
jsonVariation: (key, defaultValue) => client.jsonVariation(key, context, defaultValue),
boolVariationDetail: (key, defaultValue) =>
client.boolVariationDetail(key, context, defaultValue),
numberVariationDetail: (key, defaultValue) =>
client.numberVariationDetail(key, context, defaultValue),
stringVariationDetail: (key, defaultValue) =>
client.stringVariationDetail(key, context, defaultValue),
jsonVariationDetail: (key, defaultValue) =>
client.jsonVariationDetail(key, context, defaultValue),
allFlagsState: (options?: LDFlagsStateOptions) => client.allFlagsState(context, options),
};
}
/**
* Creates a per-request evaluation scope by binding an {@link LDServerBaseClient} to a specific
* context.
*
* @param client Any LaunchDarkly server SDK client that satisfies {@link LDServerBaseClient}.
* @param context The context to bind to this session. Typically resolved from the request
* (e.g. from auth tokens, cookies, or headers).
* @returns An {@link LDServerSession} scoped to the given context.
*/
export function createLDServerSession(
client: LDServerBaseClient,
context: LDContext,
): LDServerSession {
const session = createLDServerWrapper(client, context);
withCache().session = session;
return session;
}
/**
* Returns the {@link LDServerSession} scoped to the current request.
*
* @remarks
* **NOTE:** This function is only used to retrieve the session stored by {@link createLDServerSession}.
* You must call {@link createLDServerSession} before calling this function or it will return a null value.
*
* @example
* ```ts
* // app.tsx (entry point)
* import { createLDServerSession } from '@launchdarkly/react-sdk/server';
* const session = createLDServerSession(client, context);
*
* // component.ts
* import { useLDServerSession } from '@launchdarkly/react-sdk/server';
*
* export default function MyComponent() {
* const session = useLDServerSession();
* if (session) {
* const flagValue = await session.boolVariation('my-flag', false);
* return <div>{flagValue ? 'Yes' : 'No'}</div>;
* }
* return <div>Loading...</div>;
* }
* ```
*
* @returns The {@link LDServerSession} scoped to the current request, or `null` if no session has been created.
*/
export function useLDServerSession(): LDServerSession | null {
if (!isServer()) {
throw new Error(
'useLDServerSession must only be called on the server. ' +
'Ensure this module is not imported from client components.',
);
}
const { session } = withCache();
if (!session) {
return null;
}
return session;
}