-
Notifications
You must be signed in to change notification settings - Fork 71
Expand file tree
/
Copy pathindex.ts
More file actions
130 lines (115 loc) · 3.32 KB
/
index.ts
File metadata and controls
130 lines (115 loc) · 3.32 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
import {
init,
type LDClient,
type LDContext,
} from '@launchdarkly/vercel-server-sdk';
import { createClient } from '@vercel/edge-config';
import type { Adapter } from 'flags';
import { createCachedEdgeConfigClient as cacheEdgeConfigClient } from 'flags/utils';
export { getProviderData } from './provider';
export type { LDContext };
interface AdapterOptions<ValueType> {
defaultValue?: ValueType;
}
type AdapterResponse = {
variation: <ValueType>(
options?: AdapterOptions<ValueType>,
) => Adapter<ValueType, LDContext>;
/** The LaunchDarkly client instance used by the adapter. */
ldClient: LDClient;
};
let defaultLaunchDarklyAdapter:
| ReturnType<typeof createLaunchDarklyAdapter>
| undefined;
function assertEnv(name: string): string {
const value = process.env[name];
if (!value) {
throw new Error(
`LaunchDarkly Adapter: Missing ${name} environment variable`,
);
}
return value;
}
export function createLaunchDarklyAdapter({
projectSlug,
clientSideId,
edgeConfigConnectionString,
}: {
projectSlug: string;
clientSideId: string;
edgeConfigConnectionString: string;
}): AdapterResponse {
const edgeConfigClient = createClient(edgeConfigConnectionString);
const { run, client } = cacheEdgeConfigClient(edgeConfigClient);
let initPromise: Promise<unknown> | null = null;
const ldClient = init(clientSideId, client);
function origin(key: string) {
return `https://app.launchdarkly.com/projects/${projectSlug}/flags/${key}/`;
}
function variation<ValueType>(
options: AdapterOptions<ValueType> = {},
): Adapter<ValueType, LDContext> {
return {
origin,
async decide({ key, entities, headers }): Promise<ValueType> {
if (!ldClient.initialized()) {
if (!initPromise) initPromise = ldClient.waitForInitialization();
await initPromise;
}
return run(
headers,
() =>
ldClient.variation(
key,
entities as LDContext,
options.defaultValue,
) as ValueType,
);
},
};
}
return {
ldClient,
variation,
};
}
function getOrCreateDeaultAdapter() {
if (!defaultLaunchDarklyAdapter) {
const edgeConfigConnectionString = assertEnv('EDGE_CONFIG');
const clientSideId = assertEnv('LAUNCHDARKLY_CLIENT_SIDE_ID');
const projectSlug = assertEnv('LAUNCHDARKLY_PROJECT_SLUG');
defaultLaunchDarklyAdapter = createLaunchDarklyAdapter({
projectSlug,
clientSideId,
edgeConfigConnectionString,
});
}
return defaultLaunchDarklyAdapter;
}
/**
* The default LaunchDarkly adapter.
*
* This is a convenience object that pre-initializes the LaunchDarkly SDK and provides
* the adapter function for usage with the Flags SDK.
*
* This is the recommended way to use the LaunchDarkly adapter.
*
* ```ts
* // flags.ts
* import { flag } from 'flags/next';
* import { ldAdapter, type LDContext } from '@flags-sdk/launchdarkly';
*
* const flag = flag<boolean, LDContext>({
* key: 'my-flag',
* defaultValue: false,
* identify: () => ({ key: "user-123" }),
* adapter: ldAdapter.variation(),
* });
* ```
*/
export const ldAdapter: AdapterResponse = {
variation: (...args) => getOrCreateDeaultAdapter().variation(...args),
get ldClient() {
return getOrCreateDeaultAdapter().ldClient;
},
};