@growthbook/proxy-eval 1.1.0 → 1.1.2
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.
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "growthbook-edge-eval-cloudflare",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "GrowthBook edge evaluation for Cloudflare Workers",
|
|
5
|
+
"main": "src/index.ts",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"dev": "wrangler dev",
|
|
8
|
+
"deploy": "wrangler deploy",
|
|
9
|
+
"build": "wrangler build"
|
|
10
|
+
},
|
|
11
|
+
"dependencies": {
|
|
12
|
+
"@growthbook/proxy-eval": "^1.1.1"
|
|
13
|
+
},
|
|
14
|
+
"devDependencies": {
|
|
15
|
+
"@cloudflare/workers-types": "^4.20250926.0",
|
|
16
|
+
"typescript": "^5.8.2",
|
|
17
|
+
"wrangler": "^4.40.1"
|
|
18
|
+
}
|
|
19
|
+
}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { evaluateFeatures } from "@growthbook/proxy-eval";
|
|
2
|
+
import { StickyBucketService } from "@growthbook/growthbook";
|
|
3
|
+
|
|
4
|
+
interface Env {
|
|
5
|
+
ENVIRONMENT: string;
|
|
6
|
+
|
|
7
|
+
KV_GB_PAYLOAD: KVNamespace;
|
|
8
|
+
// NOTE: Be sure to connect a GrowthBook SDK Webhook to your KV store.
|
|
9
|
+
// Use the webhook type "Cloudflare KV" in the SDK webhook settings.
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
interface PostBody {
|
|
13
|
+
payload: any;
|
|
14
|
+
attributes: Record<string, any>;
|
|
15
|
+
forcedVariations?: Record<string, number>;
|
|
16
|
+
forcedFeatures?: Map<string, any>;
|
|
17
|
+
url?: string;
|
|
18
|
+
// NOTE: For advanced experimentation, you may want to connect a KV or cookie Sticky Bucket service
|
|
19
|
+
stickyBucketService?:
|
|
20
|
+
| (StickyBucketService & {
|
|
21
|
+
// For cookie-based service, should be a no-op:
|
|
22
|
+
connect: () => Promise<void>;
|
|
23
|
+
// For write-buffer flushes (ex: GB Proxy's implementation of RedisStickyBucketService):
|
|
24
|
+
onEvaluate?: () => Promise<void>;
|
|
25
|
+
})
|
|
26
|
+
| null;
|
|
27
|
+
ctx?: { verboseDebugging?: boolean };
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const KV_KEY = "gb_payload";
|
|
31
|
+
const CACHE_TTL = 60 * 1000; // 1 min
|
|
32
|
+
|
|
33
|
+
// Cache payload from KV
|
|
34
|
+
let cachedPayload: any = null;
|
|
35
|
+
let lastFetch = 0;
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
export default {
|
|
39
|
+
fetch: async function (
|
|
40
|
+
request: Request,
|
|
41
|
+
env: Env,
|
|
42
|
+
ctx: ExecutionContext,
|
|
43
|
+
): Promise<Response> {
|
|
44
|
+
// Handle CORS preflight requests
|
|
45
|
+
if (request.method === 'OPTIONS') {
|
|
46
|
+
return new Response(null, {
|
|
47
|
+
status: 204,
|
|
48
|
+
headers: getCORSHeaders(),
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Only allow POST requests
|
|
53
|
+
if (request.method !== 'POST') {
|
|
54
|
+
return new Response(null, {
|
|
55
|
+
status: 405,
|
|
56
|
+
headers: {
|
|
57
|
+
'Allow': 'POST',
|
|
58
|
+
...getCORSHeaders(),
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
try {
|
|
64
|
+
const body = await request.json<PostBody>().catch(() => null);
|
|
65
|
+
if (!body || typeof body !== "object") {
|
|
66
|
+
return handleInvalidRequest();
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if (!cachedPayload || Date.now() - lastFetch > CACHE_TTL) {
|
|
70
|
+
cachedPayload = await env.KV_GB_PAYLOAD.get(KV_KEY, 'json');
|
|
71
|
+
lastFetch = Date.now();
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const { attributes = {}, forcedVariations = {}, forcedFeatures = [], url = "" } = body;
|
|
75
|
+
const forcedFeaturesMap = new Map(forcedFeatures);
|
|
76
|
+
|
|
77
|
+
const evalResponse = await evaluateFeatures({
|
|
78
|
+
payload: cachedPayload,
|
|
79
|
+
attributes,
|
|
80
|
+
forcedVariations,
|
|
81
|
+
forcedFeatures: forcedFeaturesMap,
|
|
82
|
+
url,
|
|
83
|
+
// stickyBucketService,
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
// Return success response
|
|
87
|
+
return new Response(
|
|
88
|
+
JSON.stringify(evalResponse),
|
|
89
|
+
{
|
|
90
|
+
status: 200,
|
|
91
|
+
headers: {
|
|
92
|
+
'Content-Type': 'application/json',
|
|
93
|
+
...getCORSHeaders(),
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
);
|
|
97
|
+
|
|
98
|
+
} catch (error) {
|
|
99
|
+
console.error(error);
|
|
100
|
+
return handleInvalidRequest();
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function getCORSHeaders(): Record<string, string> {
|
|
106
|
+
return {
|
|
107
|
+
'Access-Control-Allow-Origin': '*', // Configure this appropriately for production
|
|
108
|
+
'Access-Control-Allow-Methods': 'POST, OPTIONS',
|
|
109
|
+
'Access-Control-Allow-Headers': 'Content-Type, Authorization',
|
|
110
|
+
'Access-Control-Max-Age': '86400',
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function handleInvalidRequest(): Response {
|
|
115
|
+
return new Response(null, {
|
|
116
|
+
status: 500,
|
|
117
|
+
headers: getCORSHeaders(),
|
|
118
|
+
});
|
|
119
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"lib": ["ES2022"],
|
|
5
|
+
"module": "ES2022",
|
|
6
|
+
"moduleResolution": "bundler",
|
|
7
|
+
"allowSyntheticDefaultImports": true,
|
|
8
|
+
"esModuleInterop": true,
|
|
9
|
+
"allowJs": true,
|
|
10
|
+
"strict": true,
|
|
11
|
+
"skipLibCheck": true,
|
|
12
|
+
"forceConsistentCasingInFileNames": true,
|
|
13
|
+
"resolveJsonModule": true,
|
|
14
|
+
"isolatedModules": true,
|
|
15
|
+
"noEmit": true,
|
|
16
|
+
"types": ["@cloudflare/workers-types"]
|
|
17
|
+
},
|
|
18
|
+
"include": ["src/**/*"],
|
|
19
|
+
"exclude": ["node_modules", "dist"]
|
|
20
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
name = "growthbook-edge-evaluator"
|
|
2
|
+
main = "src/index.ts"
|
|
3
|
+
compatibility_date = "2025-09-25"
|
|
4
|
+
|
|
5
|
+
# Variable bindings. These are arbitrary, plaintext strings (similar to environment variables)
|
|
6
|
+
# Note: Use secrets to store sensitive data.
|
|
7
|
+
# Docs: https://developers.cloudflare.com/workers/platform/environment-variables
|
|
8
|
+
|
|
9
|
+
kv_namespaces = [
|
|
10
|
+
{ binding = "KV_GB_PAYLOAD", id = "qwerty123" }
|
|
11
|
+
]
|
|
12
|
+
|
|
13
|
+
[vars]
|
|
14
|
+
NODE_ENV="production"
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@growthbook/proxy-eval",
|
|
3
3
|
"description": "Remote evaluation service for proxies, edge workers, or backend APIs",
|
|
4
|
-
"version": "1.1.
|
|
4
|
+
"version": "1.1.2",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"repository": {
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
"dev": "tsc --watch"
|
|
19
19
|
},
|
|
20
20
|
"dependencies": {
|
|
21
|
-
"@growthbook/growthbook": "^1.6.
|
|
21
|
+
"@growthbook/growthbook": "^1.6.4"
|
|
22
22
|
},
|
|
23
23
|
"devDependencies": {
|
|
24
24
|
"rimraf": "^6.0.1",
|