@growthbook/edge-utils 0.0.1

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.
Files changed (48) hide show
  1. package/LICENSE +21 -0
  2. package/dist/app.d.ts +3 -0
  3. package/dist/app.js +171 -0
  4. package/dist/app.js.map +1 -0
  5. package/dist/attributes.d.ts +5 -0
  6. package/dist/attributes.js +76 -0
  7. package/dist/attributes.js.map +1 -0
  8. package/dist/config.d.ts +33 -0
  9. package/dist/config.js +91 -0
  10. package/dist/config.js.map +1 -0
  11. package/dist/domMutations.d.ts +6 -0
  12. package/dist/domMutations.js +151 -0
  13. package/dist/domMutations.js.map +1 -0
  14. package/dist/generated/sdkWrapper.d.ts +1 -0
  15. package/dist/generated/sdkWrapper.js +5 -0
  16. package/dist/generated/sdkWrapper.js.map +1 -0
  17. package/dist/index.d.ts +5 -0
  18. package/dist/index.js +16 -0
  19. package/dist/index.js.map +1 -0
  20. package/dist/inject.d.ts +16 -0
  21. package/dist/inject.js +136 -0
  22. package/dist/inject.js.map +1 -0
  23. package/dist/redirect.d.ts +11 -0
  24. package/dist/redirect.js +43 -0
  25. package/dist/redirect.js.map +1 -0
  26. package/dist/routing.d.ts +2 -0
  27. package/dist/routing.js +47 -0
  28. package/dist/routing.js.map +1 -0
  29. package/dist/stickyBucketService.d.ts +14 -0
  30. package/dist/stickyBucketService.js +49 -0
  31. package/dist/stickyBucketService.js.map +1 -0
  32. package/dist/types.d.ts +60 -0
  33. package/dist/types.js +4 -0
  34. package/dist/types.js.map +1 -0
  35. package/package.json +31 -0
  36. package/scripts/generate-sdk-wrapper.js +22 -0
  37. package/src/app.ts +210 -0
  38. package/src/attributes.ts +97 -0
  39. package/src/config.ts +166 -0
  40. package/src/domMutations.ts +157 -0
  41. package/src/generated/sdkWrapper.ts +2 -0
  42. package/src/index.ts +13 -0
  43. package/src/inject.ts +230 -0
  44. package/src/redirect.ts +53 -0
  45. package/src/routing.ts +44 -0
  46. package/src/stickyBucketService.ts +48 -0
  47. package/src/types.ts +98 -0
  48. package/tsconfig.json +27 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2021 GrowthBook, Inc.
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/dist/app.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ import { Context } from "./types";
2
+ export declare function edgeApp<Req, Res>(context: Context<Req, Res>, req: Req, res?: Res, next?: any): Promise<unknown>;
3
+ export declare function getOriginUrl(context: Context, currentURL: string): string;
package/dist/app.js ADDED
@@ -0,0 +1,171 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ var __importDefault = (this && this.__importDefault) || function (mod) {
12
+ return (mod && mod.__esModule) ? mod : { "default": mod };
13
+ };
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.getOriginUrl = exports.edgeApp = void 0;
16
+ const growthbook_1 = require("@growthbook/growthbook");
17
+ const attributes_1 = require("./attributes");
18
+ const inject_1 = require("./inject");
19
+ const domMutations_1 = require("./domMutations");
20
+ const redirect_1 = __importDefault(require("./redirect"));
21
+ const routing_1 = require("./routing");
22
+ const stickyBucketService_1 = require("./stickyBucketService");
23
+ function edgeApp(context, req, res,
24
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
25
+ next) {
26
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s;
27
+ return __awaiter(this, void 0, void 0, function* () {
28
+ // todo: import default helpers, overwrite with context helpers
29
+ let url = ((_b = (_a = context.helpers).getRequestURL) === null || _b === void 0 ? void 0 : _b.call(_a, req)) || "";
30
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
31
+ let headers = {
32
+ "Content-Type": "text/html",
33
+ };
34
+ const cookies = {};
35
+ const setCookie = (key, value) => {
36
+ cookies[key] = value;
37
+ };
38
+ const { csp, nonce } = (0, inject_1.getCspInfo)(context);
39
+ if (csp) {
40
+ headers["Content-Security-Policy"] = csp;
41
+ }
42
+ let body = "";
43
+ // Non GET requests are proxied
44
+ if (((_d = (_c = context.helpers).getRequestMethod) === null || _d === void 0 ? void 0 : _d.call(_c, req)) !== "GET") {
45
+ return (_f = (_e = context.helpers).proxyRequest) === null || _f === void 0 ? void 0 : _f.call(_e, context, req, res, next);
46
+ }
47
+ // Check the url for routing rules (default behavior is intercept)
48
+ const route = (0, routing_1.getRoute)(context, url);
49
+ if (route.behavior === "error") {
50
+ return (_h = (_g = context.helpers).sendResponse) === null || _h === void 0 ? void 0 : _h.call(_g, context, res, headers, route.body || "", cookies, route.statusCode);
51
+ }
52
+ if (route.behavior === "proxy") {
53
+ return (_k = (_j = context.helpers).proxyRequest) === null || _k === void 0 ? void 0 : _k.call(_j, context, req, res, next);
54
+ }
55
+ const attributes = (0, attributes_1.getUserAttributes)(context, req, url, setCookie);
56
+ let domChanges = [];
57
+ const resetDomChanges = () => (domChanges = []);
58
+ let preRedirectChangeIds = [];
59
+ const setPreRedirectChangeIds = (changeIds) => (preRedirectChangeIds = changeIds);
60
+ context.config.localStorage &&
61
+ (0, growthbook_1.setPolyfills)({ localStorage: context.config.localStorage });
62
+ context.config.crypto &&
63
+ (0, growthbook_1.setPolyfills)({ SubtleCrypto: context.config.crypto });
64
+ let stickyBucketService = undefined;
65
+ if (context.config.enableStickyBucketing) {
66
+ stickyBucketService =
67
+ (_l = context.config.growthbook.edgeStickyBucketService) !== null && _l !== void 0 ? _l : new stickyBucketService_1.EdgeStickyBucketService({
68
+ context,
69
+ prefix: context.config.stickyBucketPrefix,
70
+ req,
71
+ });
72
+ }
73
+ const growthbook = new growthbook_1.GrowthBook({
74
+ apiHost: context.config.growthbook.apiHost,
75
+ clientKey: context.config.growthbook.clientKey,
76
+ decryptionKey: context.config.growthbook.decryptionKey,
77
+ attributes,
78
+ applyDomChangesCallback: (changes) => {
79
+ domChanges.push(changes);
80
+ return () => { };
81
+ },
82
+ url,
83
+ disableVisualExperiments: ["skip", "browser"].includes(context.config.runVisualEditorExperiments),
84
+ disableJsInjection: context.config.disableJsInjection,
85
+ disableUrlRedirectExperiments: ["skip", "browser"].includes(context.config.runUrlRedirectExperiments),
86
+ disableCrossOriginUrlRedirectExperiments: ["skip", "browser"].includes(context.config.runCrossOriginUrlRedirectExperiments),
87
+ stickyBucketService,
88
+ trackingCallback: context.config.disableInjections
89
+ ? context.config.growthbook.edgeTrackingCallback
90
+ : undefined,
91
+ debug: true, // todo: remove
92
+ });
93
+ yield growthbook.init({
94
+ payload: context.config.growthbook.payload,
95
+ });
96
+ const oldUrl = url;
97
+ url = yield (0, redirect_1.default)({
98
+ context: context,
99
+ req,
100
+ setCookie,
101
+ growthbook,
102
+ previousUrl: url,
103
+ resetDomChanges,
104
+ setPreRedirectChangeIds: setPreRedirectChangeIds,
105
+ });
106
+ const originUrl = getOriginUrl(context, url);
107
+ /* eslint-disable @typescript-eslint/no-explicit-any */
108
+ let fetchedResponse = undefined;
109
+ try {
110
+ fetchedResponse = (yield ((_o = (_m = context.helpers).fetch) === null || _o === void 0 ? void 0 : _o.call(_m, context, originUrl)));
111
+ if (!(fetchedResponse === null || fetchedResponse === void 0 ? void 0 : fetchedResponse.ok)) {
112
+ throw new Error("Fetch: non-2xx status returned");
113
+ }
114
+ }
115
+ catch (e) {
116
+ console.error(e);
117
+ return (_q = (_p = context.helpers).sendResponse) === null || _q === void 0 ? void 0 : _q.call(_p, context, res, headers, "Error fetching page", cookies, 500);
118
+ }
119
+ if (context.config.forwardProxyHeaders && (fetchedResponse === null || fetchedResponse === void 0 ? void 0 : fetchedResponse.headers)) {
120
+ headers = Object.assign(Object.assign({}, fetchedResponse.headers), headers);
121
+ }
122
+ body = yield fetchedResponse.text();
123
+ body = yield (0, domMutations_1.applyDomMutations)({
124
+ body,
125
+ nonce,
126
+ domChanges,
127
+ });
128
+ body = (0, inject_1.injectScript)({
129
+ context: context,
130
+ body,
131
+ nonce,
132
+ growthbook,
133
+ attributes,
134
+ preRedirectChangeIds,
135
+ url,
136
+ oldUrl,
137
+ });
138
+ return (_s = (_r = context.helpers).sendResponse) === null || _s === void 0 ? void 0 : _s.call(_r, context, res, headers, body, cookies);
139
+ });
140
+ }
141
+ exports.edgeApp = edgeApp;
142
+ function getOriginUrl(context, currentURL) {
143
+ const proxyTarget = context.config.proxyTarget;
144
+ const currentParsedURL = new URL(currentURL);
145
+ const proxyParsedURL = new URL(proxyTarget);
146
+ const protocol = proxyParsedURL.protocol
147
+ ? proxyParsedURL.protocol
148
+ : currentParsedURL.protocol;
149
+ const hostname = proxyParsedURL.hostname
150
+ ? proxyParsedURL.hostname
151
+ : currentParsedURL.hostname;
152
+ const port = proxyParsedURL.port
153
+ ? proxyParsedURL.port
154
+ : protocol === "http:"
155
+ ? "80"
156
+ : "443";
157
+ let newURL = `${protocol}//${hostname}`;
158
+ if ((protocol === "http" && port !== "80") || port !== "443") {
159
+ newURL += `:${port}`;
160
+ }
161
+ newURL += `${currentParsedURL.pathname}`;
162
+ if (currentParsedURL.search) {
163
+ newURL += currentParsedURL.search;
164
+ }
165
+ if (currentParsedURL.hash) {
166
+ newURL += currentParsedURL.hash;
167
+ }
168
+ return newURL;
169
+ }
170
+ exports.getOriginUrl = getOriginUrl;
171
+ //# sourceMappingURL=app.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"app.js","sourceRoot":"","sources":["../src/app.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ import { Attributes } from "@growthbook/growthbook";
2
+ import { Context } from "./types";
3
+ export declare function getUserAttributes<Req, Res>(ctx: Context<Req, Res>, req: Req, url: string, setCookie: (key: string, value: string) => void): Attributes;
4
+ export declare function getUUID<Req, Res>(ctx: Context<Req, Res>, req: Req): any;
5
+ export declare function getAutoAttributes<Req, Res>(ctx: Context<Req, Res>, req: Req, url: string): Attributes;
@@ -0,0 +1,76 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getAutoAttributes = exports.getUUID = exports.getUserAttributes = void 0;
4
+ // Get the user's attributes by merging the UUID cookie with any auto-attributes
5
+ function getUserAttributes(ctx, req, url, setCookie) {
6
+ const { config, helpers } = ctx;
7
+ const providedAttributes = config.growthbook.attributes || {};
8
+ if (config.skipAutoAttributes) {
9
+ return providedAttributes;
10
+ }
11
+ // get any saved attributes from the cookie
12
+ const uuid = getUUID(ctx, req);
13
+ if (config.persistUuid) {
14
+ if (!(helpers === null || helpers === void 0 ? void 0 : helpers.setCookie)) {
15
+ throw new Error("Missing required dependencies");
16
+ }
17
+ setCookie(config.uuidCookieName, uuid);
18
+ }
19
+ const autoAttributes = getAutoAttributes(ctx, req, url);
20
+ return Object.assign(Object.assign({}, autoAttributes), providedAttributes);
21
+ }
22
+ exports.getUserAttributes = getUserAttributes;
23
+ // Get or create a UUID for the user:
24
+ // - Try to get the UUID from the cookie
25
+ // - Or create a new one and store in the cookie
26
+ function getUUID(ctx, req) {
27
+ const { config, helpers } = ctx;
28
+ const crypto = (config === null || config === void 0 ? void 0 : config.crypto) || (globalThis === null || globalThis === void 0 ? void 0 : globalThis.crypto);
29
+ if (!crypto || !(helpers === null || helpers === void 0 ? void 0 : helpers.getCookie)) {
30
+ throw new Error("Missing required dependencies");
31
+ }
32
+ const genUUID = () => {
33
+ if (crypto.randomUUID)
34
+ return crypto.randomUUID();
35
+ return ("" + 1e7 + -1e3 + -4e3 + -8e3 + -1e11).replace(/[018]/g, (c) => (c ^
36
+ (crypto.getRandomValues(new Uint8Array(1))[0] &
37
+ (15 >> (c / 4)))).toString(16));
38
+ };
39
+ // get the existing UUID from cookie if set, otherwise create one
40
+ return helpers.getCookie(req, config.uuidCookieName) || genUUID();
41
+ }
42
+ exports.getUUID = getUUID;
43
+ // Infer attributes from the request
44
+ // - UUID will come from the cookie or be generated
45
+ // - Other attributes come from the request headers and URL
46
+ function getAutoAttributes(ctx, req, url) {
47
+ const { config, helpers } = ctx;
48
+ const getHeader = helpers === null || helpers === void 0 ? void 0 : helpers.getRequestHeader;
49
+ const autoAttributes = {
50
+ [config.uuidKey]: getUUID(ctx, req),
51
+ };
52
+ const ua = (getHeader === null || getHeader === void 0 ? void 0 : getHeader(req, "user-agent")) || "";
53
+ autoAttributes.browser = ua.match(/Edg/)
54
+ ? "edge"
55
+ : ua.match(/Chrome/)
56
+ ? "chrome"
57
+ : ua.match(/Firefox/)
58
+ ? "firefox"
59
+ : ua.match(/Safari/)
60
+ ? "safari"
61
+ : "unknown";
62
+ autoAttributes.deviceType = ua.match(/Mobi/) ? "mobile" : "desktop";
63
+ autoAttributes.url = url;
64
+ try {
65
+ const urlObj = new URL(url);
66
+ autoAttributes.path = urlObj.pathname;
67
+ autoAttributes.host = urlObj.host;
68
+ autoAttributes.query = urlObj.search;
69
+ }
70
+ catch (e) {
71
+ // ignore
72
+ }
73
+ return autoAttributes;
74
+ }
75
+ exports.getAutoAttributes = getAutoAttributes;
76
+ //# sourceMappingURL=attributes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"attributes.js","sourceRoot":"","sources":["../src/attributes.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,33 @@
1
+ import { Config, Context, ExperimentRunEnvironment } from "./types";
2
+ export declare const defaultContext: Context;
3
+ export interface ConfigEnv {
4
+ PROXY_TARGET?: string;
5
+ FORWARD_PROXY_HEADERS?: string;
6
+ NODE_ENV?: string;
7
+ MAX_PAYLOAD_SIZE?: string;
8
+ ROUTES?: string;
9
+ RUN_VISUAL_EDITOR_EXPERIMENTS?: ExperimentRunEnvironment;
10
+ DISABLE_JS_INJECTION?: string;
11
+ RUN_URL_REDIRECT_EXPERIMENTS?: ExperimentRunEnvironment;
12
+ RUN_CROSS_ORIGIN_URL_REDIRECT_EXPERIMENTS?: ExperimentRunEnvironment;
13
+ INJECT_REDIRECT_URL_SCRIPT?: string;
14
+ MAX_REDIRECTS?: string;
15
+ SCRIPT_INJECTION_PATTERN?: string;
16
+ DISABLE_INJECTIONS?: string;
17
+ ENABLE_STREAMING?: string;
18
+ ENABLE_STICKY_BUCKETING?: string;
19
+ STICKY_BUCKET_PREFIX?: string;
20
+ CONTENT_SECURITY_POLICY?: string;
21
+ NONCE?: string;
22
+ GROWTHBOOK_API_HOST?: string;
23
+ GROWTHBOOK_CLIENT_KEY?: string;
24
+ GROWTHBOOK_DECRYPTION_KEY?: string;
25
+ GROWTHBOOK_TRACKING_CALLBACK?: string;
26
+ GROWTHBOOK_PAYLOAD?: string;
27
+ PERSIST_UUID?: string;
28
+ UUID_COOKIE_NAME?: string;
29
+ UUID_KEY?: string;
30
+ SKIP_AUTO_ATTRIBUTES?: string;
31
+ [key: string]: any;
32
+ }
33
+ export declare function getConfig(env: ConfigEnv): Config;
package/dist/config.js ADDED
@@ -0,0 +1,91 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getConfig = exports.defaultContext = void 0;
4
+ exports.defaultContext = {
5
+ config: {
6
+ proxyTarget: "/",
7
+ forwardProxyHeaders: true,
8
+ environment: "production",
9
+ maxPayloadSize: "2mb",
10
+ runVisualEditorExperiments: "everywhere",
11
+ disableJsInjection: false,
12
+ runUrlRedirectExperiments: "browser",
13
+ runCrossOriginUrlRedirectExperiments: "browser",
14
+ injectRedirectUrlScript: true,
15
+ maxRedirects: 5,
16
+ scriptInjectionPattern: "</head>",
17
+ disableInjections: false,
18
+ enableStreaming: false,
19
+ enableStickyBucketing: false,
20
+ growthbook: {
21
+ apiHost: "",
22
+ clientKey: "",
23
+ },
24
+ persistUuid: false,
25
+ uuidCookieName: "gbuuid",
26
+ uuidKey: "id",
27
+ skipAutoAttributes: false,
28
+ },
29
+ helpers: {},
30
+ };
31
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
32
+ function getConfig(env) {
33
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r;
34
+ const config = exports.defaultContext.config;
35
+ config.proxyTarget = (_a = env.PROXY_TARGET) !== null && _a !== void 0 ? _a : exports.defaultContext.config.proxyTarget;
36
+ config.forwardProxyHeaders = ["true", "1"].includes((_b = env.FORWARD_PROXY_HEADERS) !== null && _b !== void 0 ? _b : "" + exports.defaultContext.config.forwardProxyHeaders);
37
+ config.environment = (_c = env.NODE_ENV) !== null && _c !== void 0 ? _c : exports.defaultContext.config.environment;
38
+ config.maxPayloadSize =
39
+ (_d = env.MAX_PAYLOAD_SIZE) !== null && _d !== void 0 ? _d : exports.defaultContext.config.maxPayloadSize;
40
+ try {
41
+ config.routes = JSON.parse(env.ROUTES || "[]");
42
+ }
43
+ catch (e) {
44
+ console.error("Error parsing ROUTES", e);
45
+ config.routes = [];
46
+ }
47
+ config.runVisualEditorExperiments = ((_e = env.RUN_VISUAL_EDITOR_EXPERIMENTS) !== null && _e !== void 0 ? _e : exports.defaultContext.config
48
+ .runVisualEditorExperiments);
49
+ config.disableJsInjection = ["true", "1"].includes((_f = env.DISABLE_JS_INJECTION) !== null && _f !== void 0 ? _f : "" + exports.defaultContext.config.disableJsInjection);
50
+ config.runUrlRedirectExperiments = ((_g = env.RUN_URL_REDIRECT_EXPERIMENTS) !== null && _g !== void 0 ? _g : exports.defaultContext.config
51
+ .runUrlRedirectExperiments);
52
+ config.runCrossOriginUrlRedirectExperiments =
53
+ ((_h = env.RUN_CROSS_ORIGIN_URL_REDIRECT_EXPERIMENTS) !== null && _h !== void 0 ? _h : exports.defaultContext.config
54
+ .runCrossOriginUrlRedirectExperiments);
55
+ config.injectRedirectUrlScript = ["true", "1"].includes((_j = env.INJECT_REDIRECT_URL_SCRIPT) !== null && _j !== void 0 ? _j : "" + exports.defaultContext.config.injectRedirectUrlScript);
56
+ config.maxRedirects = parseInt(env.MAX_REDIRECTS || "" + exports.defaultContext.config.maxRedirects);
57
+ config.scriptInjectionPattern =
58
+ env.SCRIPT_INJECTION_PATTERN ||
59
+ exports.defaultContext.config.scriptInjectionPattern;
60
+ config.disableInjections = ["true", "1"].includes((_k = env.DISABLE_INJECTIONS) !== null && _k !== void 0 ? _k : "" + exports.defaultContext.config.disableInjections);
61
+ config.enableStreaming = ["true", "1"].includes((_l = env.ENABLE_STREAMING) !== null && _l !== void 0 ? _l : "" + exports.defaultContext.config.enableStreaming);
62
+ config.enableStickyBucketing = ["true", "1"].includes((_m = env.ENABLE_STICKY_BUCKETING) !== null && _m !== void 0 ? _m : "" + exports.defaultContext.config.enableStickyBucketing);
63
+ "STICKY_BUCKET_PREFIX" in env &&
64
+ (config.stickyBucketPrefix = env.STICKY_BUCKET_PREFIX);
65
+ config.contentSecurityPolicy = env.CONTENT_SECURITY_POLICY || "";
66
+ // warning: for testing only; nonce should be unique per request
67
+ config.nonce = env.NONCE || undefined;
68
+ config.crypto = crypto;
69
+ // config.growthbook
70
+ config.growthbook.apiHost = ((_o = env.GROWTHBOOK_API_HOST) !== null && _o !== void 0 ? _o : "").replace(/\/*$/, "");
71
+ config.growthbook.clientKey = (_p = env.GROWTHBOOK_CLIENT_KEY) !== null && _p !== void 0 ? _p : "";
72
+ "GROWTHBOOK_DECRYPTION_KEY" in env &&
73
+ (config.growthbook.decryptionKey = env.GROWTHBOOK_DECRYPTION_KEY);
74
+ "GROWTHBOOK_TRACKING_CALLBACK" in env &&
75
+ (config.growthbook.trackingCallback = env.GROWTHBOOK_TRACKING_CALLBACK);
76
+ try {
77
+ "GROWTHBOOK_PAYLOAD" in env &&
78
+ (config.growthbook.payload = JSON.parse(env.GROWTHBOOK_PAYLOAD || ""));
79
+ }
80
+ catch (e) {
81
+ console.error("Error parsing GROWTHBOOK_PAYLOAD", e);
82
+ }
83
+ config.persistUuid = ["true", "1"].includes((_q = env.PERSIST_UUID) !== null && _q !== void 0 ? _q : "" + exports.defaultContext.config.persistUuid);
84
+ config.uuidCookieName =
85
+ env.UUID_COOKIE_NAME || exports.defaultContext.config.uuidCookieName;
86
+ config.uuidKey = env.UUID_KEY || exports.defaultContext.config.uuidKey;
87
+ config.skipAutoAttributes = ["true", "1"].includes((_r = env.SKIP_AUTO_ATTRIBUTES) !== null && _r !== void 0 ? _r : "" + exports.defaultContext.config.skipAutoAttributes);
88
+ return config;
89
+ }
90
+ exports.getConfig = getConfig;
91
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ import { AutoExperimentVariation } from "@growthbook/growthbook";
2
+ export declare function applyDomMutations({ body, nonce, domChanges, }: {
3
+ body: string;
4
+ nonce?: string;
5
+ domChanges: AutoExperimentVariation[];
6
+ }): Promise<string>;
@@ -0,0 +1,151 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.applyDomMutations = void 0;
13
+ const node_html_parser_1 = require("node-html-parser");
14
+ function applyDomMutations({ body, nonce, domChanges, }) {
15
+ return __awaiter(this, void 0, void 0, function* () {
16
+ if (!domChanges.length)
17
+ return body;
18
+ const root = (0, node_html_parser_1.parse)(body);
19
+ const headEl = root.querySelector("head");
20
+ domChanges.forEach(({ domMutations, css, js }) => {
21
+ if (css) {
22
+ const parentEl = headEl || root;
23
+ const el = (0, node_html_parser_1.parse)(`<style>${css}</style>`);
24
+ parentEl.appendChild(el);
25
+ }
26
+ if (js) {
27
+ const parentEl = headEl || root;
28
+ const el = (0, node_html_parser_1.parse)(`<script>${js}</script>`);
29
+ if (nonce) {
30
+ el.nonce = nonce;
31
+ }
32
+ // @ts-ignore
33
+ parentEl.appendChild(el);
34
+ }
35
+ domMutations === null || domMutations === void 0 ? void 0 : domMutations.forEach((mutation) => {
36
+ const { attribute: attr, action, selector, value, parentSelector, insertBeforeSelector, } = mutation;
37
+ if (attr === "html") {
38
+ if (action === "append") {
39
+ return html(selector, (val) => val + (value !== null && value !== void 0 ? value : ""));
40
+ }
41
+ else if (action === "set") {
42
+ return html(selector, () => value !== null && value !== void 0 ? value : "");
43
+ }
44
+ }
45
+ else if (attr === "class") {
46
+ if (action === "append") {
47
+ return classes(selector, (val) => {
48
+ if (value)
49
+ val.add(value);
50
+ });
51
+ }
52
+ else if (action === "remove") {
53
+ return classes(selector, (val) => {
54
+ if (value)
55
+ val.delete(value);
56
+ });
57
+ }
58
+ else if (action === "set") {
59
+ return classes(selector, (val) => {
60
+ val.clear();
61
+ if (value)
62
+ val.add(value);
63
+ });
64
+ }
65
+ }
66
+ else if (attr === "position") {
67
+ if (action === "set" && parentSelector) {
68
+ return position(selector, () => ({
69
+ insertBeforeSelector,
70
+ parentSelector,
71
+ }));
72
+ }
73
+ }
74
+ else {
75
+ if (action === "append") {
76
+ return attribute(selector, attr, (val) => val !== null ? val + (value !== null && value !== void 0 ? value : "") : value !== null && value !== void 0 ? value : "");
77
+ }
78
+ else if (action === "set") {
79
+ return attribute(selector, attr, () => value !== null && value !== void 0 ? value : "");
80
+ }
81
+ else if (action === "remove") {
82
+ return attribute(selector, attr, () => null);
83
+ }
84
+ }
85
+ });
86
+ });
87
+ body = root.toString();
88
+ return body;
89
+ function html(selector, cb) {
90
+ const els = root.querySelectorAll(selector);
91
+ els.map((el) => {
92
+ el.innerHTML = cb(el.innerHTML);
93
+ });
94
+ }
95
+ function classes(selector, cb) {
96
+ const els = root.querySelectorAll(selector);
97
+ els.map((el) => {
98
+ const classList = new Set(el.classNames);
99
+ cb(classList);
100
+ el.setAttribute("class", Array.from(classList).join(" "));
101
+ });
102
+ }
103
+ function attribute(selector, attr, cb) {
104
+ const validAttributeName = /^[a-zA-Z:_][a-zA-Z0-9:_.-]*$/;
105
+ if (!validAttributeName.test(attr)) {
106
+ return;
107
+ }
108
+ if (attr === "class" || attr === "className") {
109
+ return classes(selector, (classnames) => {
110
+ const mutatedClassnames = cb(Array.from(classnames).join(" "));
111
+ classnames.clear();
112
+ if (!mutatedClassnames)
113
+ return;
114
+ mutatedClassnames
115
+ .split(/\s+/g)
116
+ .filter(Boolean)
117
+ .forEach((c) => classnames.add(c));
118
+ });
119
+ }
120
+ const els = root.querySelectorAll(selector);
121
+ els.map((el) => {
122
+ const val = cb(el.getAttribute(attr) || "");
123
+ if (val === null) {
124
+ el.removeAttribute(attr);
125
+ }
126
+ else {
127
+ el.setAttribute(attr, val);
128
+ }
129
+ });
130
+ }
131
+ function position(selector, cb) {
132
+ const els = root.querySelectorAll(selector);
133
+ els.map((el) => {
134
+ const { insertBeforeSelector, parentSelector } = cb();
135
+ const parent = root.querySelector(parentSelector);
136
+ const insertBefore = insertBeforeSelector
137
+ ? parent === null || parent === void 0 ? void 0 : parent.querySelector(insertBeforeSelector)
138
+ : null;
139
+ if (parent && insertBefore) {
140
+ insertBefore.insertAdjacentHTML("beforebegin", el.toString());
141
+ el.remove();
142
+ }
143
+ else if (parent) {
144
+ parent.appendChild(el);
145
+ }
146
+ });
147
+ }
148
+ });
149
+ }
150
+ exports.applyDomMutations = applyDomMutations;
151
+ //# sourceMappingURL=domMutations.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"domMutations.js","sourceRoot":"","sources":["../src/domMutations.ts"],"names":[],"mappings":"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"}