better-auth-feature-flags
Version:
Ship features safely with feature flags, A/B testing, and progressive rollouts - Better Auth plugin for modern release management
1,650 lines (1,645 loc) • 55.5 kB
JavaScript
import {
__name
} from "./chunk-SHUYVCID.js";
// ../../node_modules/uncrypto/dist/crypto.node.mjs
import nodeCrypto from "crypto";
var subtle = nodeCrypto.webcrypto?.subtle || {};
// ../../node_modules/better-call/dist/index.js
var __defProp = Object.defineProperty;
var __export = /* @__PURE__ */ __name((target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
}, "__export");
function isErrorStackTraceLimitWritable() {
const desc = Object.getOwnPropertyDescriptor(Error, "stackTraceLimit");
if (desc === void 0) {
return Object.isExtensible(Error);
}
return Object.prototype.hasOwnProperty.call(desc, "writable") ? desc.writable : desc.set !== void 0;
}
__name(isErrorStackTraceLimitWritable, "isErrorStackTraceLimitWritable");
var ErrorWithStack = class extends Error {
static {
__name(this, "ErrorWithStack");
}
constructor() {
super();
this.name = "ErrorWithStack";
}
};
function hideInternalStackFrames(stack) {
const lines = stack.split("\n at ");
if (lines.length <= 1) {
return stack;
}
lines.splice(1, 1);
return lines.join("\n at ");
}
__name(hideInternalStackFrames, "hideInternalStackFrames");
function makeErrorForHideStackFrame(Base, clazz) {
class HideStackFramesError extends Base {
static {
__name(this, "HideStackFramesError");
}
#errorWithStack;
constructor(...args) {
if (isErrorStackTraceLimitWritable()) {
const limit = Error.stackTraceLimit;
Error.stackTraceLimit = 0;
super(...args);
Error.stackTraceLimit = limit;
} else {
super(...args);
}
this.#errorWithStack = new ErrorWithStack();
this.#errorWithStack.stack = hideInternalStackFrames(this.#errorWithStack.stack ?? "");
}
// use `getter` here to avoid the stack trace being captured by loggers
get errorWithStack() {
return this.#errorWithStack;
}
// This is a workaround for wpt tests that expect that the error
// constructor has a `name` property of the base class.
get ["constructor"]() {
return clazz;
}
}
return HideStackFramesError;
}
__name(makeErrorForHideStackFrame, "makeErrorForHideStackFrame");
var _statusCode = {
OK: 200,
CREATED: 201,
ACCEPTED: 202,
NO_CONTENT: 204,
MULTIPLE_CHOICES: 300,
MOVED_PERMANENTLY: 301,
FOUND: 302,
SEE_OTHER: 303,
NOT_MODIFIED: 304,
TEMPORARY_REDIRECT: 307,
BAD_REQUEST: 400,
UNAUTHORIZED: 401,
PAYMENT_REQUIRED: 402,
FORBIDDEN: 403,
NOT_FOUND: 404,
METHOD_NOT_ALLOWED: 405,
NOT_ACCEPTABLE: 406,
PROXY_AUTHENTICATION_REQUIRED: 407,
REQUEST_TIMEOUT: 408,
CONFLICT: 409,
GONE: 410,
LENGTH_REQUIRED: 411,
PRECONDITION_FAILED: 412,
PAYLOAD_TOO_LARGE: 413,
URI_TOO_LONG: 414,
UNSUPPORTED_MEDIA_TYPE: 415,
RANGE_NOT_SATISFIABLE: 416,
EXPECTATION_FAILED: 417,
"I'M_A_TEAPOT": 418,
MISDIRECTED_REQUEST: 421,
UNPROCESSABLE_ENTITY: 422,
LOCKED: 423,
FAILED_DEPENDENCY: 424,
TOO_EARLY: 425,
UPGRADE_REQUIRED: 426,
PRECONDITION_REQUIRED: 428,
TOO_MANY_REQUESTS: 429,
REQUEST_HEADER_FIELDS_TOO_LARGE: 431,
UNAVAILABLE_FOR_LEGAL_REASONS: 451,
INTERNAL_SERVER_ERROR: 500,
NOT_IMPLEMENTED: 501,
BAD_GATEWAY: 502,
SERVICE_UNAVAILABLE: 503,
GATEWAY_TIMEOUT: 504,
HTTP_VERSION_NOT_SUPPORTED: 505,
VARIANT_ALSO_NEGOTIATES: 506,
INSUFFICIENT_STORAGE: 507,
LOOP_DETECTED: 508,
NOT_EXTENDED: 510,
NETWORK_AUTHENTICATION_REQUIRED: 511
};
var InternalAPIError = class extends Error {
static {
__name(this, "InternalAPIError");
}
constructor(status = "INTERNAL_SERVER_ERROR", body = void 0, headers = {}, statusCode = typeof status === "number" ? status : _statusCode[status]) {
super(
body?.message,
body?.cause ? {
cause: body.cause
} : void 0
);
this.status = status;
this.body = body;
this.headers = headers;
this.statusCode = statusCode;
this.name = "APIError";
this.status = status;
this.headers = headers;
this.statusCode = statusCode;
this.body = body ? {
code: body?.message?.toUpperCase().replace(/ /g, "_").replace(/[^A-Z0-9_]/g, ""),
...body
} : void 0;
}
};
var APIError = makeErrorForHideStackFrame(InternalAPIError, Error);
function isAPIError(error) {
return error instanceof APIError || error?.name === "APIError";
}
__name(isAPIError, "isAPIError");
function tryDecode(str) {
try {
return str.includes("%") ? decodeURIComponent(str) : str;
} catch {
return str;
}
}
__name(tryDecode, "tryDecode");
function isJSONSerializable(value) {
if (value === void 0) {
return false;
}
const t = typeof value;
if (t === "string" || t === "number" || t === "boolean" || t === null) {
return true;
}
if (t !== "object") {
return false;
}
if (Array.isArray(value)) {
return true;
}
if (value.buffer) {
return false;
}
return value.constructor && value.constructor.name === "Object" || typeof value.toJSON === "function";
}
__name(isJSONSerializable, "isJSONSerializable");
function safeStringify(obj, replacer, space) {
let id = 0;
const seen = /* @__PURE__ */ new WeakMap();
const safeReplacer = /* @__PURE__ */ __name((key, value) => {
if (typeof value === "bigint") {
return value.toString();
}
if (typeof value === "object" && value !== null) {
if (seen.has(value)) {
return `[Circular ref-${seen.get(value)}]`;
}
seen.set(value, id++);
}
if (replacer) {
return replacer(key, value);
}
return value;
}, "safeReplacer");
return JSON.stringify(obj, safeReplacer, space);
}
__name(safeStringify, "safeStringify");
function isJSONResponse(value) {
if (!value || typeof value !== "object") {
return false;
}
return "_flag" in value && value._flag === "json";
}
__name(isJSONResponse, "isJSONResponse");
function toResponse(data, init) {
if (data instanceof Response) {
if (init?.headers instanceof Headers) {
init.headers.forEach((value, key) => {
data.headers.set(key, value);
});
}
return data;
}
const isJSON = isJSONResponse(data);
if (isJSON) {
const body2 = data.body;
const routerResponse = data.routerResponse;
if (routerResponse instanceof Response) {
return routerResponse;
}
const headers2 = new Headers({
...routerResponse?.headers,
...data.headers,
...init?.headers,
"Content-Type": "application/json"
});
return new Response(JSON.stringify(body2), {
...routerResponse,
headers: headers2,
status: data.status ?? init?.status ?? routerResponse?.status,
statusText: init?.statusText ?? routerResponse?.statusText
});
}
if (isAPIError(data)) {
return toResponse(data.body, {
status: init?.status ?? data.statusCode,
statusText: data.status.toString(),
headers: init?.headers || data.headers
});
}
let body = data;
let headers = new Headers(init?.headers);
if (!data) {
if (data === null) {
body = JSON.stringify(null);
}
headers.set("content-type", "application/json");
} else if (typeof data === "string") {
body = data;
headers.set("Content-Type", "text/plain");
} else if (data instanceof ArrayBuffer || ArrayBuffer.isView(data)) {
body = data;
headers.set("Content-Type", "application/octet-stream");
} else if (data instanceof Blob) {
body = data;
headers.set("Content-Type", data.type || "application/octet-stream");
} else if (data instanceof FormData) {
body = data;
} else if (data instanceof URLSearchParams) {
body = data;
headers.set("Content-Type", "application/x-www-form-urlencoded");
} else if (data instanceof ReadableStream) {
body = data;
headers.set("Content-Type", "application/octet-stream");
} else if (isJSONSerializable(data)) {
body = safeStringify(data);
headers.set("Content-Type", "application/json");
}
return new Response(body, {
...init,
headers
});
}
__name(toResponse, "toResponse");
async function runValidation(options, context = {}) {
let request = {
body: context.body,
query: context.query
};
if (options.body) {
const result = await options.body["~standard"].validate(context.body);
if (result.issues) {
return {
data: null,
error: fromError(result.issues, "body")
};
}
request.body = result.value;
}
if (options.query) {
const result = await options.query["~standard"].validate(context.query);
if (result.issues) {
return {
data: null,
error: fromError(result.issues, "query")
};
}
request.query = result.value;
}
if (options.requireHeaders && !context.headers) {
return {
data: null,
error: { message: "Headers is required" }
};
}
if (options.requireRequest && !context.request) {
return {
data: null,
error: { message: "Request is required" }
};
}
return {
data: request,
error: null
};
}
__name(runValidation, "runValidation");
function fromError(error, validating) {
const errorMessages = [];
for (const issue2 of error) {
const message = issue2.message;
errorMessages.push(message);
}
return {
message: `Invalid ${validating} parameters`
};
}
__name(fromError, "fromError");
var algorithm = { name: "HMAC", hash: "SHA-256" };
var getCryptoKey = /* @__PURE__ */ __name(async (secret) => {
const secretBuf = typeof secret === "string" ? new TextEncoder().encode(secret) : secret;
return await subtle.importKey("raw", secretBuf, algorithm, false, ["sign", "verify"]);
}, "getCryptoKey");
var verifySignature = /* @__PURE__ */ __name(async (base64Signature, value, secret) => {
try {
const signatureBinStr = atob(base64Signature);
const signature = new Uint8Array(signatureBinStr.length);
for (let i = 0, len = signatureBinStr.length; i < len; i++) {
signature[i] = signatureBinStr.charCodeAt(i);
}
return await subtle.verify(algorithm, secret, signature, new TextEncoder().encode(value));
} catch (e) {
return false;
}
}, "verifySignature");
var makeSignature = /* @__PURE__ */ __name(async (value, secret) => {
const key = await getCryptoKey(secret);
const signature = await subtle.sign(algorithm.name, key, new TextEncoder().encode(value));
return btoa(String.fromCharCode(...new Uint8Array(signature)));
}, "makeSignature");
var signCookieValue = /* @__PURE__ */ __name(async (value, secret) => {
const signature = await makeSignature(value, secret);
value = `${value}.${signature}`;
value = encodeURIComponent(value);
return value;
}, "signCookieValue");
var getCookieKey = /* @__PURE__ */ __name((key, prefix) => {
let finalKey = key;
if (prefix) {
if (prefix === "secure") {
finalKey = "__Secure-" + key;
} else if (prefix === "host") {
finalKey = "__Host-" + key;
} else {
return void 0;
}
}
return finalKey;
}, "getCookieKey");
function parseCookies(str) {
if (typeof str !== "string") {
throw new TypeError("argument str must be a string");
}
const cookies = /* @__PURE__ */ new Map();
let index = 0;
while (index < str.length) {
const eqIdx = str.indexOf("=", index);
if (eqIdx === -1) {
break;
}
let endIdx = str.indexOf(";", index);
if (endIdx === -1) {
endIdx = str.length;
} else if (endIdx < eqIdx) {
index = str.lastIndexOf(";", eqIdx - 1) + 1;
continue;
}
const key = str.slice(index, eqIdx).trim();
if (!cookies.has(key)) {
let val = str.slice(eqIdx + 1, endIdx).trim();
if (val.codePointAt(0) === 34) {
val = val.slice(1, -1);
}
cookies.set(key, tryDecode(val));
}
index = endIdx + 1;
}
return cookies;
}
__name(parseCookies, "parseCookies");
var _serialize = /* @__PURE__ */ __name((key, value, opt = {}) => {
let cookie;
if (opt?.prefix === "secure") {
cookie = `${`__Secure-${key}`}=${value}`;
} else if (opt?.prefix === "host") {
cookie = `${`__Host-${key}`}=${value}`;
} else {
cookie = `${key}=${value}`;
}
if (key.startsWith("__Secure-") && !opt.secure) {
opt.secure = true;
}
if (key.startsWith("__Host-")) {
if (!opt.secure) {
opt.secure = true;
}
if (opt.path !== "/") {
opt.path = "/";
}
if (opt.domain) {
opt.domain = void 0;
}
}
if (opt && typeof opt.maxAge === "number" && opt.maxAge >= 0) {
if (opt.maxAge > 3456e4) {
throw new Error(
"Cookies Max-Age SHOULD NOT be greater than 400 days (34560000 seconds) in duration."
);
}
cookie += `; Max-Age=${Math.floor(opt.maxAge)}`;
}
if (opt.domain && opt.prefix !== "host") {
cookie += `; Domain=${opt.domain}`;
}
if (opt.path) {
cookie += `; Path=${opt.path}`;
}
if (opt.expires) {
if (opt.expires.getTime() - Date.now() > 3456e7) {
throw new Error(
"Cookies Expires SHOULD NOT be greater than 400 days (34560000 seconds) in the future."
);
}
cookie += `; Expires=${opt.expires.toUTCString()}`;
}
if (opt.httpOnly) {
cookie += "; HttpOnly";
}
if (opt.secure) {
cookie += "; Secure";
}
if (opt.sameSite) {
cookie += `; SameSite=${opt.sameSite.charAt(0).toUpperCase() + opt.sameSite.slice(1)}`;
}
if (opt.partitioned) {
if (!opt.secure) {
opt.secure = true;
}
cookie += "; Partitioned";
}
return cookie;
}, "_serialize");
var serializeCookie = /* @__PURE__ */ __name((key, value, opt) => {
value = encodeURIComponent(value);
return _serialize(key, value, opt);
}, "serializeCookie");
var serializeSignedCookie = /* @__PURE__ */ __name(async (key, value, secret, opt) => {
value = await signCookieValue(value, secret);
return _serialize(key, value, opt);
}, "serializeSignedCookie");
var createInternalContext = /* @__PURE__ */ __name(async (context, {
options,
path
}) => {
const headers = new Headers();
const { data, error } = await runValidation(options, context);
if (error) {
throw new APIError(400, {
message: error.message,
code: "VALIDATION_ERROR"
});
}
const requestHeaders = "headers" in context ? context.headers instanceof Headers ? context.headers : new Headers(context.headers) : "request" in context && context.request instanceof Request ? context.request.headers : null;
const requestCookies = requestHeaders?.get("cookie");
const parsedCookies = requestCookies ? parseCookies(requestCookies) : void 0;
const internalContext = {
...context,
body: data.body,
query: data.query,
path: context.path || path,
context: "context" in context && context.context ? context.context : {},
returned: void 0,
headers: context?.headers,
request: context?.request,
params: "params" in context ? context.params : void 0,
method: context.method,
setHeader: /* @__PURE__ */ __name((key, value) => {
headers.set(key, value);
}, "setHeader"),
getHeader: /* @__PURE__ */ __name((key) => {
if (!requestHeaders) return null;
return requestHeaders.get(key);
}, "getHeader"),
getCookie: /* @__PURE__ */ __name((key, prefix) => {
const finalKey = getCookieKey(key, prefix);
if (!finalKey) {
return null;
}
return parsedCookies?.get(finalKey) || null;
}, "getCookie"),
getSignedCookie: /* @__PURE__ */ __name(async (key, secret, prefix) => {
const finalKey = getCookieKey(key, prefix);
if (!finalKey) {
return null;
}
const value = parsedCookies?.get(finalKey);
if (!value) {
return null;
}
const signatureStartPos = value.lastIndexOf(".");
if (signatureStartPos < 1) {
return null;
}
const signedValue = value.substring(0, signatureStartPos);
const signature = value.substring(signatureStartPos + 1);
if (signature.length !== 44 || !signature.endsWith("=")) {
return null;
}
const secretKey = await getCryptoKey(secret);
const isVerified = await verifySignature(signature, signedValue, secretKey);
return isVerified ? signedValue : false;
}, "getSignedCookie"),
setCookie: /* @__PURE__ */ __name((key, value, options2) => {
const cookie = serializeCookie(key, value, options2);
headers.append("set-cookie", cookie);
return cookie;
}, "setCookie"),
setSignedCookie: /* @__PURE__ */ __name(async (key, value, secret, options2) => {
const cookie = await serializeSignedCookie(key, value, secret, options2);
headers.append("set-cookie", cookie);
return cookie;
}, "setSignedCookie"),
redirect: /* @__PURE__ */ __name((url) => {
headers.set("location", url);
return new APIError("FOUND", void 0, headers);
}, "redirect"),
error: /* @__PURE__ */ __name((status, body, headers2) => {
return new APIError(status, body, headers2);
}, "error"),
json: /* @__PURE__ */ __name((json, routerResponse) => {
if (!context.asResponse) {
return json;
}
return {
body: routerResponse?.body || json,
routerResponse,
_flag: "json"
};
}, "json"),
responseHeaders: headers
};
for (const middleware of options.use || []) {
const response = await middleware({
...internalContext,
returnHeaders: true,
asResponse: false
});
if (response.response) {
Object.assign(internalContext.context, response.response);
}
if (response.headers) {
response.headers.forEach((value, key) => {
internalContext.responseHeaders.set(key, value);
});
}
}
return internalContext;
}, "createInternalContext");
function createMiddleware(optionsOrHandler, handler) {
const internalHandler = /* @__PURE__ */ __name(async (inputCtx) => {
const context = inputCtx;
const _handler = typeof optionsOrHandler === "function" ? optionsOrHandler : handler;
const options = typeof optionsOrHandler === "function" ? {} : optionsOrHandler;
const internalContext = await createInternalContext(context, {
options,
path: "/"
});
if (!_handler) {
throw new Error("handler must be defined");
}
const response = await _handler(internalContext);
const headers = internalContext.responseHeaders;
return context.returnHeaders ? {
headers,
response
} : response;
}, "internalHandler");
internalHandler.options = typeof optionsOrHandler === "function" ? {} : optionsOrHandler;
return internalHandler;
}
__name(createMiddleware, "createMiddleware");
createMiddleware.create = (opts) => {
function fn(optionsOrHandler, handler) {
if (typeof optionsOrHandler === "function") {
return createMiddleware(
{
use: opts?.use
},
optionsOrHandler
);
}
if (!handler) {
throw new Error("Middleware handler is required");
}
const middleware = createMiddleware(
{
...optionsOrHandler,
method: "*",
use: [...opts?.use || [], ...optionsOrHandler.use || []]
},
handler
);
return middleware;
}
__name(fn, "fn");
return fn;
};
var createEndpoint2 = /* @__PURE__ */ __name((path, options, handler) => {
const internalHandler = /* @__PURE__ */ __name(async (...inputCtx) => {
const context = inputCtx[0] || {};
const internalContext = await createInternalContext(context, {
options,
path
});
const response = await handler(internalContext).catch(async (e) => {
if (isAPIError(e)) {
const onAPIError = options.onAPIError;
if (onAPIError) {
await onAPIError(e);
}
if (context.asResponse) {
return e;
}
}
throw e;
});
const headers = internalContext.responseHeaders;
return context.asResponse ? toResponse(response, {
headers
}) : context.returnHeaders ? {
headers,
response
} : response;
}, "internalHandler");
internalHandler.options = options;
internalHandler.path = path;
return internalHandler;
}, "createEndpoint2");
createEndpoint2.create = (opts) => {
return (path, options, handler) => {
return createEndpoint2(
path,
{
...options,
use: [...options?.use || [], ...opts?.use || []]
},
handler
);
};
};
var NEVER = Object.freeze({
status: "aborted"
});
var $brand = Symbol("zod_brand");
var util_exports = {};
__export(util_exports, {
BIGINT_FORMAT_RANGES: /* @__PURE__ */ __name(() => BIGINT_FORMAT_RANGES, "BIGINT_FORMAT_RANGES"),
Class: /* @__PURE__ */ __name(() => Class, "Class"),
NUMBER_FORMAT_RANGES: /* @__PURE__ */ __name(() => NUMBER_FORMAT_RANGES, "NUMBER_FORMAT_RANGES"),
aborted: /* @__PURE__ */ __name(() => aborted, "aborted"),
allowsEval: /* @__PURE__ */ __name(() => allowsEval, "allowsEval"),
assert: /* @__PURE__ */ __name(() => assert, "assert"),
assertEqual: /* @__PURE__ */ __name(() => assertEqual, "assertEqual"),
assertIs: /* @__PURE__ */ __name(() => assertIs, "assertIs"),
assertNever: /* @__PURE__ */ __name(() => assertNever, "assertNever"),
assertNotEqual: /* @__PURE__ */ __name(() => assertNotEqual, "assertNotEqual"),
assignProp: /* @__PURE__ */ __name(() => assignProp, "assignProp"),
cached: /* @__PURE__ */ __name(() => cached, "cached"),
captureStackTrace: /* @__PURE__ */ __name(() => captureStackTrace, "captureStackTrace"),
cleanEnum: /* @__PURE__ */ __name(() => cleanEnum, "cleanEnum"),
cleanRegex: /* @__PURE__ */ __name(() => cleanRegex, "cleanRegex"),
clone: /* @__PURE__ */ __name(() => clone, "clone"),
createTransparentProxy: /* @__PURE__ */ __name(() => createTransparentProxy, "createTransparentProxy"),
defineLazy: /* @__PURE__ */ __name(() => defineLazy, "defineLazy"),
esc: /* @__PURE__ */ __name(() => esc, "esc"),
escapeRegex: /* @__PURE__ */ __name(() => escapeRegex, "escapeRegex"),
extend: /* @__PURE__ */ __name(() => extend, "extend"),
finalizeIssue: /* @__PURE__ */ __name(() => finalizeIssue, "finalizeIssue"),
floatSafeRemainder: /* @__PURE__ */ __name(() => floatSafeRemainder, "floatSafeRemainder"),
getElementAtPath: /* @__PURE__ */ __name(() => getElementAtPath, "getElementAtPath"),
getEnumValues: /* @__PURE__ */ __name(() => getEnumValues, "getEnumValues"),
getLengthableOrigin: /* @__PURE__ */ __name(() => getLengthableOrigin, "getLengthableOrigin"),
getParsedType: /* @__PURE__ */ __name(() => getParsedType, "getParsedType"),
getSizableOrigin: /* @__PURE__ */ __name(() => getSizableOrigin, "getSizableOrigin"),
isObject: /* @__PURE__ */ __name(() => isObject, "isObject"),
isPlainObject: /* @__PURE__ */ __name(() => isPlainObject, "isPlainObject"),
issue: /* @__PURE__ */ __name(() => issue, "issue"),
joinValues: /* @__PURE__ */ __name(() => joinValues, "joinValues"),
jsonStringifyReplacer: /* @__PURE__ */ __name(() => jsonStringifyReplacer, "jsonStringifyReplacer"),
merge: /* @__PURE__ */ __name(() => merge, "merge"),
normalizeParams: /* @__PURE__ */ __name(() => normalizeParams, "normalizeParams"),
nullish: /* @__PURE__ */ __name(() => nullish, "nullish"),
numKeys: /* @__PURE__ */ __name(() => numKeys, "numKeys"),
omit: /* @__PURE__ */ __name(() => omit, "omit"),
optionalKeys: /* @__PURE__ */ __name(() => optionalKeys, "optionalKeys"),
partial: /* @__PURE__ */ __name(() => partial, "partial"),
pick: /* @__PURE__ */ __name(() => pick, "pick"),
prefixIssues: /* @__PURE__ */ __name(() => prefixIssues, "prefixIssues"),
primitiveTypes: /* @__PURE__ */ __name(() => primitiveTypes, "primitiveTypes"),
promiseAllObject: /* @__PURE__ */ __name(() => promiseAllObject, "promiseAllObject"),
propertyKeyTypes: /* @__PURE__ */ __name(() => propertyKeyTypes, "propertyKeyTypes"),
randomString: /* @__PURE__ */ __name(() => randomString, "randomString"),
required: /* @__PURE__ */ __name(() => required, "required"),
stringifyPrimitive: /* @__PURE__ */ __name(() => stringifyPrimitive, "stringifyPrimitive"),
unwrapMessage: /* @__PURE__ */ __name(() => unwrapMessage, "unwrapMessage")
});
function assertEqual(val) {
return val;
}
__name(assertEqual, "assertEqual");
function assertNotEqual(val) {
return val;
}
__name(assertNotEqual, "assertNotEqual");
function assertIs(_arg) {
}
__name(assertIs, "assertIs");
function assertNever(_x) {
throw new Error();
}
__name(assertNever, "assertNever");
function assert(_) {
}
__name(assert, "assert");
function getEnumValues(entries) {
const numericValues = Object.values(entries).filter((v) => typeof v === "number");
const values = Object.entries(entries).filter(([k, _]) => numericValues.indexOf(+k) === -1).map(([_, v]) => v);
return values;
}
__name(getEnumValues, "getEnumValues");
function joinValues(array2, separator = "|") {
return array2.map((val) => stringifyPrimitive(val)).join(separator);
}
__name(joinValues, "joinValues");
function jsonStringifyReplacer(_, value) {
if (typeof value === "bigint")
return value.toString();
return value;
}
__name(jsonStringifyReplacer, "jsonStringifyReplacer");
function cached(getter) {
const set = false;
return {
get value() {
if (!set) {
const value = getter();
Object.defineProperty(this, "value", { value });
return value;
}
throw new Error("cached value already set");
}
};
}
__name(cached, "cached");
function nullish(input) {
return input === null || input === void 0;
}
__name(nullish, "nullish");
function cleanRegex(source) {
const start = source.startsWith("^") ? 1 : 0;
const end = source.endsWith("$") ? source.length - 1 : source.length;
return source.slice(start, end);
}
__name(cleanRegex, "cleanRegex");
function floatSafeRemainder(val, step) {
const valDecCount = (val.toString().split(".")[1] || "").length;
const stepDecCount = (step.toString().split(".")[1] || "").length;
const decCount = valDecCount > stepDecCount ? valDecCount : stepDecCount;
const valInt = Number.parseInt(val.toFixed(decCount).replace(".", ""));
const stepInt = Number.parseInt(step.toFixed(decCount).replace(".", ""));
return valInt % stepInt / 10 ** decCount;
}
__name(floatSafeRemainder, "floatSafeRemainder");
function defineLazy(object, key, getter) {
const set = false;
Object.defineProperty(object, key, {
get() {
if (!set) {
const value = getter();
object[key] = value;
return value;
}
throw new Error("cached value already set");
},
set(v) {
Object.defineProperty(object, key, {
value: v
// configurable: true,
});
},
configurable: true
});
}
__name(defineLazy, "defineLazy");
function assignProp(target, prop, value) {
Object.defineProperty(target, prop, {
value,
writable: true,
enumerable: true,
configurable: true
});
}
__name(assignProp, "assignProp");
function getElementAtPath(obj, path) {
if (!path)
return obj;
return path.reduce((acc, key) => acc?.[key], obj);
}
__name(getElementAtPath, "getElementAtPath");
function promiseAllObject(promisesObj) {
const keys = Object.keys(promisesObj);
const promises = keys.map((key) => promisesObj[key]);
return Promise.all(promises).then((results) => {
const resolvedObj = {};
for (let i = 0; i < keys.length; i++) {
resolvedObj[keys[i]] = results[i];
}
return resolvedObj;
});
}
__name(promiseAllObject, "promiseAllObject");
function randomString(length = 10) {
const chars = "abcdefghijklmnopqrstuvwxyz";
let str = "";
for (let i = 0; i < length; i++) {
str += chars[Math.floor(Math.random() * chars.length)];
}
return str;
}
__name(randomString, "randomString");
function esc(str) {
return JSON.stringify(str);
}
__name(esc, "esc");
var captureStackTrace = Error.captureStackTrace ? Error.captureStackTrace : (..._args) => {
};
function isObject(data) {
return typeof data === "object" && data !== null && !Array.isArray(data);
}
__name(isObject, "isObject");
var allowsEval = cached(() => {
if (typeof navigator !== "undefined" && navigator?.userAgent?.includes("Cloudflare")) {
return false;
}
try {
const F = Function;
new F("");
return true;
} catch (_) {
return false;
}
});
function isPlainObject(o) {
if (isObject(o) === false)
return false;
const ctor = o.constructor;
if (ctor === void 0)
return true;
const prot = ctor.prototype;
if (isObject(prot) === false)
return false;
if (Object.prototype.hasOwnProperty.call(prot, "isPrototypeOf") === false) {
return false;
}
return true;
}
__name(isPlainObject, "isPlainObject");
function numKeys(data) {
let keyCount = 0;
for (const key in data) {
if (Object.prototype.hasOwnProperty.call(data, key)) {
keyCount++;
}
}
return keyCount;
}
__name(numKeys, "numKeys");
var getParsedType = /* @__PURE__ */ __name((data) => {
const t = typeof data;
switch (t) {
case "undefined":
return "undefined";
case "string":
return "string";
case "number":
return Number.isNaN(data) ? "nan" : "number";
case "boolean":
return "boolean";
case "function":
return "function";
case "bigint":
return "bigint";
case "symbol":
return "symbol";
case "object":
if (Array.isArray(data)) {
return "array";
}
if (data === null) {
return "null";
}
if (data.then && typeof data.then === "function" && data.catch && typeof data.catch === "function") {
return "promise";
}
if (typeof Map !== "undefined" && data instanceof Map) {
return "map";
}
if (typeof Set !== "undefined" && data instanceof Set) {
return "set";
}
if (typeof Date !== "undefined" && data instanceof Date) {
return "date";
}
if (typeof File !== "undefined" && data instanceof File) {
return "file";
}
return "object";
default:
throw new Error(`Unknown data type: ${t}`);
}
}, "getParsedType");
var propertyKeyTypes = /* @__PURE__ */ new Set(["string", "number", "symbol"]);
var primitiveTypes = /* @__PURE__ */ new Set(["string", "number", "bigint", "boolean", "symbol", "undefined"]);
function escapeRegex(str) {
return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
}
__name(escapeRegex, "escapeRegex");
function clone(inst, def, params) {
const cl = new inst._zod.constr(def ?? inst._zod.def);
if (!def || params?.parent)
cl._zod.parent = inst;
return cl;
}
__name(clone, "clone");
function normalizeParams(_params) {
const params = _params;
if (!params)
return {};
if (typeof params === "string")
return { error: /* @__PURE__ */ __name(() => params, "error") };
if (params?.message !== void 0) {
if (params?.error !== void 0)
throw new Error("Cannot specify both `message` and `error` params");
params.error = params.message;
}
delete params.message;
if (typeof params.error === "string")
return { ...params, error: /* @__PURE__ */ __name(() => params.error, "error") };
return params;
}
__name(normalizeParams, "normalizeParams");
function createTransparentProxy(getter) {
let target;
return new Proxy({}, {
get(_, prop, receiver) {
target ?? (target = getter());
return Reflect.get(target, prop, receiver);
},
set(_, prop, value, receiver) {
target ?? (target = getter());
return Reflect.set(target, prop, value, receiver);
},
has(_, prop) {
target ?? (target = getter());
return Reflect.has(target, prop);
},
deleteProperty(_, prop) {
target ?? (target = getter());
return Reflect.deleteProperty(target, prop);
},
ownKeys(_) {
target ?? (target = getter());
return Reflect.ownKeys(target);
},
getOwnPropertyDescriptor(_, prop) {
target ?? (target = getter());
return Reflect.getOwnPropertyDescriptor(target, prop);
},
defineProperty(_, prop, descriptor) {
target ?? (target = getter());
return Reflect.defineProperty(target, prop, descriptor);
}
});
}
__name(createTransparentProxy, "createTransparentProxy");
function stringifyPrimitive(value) {
if (typeof value === "bigint")
return value.toString() + "n";
if (typeof value === "string")
return `"${value}"`;
return `${value}`;
}
__name(stringifyPrimitive, "stringifyPrimitive");
function optionalKeys(shape) {
return Object.keys(shape).filter((k) => {
return shape[k]._zod.optin === "optional" && shape[k]._zod.optout === "optional";
});
}
__name(optionalKeys, "optionalKeys");
var NUMBER_FORMAT_RANGES = {
safeint: [Number.MIN_SAFE_INTEGER, Number.MAX_SAFE_INTEGER],
int32: [-2147483648, 2147483647],
uint32: [0, 4294967295],
float32: [-34028234663852886e22, 34028234663852886e22],
float64: [-Number.MAX_VALUE, Number.MAX_VALUE]
};
var BIGINT_FORMAT_RANGES = {
int64: [/* @__PURE__ */ BigInt("-9223372036854775808"), /* @__PURE__ */ BigInt("9223372036854775807")],
uint64: [/* @__PURE__ */ BigInt(0), /* @__PURE__ */ BigInt("18446744073709551615")]
};
function pick(schema, mask) {
const newShape = {};
const currDef = schema._zod.def;
for (const key in mask) {
if (!(key in currDef.shape)) {
throw new Error(`Unrecognized key: "${key}"`);
}
if (!mask[key])
continue;
newShape[key] = currDef.shape[key];
}
return clone(schema, {
...schema._zod.def,
shape: newShape,
checks: []
});
}
__name(pick, "pick");
function omit(schema, mask) {
const newShape = { ...schema._zod.def.shape };
const currDef = schema._zod.def;
for (const key in mask) {
if (!(key in currDef.shape)) {
throw new Error(`Unrecognized key: "${key}"`);
}
if (!mask[key])
continue;
delete newShape[key];
}
return clone(schema, {
...schema._zod.def,
shape: newShape,
checks: []
});
}
__name(omit, "omit");
function extend(schema, shape) {
if (!isPlainObject(shape)) {
throw new Error("Invalid input to extend: expected a plain object");
}
const def = {
...schema._zod.def,
get shape() {
const _shape = { ...schema._zod.def.shape, ...shape };
assignProp(this, "shape", _shape);
return _shape;
},
checks: []
// delete existing checks
};
return clone(schema, def);
}
__name(extend, "extend");
function merge(a, b) {
return clone(a, {
...a._zod.def,
get shape() {
const _shape = { ...a._zod.def.shape, ...b._zod.def.shape };
assignProp(this, "shape", _shape);
return _shape;
},
catchall: b._zod.def.catchall,
checks: []
// delete existing checks
});
}
__name(merge, "merge");
function partial(Class2, schema, mask) {
const oldShape = schema._zod.def.shape;
const shape = { ...oldShape };
if (mask) {
for (const key in mask) {
if (!(key in oldShape)) {
throw new Error(`Unrecognized key: "${key}"`);
}
if (!mask[key])
continue;
shape[key] = Class2 ? new Class2({
type: "optional",
innerType: oldShape[key]
}) : oldShape[key];
}
} else {
for (const key in oldShape) {
shape[key] = Class2 ? new Class2({
type: "optional",
innerType: oldShape[key]
}) : oldShape[key];
}
}
return clone(schema, {
...schema._zod.def,
shape,
checks: []
});
}
__name(partial, "partial");
function required(Class2, schema, mask) {
const oldShape = schema._zod.def.shape;
const shape = { ...oldShape };
if (mask) {
for (const key in mask) {
if (!(key in shape)) {
throw new Error(`Unrecognized key: "${key}"`);
}
if (!mask[key])
continue;
shape[key] = new Class2({
type: "nonoptional",
innerType: oldShape[key]
});
}
} else {
for (const key in oldShape) {
shape[key] = new Class2({
type: "nonoptional",
innerType: oldShape[key]
});
}
}
return clone(schema, {
...schema._zod.def,
shape,
// optional: [],
checks: []
});
}
__name(required, "required");
function aborted(x, startIndex = 0) {
for (let i = startIndex; i < x.issues.length; i++) {
if (x.issues[i]?.continue !== true)
return true;
}
return false;
}
__name(aborted, "aborted");
function prefixIssues(path, issues) {
return issues.map((iss) => {
var _a;
(_a = iss).path ?? (_a.path = []);
iss.path.unshift(path);
return iss;
});
}
__name(prefixIssues, "prefixIssues");
function unwrapMessage(message) {
return typeof message === "string" ? message : message?.message;
}
__name(unwrapMessage, "unwrapMessage");
function finalizeIssue(iss, ctx, config2) {
const full = { ...iss, path: iss.path ?? [] };
if (!iss.message) {
const message = unwrapMessage(iss.inst?._zod.def?.error?.(iss)) ?? unwrapMessage(ctx?.error?.(iss)) ?? unwrapMessage(config2.customError?.(iss)) ?? unwrapMessage(config2.localeError?.(iss)) ?? "Invalid input";
full.message = message;
}
delete full.inst;
delete full.continue;
if (!ctx?.reportInput) {
delete full.input;
}
return full;
}
__name(finalizeIssue, "finalizeIssue");
function getSizableOrigin(input) {
if (input instanceof Set)
return "set";
if (input instanceof Map)
return "map";
if (input instanceof File)
return "file";
return "unknown";
}
__name(getSizableOrigin, "getSizableOrigin");
function getLengthableOrigin(input) {
if (Array.isArray(input))
return "array";
if (typeof input === "string")
return "string";
return "unknown";
}
__name(getLengthableOrigin, "getLengthableOrigin");
function issue(...args) {
const [iss, input, inst] = args;
if (typeof iss === "string") {
return {
message: iss,
code: "custom",
input,
inst
};
}
return { ...iss };
}
__name(issue, "issue");
function cleanEnum(obj) {
return Object.entries(obj).filter(([k, _]) => {
return Number.isNaN(Number.parseInt(k, 10));
}).map((el) => el[1]);
}
__name(cleanEnum, "cleanEnum");
var Class = class {
static {
__name(this, "Class");
}
constructor(..._args) {
}
};
var $output = Symbol("ZodOutput");
var $input = Symbol("ZodInput");
// src/middleware/validation.ts
var DEFAULT_HEADER_CONFIG = [
{
name: "x-feature-flag-segment",
type: "string",
maxLength: 50,
pattern: /^[a-zA-Z0-9_-]+$/
},
{
name: "x-feature-flag-cohort",
type: "string",
maxLength: 50,
pattern: /^[a-zA-Z0-9_-]+$/
},
{
name: "x-ab-test-group",
type: "enum",
enumValues: ["control", "variant-a", "variant-b", "variant-c"]
},
{
name: "x-experiment-id",
type: "string",
maxLength: 100,
pattern: /^[a-zA-Z0-9_.-]+$/
},
{
name: "x-client-version",
type: "string",
maxLength: 20,
pattern: /^[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9]+)?$/
},
{
name: "x-deployment-ring",
type: "enum",
enumValues: ["canary", "preview", "production"]
},
{
name: "x-user-subscription",
type: "enum",
enumValues: ["free", "pro", "enterprise"]
},
{
name: "x-beta-features",
type: "boolean"
}
];
var BLACKLISTED_KEYS = [
"__proto__",
"constructor",
"prototype",
"hasOwnProperty",
"toString",
"valueOf"
];
function validateContextAttribute(key, value, config = {}) {
const {
maxStringLength = 10240,
// 10KB for strings
maxObjectDepth = 5,
maxArrayLength = 100,
maxTotalSize = 51200,
// 50KB total
allowedKeyPattern = /^[a-zA-Z0-9_.-]+$/
} = config;
if (BLACKLISTED_KEYS.includes(key)) {
return false;
}
if (!allowedKeyPattern.test(key)) {
return false;
}
try {
const serialized = JSON.stringify(value);
if (serialized.length > maxTotalSize) {
return false;
}
} catch {
return false;
}
function validateValue(val, depth = 0) {
if (depth > maxObjectDepth) {
return false;
}
if (val === null || val === void 0) {
return true;
}
if (typeof val === "string") {
return val.length <= maxStringLength;
}
if (typeof val === "number") {
return isFinite(val);
}
if (typeof val === "boolean") {
return true;
}
if (Array.isArray(val)) {
if (val.length > maxArrayLength) {
return false;
}
return val.every((item) => validateValue(item, depth + 1));
}
if (typeof val === "object") {
const keys = Object.keys(val);
if (keys.length > 100) {
return false;
}
for (const k of keys) {
if (BLACKLISTED_KEYS.includes(k)) {
return false;
}
}
if (Object.prototype.hasOwnProperty.call(val, "__proto__") || Object.prototype.hasOwnProperty.call(val, "prototype")) {
return false;
}
return keys.every((k) => validateValue(val[k], depth + 1));
}
return false;
}
__name(validateValue, "validateValue");
return validateValue(value);
}
__name(validateContextAttribute, "validateContextAttribute");
function isValidHeaderValue(value, config) {
if (!value || value.length === 0) {
return !config.required;
}
if (config.maxLength && value.length > config.maxLength) {
return false;
}
switch (config.type) {
case "string":
if (config.pattern && !config.pattern.test(value)) {
return false;
}
break;
case "number":
const num = Number(value);
if (isNaN(num) || !isFinite(num)) {
return false;
}
break;
case "boolean":
if (value !== "true" && value !== "false") {
return false;
}
break;
case "enum":
if (!config.enumValues?.includes(value)) {
return false;
}
break;
case "json":
try {
JSON.parse(value);
} catch {
return false;
}
break;
}
return true;
}
__name(isValidHeaderValue, "isValidHeaderValue");
function sanitizeHeaderValue(value, config) {
if (!value) return null;
if (config.sanitize) {
value = config.sanitize(value);
}
switch (config.type) {
case "string":
return value.replace(/[\x00-\x1F\x7F]/g, "").trim();
case "number":
return Number(value);
case "boolean":
return value === "true";
case "enum":
return value;
// Already validated
case "json":
try {
const parsed = JSON.parse(value);
if (typeof parsed === "object" && parsed !== null && !Array.isArray(parsed)) {
const clean = {};
for (const [k, v] of Object.entries(parsed)) {
if (!BLACKLISTED_KEYS.includes(k)) {
clean[k] = v;
}
}
return clean;
}
return parsed;
} catch {
return null;
}
default:
return value;
}
}
__name(sanitizeHeaderValue, "sanitizeHeaderValue");
function extractSecureCustomAttributes(ctx, headerConfig = DEFAULT_HEADER_CONFIG, options = {}) {
const attributes = {};
if (!ctx.headers) return attributes;
for (const config of headerConfig) {
const value = ctx.headers.get?.(config.name);
if (value !== void 0 && value !== null) {
if (!isValidHeaderValue(value, config)) {
if (options.logInvalid) {
console.warn(
`[feature-flags] Invalid value for header ${config.name}: ${value}`
);
}
continue;
}
const sanitized = sanitizeHeaderValue(value, config);
if (sanitized !== null) {
const attrName = config.name.substring(2).replace(/-([a-z])/g, (_, letter) => letter.toUpperCase());
attributes[attrName] = sanitized;
}
}
}
if (options.strict && options.logInvalid && ctx.headers) {
for (const [key] of ctx.headers.entries()) {
if (key.startsWith("x-feature-") || key.startsWith("x-targeting-")) {
const isWhitelisted = headerConfig.some((h) => h.name === key);
if (!isWhitelisted) {
console.warn(
`[feature-flags] Rejected non-whitelisted header: ${key}`
);
}
}
}
}
attributes._headerSource = true;
attributes._validated = true;
attributes._timestamp = Date.now();
return attributes;
}
__name(extractSecureCustomAttributes, "extractSecureCustomAttributes");
// src/middleware/context.ts
function createFeatureFlagsMiddleware(pc) {
return createMiddleware(async (ctx) => {
const evaluationContext = await buildMinimalContext(ctx, pc);
const evaluate = createEvaluator(ctx, pc, evaluationContext);
const evaluateBatch = createBatchEvaluator(ctx, pc, evaluationContext);
return {
featureFlags: {
evaluate,
evaluateBatch,
context: evaluationContext
}
};
});
}
__name(createFeatureFlagsMiddleware, "createFeatureFlagsMiddleware");
async function buildMinimalContext(ctx, _pluginContext) {
const context = {
userId: "anonymous",
attributes: {}
};
if (ctx.path && context.attributes) context.attributes.requestPath = ctx.path;
if (ctx.method && context.attributes)
context.attributes.requestMethod = ctx.method;
if (context.attributes)
context.attributes.timestamp = (/* @__PURE__ */ new Date()).toISOString();
return context;
}
__name(buildMinimalContext, "buildMinimalContext");
function createEvaluator(_ctx, pluginContext, evaluationContext) {
return async (key, defaultValue) => {
try {
const { storage } = pluginContext;
const organizationId = pluginContext.config.multiTenant.enabled ? evaluationContext.organizationId : void 0;
const flag = await storage.getFlag(key, organizationId);
if (!flag || !flag.enabled) {
return {
value: defaultValue,
reason: flag ? "disabled" : "not_found"
};
}
const { evaluateFlags } = await import("./evaluation-VREABOZR.js");
return await evaluateFlags(flag, evaluationContext, pluginContext);
} catch (error) {
console.error(`[feature-flags] Error evaluating flag ${key}:`, error);
return {
value: defaultValue,
reason: "error"
};
}
};
}
__name(createEvaluator, "createEvaluator");
function createBatchEvaluator(ctx, pluginContext, evaluationContext) {
return async (keys) => {
const evaluate = createEvaluator(ctx, pluginContext, evaluationContext);
const results = {};
const evaluations = await Promise.all(
keys.map(async (key) => {
const result = await evaluate(key);
return { key, result };
})
);
for (const { key, result } of evaluations) {
results[key] = result;
}
return results;
};
}
__name(createBatchEvaluator, "createBatchEvaluator");
async function buildEvaluationContext(ctx, session, pluginContext, options = {}) {
const context = {
userId: session?.user?.id || "anonymous",
attributes: {}
};
if (session?.user && context.attributes) {
const userAttrs = [
["email", session.user.email],
["name", session.user.name],
["createdAt", session.user.createdAt],
["roles", session.user.roles || []]
];
for (const [key, value] of userAttrs) {
if (value !== void 0 && validateContextAttribute(key, value)) {
context.attributes[key] = value;
}
}
}
if (pluginContext.config.multiTenant.enabled) {
const organizationId = getOrganizationId(session, pluginContext);
if (organizationId) {
context.organizationId = organizationId;
if (context.attributes)
context.attributes.organizationId = organizationId;
}
}
if (options.collectClientInfo) {
const clientInfo = extractClientInfo(ctx);
if (!options.collectDevice) {
delete clientInfo.device;
delete clientInfo.browser;
delete clientInfo.os;
delete clientInfo.platform;
}
context.attributes = {
...context.attributes,
...clientInfo
};
} else if (options.collectDevice) {
const userAgent = ctx.headers?.get?.("user-agent");
if (userAgent && context.attributes) {
const uaInfo = parseUserAgent(userAgent);
context.attributes.device = uaInfo.device;
context.attributes.browser = uaInfo.browser;
context.attributes.os = uaInfo.os;
if (uaInfo.platform) {
context.attributes.platform = uaInfo.platform;
}
}
}
if (options.collectCustomHeaders) {
const headerConfig = pluginContext.config.customHeaders?.whitelist;
const extractOptions = {
logInvalid: pluginContext.config.customHeaders?.logInvalid || false,
strict: pluginContext.config.customHeaders?.strict || false
};
const customAttributes = extractSecureCustomAttributes(
ctx,
headerConfig,
extractOptions
);
for (const [key, value] of Object.entries(customAttributes)) {
if (validateContextAttribute(key, value)) {
if (context.attributes) {
context.attributes[key] = value;
}
}
}
}
if (options.collectGeo) {
const geoData = extractGeoData(ctx);
if (geoData) {
context.attributes = {
...context.attributes,
...geoData
};
}
}
if (options.allowedAttributes && options.allowedAttributes.length > 0) {
const filtered = {};
for (const attr of options.allowedAttributes) {
if (context.attributes && attr in context.attributes) {
filtered[attr] = context.attributes[attr];
}
}
context.attributes = filtered;
}
if (ctx.path && context.attributes) context.attributes.requestPath = ctx.path;
if (ctx.method && context.attributes)
context.attributes.requestMethod = ctx.method;
if (context.attributes)
context.attributes.timestamp = (/* @__PURE__ */ new Date()).toISOString();
return context;
}
__name(buildEvaluationContext, "buildEvaluationContext");
function getOrganizationId(session, pluginContext) {
if (!pluginContext.config.multiTenant.enabled) {