Skip to content

Commit 08a3309

Browse files
Configure repo as public package (growthbook#6)
1 parent 6b2e654 commit 08a3309

12 files changed

+62
-1409
lines changed

.babelrc.js

-30
This file was deleted.

package.json

+4-19
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
11
{
2-
"name": "growthbook-proxy",
2+
"name": "@growthbook/proxy",
33
"engines": {
44
"node": ">=16"
55
},
66
"description": "GrowthBook proxy server for caching, realtime updates, telemetry, etc",
7-
"private": true,
8-
"version": "1.0.2",
9-
"main": "dist/index.js",
7+
"version": "1.0.3",
8+
"main": "dist/app.js",
109
"license": "MIT",
1110
"repository": {
1211
"type": "git",
@@ -16,13 +15,10 @@
1615
"scripts": {
1716
"lint": "eslint './**/*.{ts,js}' --fix --max-warnings 0 --cache --cache-strategy content",
1817
"build:clean": "rimraf -rf dist",
19-
"build:esm": "cross-env BABEL_ENV=esmUnbundled babel src --extensions '.ts' --out-dir 'dist/esm' --source-maps",
20-
"build:cjs": "cross-env BABEL_ENV=cjs babel src --extensions '.ts' --out-dir 'dist/cjs' --source-maps",
21-
"build:bundles": "rollup -c",
2218
"build": "tsc",
2319
"type-check": "tsc --pretty --noEmit",
2420
"start": "node dist/src/index.js",
25-
"dev": "concurrently 'tsc --watch' 'nodemon -q dist/src/index.js | yarn pino-pretty'"
21+
"dev": "concurrently 'tsc --watch' 'nodemon -q dist/index.js | yarn pino-pretty'"
2622
},
2723
"dependencies": {
2824
"cors": "^2.8.5",
@@ -37,15 +33,6 @@
3733
"sse-pubsub": "^1.4.1"
3834
},
3935
"devDependencies": {
40-
"@babel/cli": "^7.19.3",
41-
"@babel/core": "^7.19.6",
42-
"@babel/preset-env": "^7.19.4",
43-
"@babel/preset-typescript": "^7.18.6",
44-
"@rollup/plugin-babel": "^5.3.0",
45-
"@rollup/plugin-commonjs": "^20.0.0",
46-
"@rollup/plugin-json": "^6.0.0",
47-
"@rollup/plugin-node-resolve": "^13.0.4",
48-
"@rollup/plugin-replace": "^3.0.0",
4936
"@types/cors": "^2.8.13",
5037
"@types/express": "^4.17.15",
5138
"@types/node": "^18.11.18",
@@ -64,8 +51,6 @@
6451
"pino-pretty": "^9.1.1",
6552
"prettier": "^2.8.1",
6653
"rimraf": "^3.0.2",
67-
"rollup": "^2.56.3",
68-
"rollup-plugin-terser": "^7.0.2",
6954
"typescript": "4.7.4"
7055
}
7156
}

rollup.config.js

-38
This file was deleted.

src/app.ts

+7-2
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ import fs from "fs";
22
import path from "path";
33
import { Express } from "express";
44
import cors from "cors";
5-
import { version } from "../package.json";
65
import { adminRouter } from "./controllers/adminController";
76
import { eventStreamRouter } from "./controllers/eventStreamController";
87
import { featuresRouter } from "./controllers/featuresController";
@@ -13,6 +12,8 @@ import { eventStreamManager } from "./services/eventStreamManager";
1312
import { Context, GrowthBookProxy } from "./types";
1413
import logger, { initializeLogger } from "./services/logger";
1514

15+
export { Context, GrowthBookProxy, CacheEngine } from "./types";
16+
1617
let build: { sha: string; date: string };
1718
function getBuild() {
1819
if (!build) {
@@ -58,6 +59,9 @@ export const growthBookProxy = async (
5859
app: Express,
5960
context?: Partial<Context>
6061
): Promise<GrowthBookProxy> => {
62+
const packageJson = require("../package.json");
63+
const version = (packageJson.version ?? "unknown") + "";
64+
6165
const ctx: Context = { ...defaultContext, ...context };
6266
app.locals.ctx = ctx;
6367

@@ -94,5 +98,6 @@ export const growthBookProxy = async (
9498
eventStreamManager,
9599
logger,
96100
},
101+
version,
97102
};
98-
};
103+
};

src/index.ts

+10-1
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,17 @@ import { growthBookProxy } from "./app";
33

44
// Sample implementation for the GrowthBookProxy
55
(async () => {
6-
const { app, context } = await init();
6+
const { app, server, context } = await init();
77

88
// creating and starting the proxy is a one-liner
99
await growthBookProxy(app, context);
10+
11+
process.on("SIGTERM", () => {
12+
console.info("SIGTERM signal received: closing HTTP server");
13+
// Todo: perform any cleanup
14+
server.close(() => {
15+
console.info("HTTP server closed");
16+
process.exit(0);
17+
});
18+
});
1019
})();

src/init.ts

+7-6
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ import express from "express";
22
import * as spdy from "spdy";
33
import dotenv from "dotenv";
44
import { CacheEngine, Context } from "./types";
5-
import logger from "./services/logger";
65
dotenv.config({ path: "./.env.local" });
76

87
export default async () => {
@@ -40,24 +39,26 @@ export default async () => {
4039

4140
// Start Express
4241
const app = express();
42+
/* eslint-disable @typescript-eslint/no-explicit-any */
43+
let server: any = null;
4344

4445
// Start app
4546
if (USE_HTTP2) {
46-
const server = spdy.createServer(
47+
server = spdy.createServer(
4748
{
4849
key: HTTPS_KEY,
4950
cert: HTTPS_CERT,
5051
},
5152
app
5253
);
5354
server.listen(PROXY_PORT, () => {
54-
logger.info(`GrowthBook proxy running over HTTP2, port ${PROXY_PORT}`);
55+
console.info(`GrowthBook proxy running over HTTP2, port ${PROXY_PORT}`);
5556
});
5657
} else {
57-
app.listen(PROXY_PORT, () => {
58-
logger.info(`GrowthBook proxy running over HTTP1.1, port ${PROXY_PORT}`);
58+
server = app.listen(PROXY_PORT, () => {
59+
console.info(`GrowthBook proxy running over HTTP1.1, port ${PROXY_PORT}`);
5960
});
6061
}
6162

62-
return { app, context };
63+
return { app, server, context };
6364
};

src/middleware/cache/readThroughCacheMiddleware.ts

+9-3
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
1+
import { IncomingMessage, ServerResponse } from "http";
2+
import { Response } from "express";
13
import {
24
createProxyMiddleware,
35
RequestHandler,
46
responseInterceptor,
57
} from "http-proxy-middleware";
6-
import { Request, Response } from "express";
78
import { featuresCache } from "../../services/cache";
89
import logger from "../../services/logger";
910
import { eventStreamManager } from "../../services/eventStreamManager";
@@ -13,7 +14,12 @@ const errorCounts: Record<string, number> = {};
1314

1415
const interceptor = (proxyTarget: string) =>
1516
responseInterceptor(
16-
async (responseBuffer, proxyRes, req: Request, res: Response) => {
17+
async (
18+
responseBuffer,
19+
proxyRes,
20+
req: IncomingMessage,
21+
res: ServerResponse
22+
) => {
1723
// got response, reset error count
1824
errorCounts[proxyTarget] = 0;
1925

@@ -27,7 +33,7 @@ const interceptor = (proxyTarget: string) =>
2733

2834
// refresh the cache
2935
try {
30-
const apiKey = res.locals.apiKey;
36+
const apiKey = (res as Response).locals.apiKey;
3137
const responseJson = JSON.parse(response);
3238
const oldEntry = await featuresCache.get(apiKey);
3339

src/middleware/cache/refreshStaleCacheMiddleware.ts

+1-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import got from "got";
22
import { Request, Response } from "express";
3-
import { version } from "../../../package.json";
43
import { featuresCache } from "../../services/cache";
54
import logger from "../../services/logger";
65
import { eventStreamManager } from "../../services/eventStreamManager";
@@ -22,7 +21,7 @@ export default ({ proxyTarget }: { proxyTarget: string }) =>
2221
activeFetchUrls.add(url);
2322
// eslint-disable-next-line no-async-promise-executor
2423
const entry = await got
25-
.get(url, { headers: { "User-Agent": `GrowthBook Proxy ${version}` } })
24+
.get(url, { headers: { "User-Agent": `GrowthBook Proxy` } })
2625
.json()
2726
.catch((e) => logger.error(e, "Refresh stale cache error"))
2827
.finally(() => activeFetchUrls.delete(url));

src/services/registrar/index.ts

+10-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import got from "got";
22
import { Context } from "../../types";
3-
import { version } from "../../../package.json";
43
import logger from "../logger";
54
import { getApiHostFromEnv, getConnectionsFromEnv } from "./helper";
65

@@ -96,7 +95,7 @@ export class Registrar {
9695
const url = `${this.growthbookApiHost}/api/v1/sdk-connections?withProxy=1&limit=100`;
9796
const headers = {
9897
Authorization: `Bearer ${this.secretApiKey}`,
99-
"User-Agent": `GrowthBook Proxy ${version}`,
98+
"User-Agent": `GrowthBook Proxy`,
10099
};
101100
const resp = (await got
102101
.get(url, { headers })
@@ -108,6 +107,7 @@ export class Registrar {
108107
if (resp?.connections) {
109108
const oldConnections = this.getAllConnections();
110109

110+
const newKeys: Set<string> = new Set();
111111
resp.connections.forEach((doc: ConnectionDoc) => {
112112
const connection: Partial<Connection> = {
113113
apiKey: doc.key,
@@ -116,8 +116,16 @@ export class Registrar {
116116
useEncryption: doc.encryptPayload,
117117
};
118118
this.setConnection(doc.key, connection);
119+
newKeys.add(doc.key);
119120
});
120121

122+
// clean up stale connections
123+
for (const key in oldConnections) {
124+
if (!newKeys.has(key)) {
125+
this.deleteConnection(key);
126+
}
127+
}
128+
121129
const newConnections = this.getAllConnections();
122130
const hasChanges =
123131
JSON.stringify(newConnections) !== JSON.stringify(oldConnections);

src/types.ts

+1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ export interface GrowthBookProxy {
1313
eventStreamManager: EventStreamManager;
1414
logger: HttpLogger["logger"];
1515
};
16+
version: string;
1617
}
1718

1819
export interface Context {

tsconfig.json

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,20 @@
11
{
22
"compilerOptions": {
33
"module": "commonjs",
4+
"strict": true,
45
"esModuleInterop": true,
56
"allowSyntheticDefaultImports": true,
67
"target": "es6",
78
"noImplicitAny": true,
89
"moduleResolution": "node",
910
"sourceMap": true,
1011
"outDir": "dist",
12+
"declaration": true,
1113
"typeRoots": ["node_modules/@types", "./typings"],
1214
"strictNullChecks": true,
1315
"strictBindCallApply": true,
1416
"strictPropertyInitialization": true,
15-
"incremental": true,
16-
"resolveJsonModule": true
17+
"incremental": true
1718
},
1819
"include": ["src/**/*"]
1920
}

0 commit comments

Comments
 (0)