personalization-middleware
Version:
Next.js middleware for request-based personalization
168 lines (163 loc) • 5.48 kB
JavaScript
;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// src/index.ts
var index_exports = {};
__export(index_exports, {
collectRequestContext: () => collectRequestContext,
config: () => config,
createMiddleware: () => createMiddleware,
createPersonalizationMiddleware: () => createPersonalizationMiddleware,
createSegmentEvaluator: () => createSegmentEvaluator
});
module.exports = __toCommonJS(index_exports);
// src/middleware.ts
var import_server = require("next/server");
// src/contextCollector.ts
var import_ua_parser_js = require("ua-parser-js");
var SESSION_COOKIE_NAME = "personalization_session";
var USER_ID_COOKIE_NAME = "user_id";
function extractDeviceInfo(userAgent) {
const parser = new import_ua_parser_js.UAParser(userAgent);
const browser = parser.getBrowser();
const os = parser.getOS();
const device = parser.getDevice();
return {
deviceType: device.type || void 0,
browser: browser.name || void 0,
os: os.name || void 0
};
}
function extractUtmParams(request) {
const searchParams = request.nextUrl.searchParams;
const utm = {};
for (const [key, value] of searchParams.entries()) {
if (key.startsWith("utm_")) {
utm[key] = value;
}
}
return utm;
}
function extractGeolocation(request) {
const geoHeader = request.headers.get("x-geo-location");
if (!geoHeader) return void 0;
try {
return JSON.parse(geoHeader);
} catch {
return void 0;
}
}
function collectRequestContext(request) {
const userAgent = request.headers.get("user-agent") || "";
return {
utm: extractUtmParams(request),
device: extractDeviceInfo(userAgent),
geolocation: extractGeolocation(request),
userId: request.cookies.get(USER_ID_COOKIE_NAME)?.value,
sessionId: request.cookies.get(SESSION_COOKIE_NAME)?.value,
referrer: request.headers.get("referer") || void 0,
// Add any custom attributes here if needed
customAttributes: {}
};
}
// src/segmentEvaluator.ts
var SegmentEvaluator = class {
constructor(config2) {
this.config = {
timeout: 5e3,
maxRetries: 2,
...config2
};
}
async evaluateSegments(context) {
try {
console.log(
`[SegmentEvaluator] Fetching segments from ${this.config.apiEndpoint}`
);
const response = await fetch(this.config.apiEndpoint, {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${this.config.apiKey}`
},
body: JSON.stringify({ context })
});
if (!response.ok) {
console.error(
`[SegmentEvaluator] API request failed with status: ${response.status}`
);
const errorBody = await response.text();
console.error(`[SegmentEvaluator] Error body: ${errorBody}`);
return { segments: [], context, timestamp: Date.now(), requestId: "" };
}
const result = await response.json();
console.log(`[SegmentEvaluator] Successfully received segments.`);
return result;
} catch (error) {
console.error(
"[SegmentEvaluator] Critical error during segment evaluation:",
error
);
return { segments: [], context, timestamp: Date.now(), requestId: "" };
}
}
};
function createSegmentEvaluator(config2) {
return new SegmentEvaluator(config2);
}
// src/middleware.ts
var DEFAULT_CONFIG = {
headerName: "x-user-segments"
};
function createMiddleware(config2) {
const finalConfig = { ...DEFAULT_CONFIG, ...config2 };
const evaluator = createSegmentEvaluator({
apiEndpoint: config2.apiEndpoint,
apiKey: config2.apiKey
});
return async function middleware(request) {
console.log("\u{1F50D} Middleware triggered for:", request.url);
try {
const context = collectRequestContext(request);
const result = await evaluator.evaluateSegments(context);
console.log("\u{1F50D} Result:", result);
const response = import_server.NextResponse.next();
const segments = result.segments?.join(",") || "";
response.headers.set(finalConfig.headerName, segments);
return response;
} catch (error) {
console.error("Personalization middleware error:", error);
const response = import_server.NextResponse.next();
response.headers.set(finalConfig.headerName, "");
return response;
}
};
}
var createPersonalizationMiddleware = createMiddleware;
var config = {
matcher: ["/((?!_next/static|_next/image|favicon.ico|public/).*)"]
};
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
collectRequestContext,
config,
createMiddleware,
createPersonalizationMiddleware,
createSegmentEvaluator
});
//# sourceMappingURL=index.js.map