UNPKG

flags

Version:

Flags SDK by Vercel - The feature flags toolkit for Next.js and SvelteKit

474 lines (446 loc) 17.5 kB
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }// package.json var name = "flags"; var version = "4.0.1"; // src/lib/tracing.ts var _async_hooks = require('async_hooks'); var vercelFlagsTraceSymbol = Symbol.for("flags:global-trace"); function setTracerProvider(tracer) { Reflect.set(globalThis, vercelFlagsTraceSymbol, tracer); } function getTracer() { const maybeTraceApi = Reflect.get(globalThis, vercelFlagsTraceSymbol); return _optionalChain([maybeTraceApi, 'optionalAccess', _ => _.getTracer, 'call', _2 => _2(name, version)]); } function isPromise(p) { return p !== null && typeof p === "object" && "then" in p && typeof p.then === "function"; } var spanContext = new (0, _async_hooks.AsyncLocalStorage)(); function setSpanAttribute(name2, value) { _optionalChain([spanContext, 'access', _3 => _3.getStore, 'call', _4 => _4(), 'optionalAccess', _5 => _5.set, 'call', _6 => _6(name2, value)]); } function trace(fn, options = { name: fn.name }) { const traced = function(...args) { const tracer = getTracer(); if (!tracer) return fn.apply(this, args); const shouldTrace = process.env.VERCEL_FLAGS_TRACE_VERBOSE === "true" || options.isVerboseTrace === false; if (!shouldTrace) return fn.apply(this, args); return spanContext.run( /* @__PURE__ */ new Map(), () => tracer.startActiveSpan(options.name, (span) => { if (options.attributes) span.setAttributes(options.attributes); try { const result = fn.apply(this, args); if (isPromise(result)) { result.then((value) => { if (options.attributesSuccess) { span.setAttributes( options.attributesSuccess( value ) ); } _optionalChain([spanContext, 'access', _7 => _7.getStore, 'call', _8 => _8(), 'optionalAccess', _9 => _9.forEach, 'call', _10 => _10((value2, key) => { span.setAttribute(key, value2); })]); span.setStatus({ code: 1 }); span.end(); }).catch((error) => { if (options.attributesError) { span.setAttributes(options.attributesError(error)); } span.setStatus({ code: 2, // 2 = Error message: error instanceof Error ? error.message : void 0 }); _optionalChain([spanContext, 'access', _11 => _11.getStore, 'call', _12 => _12(), 'optionalAccess', _13 => _13.forEach, 'call', _14 => _14((value, key) => { span.setAttribute(key, value); })]); span.end(); }); } else { if (options.attributesSuccess) { span.setAttributes(options.attributesSuccess(result)); } _optionalChain([spanContext, 'access', _15 => _15.getStore, 'call', _16 => _16(), 'optionalAccess', _17 => _17.forEach, 'call', _18 => _18((value, key) => { span.setAttribute(key, value); })]); span.setStatus({ code: 1 }); span.end(); } return result; } catch (error) { if (options.attributesError) { span.setAttributes(options.attributesError(error)); } span.setStatus({ code: 2, // 2 = Error message: error instanceof Error ? error.message : void 0 }); _optionalChain([spanContext, 'access', _19 => _19.getStore, 'call', _20 => _20(), 'optionalAccess', _21 => _21.forEach, 'call', _22 => _22((value, key) => { span.setAttribute(key, value); })]); span.end(); throw error; } }) ); }; return traced; } // src/lib/crypto.ts var _jose = require('jose'); var hasPurpose = (pur, expectedPurpose) => { return Array.isArray(pur) ? pur.includes(expectedPurpose) : pur === expectedPurpose; }; async function encryptJwe(payload, secret, expirationTime) { const encodedSecret = _jose.base64url.decode(secret); if (encodedSecret.length !== 32) { throw new Error( "flags: Invalid secret, it must be a 256-bit key (32 bytes)" ); } return new (0, _jose.EncryptJWT)(payload).setExpirationTime(expirationTime).setProtectedHeader({ alg: "dir", enc: "A256GCM" }).encrypt(encodedSecret); } async function decryptJwe(text, verify, secret) { if (typeof text !== "string") return; const encodedSecret = _jose.base64url.decode(secret); if (encodedSecret.length !== 32) { throw new Error( "flags: Invalid secret, it must be a 256-bit key (32 bytes)" ); } try { const { payload } = await _jose.jwtDecrypt.call(void 0, text, encodedSecret); const decoded = payload; return verify(decoded) ? decoded : void 0; } catch (e) { return void 0; } } async function encryptOverrides(overrides, secret = _optionalChain([process, 'optionalAccess', _23 => _23.env, 'optionalAccess', _24 => _24.FLAGS_SECRET]), expirationTime = "1y") { if (!secret) throw new Error("flags: Missing FLAGS_SECRET"); return encryptJwe({ o: overrides, pur: "overrides" }, secret, expirationTime); } async function decryptOverrides(encryptedData, secret = _optionalChain([process, 'optionalAccess', _25 => _25.env, 'optionalAccess', _26 => _26.FLAGS_SECRET])) { if (!secret) throw new Error("flags: Missing FLAGS_SECRET"); const contents = await decryptJwe( encryptedData, (data) => hasPurpose(data.pur, "overrides") && Object.hasOwn(data, "o"), secret ); return _optionalChain([contents, 'optionalAccess', _27 => _27.o]); } async function encryptFlagValues(flagValues, secret = _optionalChain([process, 'optionalAccess', _28 => _28.env, 'optionalAccess', _29 => _29.FLAGS_SECRET]), expirationTime = "1y") { if (!secret) throw new Error("flags: Missing FLAGS_SECRET"); return encryptJwe({ v: flagValues, pur: "values" }, secret, expirationTime); } async function decryptFlagValues(encryptedData, secret = _optionalChain([process, 'optionalAccess', _30 => _30.env, 'optionalAccess', _31 => _31.FLAGS_SECRET])) { if (!secret) throw new Error("flags: Missing FLAGS_SECRET"); const contents = await decryptJwe( encryptedData, (data) => hasPurpose(data.pur, "values") && Object.hasOwn(data, "v"), secret ); return _optionalChain([contents, 'optionalAccess', _32 => _32.v]); } async function encryptFlagDefinitions(flagDefinitions, secret = _optionalChain([process, 'optionalAccess', _33 => _33.env, 'optionalAccess', _34 => _34.FLAGS_SECRET]), expirationTime = "1y") { if (!secret) throw new Error("flags: Missing FLAGS_SECRET"); return encryptJwe( { d: flagDefinitions, pur: "definitions" }, secret, expirationTime ); } async function decryptFlagDefinitions(encryptedData, secret = _optionalChain([process, 'optionalAccess', _35 => _35.env, 'optionalAccess', _36 => _36.FLAGS_SECRET])) { if (!secret) throw new Error("flags: Missing FLAGS_SECRET"); const contents = await decryptJwe( encryptedData, (data) => data.pur === "definitions" && Object.hasOwn(data, "d"), secret ); return _optionalChain([contents, 'optionalAccess', _37 => _37.d]); } async function createAccessProof(secret = _optionalChain([process, 'optionalAccess', _38 => _38.env, 'optionalAccess', _39 => _39.FLAGS_SECRET]), expirationTime = "1y") { if (!secret) throw new Error("flags: Missing FLAGS_SECRET"); return encryptJwe({ pur: "proof" }, secret, expirationTime); } async function verifyAccessProof(encryptedData, secret = _optionalChain([process, 'optionalAccess', _40 => _40.env, 'optionalAccess', _41 => _41.FLAGS_SECRET])) { if (!secret) throw new Error("flags: Missing FLAGS_SECRET"); const contents = await decryptJwe(encryptedData, (data) => hasPurpose(data.pur, "proof"), secret); return Boolean(contents); } // src/lib/verify-access.ts var verifyAccess = trace( async function verifyAccess2(authHeader, secret = _optionalChain([process, 'optionalAccess', _42 => _42.env, 'optionalAccess', _43 => _43.FLAGS_SECRET])) { if (!authHeader) return false; if (!secret) throw new Error( "flags: verifyAccess was called without a secret. Please set FLAGS_SECRET environment variable." ); const valid = await verifyAccessProof( authHeader.replace(/^Bearer /i, ""), secret ); return valid; }, { isVerboseTrace: false, name: "verifyAccess" } ); // src/lib/report-value.ts function reportValue(key, value) { const symbol = Symbol.for("@vercel/request-context"); const ctx = _optionalChain([Reflect, 'access', _44 => _44.get, 'call', _45 => _45(globalThis, symbol), 'optionalAccess', _46 => _46.get, 'call', _47 => _47()]); _optionalChain([ctx, 'optionalAccess', _48 => _48.flags, 'optionalAccess', _49 => _49.reportValue, 'call', _50 => _50(key, value, { sdkVersion: version })]); } function internalReportValue(key, value, data) { const symbol = Symbol.for("@vercel/request-context"); const ctx = _optionalChain([Reflect, 'access', _51 => _51.get, 'call', _52 => _52(globalThis, symbol), 'optionalAccess', _53 => _53.get, 'call', _54 => _54()]); _optionalChain([ctx, 'optionalAccess', _55 => _55.flags, 'optionalAccess', _56 => _56.reportValue, 'call', _57 => _57(key, value, { sdkVersion: version, ...data })]); } // src/spec-extension/adapters/reflect.ts var ReflectAdapter = class { static get(target, prop, receiver) { const value = Reflect.get(target, prop, receiver); if (typeof value === "function") { return value.bind(target); } return value; } static set(target, prop, value, receiver) { return Reflect.set(target, prop, value, receiver); } static has(target, prop) { return Reflect.has(target, prop); } static deleteProperty(target, prop) { return Reflect.deleteProperty(target, prop); } }; // src/spec-extension/adapters/headers.ts var ReadonlyHeadersError = class _ReadonlyHeadersError extends Error { constructor() { super( "Headers cannot be modified. Read more: https://nextjs.org/docs/app/api-reference/functions/headers" ); } static callable() { throw new _ReadonlyHeadersError(); } }; var HeadersAdapter = class _HeadersAdapter extends Headers { constructor(headers) { super(); this.headers = new Proxy(headers, { get(target, prop, receiver) { if (typeof prop === "symbol") { return ReflectAdapter.get(target, prop, receiver); } const lowercased = prop.toLowerCase(); const original = Object.keys(headers).find( (o) => o.toLowerCase() === lowercased ); if (typeof original === "undefined") return; return ReflectAdapter.get(target, original, receiver); }, set(target, prop, value, receiver) { if (typeof prop === "symbol") { return ReflectAdapter.set(target, prop, value, receiver); } const lowercased = prop.toLowerCase(); const original = Object.keys(headers).find( (o) => o.toLowerCase() === lowercased ); return ReflectAdapter.set(target, _nullishCoalesce(original, () => ( prop)), value, receiver); }, has(target, prop) { if (typeof prop === "symbol") return ReflectAdapter.has(target, prop); const lowercased = prop.toLowerCase(); const original = Object.keys(headers).find( (o) => o.toLowerCase() === lowercased ); if (typeof original === "undefined") return false; return ReflectAdapter.has(target, original); }, deleteProperty(target, prop) { if (typeof prop === "symbol") return ReflectAdapter.deleteProperty(target, prop); const lowercased = prop.toLowerCase(); const original = Object.keys(headers).find( (o) => o.toLowerCase() === lowercased ); if (typeof original === "undefined") return true; return ReflectAdapter.deleteProperty(target, original); } }); } /** * Seals a Headers instance to prevent modification by throwing an error when * any mutating method is called. */ static seal(headers) { return new Proxy(headers, { get(target, prop, receiver) { switch (prop) { case "append": case "delete": case "set": return ReadonlyHeadersError.callable; default: return ReflectAdapter.get(target, prop, receiver); } } }); } /** * Merges a header value into a string. This stores multiple values as an * array, so we need to merge them into a string. * * @param value a header value * @returns a merged header value (a string) */ merge(value) { if (Array.isArray(value)) return value.join(", "); return value; } /** * Creates a Headers instance from a plain object or a Headers instance. * * @param headers a plain object or a Headers instance * @returns a headers instance */ static from(headers) { if (headers instanceof Headers) return headers; return new _HeadersAdapter(headers); } append(name2, value) { const existing = this.headers[name2]; if (typeof existing === "string") { this.headers[name2] = [existing, value]; } else if (Array.isArray(existing)) { existing.push(value); } else { this.headers[name2] = value; } } delete(name2) { delete this.headers[name2]; } get(name2) { const value = this.headers[name2]; if (typeof value !== "undefined") return this.merge(value); return null; } has(name2) { return typeof this.headers[name2] !== "undefined"; } set(name2, value) { this.headers[name2] = value; } forEach(callbackfn, thisArg) { for (const [name2, value] of this.entries()) { callbackfn.call(thisArg, value, name2, this); } } *entries() { for (const key of Object.keys(this.headers)) { const name2 = key.toLowerCase(); const value = this.get(name2); yield [name2, value]; } } *keys() { for (const key of Object.keys(this.headers)) { const name2 = key.toLowerCase(); yield name2; } } *values() { for (const key of Object.keys(this.headers)) { const value = this.get(key); yield value; } } [Symbol.iterator]() { return this.entries(); } }; // src/spec-extension/adapters/request-cookies.ts var ReadonlyRequestCookiesError = class _ReadonlyRequestCookiesError extends Error { constructor() { super( "Cookies can only be modified in a Server Action or Route Handler. Read more: https://nextjs.org/docs/app/api-reference/functions/cookies#options" ); } static callable() { throw new _ReadonlyRequestCookiesError(); } }; var RequestCookiesAdapter = class { static seal(cookies) { return new Proxy(cookies, { get(target, prop, receiver) { switch (prop) { case "clear": case "delete": case "set": return ReadonlyRequestCookiesError.callable; default: return ReflectAdapter.get(target, prop, receiver); } } }); } }; var SYMBOL_MODIFY_COOKIE_VALUES = Symbol.for("next.mutated.cookies"); // src/lib/merge-provider-data.ts async function mergeProviderData(itemsPromises) { const items = await Promise.all( itemsPromises.map((p) => Promise.resolve(p).catch(() => null)) ); return items.filter((item) => Boolean(item)).reduce( (acc, item) => { Object.entries(item.definitions).forEach(([key, definition]) => { if (!acc.definitions[key]) acc.definitions[key] = {}; Object.assign(acc.definitions[key], definition); }); if (Array.isArray(item.hints)) acc.hints.push(...item.hints); return acc; }, { definitions: {}, hints: [] } ); } exports.version = version; exports.setTracerProvider = setTracerProvider; exports.setSpanAttribute = setSpanAttribute; exports.trace = trace; exports.encryptOverrides = encryptOverrides; exports.decryptOverrides = decryptOverrides; exports.encryptFlagValues = encryptFlagValues; exports.decryptFlagValues = decryptFlagValues; exports.encryptFlagDefinitions = encryptFlagDefinitions; exports.decryptFlagDefinitions = decryptFlagDefinitions; exports.createAccessProof = createAccessProof; exports.verifyAccessProof = verifyAccessProof; exports.verifyAccess = verifyAccess; exports.reportValue = reportValue; exports.internalReportValue = internalReportValue; exports.HeadersAdapter = HeadersAdapter; exports.RequestCookiesAdapter = RequestCookiesAdapter; exports.mergeProviderData = mergeProviderData; //# sourceMappingURL=chunk-RIRV5SST.cjs.map