@se-studio/ab-testing 0.0.0-next16-20260822103555
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/CHANGELOG.md +1125 -0
- package/README.md +412 -0
- package/dist/codegen/cli.d.ts +3 -0
- package/dist/codegen/cli.d.ts.map +1 -0
- package/dist/codegen/cli.js +121 -0
- package/dist/codegen/cli.js.map +1 -0
- package/dist/codegen/generator.d.ts +42 -0
- package/dist/codegen/generator.d.ts.map +1 -0
- package/dist/codegen/generator.js +56 -0
- package/dist/codegen/generator.js.map +1 -0
- package/dist/codegen/index.d.ts +9 -0
- package/dist/codegen/index.d.ts.map +1 -0
- package/dist/codegen/index.js +8 -0
- package/dist/codegen/index.js.map +1 -0
- package/dist/components/AbTestReporter.d.ts +2 -0
- package/dist/components/AbTestReporter.d.ts.map +1 -0
- package/dist/components/AbTestReporter.js +19 -0
- package/dist/components/AbTestReporter.js.map +1 -0
- package/dist/components/AbTestUtmScript.d.ts +17 -0
- package/dist/components/AbTestUtmScript.d.ts.map +1 -0
- package/dist/components/AbTestUtmScript.js +43 -0
- package/dist/components/AbTestUtmScript.js.map +1 -0
- package/dist/components/index.d.ts +3 -0
- package/dist/components/index.d.ts.map +1 -0
- package/dist/components/index.js +3 -0
- package/dist/components/index.js.map +1 -0
- package/dist/hooks/index.d.ts +8 -0
- package/dist/hooks/index.d.ts.map +1 -0
- package/dist/hooks/index.js +7 -0
- package/dist/hooks/index.js.map +1 -0
- package/dist/hooks/useAbTestAssignments.d.ts +56 -0
- package/dist/hooks/useAbTestAssignments.d.ts.map +1 -0
- package/dist/hooks/useAbTestAssignments.js +87 -0
- package/dist/hooks/useAbTestAssignments.js.map +1 -0
- package/dist/index.d.ts +17 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +17 -0
- package/dist/index.js.map +1 -0
- package/dist/middleware/assignment.d.ts +31 -0
- package/dist/middleware/assignment.d.ts.map +1 -0
- package/dist/middleware/assignment.js +125 -0
- package/dist/middleware/assignment.js.map +1 -0
- package/dist/middleware/cache.d.ts +30 -0
- package/dist/middleware/cache.d.ts.map +1 -0
- package/dist/middleware/cache.js +140 -0
- package/dist/middleware/cache.js.map +1 -0
- package/dist/middleware/cookies.d.ts +44 -0
- package/dist/middleware/cookies.d.ts.map +1 -0
- package/dist/middleware/cookies.js +66 -0
- package/dist/middleware/cookies.js.map +1 -0
- package/dist/middleware/handler.d.ts +53 -0
- package/dist/middleware/handler.d.ts.map +1 -0
- package/dist/middleware/handler.js +201 -0
- package/dist/middleware/handler.js.map +1 -0
- package/dist/middleware/index.d.ts +15 -0
- package/dist/middleware/index.d.ts.map +1 -0
- package/dist/middleware/index.js +13 -0
- package/dist/middleware/index.js.map +1 -0
- package/dist/middleware/prune.d.ts +13 -0
- package/dist/middleware/prune.d.ts.map +1 -0
- package/dist/middleware/prune.js +29 -0
- package/dist/middleware/prune.js.map +1 -0
- package/dist/middleware/types.d.ts +103 -0
- package/dist/middleware/types.d.ts.map +1 -0
- package/dist/middleware/types.js +2 -0
- package/dist/middleware/types.js.map +1 -0
- package/dist/middleware/urlParams.d.ts +7 -0
- package/dist/middleware/urlParams.d.ts.map +1 -0
- package/dist/middleware/urlParams.js +20 -0
- package/dist/middleware/urlParams.js.map +1 -0
- package/dist/middleware/variantId.d.ts +9 -0
- package/dist/middleware/variantId.d.ts.map +1 -0
- package/dist/middleware/variantId.js +12 -0
- package/dist/middleware/variantId.js.map +1 -0
- package/dist/reporting/buildExperimentImpression.d.ts +6 -0
- package/dist/reporting/buildExperimentImpression.d.ts.map +1 -0
- package/dist/reporting/buildExperimentImpression.js +22 -0
- package/dist/reporting/buildExperimentImpression.js.map +1 -0
- package/dist/types.d.ts +144 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +7 -0
- package/dist/types.js.map +1 -0
- package/dist/utils.d.ts +12 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +20 -0
- package/dist/utils.js.map +1 -0
- package/dist/webhook/handler.d.ts +109 -0
- package/dist/webhook/handler.d.ts.map +1 -0
- package/dist/webhook/handler.js +128 -0
- package/dist/webhook/handler.js.map +1 -0
- package/dist/webhook/index.d.ts +8 -0
- package/dist/webhook/index.d.ts.map +1 -0
- package/dist/webhook/index.js +7 -0
- package/dist/webhook/index.js.map +1 -0
- package/docs/llms.md +106 -0
- package/package.json +91 -0
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
/** Global cache instance */
|
|
2
|
+
let globalTestsCache = null;
|
|
3
|
+
/**
|
|
4
|
+
* Pre-compute weights and lookups for a test.
|
|
5
|
+
*/
|
|
6
|
+
export function computeCachedTest(test) {
|
|
7
|
+
const readyWeights = {};
|
|
8
|
+
const variantLabelLookup = {};
|
|
9
|
+
const reportingEventLookup = {};
|
|
10
|
+
const urlParamsLookup = {};
|
|
11
|
+
const reportingParamsLookup = {};
|
|
12
|
+
const config = test.configuration;
|
|
13
|
+
if (config && config.length > 0) {
|
|
14
|
+
// Control bucket (index 0)
|
|
15
|
+
readyWeights['control'] = config[0]?.weight ?? 0;
|
|
16
|
+
variantLabelLookup['control'] = config[0]?.variant_label;
|
|
17
|
+
reportingEventLookup['control'] = config[0]?.reporting_event;
|
|
18
|
+
urlParamsLookup['control'] = config[0]?.url_params;
|
|
19
|
+
reportingParamsLookup['control'] = config[0]?.reporting_params;
|
|
20
|
+
// Variant buckets (index 1+)
|
|
21
|
+
for (let i = 0; i < test.variants.length; i++) {
|
|
22
|
+
const variantConfig = config[i + 1];
|
|
23
|
+
const variant = test.variants[i];
|
|
24
|
+
if (variant) {
|
|
25
|
+
readyWeights[variant.id] = variantConfig?.weight ?? 0;
|
|
26
|
+
variantLabelLookup[variant.id] = variantConfig?.variant_label;
|
|
27
|
+
reportingEventLookup[variant.id] = variantConfig?.reporting_event;
|
|
28
|
+
urlParamsLookup[variant.id] = variantConfig?.url_params;
|
|
29
|
+
reportingParamsLookup[variant.id] = variantConfig?.reporting_params;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
else {
|
|
34
|
+
// Fallback: equal weights if no configuration
|
|
35
|
+
const totalOptions = test.variants.length + 1;
|
|
36
|
+
const equalWeight = 1 / totalOptions;
|
|
37
|
+
readyWeights['control'] = equalWeight;
|
|
38
|
+
for (const variant of test.variants) {
|
|
39
|
+
readyWeights[variant.id] = equalWeight;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
// Pre-compute variant lookup (ID -> Slug)
|
|
43
|
+
const variantLookup = { control: 'control' };
|
|
44
|
+
for (const variant of test.variants) {
|
|
45
|
+
variantLookup[variant.id] = variant.slug;
|
|
46
|
+
}
|
|
47
|
+
return {
|
|
48
|
+
...test,
|
|
49
|
+
readyWeights,
|
|
50
|
+
variantLookup,
|
|
51
|
+
variantLabelLookup,
|
|
52
|
+
reportingEventLookup,
|
|
53
|
+
urlParamsLookup,
|
|
54
|
+
reportingParamsLookup,
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Normalize a URL path for consistent matching.
|
|
59
|
+
* - Removes trailing slashes (except for root)
|
|
60
|
+
* - Ensures leading slash
|
|
61
|
+
*/
|
|
62
|
+
export function normalizePath(path) {
|
|
63
|
+
// Handle "index" as root
|
|
64
|
+
if (path === 'index') {
|
|
65
|
+
return '/';
|
|
66
|
+
}
|
|
67
|
+
// Ensure leading slash
|
|
68
|
+
let normalized = path.startsWith('/') ? path : `/${path}`;
|
|
69
|
+
// Remove trailing slash (except for root)
|
|
70
|
+
if (normalized.endsWith('/') && normalized.length > 1) {
|
|
71
|
+
normalized = normalized.slice(0, -1);
|
|
72
|
+
}
|
|
73
|
+
return normalized;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Build cached tests map from raw tests.
|
|
77
|
+
*/
|
|
78
|
+
function buildTestsByPath(tests) {
|
|
79
|
+
const testsByPath = new Map();
|
|
80
|
+
for (const test of tests) {
|
|
81
|
+
if (!test.enabled)
|
|
82
|
+
continue;
|
|
83
|
+
const normalizedControlPath = normalizePath(test.controlSlug);
|
|
84
|
+
const cachedTest = computeCachedTest(test);
|
|
85
|
+
const existing = testsByPath.get(normalizedControlPath) || [];
|
|
86
|
+
existing.push(cachedTest);
|
|
87
|
+
testsByPath.set(normalizedControlPath, existing);
|
|
88
|
+
}
|
|
89
|
+
return testsByPath;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Get cached tests, refreshing from store if needed.
|
|
93
|
+
*
|
|
94
|
+
* @param store - The blob store to fetch tests from
|
|
95
|
+
* @param cacheTtlMs - Cache time-to-live in milliseconds
|
|
96
|
+
* @param devTestData - Optional test data for development mode
|
|
97
|
+
* @returns Map of tests indexed by normalized control path
|
|
98
|
+
*/
|
|
99
|
+
export async function getCachedTests(store, cacheTtlMs, devTestData) {
|
|
100
|
+
const now = Date.now();
|
|
101
|
+
// Return cached data if still valid
|
|
102
|
+
if (globalTestsCache && now - globalTestsCache.timestamp < cacheTtlMs) {
|
|
103
|
+
return globalTestsCache.testsByPath;
|
|
104
|
+
}
|
|
105
|
+
try {
|
|
106
|
+
let tests;
|
|
107
|
+
// Use dev test data if provided
|
|
108
|
+
if (devTestData && process.env.NODE_ENV === 'development') {
|
|
109
|
+
tests = devTestData;
|
|
110
|
+
}
|
|
111
|
+
else {
|
|
112
|
+
tests = await store.values();
|
|
113
|
+
}
|
|
114
|
+
const testsByPath = buildTestsByPath(tests);
|
|
115
|
+
globalTestsCache = {
|
|
116
|
+
timestamp: now,
|
|
117
|
+
testsByPath,
|
|
118
|
+
};
|
|
119
|
+
return testsByPath;
|
|
120
|
+
}
|
|
121
|
+
catch (error) {
|
|
122
|
+
// biome-ignore lint/suspicious/noConsole: Error logging in middleware
|
|
123
|
+
console.error('Error fetching A/B tests:', error);
|
|
124
|
+
// Return stale cache if available, otherwise empty map
|
|
125
|
+
return globalTestsCache?.testsByPath || new Map();
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Clear the test cache. Useful for testing or forcing a refresh.
|
|
130
|
+
*/
|
|
131
|
+
export function clearTestCache() {
|
|
132
|
+
globalTestsCache = null;
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Get the current cache state. Useful for debugging.
|
|
136
|
+
*/
|
|
137
|
+
export function getTestCacheState() {
|
|
138
|
+
return globalTestsCache;
|
|
139
|
+
}
|
|
140
|
+
//# sourceMappingURL=cache.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cache.js","sourceRoot":"","sources":["../../src/middleware/cache.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import type { AbTestAssignment, AbTestCookie } from '../types';
|
|
2
|
+
/** Default cookie name for A/B test assignments */
|
|
3
|
+
export declare const DEFAULT_COOKIE_NAME = "ab-test-info";
|
|
4
|
+
/** Default cookie max age: 30 days in seconds */
|
|
5
|
+
export declare const DEFAULT_COOKIE_MAX_AGE: number;
|
|
6
|
+
/**
|
|
7
|
+
* Parse the A/B test cookie value into an assignment map.
|
|
8
|
+
*
|
|
9
|
+
* @param cookieValue - The raw cookie value string
|
|
10
|
+
* @returns Parsed assignment map, or empty object if invalid
|
|
11
|
+
*/
|
|
12
|
+
export declare function parseCookie(cookieValue: string | undefined): AbTestCookie;
|
|
13
|
+
/**
|
|
14
|
+
* Serialize an assignment map to a cookie value string.
|
|
15
|
+
*
|
|
16
|
+
* @param assignments - The assignment map to serialize
|
|
17
|
+
* @returns Serialized cookie value
|
|
18
|
+
*/
|
|
19
|
+
export declare function serializeCookie(assignments: AbTestCookie): string;
|
|
20
|
+
/**
|
|
21
|
+
* Get a specific test assignment from the cookie.
|
|
22
|
+
*
|
|
23
|
+
* @param cookie - Parsed cookie object
|
|
24
|
+
* @param testId - The test ID to look up
|
|
25
|
+
* @returns The assignment for this test, or undefined
|
|
26
|
+
*/
|
|
27
|
+
export declare function getAssignment(cookie: AbTestCookie, testId: string): AbTestAssignment | undefined;
|
|
28
|
+
/**
|
|
29
|
+
* Set a test assignment in the cookie object.
|
|
30
|
+
*
|
|
31
|
+
* @param cookie - Parsed cookie object (will be mutated)
|
|
32
|
+
* @param testId - The test ID
|
|
33
|
+
* @param assignment - The assignment to store
|
|
34
|
+
*/
|
|
35
|
+
export declare function setAssignment(cookie: AbTestCookie, testId: string, assignment: AbTestAssignment): void;
|
|
36
|
+
/**
|
|
37
|
+
* Read an A/B test cookie from a document.cookie string (client-side).
|
|
38
|
+
*
|
|
39
|
+
* @param documentCookie - The document.cookie string
|
|
40
|
+
* @param cookieName - The cookie name to look for
|
|
41
|
+
* @returns Parsed assignment map, or empty object if not found
|
|
42
|
+
*/
|
|
43
|
+
export declare function readCookieFromDocument(documentCookie: string, cookieName?: string): AbTestCookie;
|
|
44
|
+
//# sourceMappingURL=cookies.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cookies.d.ts","sourceRoot":"","sources":["../../src/middleware/cookies.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAE/D,mDAAmD;AACnD,eAAO,MAAM,mBAAmB,iBAAiB,CAAC;AAElD,iDAAiD;AACjD,eAAO,MAAM,sBAAsB,QAAoB,CAAC;AAExD;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,WAAW,EAAE,MAAM,GAAG,SAAS,GAAG,YAAY,CAWzE;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,WAAW,EAAE,YAAY,GAAG,MAAM,CAEjE;AAED;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,MAAM,GAAG,gBAAgB,GAAG,SAAS,CAEhG;AAED;;;;;;GAMG;AACH,wBAAgB,aAAa,CAC3B,MAAM,EAAE,YAAY,EACpB,MAAM,EAAE,MAAM,EACd,UAAU,EAAE,gBAAgB,GAC3B,IAAI,CAEN;AAED;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CACpC,cAAc,EAAE,MAAM,EACtB,UAAU,GAAE,MAA4B,GACvC,YAAY,CAMd"}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/** Default cookie name for A/B test assignments */
|
|
2
|
+
export const DEFAULT_COOKIE_NAME = 'ab-test-info';
|
|
3
|
+
/** Default cookie max age: 30 days in seconds */
|
|
4
|
+
export const DEFAULT_COOKIE_MAX_AGE = 60 * 60 * 24 * 30;
|
|
5
|
+
/**
|
|
6
|
+
* Parse the A/B test cookie value into an assignment map.
|
|
7
|
+
*
|
|
8
|
+
* @param cookieValue - The raw cookie value string
|
|
9
|
+
* @returns Parsed assignment map, or empty object if invalid
|
|
10
|
+
*/
|
|
11
|
+
export function parseCookie(cookieValue) {
|
|
12
|
+
if (!cookieValue) {
|
|
13
|
+
return {};
|
|
14
|
+
}
|
|
15
|
+
try {
|
|
16
|
+
const decoded = decodeURIComponent(cookieValue);
|
|
17
|
+
return JSON.parse(decoded);
|
|
18
|
+
}
|
|
19
|
+
catch {
|
|
20
|
+
return {};
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Serialize an assignment map to a cookie value string.
|
|
25
|
+
*
|
|
26
|
+
* @param assignments - The assignment map to serialize
|
|
27
|
+
* @returns Serialized cookie value
|
|
28
|
+
*/
|
|
29
|
+
export function serializeCookie(assignments) {
|
|
30
|
+
return JSON.stringify(assignments);
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Get a specific test assignment from the cookie.
|
|
34
|
+
*
|
|
35
|
+
* @param cookie - Parsed cookie object
|
|
36
|
+
* @param testId - The test ID to look up
|
|
37
|
+
* @returns The assignment for this test, or undefined
|
|
38
|
+
*/
|
|
39
|
+
export function getAssignment(cookie, testId) {
|
|
40
|
+
return cookie[testId];
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Set a test assignment in the cookie object.
|
|
44
|
+
*
|
|
45
|
+
* @param cookie - Parsed cookie object (will be mutated)
|
|
46
|
+
* @param testId - The test ID
|
|
47
|
+
* @param assignment - The assignment to store
|
|
48
|
+
*/
|
|
49
|
+
export function setAssignment(cookie, testId, assignment) {
|
|
50
|
+
cookie[testId] = assignment;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Read an A/B test cookie from a document.cookie string (client-side).
|
|
54
|
+
*
|
|
55
|
+
* @param documentCookie - The document.cookie string
|
|
56
|
+
* @param cookieName - The cookie name to look for
|
|
57
|
+
* @returns Parsed assignment map, or empty object if not found
|
|
58
|
+
*/
|
|
59
|
+
export function readCookieFromDocument(documentCookie, cookieName = DEFAULT_COOKIE_NAME) {
|
|
60
|
+
const match = documentCookie.match(new RegExp(`(^| )${cookieName}=([^;]+)`));
|
|
61
|
+
if (!match?.[2]) {
|
|
62
|
+
return {};
|
|
63
|
+
}
|
|
64
|
+
return parseCookie(match[2]);
|
|
65
|
+
}
|
|
66
|
+
//# sourceMappingURL=cookies.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cookies.js","sourceRoot":"","sources":["../../src/middleware/cookies.ts"],"names":[],"mappings":"AAEA,mDAAmD;AACnD,MAAM,CAAC,MAAM,mBAAmB,GAAG,cAAc,CAAC;AAElD,iDAAiD;AACjD,MAAM,CAAC,MAAM,sBAAsB,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC;AAExD;;;;;GAKG;AACH,MAAM,UAAU,WAAW,CAAC,WAA+B;IACzD,IAAI,CAAC,WAAW,EAAE,CAAC;QACjB,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,kBAAkB,CAAC,WAAW,CAAC,CAAC;QAChD,OAAO,IAAI,CAAC,KAAK,CAAC,OAAO,CAAiB,CAAC;IAC7C,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,eAAe,CAAC,WAAyB;IACvD,OAAO,IAAI,CAAC,SAAS,CAAC,WAAW,CAAC,CAAC;AACrC,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,aAAa,CAAC,MAAoB,EAAE,MAAc;IAChE,OAAO,MAAM,CAAC,MAAM,CAAC,CAAC;AACxB,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,aAAa,CAC3B,MAAoB,EACpB,MAAc,EACd,UAA4B;IAE5B,MAAM,CAAC,MAAM,CAAC,GAAG,UAAU,CAAC;AAC9B,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,sBAAsB,CACpC,cAAsB,EACtB,aAAqB,mBAAmB;IAExC,MAAM,KAAK,GAAG,cAAc,CAAC,KAAK,CAAC,IAAI,MAAM,CAAC,QAAQ,UAAU,UAAU,CAAC,CAAC,CAAC;IAC7E,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAChB,OAAO,EAAE,CAAC;IACZ,CAAC;IACD,OAAO,WAAW,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;AAC/B,CAAC"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import type { NextRequest } from 'next/server';
|
|
2
|
+
import { NextResponse } from 'next/server';
|
|
3
|
+
import type { AbTestAssignment } from '../types';
|
|
4
|
+
import type { AbTestMiddlewareConfig, AbTestResult, CachedAbTest } from './types';
|
|
5
|
+
/**
|
|
6
|
+
* Create an A/B test middleware handler.
|
|
7
|
+
*
|
|
8
|
+
* This factory function returns a middleware handler that can be called
|
|
9
|
+
* from your Next.js middleware to process A/B test assignments.
|
|
10
|
+
*
|
|
11
|
+
* @param config - Middleware configuration
|
|
12
|
+
* @returns Async function that processes requests and returns NextResponse or null
|
|
13
|
+
*/
|
|
14
|
+
export declare function createAbTestMiddleware(config: AbTestMiddlewareConfig): (request: NextRequest) => Promise<NextResponse | null>;
|
|
15
|
+
/**
|
|
16
|
+
* Low-level function to process an A/B test request.
|
|
17
|
+
* Use this if you need more control over the response handling.
|
|
18
|
+
*/
|
|
19
|
+
export declare function processAbTestRequest(pathname: string, searchParams: URLSearchParams, cookieValue: string | undefined, store: ReturnType<AbTestMiddlewareConfig['getStore']>, cacheTtlMs: number, devTestData?: AbTestMiddlewareConfig['devTestData'], options?: Pick<AbTestMiddlewareConfig, 'pruneStaleAssignments' | 'respectExistingUrlParams' | 'onValidationFailure'>): Promise<AbTestResult>;
|
|
20
|
+
/**
|
|
21
|
+
* Options for the static A/B test middleware.
|
|
22
|
+
*/
|
|
23
|
+
export interface StaticAbTestMiddlewareOptions {
|
|
24
|
+
/** Name of the cookie used to store A/B test assignments. @default "ab-test-info" */
|
|
25
|
+
cookieName?: string;
|
|
26
|
+
/** Cookie max age in seconds. @default 2592000 (30 days) */
|
|
27
|
+
cookieMaxAge?: number;
|
|
28
|
+
/**
|
|
29
|
+
* Path prefix to prepend when rewriting to a variant.
|
|
30
|
+
* e.g. "/page-test" → rewrites to "/page-test/slug" instead of "/slug".
|
|
31
|
+
* @default "" (no prefix)
|
|
32
|
+
*/
|
|
33
|
+
variantPathPrefix?: string;
|
|
34
|
+
/** Optional function to skip processing for certain pathnames. */
|
|
35
|
+
shouldProcess?: (pathname: string) => boolean;
|
|
36
|
+
/** Optional callback when a cookie assignment fails validation. */
|
|
37
|
+
onValidationFailure?: (testId: string, assignment: AbTestAssignment) => void;
|
|
38
|
+
/**
|
|
39
|
+
* Remove cookie entries for tests no longer in the active config.
|
|
40
|
+
* @default true
|
|
41
|
+
*/
|
|
42
|
+
pruneStaleAssignments?: boolean;
|
|
43
|
+
/**
|
|
44
|
+
* When injecting url_params, skip keys already present on the request URL.
|
|
45
|
+
* @default true
|
|
46
|
+
*/
|
|
47
|
+
respectExistingUrlParams?: boolean;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Create a synchronous A/B test middleware from a pre-generated static testsByPath map.
|
|
51
|
+
*/
|
|
52
|
+
export declare function createStaticAbTestMiddleware(staticTestsByPath: Record<string, CachedAbTest[]>, options?: StaticAbTestMiddlewareOptions): (request: NextRequest) => NextResponse | null;
|
|
53
|
+
//# sourceMappingURL=handler.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"handler.d.ts","sourceRoot":"","sources":["../../src/middleware/handler.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC/C,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,KAAK,EAAE,gBAAgB,EAAgB,MAAM,UAAU,CAAC;AAY/D,OAAO,KAAK,EAAE,sBAAsB,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAkFlF;;;;;;;;GAQG;AACH,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,sBAAsB,IAc5B,SAAS,WAAW,KAAG,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,CAgE3F;AAED;;;GAGG;AACH,wBAAsB,oBAAoB,CACxC,QAAQ,EAAE,MAAM,EAChB,YAAY,EAAE,eAAe,EAC7B,WAAW,EAAE,MAAM,GAAG,SAAS,EAC/B,KAAK,EAAE,UAAU,CAAC,sBAAsB,CAAC,UAAU,CAAC,CAAC,EACrD,UAAU,EAAE,MAAM,EAClB,WAAW,CAAC,EAAE,sBAAsB,CAAC,aAAa,CAAC,EACnD,OAAO,GAAE,IAAI,CACX,sBAAsB,EACtB,uBAAuB,GAAG,0BAA0B,GAAG,qBAAqB,CACxE,GACL,OAAO,CAAC,YAAY,CAAC,CA2CvB;AAED;;GAEG;AACH,MAAM,WAAW,6BAA6B;IAC5C,qFAAqF;IACrF,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,4DAA4D;IAC5D,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;;;OAIG;IACH,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,kEAAkE;IAClE,aAAa,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC;IAC9C,mEAAmE;IACnE,mBAAmB,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,gBAAgB,KAAK,IAAI,CAAC;IAC7E;;;OAGG;IACH,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAChC;;;OAGG;IACH,wBAAwB,CAAC,EAAE,OAAO,CAAC;CACpC;AAED;;GAEG;AACH,wBAAgB,4BAA4B,CAC1C,iBAAiB,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,EAAE,CAAC,EACjD,OAAO,GAAE,6BAAkC,IAeJ,SAAS,WAAW,KAAG,YAAY,GAAG,IAAI,CAiDlF"}
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
import { NextResponse } from 'next/server';
|
|
2
|
+
import { createAssignment, isValidAssignment, selectVariant } from './assignment';
|
|
3
|
+
import { getCachedTests, normalizePath } from './cache';
|
|
4
|
+
import { DEFAULT_COOKIE_MAX_AGE, DEFAULT_COOKIE_NAME, getAssignment, parseCookie, serializeCookie, setAssignment, } from './cookies';
|
|
5
|
+
import { getActiveTestIds, pruneStaleAssignments } from './prune';
|
|
6
|
+
import { applyUrlParams } from './urlParams';
|
|
7
|
+
function searchParamsMatch(test, searchParams) {
|
|
8
|
+
if (!test.searchParameters) {
|
|
9
|
+
return true;
|
|
10
|
+
}
|
|
11
|
+
const testSearchParams = new URLSearchParams(test.searchParameters);
|
|
12
|
+
for (const [key, value] of testSearchParams.entries()) {
|
|
13
|
+
if (searchParams.get(key) !== value) {
|
|
14
|
+
return false;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
return true;
|
|
18
|
+
}
|
|
19
|
+
function resolveAssignment(test, cookie, normalizedPathname, onValidationFailure) {
|
|
20
|
+
let assignmentData = getAssignment(cookie, test.id);
|
|
21
|
+
if (assignmentData && !isValidAssignment(test, assignmentData)) {
|
|
22
|
+
onValidationFailure?.(test.id, assignmentData);
|
|
23
|
+
assignmentData = undefined;
|
|
24
|
+
}
|
|
25
|
+
if (!assignmentData) {
|
|
26
|
+
const { selectedKey, variantSlug } = selectVariant(test);
|
|
27
|
+
assignmentData = createAssignment(test, selectedKey, variantSlug, normalizedPathname);
|
|
28
|
+
setAssignment(cookie, test.id, assignmentData);
|
|
29
|
+
}
|
|
30
|
+
return assignmentData;
|
|
31
|
+
}
|
|
32
|
+
function buildRewriteUrl(assignment, variantPathPrefix) {
|
|
33
|
+
const variantSlug = assignment.variant_slug;
|
|
34
|
+
if (!variantSlug || variantSlug === 'control') {
|
|
35
|
+
return undefined;
|
|
36
|
+
}
|
|
37
|
+
const cleanSlug = variantSlug.startsWith('/') ? variantSlug.slice(1) : variantSlug;
|
|
38
|
+
return `${variantPathPrefix}/${cleanSlug}`;
|
|
39
|
+
}
|
|
40
|
+
function applyTestUrlParams(url, test, assignment, respectExistingUrlParams) {
|
|
41
|
+
if (!test.injectUrlParams) {
|
|
42
|
+
return false;
|
|
43
|
+
}
|
|
44
|
+
return applyUrlParams(url, assignment.url_params, respectExistingUrlParams);
|
|
45
|
+
}
|
|
46
|
+
function setCookieHeader(response, cookie, cookieName, cookieMaxAge) {
|
|
47
|
+
const cookieValue = serializeCookie(cookie);
|
|
48
|
+
if (cookieValue) {
|
|
49
|
+
response.cookies.set(cookieName, cookieValue, {
|
|
50
|
+
path: '/',
|
|
51
|
+
maxAge: cookieMaxAge,
|
|
52
|
+
sameSite: 'lax',
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Create an A/B test middleware handler.
|
|
58
|
+
*
|
|
59
|
+
* This factory function returns a middleware handler that can be called
|
|
60
|
+
* from your Next.js middleware to process A/B test assignments.
|
|
61
|
+
*
|
|
62
|
+
* @param config - Middleware configuration
|
|
63
|
+
* @returns Async function that processes requests and returns NextResponse or null
|
|
64
|
+
*/
|
|
65
|
+
export function createAbTestMiddleware(config) {
|
|
66
|
+
const { getStore, cacheTtlMs = 60000, cookieName = DEFAULT_COOKIE_NAME, cookieMaxAge = DEFAULT_COOKIE_MAX_AGE, shouldProcess, devTestData, onValidationFailure, variantPathPrefix = '', pruneStaleAssignments: pruneStale = true, respectExistingUrlParams = true, } = config;
|
|
67
|
+
return async function abTestMiddleware(request) {
|
|
68
|
+
const pathname = request.nextUrl.pathname;
|
|
69
|
+
if (shouldProcess && !shouldProcess(pathname)) {
|
|
70
|
+
return null;
|
|
71
|
+
}
|
|
72
|
+
try {
|
|
73
|
+
const normalizedPathname = normalizePath(pathname);
|
|
74
|
+
const store = await Promise.resolve(getStore());
|
|
75
|
+
const testsByPath = await getCachedTests(store, cacheTtlMs, devTestData);
|
|
76
|
+
const activeTestIds = pruneStale ? getActiveTestIds(testsByPath) : null;
|
|
77
|
+
const cookieValue = request.cookies.get(cookieName)?.value;
|
|
78
|
+
const cookie = parseCookie(cookieValue);
|
|
79
|
+
const prunedStale = activeTestIds ? pruneStaleAssignments(cookie, activeTestIds) : false;
|
|
80
|
+
const tests = testsByPath.get(normalizedPathname);
|
|
81
|
+
if (tests && tests.length > 0) {
|
|
82
|
+
for (const test of tests) {
|
|
83
|
+
if (!searchParamsMatch(test, request.nextUrl.searchParams)) {
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
86
|
+
const assignment = resolveAssignment(test, cookie, normalizedPathname, onValidationFailure);
|
|
87
|
+
const rewriteUrl = buildRewriteUrl(assignment, variantPathPrefix);
|
|
88
|
+
const url = request.nextUrl.clone();
|
|
89
|
+
if (rewriteUrl) {
|
|
90
|
+
url.pathname = rewriteUrl;
|
|
91
|
+
}
|
|
92
|
+
const paramsChanged = applyTestUrlParams(url, test, assignment, respectExistingUrlParams);
|
|
93
|
+
const urlChanged = url.pathname !== request.nextUrl.pathname || url.search !== request.nextUrl.search;
|
|
94
|
+
const response = rewriteUrl || paramsChanged || urlChanged
|
|
95
|
+
? NextResponse.rewrite(url)
|
|
96
|
+
: NextResponse.next();
|
|
97
|
+
setCookieHeader(response, cookie, cookieName, cookieMaxAge);
|
|
98
|
+
return response;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
if (prunedStale) {
|
|
102
|
+
const response = NextResponse.next();
|
|
103
|
+
setCookieHeader(response, cookie, cookieName, cookieMaxAge);
|
|
104
|
+
return response;
|
|
105
|
+
}
|
|
106
|
+
return null;
|
|
107
|
+
}
|
|
108
|
+
catch (error) {
|
|
109
|
+
// biome-ignore lint/suspicious/noConsole: Error logging in middleware
|
|
110
|
+
console.error('Middleware A/B test error:', error);
|
|
111
|
+
return null;
|
|
112
|
+
}
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Low-level function to process an A/B test request.
|
|
117
|
+
* Use this if you need more control over the response handling.
|
|
118
|
+
*/
|
|
119
|
+
export async function processAbTestRequest(pathname, searchParams, cookieValue, store, cacheTtlMs, devTestData, options = {}) {
|
|
120
|
+
const { pruneStaleAssignments: pruneStale = true, onValidationFailure } = options;
|
|
121
|
+
const normalizedPathname = normalizePath(pathname);
|
|
122
|
+
const resolvedStore = await Promise.resolve(store);
|
|
123
|
+
const testsByPath = await getCachedTests(resolvedStore, cacheTtlMs, devTestData);
|
|
124
|
+
const activeTestIds = pruneStale ? getActiveTestIds(testsByPath) : null;
|
|
125
|
+
const cookie = parseCookie(cookieValue);
|
|
126
|
+
const prunedStale = activeTestIds ? pruneStaleAssignments(cookie, activeTestIds) : false;
|
|
127
|
+
const tests = testsByPath.get(normalizedPathname);
|
|
128
|
+
if (!tests || tests.length === 0) {
|
|
129
|
+
return {
|
|
130
|
+
matched: false,
|
|
131
|
+
prunedStale,
|
|
132
|
+
cookieValue: prunedStale ? serializeCookie(cookie) : undefined,
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
for (const test of tests) {
|
|
136
|
+
if (!searchParamsMatch(test, searchParams)) {
|
|
137
|
+
continue;
|
|
138
|
+
}
|
|
139
|
+
const assignment = resolveAssignment(test, cookie, normalizedPathname, onValidationFailure);
|
|
140
|
+
const rewriteUrl = buildRewriteUrl(assignment, '');
|
|
141
|
+
return {
|
|
142
|
+
matched: true,
|
|
143
|
+
test,
|
|
144
|
+
assignment,
|
|
145
|
+
rewriteUrl,
|
|
146
|
+
cookieValue: serializeCookie(cookie),
|
|
147
|
+
prunedStale,
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
return {
|
|
151
|
+
matched: false,
|
|
152
|
+
prunedStale,
|
|
153
|
+
cookieValue: prunedStale ? serializeCookie(cookie) : undefined,
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Create a synchronous A/B test middleware from a pre-generated static testsByPath map.
|
|
158
|
+
*/
|
|
159
|
+
export function createStaticAbTestMiddleware(staticTestsByPath, options = {}) {
|
|
160
|
+
const { cookieName = DEFAULT_COOKIE_NAME, cookieMaxAge = DEFAULT_COOKIE_MAX_AGE, variantPathPrefix = '', shouldProcess, onValidationFailure, pruneStaleAssignments: pruneStale = true, respectExistingUrlParams = true, } = options;
|
|
161
|
+
const testsByPathMap = new Map(Object.entries(staticTestsByPath));
|
|
162
|
+
const activeTestIds = pruneStale ? getActiveTestIds(testsByPathMap) : null;
|
|
163
|
+
return function staticAbTestMiddleware(request) {
|
|
164
|
+
const pathname = request.nextUrl.pathname;
|
|
165
|
+
if (shouldProcess && !shouldProcess(pathname)) {
|
|
166
|
+
return null;
|
|
167
|
+
}
|
|
168
|
+
const normalizedPathname = normalizePath(pathname);
|
|
169
|
+
const cookieValue = request.cookies.get(cookieName)?.value;
|
|
170
|
+
const cookie = parseCookie(cookieValue);
|
|
171
|
+
const prunedStale = activeTestIds ? pruneStaleAssignments(cookie, activeTestIds) : false;
|
|
172
|
+
const tests = testsByPathMap.get(normalizedPathname);
|
|
173
|
+
if (tests && tests.length > 0) {
|
|
174
|
+
for (const test of tests) {
|
|
175
|
+
if (!searchParamsMatch(test, request.nextUrl.searchParams)) {
|
|
176
|
+
continue;
|
|
177
|
+
}
|
|
178
|
+
const assignment = resolveAssignment(test, cookie, normalizedPathname, onValidationFailure);
|
|
179
|
+
const rewriteUrl = buildRewriteUrl(assignment, variantPathPrefix);
|
|
180
|
+
const url = request.nextUrl.clone();
|
|
181
|
+
if (rewriteUrl) {
|
|
182
|
+
url.pathname = rewriteUrl;
|
|
183
|
+
}
|
|
184
|
+
const paramsChanged = applyTestUrlParams(url, test, assignment, respectExistingUrlParams);
|
|
185
|
+
const urlChanged = url.pathname !== request.nextUrl.pathname || url.search !== request.nextUrl.search;
|
|
186
|
+
const response = rewriteUrl || paramsChanged || urlChanged
|
|
187
|
+
? NextResponse.rewrite(url)
|
|
188
|
+
: NextResponse.next();
|
|
189
|
+
setCookieHeader(response, cookie, cookieName, cookieMaxAge);
|
|
190
|
+
return response;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
if (prunedStale) {
|
|
194
|
+
const response = NextResponse.next();
|
|
195
|
+
setCookieHeader(response, cookie, cookieName, cookieMaxAge);
|
|
196
|
+
return response;
|
|
197
|
+
}
|
|
198
|
+
return null;
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
//# sourceMappingURL=handler.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"handler.js","sourceRoot":"","sources":["../../src/middleware/handler.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A/B Testing Middleware
|
|
3
|
+
*
|
|
4
|
+
* Helpers for implementing server-side A/B testing in Next.js middleware.
|
|
5
|
+
*/
|
|
6
|
+
export { createAssignment, isValidAssignment, selectVariant } from './assignment';
|
|
7
|
+
export { clearTestCache, computeCachedTest, getCachedTests, getTestCacheState, normalizePath, } from './cache';
|
|
8
|
+
export { DEFAULT_COOKIE_MAX_AGE, DEFAULT_COOKIE_NAME, getAssignment, parseCookie, readCookieFromDocument, serializeCookie, setAssignment, } from './cookies';
|
|
9
|
+
export type { StaticAbTestMiddlewareOptions } from './handler';
|
|
10
|
+
export { createAbTestMiddleware, createStaticAbTestMiddleware, processAbTestRequest, } from './handler';
|
|
11
|
+
export { getActiveTestIds, pruneStaleAssignments } from './prune';
|
|
12
|
+
export type { AbTestMiddlewareConfig, AbTestResult, CachedAbTest, TestsCache, } from './types';
|
|
13
|
+
export { applyUrlParams } from './urlParams';
|
|
14
|
+
export { buildVariantId } from './variantId';
|
|
15
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/middleware/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAClF,OAAO,EACL,cAAc,EACd,iBAAiB,EACjB,cAAc,EACd,iBAAiB,EACjB,aAAa,GACd,MAAM,SAAS,CAAC;AACjB,OAAO,EACL,sBAAsB,EACtB,mBAAmB,EACnB,aAAa,EACb,WAAW,EACX,sBAAsB,EACtB,eAAe,EACf,aAAa,GACd,MAAM,WAAW,CAAC;AACnB,YAAY,EAAE,6BAA6B,EAAE,MAAM,WAAW,CAAC;AAC/D,OAAO,EACL,sBAAsB,EACtB,4BAA4B,EAC5B,oBAAoB,GACrB,MAAM,WAAW,CAAC;AACnB,OAAO,EAAE,gBAAgB,EAAE,qBAAqB,EAAE,MAAM,SAAS,CAAC;AAClE,YAAY,EACV,sBAAsB,EACtB,YAAY,EACZ,YAAY,EACZ,UAAU,GACX,MAAM,SAAS,CAAC;AACjB,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A/B Testing Middleware
|
|
3
|
+
*
|
|
4
|
+
* Helpers for implementing server-side A/B testing in Next.js middleware.
|
|
5
|
+
*/
|
|
6
|
+
export { createAssignment, isValidAssignment, selectVariant } from './assignment';
|
|
7
|
+
export { clearTestCache, computeCachedTest, getCachedTests, getTestCacheState, normalizePath, } from './cache';
|
|
8
|
+
export { DEFAULT_COOKIE_MAX_AGE, DEFAULT_COOKIE_NAME, getAssignment, parseCookie, readCookieFromDocument, serializeCookie, setAssignment, } from './cookies';
|
|
9
|
+
export { createAbTestMiddleware, createStaticAbTestMiddleware, processAbTestRequest, } from './handler';
|
|
10
|
+
export { getActiveTestIds, pruneStaleAssignments } from './prune';
|
|
11
|
+
export { applyUrlParams } from './urlParams';
|
|
12
|
+
export { buildVariantId } from './variantId';
|
|
13
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/middleware/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAClF,OAAO,EACL,cAAc,EACd,iBAAiB,EACjB,cAAc,EACd,iBAAiB,EACjB,aAAa,GACd,MAAM,SAAS,CAAC;AACjB,OAAO,EACL,sBAAsB,EACtB,mBAAmB,EACnB,aAAa,EACb,WAAW,EACX,sBAAsB,EACtB,eAAe,EACf,aAAa,GACd,MAAM,WAAW,CAAC;AAEnB,OAAO,EACL,sBAAsB,EACtB,4BAA4B,EAC5B,oBAAoB,GACrB,MAAM,WAAW,CAAC;AACnB,OAAO,EAAE,gBAAgB,EAAE,qBAAqB,EAAE,MAAM,SAAS,CAAC;AAOlE,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { AbTestCookie } from '../types';
|
|
2
|
+
import type { CachedAbTest } from './types';
|
|
3
|
+
/**
|
|
4
|
+
* Collect all active test IDs from a testsByPath map.
|
|
5
|
+
*/
|
|
6
|
+
export declare function getActiveTestIds(testsByPath: Record<string, CachedAbTest[]> | Map<string, CachedAbTest[]>): Set<string>;
|
|
7
|
+
/**
|
|
8
|
+
* Remove cookie entries for tests no longer in the active config.
|
|
9
|
+
*
|
|
10
|
+
* @returns true if any keys were removed
|
|
11
|
+
*/
|
|
12
|
+
export declare function pruneStaleAssignments(cookie: AbTestCookie, activeTestIds: Set<string>): boolean;
|
|
13
|
+
//# sourceMappingURL=prune.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prune.d.ts","sourceRoot":"","sources":["../../src/middleware/prune.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAC7C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAE5C;;GAEG;AACH,wBAAgB,gBAAgB,CAC9B,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,EAAE,CAAC,GAAG,GAAG,CAAC,MAAM,EAAE,YAAY,EAAE,CAAC,GACxE,GAAG,CAAC,MAAM,CAAC,CAWb;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,YAAY,EAAE,aAAa,EAAE,GAAG,CAAC,MAAM,CAAC,GAAG,OAAO,CAW/F"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Collect all active test IDs from a testsByPath map.
|
|
3
|
+
*/
|
|
4
|
+
export function getActiveTestIds(testsByPath) {
|
|
5
|
+
const ids = new Set();
|
|
6
|
+
const testLists = testsByPath instanceof Map ? testsByPath.values() : Object.values(testsByPath);
|
|
7
|
+
for (const tests of testLists) {
|
|
8
|
+
for (const test of tests) {
|
|
9
|
+
ids.add(test.id);
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
return ids;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Remove cookie entries for tests no longer in the active config.
|
|
16
|
+
*
|
|
17
|
+
* @returns true if any keys were removed
|
|
18
|
+
*/
|
|
19
|
+
export function pruneStaleAssignments(cookie, activeTestIds) {
|
|
20
|
+
let pruned = false;
|
|
21
|
+
for (const testId of Object.keys(cookie)) {
|
|
22
|
+
if (!activeTestIds.has(testId)) {
|
|
23
|
+
delete cookie[testId];
|
|
24
|
+
pruned = true;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
return pruned;
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=prune.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prune.js","sourceRoot":"","sources":["../../src/middleware/prune.ts"],"names":[],"mappings":"AAGA;;GAEG;AACH,MAAM,UAAU,gBAAgB,CAC9B,WAAyE;IAEzE,MAAM,GAAG,GAAG,IAAI,GAAG,EAAU,CAAC;IAC9B,MAAM,SAAS,GAAG,WAAW,YAAY,GAAG,CAAC,CAAC,CAAC,WAAW,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC;IAEjG,KAAK,MAAM,KAAK,IAAI,SAAS,EAAE,CAAC;QAC9B,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACzB,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACnB,CAAC;IACH,CAAC;IAED,OAAO,GAAG,CAAC;AACb,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,qBAAqB,CAAC,MAAoB,EAAE,aAA0B;IACpF,IAAI,MAAM,GAAG,KAAK,CAAC;IAEnB,KAAK,MAAM,MAAM,IAAI,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC;QACzC,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC;YAC/B,OAAO,MAAM,CAAC,MAAM,CAAC,CAAC;YACtB,MAAM,GAAG,IAAI,CAAC;QAChB,CAAC;IACH,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC"}
|