flags
Version:
Flags SDK by Vercel - The feature flags toolkit for Next.js and SvelteKit
514 lines (480 loc) • 18.2 kB
JavaScript
Object.defineProperty(exports, "__esModule", {value: true}); function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { newObj[key] = obj[key]; } } } newObj.default = obj; return newObj; } } 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; }
var _chunkQLJ65HXQcjs = require('./chunk-QLJ65HXQ.cjs');
var _chunkRIRV5SSTcjs = require('./chunk-RIRV5SST.cjs');
require('./chunk-MEG7RDX7.cjs');
// src/next/index.ts
var _cookies = require('@edge-runtime/cookies');
// src/next/overrides.ts
var memoizedDecrypt = _chunkQLJ65HXQcjs.memoizeOne.call(void 0,
(text) => _chunkRIRV5SSTcjs.decryptOverrides.call(void 0, text),
(a, b) => a[0] === b[0],
// only the first argument gets compared
{ cachePromiseRejection: true }
);
async function getOverrides(cookie) {
if (typeof cookie === "string" && cookie !== "") {
const cookieOverrides = await memoizedDecrypt(cookie);
return _nullishCoalesce(cookieOverrides, () => ( null));
}
return null;
}
// src/next/precompute.ts
async function evaluate(flags) {
return Promise.all(flags.map((flag2) => flag2()));
}
async function precompute(flags) {
const values = await evaluate(flags);
return serialize2(flags, values);
}
function combine(flags, values) {
return Object.fromEntries(flags.map((flag2, i) => [flag2.key, values[i]]));
}
async function serialize2(flags, values, secret = process.env.FLAGS_SECRET) {
if (!secret) {
throw new Error("flags: Can not serialize due to missing secret");
}
return _chunkQLJ65HXQcjs.serialize.call(void 0, combine(flags, values), flags, secret);
}
async function deserialize2(flags, code, secret = process.env.FLAGS_SECRET) {
if (!secret) {
throw new Error("flags: Can not serialize due to missing secret");
}
return _chunkQLJ65HXQcjs.deserialize.call(void 0, code, flags, secret);
}
async function getPrecomputed(flagOrFlags, precomputeFlags, code, secret = process.env.FLAGS_SECRET) {
if (!secret) {
throw new Error(
"flags: getPrecomputed was called without a secret. Please set FLAGS_SECRET environment variable."
);
}
const flagSet = await deserialize2(precomputeFlags, code, secret);
if (Array.isArray(flagOrFlags)) {
return flagOrFlags.map((flag2) => flagSet[flag2.key]);
} else {
return flagSet[flagOrFlags.key];
}
}
function* cartesianIterator(items) {
const remainder = items.length > 1 ? cartesianIterator(items.slice(1)) : [[]];
for (let r of remainder)
for (let h of items.at(0))
yield [h, ...r];
}
async function generatePermutations(flags, filter = null, secret = process.env.FLAGS_SECRET) {
if (!secret) {
throw new Error(
"flags: generatePermutations was called without a secret. Please set FLAGS_SECRET environment variable."
);
}
const options = flags.map((flag2) => {
if (!flag2.options)
return [false, true];
return flag2.options.map((option) => option.value);
});
const list = [];
for (const permutation of cartesianIterator(options)) {
const permObject = permutation.reduce(
(acc, value, index) => {
acc[flags[index].key] = value;
return acc;
},
{}
);
if (!filter || filter(permObject))
list.push(permObject);
}
return Promise.all(list.map((values) => _chunkQLJ65HXQcjs.serialize.call(void 0, values, flags, secret)));
}
// src/next/is-internal-next-error.ts
var REACT_POSTPONE_TYPE = Symbol.for("react.postpone");
function isPostpone(error) {
return typeof error === "object" && error !== null && "$$typeof" in error && error.$$typeof === REACT_POSTPONE_TYPE;
}
function isInternalNextError(error) {
if (isPostpone(error))
return true;
if (typeof error !== "object" || error === null || !("digest" in error) || typeof error.digest !== "string") {
return false;
}
const errorCode = error.digest.split(";")[0];
return errorCode === "NEXT_REDIRECT" || errorCode === "DYNAMIC_SERVER_USAGE" || errorCode === "BAILOUT_TO_CLIENT_SIDE_RENDERING" || errorCode === "NEXT_NOT_FOUND";
}
// src/next/dedupe.ts
function createCacheNode() {
return {
s: 0 /* UNTERMINATED */,
v: void 0,
o: null,
p: null
};
}
var cacheRegistry = /* @__PURE__ */ new WeakMap();
function dedupe(fn) {
const requestStore = /* @__PURE__ */ new WeakMap();
const dedupedFn = async function(...args) {
const { headers } = await Promise.resolve().then(() => _interopRequireWildcard(require("next/headers")));
const h = await headers();
let cacheNode = requestStore.get(h);
if (!cacheNode) {
cacheNode = createCacheNode();
requestStore.set(h, cacheNode);
}
for (let i = 0; i < args.length; i++) {
const arg = args[i];
if (typeof arg === "function" || typeof arg === "object" && arg !== null) {
let objectCache = cacheNode.o;
if (objectCache === null) {
cacheNode.o = objectCache = /* @__PURE__ */ new WeakMap();
}
const objectNode = objectCache.get(arg);
if (objectNode === void 0) {
cacheNode = createCacheNode();
objectCache.set(arg, cacheNode);
} else {
cacheNode = objectNode;
}
} else {
let primitiveCache = cacheNode.p;
if (primitiveCache === null) {
cacheNode.p = primitiveCache = /* @__PURE__ */ new Map();
}
const primitiveNode = primitiveCache.get(arg);
if (primitiveNode === void 0) {
cacheNode = createCacheNode();
primitiveCache.set(arg, cacheNode);
} else {
cacheNode = primitiveNode;
}
}
}
if (cacheNode.s === 1 /* TERMINATED */) {
return cacheNode.v;
}
if (cacheNode.s === 2 /* ERRORED */) {
throw cacheNode.v;
}
try {
const result = fn.apply(this, args);
cacheNode.s = 1 /* TERMINATED */;
cacheNode.v = result;
return result;
} catch (error) {
cacheNode.s = 2 /* ERRORED */;
cacheNode.v = error;
throw error;
}
};
cacheRegistry.set(dedupedFn, requestStore);
return dedupedFn;
}
async function clearDedupeCacheForCurrentRequest(dedupedFn) {
if (typeof dedupedFn !== "function") {
throw new Error("dedupe: not a function");
}
const requestStore = cacheRegistry.get(dedupedFn);
if (!requestStore) {
throw new Error("dedupe: cache not found");
}
const { headers } = await Promise.resolve().then(() => _interopRequireWildcard(require("next/headers")));
const h = await headers();
return requestStore.delete(h);
}
// src/next/create-flags-discovery-endpoint.ts
function createFlagsDiscoveryEndpoint(getApiData, options) {
return async (request) => {
const access = await _chunkRIRV5SSTcjs.verifyAccess.call(void 0,
request.headers.get("Authorization"),
_optionalChain([options, 'optionalAccess', _ => _.secret])
);
if (!access)
return Response.json(null, { status: 401 });
const apiData = await getApiData(request);
return new Response(JSON.stringify(apiData), {
headers: {
"x-flags-sdk-version": _chunkRIRV5SSTcjs.version,
"content-type": "application/json"
}
});
};
}
// src/next/index.ts
var evaluationCache = /* @__PURE__ */ new WeakMap();
function getCachedValuePromise(headers, flagKey, entitiesKey) {
const map = _optionalChain([evaluationCache, 'access', _2 => _2.get, 'call', _3 => _3(headers), 'optionalAccess', _4 => _4.get, 'call', _5 => _5(flagKey)]);
if (!map)
return void 0;
return map.get(entitiesKey);
}
function setCachedValuePromise(headers, flagKey, entitiesKey, flagValue) {
const byHeaders = evaluationCache.get(headers);
if (!byHeaders) {
evaluationCache.set(
headers,
/* @__PURE__ */ new Map([[flagKey, /* @__PURE__ */ new Map([[entitiesKey, flagValue]])]])
);
return;
}
const byFlagKey = byHeaders.get(flagKey);
if (!byFlagKey) {
byHeaders.set(flagKey, /* @__PURE__ */ new Map([[entitiesKey, flagValue]]));
return;
}
byFlagKey.set(entitiesKey, flagValue);
}
var transformMap = /* @__PURE__ */ new WeakMap();
var headersMap = /* @__PURE__ */ new WeakMap();
var cookiesMap = /* @__PURE__ */ new WeakMap();
var identifyArgsMap = /* @__PURE__ */ new WeakMap();
function transformToHeaders(incomingHeaders) {
const cached = transformMap.get(incomingHeaders);
if (cached !== void 0)
return cached;
const headers = new Headers();
for (const [key, value] of Object.entries(incomingHeaders)) {
if (Array.isArray(value)) {
value.forEach((item) => headers.append(key, item));
} else if (value !== void 0) {
headers.append(key, value);
}
}
transformMap.set(incomingHeaders, headers);
return headers;
}
function sealHeaders(headers) {
const cached = headersMap.get(headers);
if (cached !== void 0)
return cached;
const sealed = _chunkRIRV5SSTcjs.HeadersAdapter.seal(headers);
headersMap.set(headers, sealed);
return sealed;
}
function sealCookies(headers) {
const cached = cookiesMap.get(headers);
if (cached !== void 0)
return cached;
const sealed = _chunkRIRV5SSTcjs.RequestCookiesAdapter.seal(new (0, _cookies.RequestCookies)(headers));
cookiesMap.set(headers, sealed);
return sealed;
}
function isIdentifyFunction(identify) {
return typeof identify === "function";
}
async function getEntities(identify, dedupeCacheKey, readonlyHeaders, readonlyCookies) {
if (!identify)
return void 0;
if (!isIdentifyFunction(identify))
return identify;
const args = identifyArgsMap.get(dedupeCacheKey);
if (args)
return identify(...args);
const nextArgs = [
{ headers: readonlyHeaders, cookies: readonlyCookies }
];
identifyArgsMap.set(dedupeCacheKey, nextArgs);
return identify(...nextArgs);
}
function getDecide(definition) {
return function decide(params) {
if (typeof definition.decide === "function") {
return definition.decide(params);
}
if (typeof _optionalChain([definition, 'access', _6 => _6.adapter, 'optionalAccess', _7 => _7.decide]) === "function") {
return definition.adapter.decide({ key: definition.key, ...params });
}
throw new Error(`flags: No decide function provided for ${definition.key}`);
};
}
function getIdentify(definition) {
return function identify(params) {
if (typeof definition.identify === "function") {
return definition.identify(params);
}
if (typeof _optionalChain([definition, 'access', _8 => _8.adapter, 'optionalAccess', _9 => _9.identify]) === "function") {
return definition.adapter.identify(params);
}
return definition.identify;
};
}
function getRun(definition, decide) {
return async function run(options) {
let readonlyHeaders;
let readonlyCookies;
let dedupeCacheKey;
if (options.request) {
const headers = transformToHeaders(options.request.headers);
readonlyHeaders = sealHeaders(headers);
readonlyCookies = sealCookies(headers);
dedupeCacheKey = options.request.headers;
} else {
const { headers, cookies } = await Promise.resolve().then(() => _interopRequireWildcard(require("next/headers")));
const [headersStore, cookiesStore] = await Promise.all([
headers(),
cookies()
]);
readonlyHeaders = headersStore;
readonlyCookies = cookiesStore;
dedupeCacheKey = headersStore;
}
const overrides = await getOverrides(
_optionalChain([readonlyCookies, 'access', _10 => _10.get, 'call', _11 => _11("vercel-flag-overrides"), 'optionalAccess', _12 => _12.value])
);
const entities = await getEntities(
options.identify,
dedupeCacheKey,
readonlyHeaders,
readonlyCookies
);
const entitiesKey = _nullishCoalesce(JSON.stringify(entities), () => ( ""));
const cachedValue = getCachedValuePromise(
readonlyHeaders,
definition.key,
entitiesKey
);
if (cachedValue !== void 0) {
_chunkRIRV5SSTcjs.setSpanAttribute.call(void 0, "method", "cached");
const value = await cachedValue;
return value;
}
if (overrides && overrides[definition.key] !== void 0) {
_chunkRIRV5SSTcjs.setSpanAttribute.call(void 0, "method", "override");
const decision2 = overrides[definition.key];
setCachedValuePromise(
readonlyHeaders,
definition.key,
entitiesKey,
Promise.resolve(decision2)
);
_chunkRIRV5SSTcjs.internalReportValue.call(void 0, definition.key, decision2, {
reason: "override"
});
return decision2;
}
const decisionPromise = (async () => {
return decide({
// @ts-expect-error TypeScript will not be able to process `getPrecomputed` when added to `Decide`. It is, however, part of the `Adapter` type
defaultValue: definition.defaultValue,
headers: readonlyHeaders,
cookies: readonlyCookies,
entities
});
})().then(
(value) => {
if (value !== void 0)
return value;
if (definition.defaultValue !== void 0)
return definition.defaultValue;
throw new Error(
`flags: Flag "${definition.key}" must have a defaultValue or a decide function that returns a value`
);
},
(error) => {
if (isInternalNextError(error))
throw error;
if (definition.defaultValue !== void 0) {
if (process.env.NODE_ENV === "development") {
console.info(
`flags: Flag "${definition.key}" is falling back to its defaultValue`
);
} else {
console.warn(
`flags: Flag "${definition.key}" is falling back to its defaultValue after catching the following error`,
error
);
}
return definition.defaultValue;
}
console.warn(
`flags: Flag "${definition.key}" could not be evaluated`
);
throw error;
}
);
setCachedValuePromise(
readonlyHeaders,
definition.key,
entitiesKey,
decisionPromise
);
const decision = await decisionPromise;
if (_optionalChain([definition, 'access', _13 => _13.config, 'optionalAccess', _14 => _14.reportValue]) !== false) {
_chunkRIRV5SSTcjs.reportValue.call(void 0, definition.key, decision);
}
return decision;
};
}
function getOrigin(definition) {
if (definition.origin)
return definition.origin;
if (typeof _optionalChain([definition, 'access', _15 => _15.adapter, 'optionalAccess', _16 => _16.origin]) === "function")
return definition.adapter.origin(definition.key);
return _optionalChain([definition, 'access', _17 => _17.adapter, 'optionalAccess', _18 => _18.origin]);
}
function flag(definition) {
const decide = getDecide(definition);
const identify = getIdentify(definition);
const run = getRun(definition, decide);
const origin = getOrigin(definition);
const flag2 = _chunkRIRV5SSTcjs.trace.call(void 0,
async (...args) => {
_chunkRIRV5SSTcjs.setSpanAttribute.call(void 0, "method", "decided");
if (typeof args[0] === "string" && Array.isArray(args[1])) {
const [precomputedCode, precomputedGroup, secret] = args;
if (precomputedCode && precomputedGroup) {
_chunkRIRV5SSTcjs.setSpanAttribute.call(void 0, "method", "precomputed");
return getPrecomputed(
flag2,
precomputedGroup,
precomputedCode,
secret
);
}
}
if (args[0] && typeof args[0] === "object" && "headers" in args[0]) {
const [request] = args;
return run({ identify, request });
}
return run({ identify, request: void 0 });
},
{
name: "flag",
isVerboseTrace: false,
attributes: { key: definition.key }
}
);
flag2.key = definition.key;
flag2.defaultValue = definition.defaultValue;
flag2.origin = origin;
flag2.options = _chunkQLJ65HXQcjs.normalizeOptions.call(void 0, definition.options);
flag2.description = definition.description;
flag2.identify = identify ? _chunkRIRV5SSTcjs.trace.call(void 0, identify, {
isVerboseTrace: false,
name: "identify",
attributes: { key: definition.key }
}) : identify;
flag2.decide = _chunkRIRV5SSTcjs.trace.call(void 0, decide, {
isVerboseTrace: false,
name: "decide",
attributes: { key: definition.key }
});
flag2.run = _chunkRIRV5SSTcjs.trace.call(void 0, run, {
isVerboseTrace: false,
name: "run",
attributes: { key: definition.key }
});
return flag2;
}
function getProviderData(flags) {
const definitions = Object.values(flags).filter((i) => !Array.isArray(i)).reduce((acc, d) => {
acc[d.key] = {
options: d.options,
origin: d.origin,
description: d.description,
defaultValue: d.defaultValue,
declaredInCode: true
};
return acc;
}, {});
return { definitions, hints: [] };
}
exports.clearDedupeCacheForCurrentRequest = clearDedupeCacheForCurrentRequest; exports.combine = combine; exports.createFlagsDiscoveryEndpoint = createFlagsDiscoveryEndpoint; exports.dedupe = dedupe; exports.deserialize = deserialize2; exports.evaluate = evaluate; exports.flag = flag; exports.generatePermutations = generatePermutations; exports.getPrecomputed = getPrecomputed; exports.getProviderData = getProviderData; exports.precompute = precompute; exports.serialize = serialize2;
//# sourceMappingURL=next.cjs.map
;