UNPKG

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
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) {