flags
Version:
Flags SDK by Vercel - The feature flags toolkit for Next.js and SvelteKit
474 lines (446 loc) • 17.5 kB
JavaScript
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
;