Skip to content

Commit bd7e10a

Browse files
AAorrisdferber90
andauthored
(statsig-adapter): Release Statsig Adapter (#43)
* (statsig-adapter): Move Edge Config to peer deps, more consistent api key naming * Move dynamic imports into initializeStatsig * Add default adapter pattern * Add changeset * Add layerParameter to get config parameters from experiments in a layer * (statsig-adapter) Focus on feature management (gates, dynamic configs), add origin, add exposure logging option * (statsig-adapter) Update key handling and default export * (refactoring, wip): move some runtime details out of the main implementation of the Statsig adapter * Use statsig-node-lite * Switch default environment variable to STATSIG_SERVER_SECRET" * (statsig-adapter) Add readme * Update README — Remove details on precompute, add optional peer deps to install command * Add peerDependenciesMeta * Update changeset text * Refactor Statsig Adapter Add experimentation primitives, disable exposure logging by default, identify StatsigUser instead of `{ statsigUser: StatsigUser }` * (statsig-adapter) Update default environment variables * use envvar: EXPERIMENTATION_CONFIG_ITEM_KEY * Update packages/adapter-statsig/README.md Co-authored-by: Dominik Ferber <[email protected]> * Update packages/adapter-statsig/README.md Co-authored-by: Dominik Ferber <[email protected]> * Update README, docstrings * upgrade statsig-node-lite * remove edge config devDep * reduce propagation time of Statsig updates * update deps * update lockfile * prefix error * simplify README * extend package.json * fix type * switch example to gate * expose types of Statsig SDK * work around syncing issue --------- Co-authored-by: Dominik Ferber <[email protected]>
1 parent f030fc5 commit bd7e10a

File tree

8 files changed

+1482
-1123
lines changed

8 files changed

+1482
-1123
lines changed

.changeset/lucky-rockets-chew.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@flags-sdk/statsig': minor
3+
---
4+
5+
Initial support for Feature Gates and Dynamic Configs

packages/adapter-statsig/README.md

+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
# Flags SDK — Statsig Provider
2+
3+
The [Statsig provider](https://flags-sdk.dev/docs/api-reference/adapters/statsig) for the [Flags SDK](https://flags-sdk.dev/) contains support for Statsig's Feature Gates, Dynamic Config, Experiments, Autotune and Layers.
4+
5+
## Setup
6+
7+
The Statsig provider is available in the `@flags-sdk/statsig` module. You can install it with
8+
9+
```bash
10+
pnpm i @flags-sdk/statsig
11+
```
12+
13+
## Provider Instance
14+
15+
You can import the default adapter instance `statigAdapter` from `@flags-sdk/statsig`:
16+
17+
```ts
18+
import { statsigAdapter } from '@flags-sdk/statsig';
19+
```
20+
21+
## Example
22+
23+
```ts
24+
import { flag } from 'flags/next';
25+
import { statsigAdapter } from '@flags-sdk/statsig';
26+
27+
export const marketingGate = flag<boolean>({
28+
key: 'marketing_gate',
29+
adapter: statsigAdapter.featureGate((config) => config.value),
30+
});
31+
```
32+
33+
## Documentation
34+
35+
Please check out the [Statsig provider documentation](https://flags-sdk.dev/docs/api-reference/adapters/statsig) for more information.

packages/adapter-statsig/package.json

+23-10
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,26 @@
11
{
22
"name": "@flags-sdk/statsig",
33
"version": "0.1.0",
4-
"description": "",
5-
"keywords": [],
4+
"description": "Statsig provider for the Flags SDK",
5+
"keywords": [
6+
"flags-sdk",
7+
"statsig",
8+
"dynamic config",
9+
"vercel",
10+
"edge config",
11+
"experiments",
12+
"experimentation",
13+
"ab testing"
14+
],
15+
"homepage": "https://flags-sdk.dev/docs",
16+
"bugs": {
17+
"url": "https://github.com/vercel/flags/issues"
18+
},
19+
"repository": {
20+
"type": "git",
21+
"url": "git+https://github.com/vercel/flags.git"
22+
},
623
"license": "MIT",
7-
"author": "",
824
"sideEffects": false,
925
"type": "module",
1026
"exports": {
@@ -47,26 +63,23 @@
4763
"type-check": "tsc --noEmit"
4864
},
4965
"dependencies": {
50-
"@vercel/edge-config": "1.2.0",
51-
"statsig-node": "5.31.0",
52-
"statsig-node-vercel": "0.4.0"
66+
"@vercel/edge-config": "^1.2.0",
67+
"@vercel/functions": "^1.5.2",
68+
"statsig-node-lite": "^0.4.2",
69+
"statsig-node-vercel": "^0.7.0"
5370
},
5471
"devDependencies": {
5572
"@types/node": "20.11.17",
56-
"@vercel/edge-config": "1.2.0",
5773
"eslint-config-custom": "workspace:*",
5874
"flags": "workspace:*",
5975
"msw": "2.6.4",
6076
"rimraf": "6.0.1",
61-
"statsig-node": "5.31.0",
62-
"statsig-node-vercel": "0.4.0",
6377
"tsconfig": "workspace:*",
6478
"tsup": "8.0.1",
6579
"typescript": "5.6.3",
6680
"vite": "5.1.1",
6781
"vitest": "1.4.0"
6882
},
69-
"peerDependencies": {},
7083
"publishConfig": {
7184
"access": "public"
7285
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
import Statsig from 'statsig-node-lite';
2+
3+
declare global {
4+
var EdgeRuntime: string | undefined;
5+
}
6+
7+
export const isEdgeRuntime = (): boolean => {
8+
return EdgeRuntime !== undefined;
9+
};
10+
11+
/**
12+
* The Edge Config Data Adapter is an optional peer dependency that allows
13+
* the Statsig SDK to retrieve its data from Edge Config instead of over the network.
14+
*/
15+
export async function createEdgeConfigDataAdapter(options: {
16+
edgeConfigItemKey: string;
17+
edgeConfigConnectionString: string;
18+
}) {
19+
// Edge Config adapter requires `@vercel/edge-config` and `statsig-node-vercel`
20+
// Since it is a peer dependency, we will import it dynamically
21+
const { EdgeConfigDataAdapter } = await import('statsig-node-vercel');
22+
const { createClient } = await import('@vercel/edge-config');
23+
return new EdgeConfigDataAdapter({
24+
edgeConfigItemKey: options.edgeConfigItemKey,
25+
edgeConfigClient: createClient(options.edgeConfigConnectionString, {
26+
// We disable the development cache as Statsig caches for 10 seconds internally,
27+
// and we want to avoid situations where Statsig tries to read the latest value,
28+
// but hits the development cache and then caches the outdated value for another 10 seconds,
29+
// as this would lead to the developer having to wait 20 seconds to see the latest value.
30+
disableDevelopmentCache: true,
31+
}),
32+
});
33+
}
34+
35+
/**
36+
* Edge runtime does not support timers outside of a request context.
37+
*
38+
* Statsig syncs config specs outside of the request context,
39+
* so we will support it in triggering config spec synchronization in this case.
40+
*/
41+
export const createSyncingHandler = (): null | (() => void) => {
42+
// Syncing both in Edge Runtime and Node.js for now, as the sync is otherwise
43+
// not working during local development.
44+
//
45+
// This needs to be fixed in statsig-node-lite in the future.
46+
//
47+
// Ideally the Statsig SDK would not sync at all and instead always read from Edge Config,
48+
// this would provide two benefits:
49+
// - changes would propagate immediately instead of being cached for 5s or 10s
50+
// - the broken syncing due to issues in Date.now in Edge Runtime would be irrelevant
51+
//
52+
// if (typeof EdgeRuntime === 'undefined') return null;
53+
54+
const timerInterval = 5_000;
55+
let isSyncingConfigSpecs = false;
56+
let nextConfigSpecSyncTime = Date.now() + timerInterval;
57+
return (): void => {
58+
if (Date.now() >= nextConfigSpecSyncTime && !isSyncingConfigSpecs) {
59+
try {
60+
isSyncingConfigSpecs = true;
61+
const sync = Statsig.syncConfigSpecs().finally(() => {
62+
isSyncingConfigSpecs = false;
63+
nextConfigSpecSyncTime = Date.now() + timerInterval;
64+
});
65+
import('@vercel/functions').then(({ waitUntil }) => {
66+
waitUntil(sync);
67+
});
68+
} catch (e) {
69+
// continue
70+
}
71+
}
72+
};
73+
};

0 commit comments

Comments
 (0)