UNPKG

flags

Version:

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

406 lines (397 loc) 12 kB
// package.json var name = "flags"; var version = "3.2.0"; // src/lib/tracing.ts import { AsyncLocalStorage } from "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 maybeTraceApi?.getTracer(name, version); } function isPromise(p) { return p !== null && typeof p === "object" && "then" in p && typeof p.then === "function"; } var spanContext = new AsyncLocalStorage(); function setSpanAttribute(name2, value) { spanContext.getStore()?.set(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 ) ); } spanContext.getStore()?.forEach((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 }); spanContext.getStore()?.forEach((value, key) => { span.setAttribute(key, value); }); span.end(); }); } else { if (options.attributesSuccess) { span.setAttributes(options.attributesSuccess(result)); } spanContext.getStore()?.forEach((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 }); spanContext.getStore()?.forEach((value, key) => { span.setAttribute(key, value); }); span.end(); throw error; } }) ); }; return traced; } // src/lib/crypto.ts import { jwtDecrypt, base64url, EncryptJWT } from "jose"; async function encryptJWT(payload, expirationTime, secret) { return new EncryptJWT(payload).setExpirationTime(expirationTime).setProtectedHeader({ alg: "dir", enc: "A256GCM" }).encrypt(base64url.decode(secret)); } async function decryptJWT(cookie, verify, secret) { if (typeof cookie !== "string") return; try { const { payload } = await jwtDecrypt(cookie, base64url.decode(secret)); const decoded = payload; if (!verify || verify(decoded)) { delete decoded.iat; delete decoded.exp; return decoded; } } catch { } } async function encrypt(value, secret = process?.env?.FLAGS_SECRET) { if (!secret) throw new Error("Missing FLAGS_SECRET"); return encryptJWT({ c: value }, "1y", secret); } async function decrypt(encryptedData, secret = process?.env?.FLAGS_SECRET) { if (!secret) throw new Error("Missing FLAGS_SECRET"); const content = await decryptJWT(encryptedData, null, secret); return content?.c; } // src/lib/verify-access.ts var verifyAccess = trace( async function verifyAccess2(authHeader, secret = process?.env?.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 data = await decrypt( authHeader?.replace(/^Bearer /i, ""), secret ); return data !== void 0; }, { isVerboseTrace: false, name: "verifyAccess" } ); // src/lib/report-value.ts function reportValue(key, value) { const symbol = Symbol.for("@vercel/request-context"); const ctx = Reflect.get(globalThis, symbol)?.get(); ctx?.flags?.reportValue(key, value, { sdkVersion: version }); } function internalReportValue(key, value, data) { const symbol = Symbol.for("@vercel/request-context"); const ctx = Reflect.get(globalThis, symbol)?.get(); ctx?.flags?.reportValue(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, 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: [] } ); } export { setTracerProvider, setSpanAttribute, trace, encrypt, decrypt, verifyAccess, reportValue, internalReportValue, HeadersAdapter, RequestCookiesAdapter, mergeProviderData }; //# sourceMappingURL=chunk-RDI4Q5KS.js.map