statsig-node-vercel 0.5.0 → 0.7.0-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/EdgeConfigDataAdapter.ts +47 -16
- package/README.md +29 -16
- package/__tests__/EdgeConfigDataAdapter.test.ts +25 -15
- package/dist/EdgeConfigDataAdapter.d.ts +28 -3
- package/dist/EdgeConfigDataAdapter.js +18 -12
- package/package.json +4 -4
package/EdgeConfigDataAdapter.ts
CHANGED
|
@@ -1,28 +1,55 @@
|
|
|
1
|
-
import { AdapterResponse, IDataAdapter } from
|
|
2
|
-
import {
|
|
1
|
+
import { AdapterResponse, IDataAdapter } from "statsig-node-lite";
|
|
2
|
+
import type { EdgeConfigClient } from "@vercel/edge-config";
|
|
3
3
|
|
|
4
4
|
export class EdgeConfigDataAdapter implements IDataAdapter {
|
|
5
|
-
|
|
5
|
+
/**
|
|
6
|
+
* The key under which Statisg specs are stored in Edge Config
|
|
7
|
+
*/
|
|
8
|
+
private edgeConfigItemKey: string;
|
|
9
|
+
/**
|
|
10
|
+
* A fully configured Edge Config client
|
|
11
|
+
*/
|
|
6
12
|
private edgeConfigClient: EdgeConfigClient;
|
|
7
13
|
private supportConfigSpecPolling: boolean = false;
|
|
8
14
|
|
|
9
|
-
public constructor(
|
|
10
|
-
|
|
11
|
-
|
|
15
|
+
public constructor(options: {
|
|
16
|
+
/**
|
|
17
|
+
* The key under which Statsig specs are stored in Edge Config
|
|
18
|
+
*/
|
|
19
|
+
edgeConfigItemKey: string;
|
|
20
|
+
/**
|
|
21
|
+
* A fully configured Edge Config client.
|
|
22
|
+
*
|
|
23
|
+
* @example <caption>Creating an Edge Config client</caption>
|
|
24
|
+
*
|
|
25
|
+
* ```js
|
|
26
|
+
* import { createClient } from "@vercel/edge-config";
|
|
27
|
+
*
|
|
28
|
+
* createClient(process.env.EDGE_CONFIG)
|
|
29
|
+
* ```
|
|
30
|
+
*/
|
|
31
|
+
edgeConfigClient: EdgeConfigClient;
|
|
32
|
+
}) {
|
|
33
|
+
this.edgeConfigItemKey = options.edgeConfigItemKey;
|
|
34
|
+
this.edgeConfigClient = options.edgeConfigClient;
|
|
12
35
|
}
|
|
13
36
|
|
|
14
37
|
// eslint-disable-next-line @typescript-eslint/require-await
|
|
15
38
|
public async get(key: string): Promise<AdapterResponse> {
|
|
16
|
-
if (key
|
|
17
|
-
return {
|
|
39
|
+
if (!this.isConfgSpecKey(key)) {
|
|
40
|
+
return {
|
|
41
|
+
error: new Error(`Edge Config Adapter Only Supports Config Specs`),
|
|
42
|
+
};
|
|
18
43
|
}
|
|
19
44
|
|
|
20
|
-
const data = await this.edgeConfigClient.get(this.
|
|
45
|
+
const data = await this.edgeConfigClient.get(this.edgeConfigItemKey);
|
|
21
46
|
if (data == null) {
|
|
22
47
|
return { error: new Error(`key (${key}) does not exist`) };
|
|
23
48
|
}
|
|
24
49
|
if (typeof data !== "object") {
|
|
25
|
-
return {
|
|
50
|
+
return {
|
|
51
|
+
error: new Error(`Edge Config value expected to be an object or array`),
|
|
52
|
+
};
|
|
26
53
|
}
|
|
27
54
|
return { result: data };
|
|
28
55
|
}
|
|
@@ -31,13 +58,13 @@ export class EdgeConfigDataAdapter implements IDataAdapter {
|
|
|
31
58
|
public async set(
|
|
32
59
|
key: string,
|
|
33
60
|
value: string,
|
|
34
|
-
time?: number | undefined
|
|
61
|
+
time?: number | undefined
|
|
35
62
|
): Promise<void> {
|
|
36
63
|
// no-op. Statsig's Edge Config integration keeps config specs synced through Statsig's service
|
|
37
64
|
}
|
|
38
65
|
|
|
39
66
|
public async initialize(): Promise<void> {
|
|
40
|
-
const data = await this.edgeConfigClient.get(this.
|
|
67
|
+
const data = await this.edgeConfigClient.get(this.edgeConfigItemKey);
|
|
41
68
|
|
|
42
69
|
if (data) {
|
|
43
70
|
this.supportConfigSpecPolling = true;
|
|
@@ -45,14 +72,18 @@ export class EdgeConfigDataAdapter implements IDataAdapter {
|
|
|
45
72
|
}
|
|
46
73
|
|
|
47
74
|
// eslint-disable-next-line @typescript-eslint/require-await
|
|
48
|
-
public async shutdown(): Promise<void> {
|
|
49
|
-
|
|
50
|
-
}
|
|
75
|
+
public async shutdown(): Promise<void> {}
|
|
51
76
|
|
|
52
77
|
public supportsPollingUpdatesFor(key: string): boolean {
|
|
53
|
-
if (key
|
|
78
|
+
if (this.isConfgSpecKey(key)) {
|
|
54
79
|
return this.supportConfigSpecPolling;
|
|
55
80
|
}
|
|
56
81
|
return false;
|
|
57
82
|
}
|
|
83
|
+
|
|
84
|
+
private isConfgSpecKey(key: string): boolean {
|
|
85
|
+
const v2CacheKeyPattern =
|
|
86
|
+
/^statsig\|\/v[12]\/download_config_specs\|.+\|.+/;
|
|
87
|
+
return key === "statsig.cache" || v2CacheKeyPattern.test(key);
|
|
88
|
+
}
|
|
58
89
|
}
|
package/README.md
CHANGED
|
@@ -1,30 +1,43 @@
|
|
|
1
1
|
# Statsig Node Server SDK - Edge Config Adapter
|
|
2
|
-
|
|
2
|
+
|
|
3
|
+
[](https://badge.fury.io/js/statsig-node-vercel)
|
|
3
4
|
|
|
4
5
|
A first party Edge Config integration with the [Statsig server-side Node.js SDK](https://github.com/statsig-io/node-js-server-sdk).
|
|
5
6
|
|
|
6
7
|
## Quick Setup
|
|
8
|
+
|
|
7
9
|
1. Install the Statsig Node SDK
|
|
8
|
-
|
|
10
|
+
|
|
11
|
+
```sh
|
|
9
12
|
npm install statsig-node@5.3.0
|
|
10
13
|
```
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
+
|
|
15
|
+
2. Install this package and the Edge Config SDK
|
|
16
|
+
|
|
17
|
+
```sh
|
|
18
|
+
npm install statsig-node-vercel @vercel/edge-config
|
|
14
19
|
```
|
|
20
|
+
|
|
15
21
|
3. Install the [Statsig Vercel Integration](https://vercel.com/integrations/statsig)
|
|
16
|
-
4. Import the
|
|
17
|
-
|
|
18
|
-
|
|
22
|
+
4. Import the packages
|
|
23
|
+
|
|
24
|
+
```js
|
|
25
|
+
import { EdgeConfigDataAdapter } from "statsig-node-vercel";
|
|
26
|
+
import { createClient } from "@vercel/edge-config";
|
|
19
27
|
```
|
|
28
|
+
|
|
20
29
|
5. Create an instance of the `EdgeConfigDataAdapter`
|
|
30
|
+
|
|
31
|
+
```js
|
|
32
|
+
const edgeConfigClient = createClient(process.env.EDGE_CONFIG);
|
|
33
|
+
const dataAdapter = new EdgeConfigDataAdapter({
|
|
34
|
+
edgeConfigClient: edgeConfigClient,
|
|
35
|
+
edgeConfigItemKey: "ITEM_KEY_FROM_INSTALLATION", // something like "statsig-5FSfBpWM9kUPqeKRlZPkod"
|
|
36
|
+
})
|
|
21
37
|
```
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
```
|
|
26
|
-
await statsig.initialize(
|
|
27
|
-
'server-secret-key',
|
|
28
|
-
{ dataAdapter: dataAdapter },
|
|
29
|
-
);
|
|
38
|
+
|
|
39
|
+
6. When initializing the `statsig` sdk, add the adapter to options, along with the initStrategyForIDLists and disableIdListsSync options to avoid a blocking network call for ID Lists. However, if you are using Statsig ID lists for evaluation, you'll need to omit those options and incur a network request to grab ID lists.
|
|
40
|
+
|
|
41
|
+
```js
|
|
42
|
+
await statsig.initialize("server-secret-key", { dataAdapter: dataAdapter, initStrategyForIDLists: 'none', disableIdListsSync: true });
|
|
30
43
|
```
|
|
@@ -1,26 +1,36 @@
|
|
|
1
|
-
import { EdgeConfigDataAdapter } from
|
|
2
|
-
import
|
|
1
|
+
import { EdgeConfigDataAdapter } from "../EdgeConfigDataAdapter";
|
|
2
|
+
import { createClient } from "@vercel/edge-config";
|
|
3
|
+
import fetchMock from "jest-fetch-mock";
|
|
3
4
|
|
|
4
|
-
describe(
|
|
5
|
-
const
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
describe("Validate edge config adapter functionality", () => {
|
|
6
|
+
const edgeConfigClient = createClient(process.env.EDGE_CONFIG);
|
|
7
|
+
const dataAdapter = new EdgeConfigDataAdapter({
|
|
8
|
+
edgeConfigItemKey: "statsig-companyid",
|
|
9
|
+
edgeConfigClient,
|
|
10
|
+
});
|
|
8
11
|
|
|
9
12
|
beforeEach(async () => {
|
|
10
|
-
fetchMock.enableMocks()
|
|
11
|
-
fetchMock.mockResponse('"
|
|
13
|
+
fetchMock.enableMocks();
|
|
14
|
+
fetchMock.mockResponse('{"a":1}');
|
|
12
15
|
await dataAdapter.initialize();
|
|
13
|
-
});
|
|
16
|
+
});
|
|
14
17
|
|
|
15
18
|
afterEach(async () => {
|
|
16
19
|
await dataAdapter.shutdown();
|
|
17
20
|
});
|
|
18
21
|
|
|
19
|
-
test(
|
|
22
|
+
test("Simple get", async () => {
|
|
20
23
|
const { result: gates } = await dataAdapter.get("statsig.cache");
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
24
|
+
expect(gates).toEqual({a: 1});
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
test('Simple get v2 key', async () => {
|
|
28
|
+
const { result: gates } = await dataAdapter.get("statsig|/v1/download_config_specs|plain_text|1234");
|
|
29
|
+
expect(gates).toEqual({a: 1});
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
test('Simple get v2 key with dcs v2', async () => {
|
|
33
|
+
const { result: gates } = await dataAdapter.get("statsig|/v2/download_config_specs|plain_text|1234");
|
|
34
|
+
expect(gates).toEqual({a: 1});
|
|
25
35
|
});
|
|
26
|
-
})
|
|
36
|
+
});
|
|
@@ -1,12 +1,37 @@
|
|
|
1
|
-
import { AdapterResponse, IDataAdapter } from
|
|
1
|
+
import { AdapterResponse, IDataAdapter } from "statsig-node-lite";
|
|
2
|
+
import type { EdgeConfigClient } from "@vercel/edge-config";
|
|
2
3
|
export declare class EdgeConfigDataAdapter implements IDataAdapter {
|
|
3
|
-
|
|
4
|
+
/**
|
|
5
|
+
* The key under which Statisg specs are stored in Edge Config
|
|
6
|
+
*/
|
|
7
|
+
private edgeConfigItemKey;
|
|
8
|
+
/**
|
|
9
|
+
* A fully configured Edge Config client
|
|
10
|
+
*/
|
|
4
11
|
private edgeConfigClient;
|
|
5
12
|
private supportConfigSpecPolling;
|
|
6
|
-
constructor(
|
|
13
|
+
constructor(options: {
|
|
14
|
+
/**
|
|
15
|
+
* The key under which Statsig specs are stored in Edge Config
|
|
16
|
+
*/
|
|
17
|
+
edgeConfigItemKey: string;
|
|
18
|
+
/**
|
|
19
|
+
* A fully configured Edge Config client.
|
|
20
|
+
*
|
|
21
|
+
* @example <caption>Creating an Edge Config client</caption>
|
|
22
|
+
*
|
|
23
|
+
* ```js
|
|
24
|
+
* import { createClient } from "@vercel/edge-config";
|
|
25
|
+
*
|
|
26
|
+
* createClient(process.env.EDGE_CONFIG)
|
|
27
|
+
* ```
|
|
28
|
+
*/
|
|
29
|
+
edgeConfigClient: EdgeConfigClient;
|
|
30
|
+
});
|
|
7
31
|
get(key: string): Promise<AdapterResponse>;
|
|
8
32
|
set(key: string, value: string, time?: number | undefined): Promise<void>;
|
|
9
33
|
initialize(): Promise<void>;
|
|
10
34
|
shutdown(): Promise<void>;
|
|
11
35
|
supportsPollingUpdatesFor(key: string): boolean;
|
|
36
|
+
private isConfgSpecKey;
|
|
12
37
|
}
|
|
@@ -10,25 +10,28 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
10
10
|
};
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
12
|
exports.EdgeConfigDataAdapter = void 0;
|
|
13
|
-
const edge_config_1 = require("@vercel/edge-config");
|
|
14
13
|
class EdgeConfigDataAdapter {
|
|
15
|
-
constructor(
|
|
14
|
+
constructor(options) {
|
|
16
15
|
this.supportConfigSpecPolling = false;
|
|
17
|
-
this.
|
|
18
|
-
this.edgeConfigClient =
|
|
16
|
+
this.edgeConfigItemKey = options.edgeConfigItemKey;
|
|
17
|
+
this.edgeConfigClient = options.edgeConfigClient;
|
|
19
18
|
}
|
|
20
19
|
// eslint-disable-next-line @typescript-eslint/require-await
|
|
21
20
|
get(key) {
|
|
22
21
|
return __awaiter(this, void 0, void 0, function* () {
|
|
23
|
-
if (key
|
|
24
|
-
return {
|
|
22
|
+
if (!this.isConfgSpecKey(key)) {
|
|
23
|
+
return {
|
|
24
|
+
error: new Error(`Edge Config Adapter Only Supports Config Specs`),
|
|
25
|
+
};
|
|
25
26
|
}
|
|
26
|
-
const data = yield this.edgeConfigClient.get(this.
|
|
27
|
+
const data = yield this.edgeConfigClient.get(this.edgeConfigItemKey);
|
|
27
28
|
if (data == null) {
|
|
28
29
|
return { error: new Error(`key (${key}) does not exist`) };
|
|
29
30
|
}
|
|
30
31
|
if (typeof data !== "object") {
|
|
31
|
-
return {
|
|
32
|
+
return {
|
|
33
|
+
error: new Error(`Edge Config value expected to be an object or array`),
|
|
34
|
+
};
|
|
32
35
|
}
|
|
33
36
|
return { result: data };
|
|
34
37
|
});
|
|
@@ -41,7 +44,7 @@ class EdgeConfigDataAdapter {
|
|
|
41
44
|
}
|
|
42
45
|
initialize() {
|
|
43
46
|
return __awaiter(this, void 0, void 0, function* () {
|
|
44
|
-
const data = yield this.edgeConfigClient.get(this.
|
|
47
|
+
const data = yield this.edgeConfigClient.get(this.edgeConfigItemKey);
|
|
45
48
|
if (data) {
|
|
46
49
|
this.supportConfigSpecPolling = true;
|
|
47
50
|
}
|
|
@@ -49,14 +52,17 @@ class EdgeConfigDataAdapter {
|
|
|
49
52
|
}
|
|
50
53
|
// eslint-disable-next-line @typescript-eslint/require-await
|
|
51
54
|
shutdown() {
|
|
52
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
53
|
-
});
|
|
55
|
+
return __awaiter(this, void 0, void 0, function* () { });
|
|
54
56
|
}
|
|
55
57
|
supportsPollingUpdatesFor(key) {
|
|
56
|
-
if (key
|
|
58
|
+
if (this.isConfgSpecKey(key)) {
|
|
57
59
|
return this.supportConfigSpecPolling;
|
|
58
60
|
}
|
|
59
61
|
return false;
|
|
60
62
|
}
|
|
63
|
+
isConfgSpecKey(key) {
|
|
64
|
+
const v2CacheKeyPattern = /^statsig\|\/v[12]\/download_config_specs\|.+\|.+/;
|
|
65
|
+
return key === "statsig.cache" || v2CacheKeyPattern.test(key);
|
|
66
|
+
}
|
|
61
67
|
}
|
|
62
68
|
exports.EdgeConfigDataAdapter = EdgeConfigDataAdapter;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "statsig-node-vercel",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0-beta.0",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -12,10 +12,10 @@
|
|
|
12
12
|
"author": "",
|
|
13
13
|
"license": "ISC",
|
|
14
14
|
"dependencies": {
|
|
15
|
-
"statsig-node": "
|
|
15
|
+
"statsig-node-lite": "^0.4.2"
|
|
16
16
|
},
|
|
17
17
|
"peerDependencies": {
|
|
18
|
-
"@vercel/edge-config": "^0.
|
|
18
|
+
"@vercel/edge-config": "^1.0.0"
|
|
19
19
|
},
|
|
20
20
|
"devDependencies": {
|
|
21
21
|
"@babel/core": "^7.18.13",
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
"@babel/preset-typescript": "^7.18.6",
|
|
24
24
|
"@types/jest": "^28.1.8",
|
|
25
25
|
"@types/node": "^20.14.10",
|
|
26
|
-
"@vercel/edge-config": "^0.
|
|
26
|
+
"@vercel/edge-config": "^1.0.0",
|
|
27
27
|
"jest": "^29.0.0",
|
|
28
28
|
"jest-fetch-mock": "^3.0.3"
|
|
29
29
|
}
|