UNPKG

@vercel/flags

Version:

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

613 lines (585 loc) 21.4 kB
"use strict";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 _chunkAVMIIWOFcjs = require('./chunk-AVMIIWOF.cjs'); var _chunkJYRFFBKMcjs = require('./chunk-JYRFFBKM.cjs'); require('./chunk-MEG7RDX7.cjs'); // src/next/index.ts var _cookies = require('@edge-runtime/cookies'); // src/next/async-memoize-one.ts function memoizeOne(fn, isEqual, { cachePromiseRejection = false } = {}) { let calledOnce = false; let oldArgs; let lastResult; function memoized(...newArgs) { if (calledOnce && isEqual(newArgs, oldArgs)) return lastResult; lastResult = fn.apply(this, newArgs); if (!cachePromiseRejection && lastResult.catch) { lastResult.catch(() => calledOnce = false); } calledOnce = true; oldArgs = newArgs; return lastResult; } return memoized; } // src/next/overrides.ts var memoizedDecrypt = memoizeOne( (text) => _chunkJYRFFBKMcjs.decrypt.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/serialization.ts var _jose = require('jose'); var memoizedVerify = memoizeOne( (code, secret) => _jose.compactVerify.call(void 0, code, _jose.base64url.decode(secret), { algorithms: ["HS256"] }), (a, b) => a[0] === b[0] && a[1] === b[1], // only first two args matter { cachePromiseRejection: true } ); var memoizedSign = memoizeOne( (uint8Array, secret) => new (0, _jose.CompactSign)(uint8Array).setProtectedHeader({ alg: "HS256" }).sign(_jose.base64url.decode(secret)), (a, b) => ( // matchedIndices array must be equal a[0].length === b[0].length && a[0].every((v, i) => b[0][i] === v) && // secrets must be equal a[1] === b[1] ), { cachePromiseRejection: true } ); function splitUint8Array(array, index) { const firstHalf = array.slice(0, index); const secondHalf = array.slice(index); return [firstHalf, secondHalf]; } async function deserialize(code, flags, secret) { const { payload } = await memoizedVerify(code, secret); const [matchedIndicesArray, valuesUint8Array] = payload.length === flags.length ? [payload] : splitUint8Array(payload, flags.length); const valuesArray = valuesUint8Array ? ( // re-add opening and closing brackets since we remove them when serializing JSON.parse(`[${new TextDecoder().decode(valuesUint8Array)}]`) ) : null; let spilled = 0; return matchedIndicesArray.reduce( (acc, valueIndex, index) => { const flag2 = flags[index]; if (!flag2) { throw new Error(`@vercel/flags: No flag at index ${index}`); } switch (valueIndex) { case 253 /* BOOLEAN_FALSE */: acc[flag2.key] = false; break; case 254 /* BOOLEAN_TRUE */: acc[flag2.key] = true; break; case 255 /* UNLISTED_VALUE */: acc[flag2.key] = valuesArray[spilled++]; break; case 252 /* NULL */: acc[flag2.key] = null; break; default: acc[flag2.key] = _optionalChain([flag2, 'access', _ => _.options, 'optionalAccess', _2 => _2[valueIndex], 'optionalAccess', _3 => _3.value]); } return acc; }, {} ); } var matchIndex = /* @__PURE__ */ function() { const stringifiedOptionsCache = /* @__PURE__ */ new Map(); return function matchIndex2(options, value) { const t = typeof value; if (value === null || t === "boolean" || t === "string" || t === "number") { return options.findIndex((v) => v.value === value); } const stringifiedValue = JSON.stringify(value); let stringifiedOptions = stringifiedOptionsCache.get(options); if (!stringifiedOptions) { stringifiedOptions = options.map((o) => JSON.stringify(o.value)); stringifiedOptionsCache.set(options, stringifiedOptions); } return stringifiedOptions.findIndex( (stringifiedOption) => stringifiedOption === stringifiedValue ); }; }(); function joinUint8Arrays(array1, array2) { const combined = new Uint8Array(array1.length + array2.length); combined.set(array1); combined.set(array2, array1.length); return combined; } async function serialize(flagSet, flags, secret) { const unlistedValues = []; const matchedIndices = new Uint8Array( flags.map((flag2) => { const options = Array.isArray(flag2.options) ? flag2.options : []; const value = flagSet[flag2.key]; if (!Object.prototype.hasOwnProperty.call(flagSet, flag2.key) || value === void 0) { throw new Error(`@vercel/flags: Missing value for flag "${flag2.key}"`); } switch (value) { case null: return 252 /* NULL */; case false: return 253 /* BOOLEAN_FALSE */; case true: return 254 /* BOOLEAN_TRUE */; } const matchedIndex = matchIndex(options, value); if (matchedIndex > -1) return matchedIndex; unlistedValues.push(value); return 255 /* UNLISTED_VALUE */; }) ); let joined; if (unlistedValues.length > 0) { const jsonArray = new TextEncoder().encode( // slicing removes opening and closing array brackets as they'll always be // there and we can re-add them when deserializing JSON.stringify(unlistedValues).slice(1, -1) ); joined = joinUint8Arrays(matchedIndices, jsonArray); } else { joined = matchedIndices; } return memoizedSign(joined, secret); } // 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("@vercel/flags: Can not serialize due to missing secret"); } return serialize(combine(flags, values), flags, secret); } async function deserialize2(flags, code, secret = process.env.FLAGS_SECRET) { if (!secret) { throw new Error("@vercel/flags: Can not serialize due to missing secret"); } return deserialize(code, flags, secret); } async function getPrecomputed(flagOrFlags, precomputeFlags, code, secret = process.env.FLAGS_SECRET) { if (!secret) { throw new Error( "@vercel/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( "@vercel/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) => serialize(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 }; } function dedupe(fn) { const requestStore = /* @__PURE__ */ new WeakMap(); return 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; } }; } // src/next/index.ts var evaluationCache = /* @__PURE__ */ new WeakMap(); function getCachedValuePromise(headers, flagKey, entitiesKey) { const map = _optionalChain([evaluationCache, 'access', _4 => _4.get, 'call', _5 => _5(headers), 'optionalAccess', _6 => _6.get, 'call', _7 => _7(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 = _chunkJYRFFBKMcjs.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 = _chunkJYRFFBKMcjs.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', _8 => _8.adapter, 'optionalAccess', _9 => _9.decide]) === "function") { return definition.adapter.decide({ key: definition.key, ...params }); } throw new Error( `@vercel/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', _10 => _10.adapter, 'optionalAccess', _11 => _11.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', _12 => _12.get, 'call', _13 => _13("vercel-flag-overrides"), 'optionalAccess', _14 => _14.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) { _chunkJYRFFBKMcjs.setSpanAttribute.call(void 0, "method", "cached"); const value = await cachedValue; return value; } if (overrides && overrides[definition.key] !== void 0) { _chunkJYRFFBKMcjs.setSpanAttribute.call(void 0, "method", "override"); const decision2 = overrides[definition.key]; setCachedValuePromise( readonlyHeaders, definition.key, entitiesKey, Promise.resolve(decision2) ); _chunkJYRFFBKMcjs.internalReportValue.call(void 0, definition.key, decision2, { reason: "override" }); return decision2; } const decisionPromise = (async () => { return decide({ headers: readonlyHeaders, cookies: readonlyCookies, entities }); })().then( (value) => { if (value !== void 0) return value; if (definition.defaultValue !== void 0) return definition.defaultValue; throw new Error( `@vercel/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( `@vercel/flags: Flag "${definition.key}" is falling back to its defaultValue` ); } else { console.warn( `@vercel/flags: Flag "${definition.key}" is falling back to its defaultValue after catching the following error`, error ); } return definition.defaultValue; } console.warn( `@vercel/flags: Flag "${definition.key}" could not be evaluated` ); throw error; } ); setCachedValuePromise( readonlyHeaders, definition.key, entitiesKey, decisionPromise ); const decision = await decisionPromise; if (_optionalChain([definition, 'access', _15 => _15.config, 'optionalAccess', _16 => _16.reportValue]) !== false) { _chunkJYRFFBKMcjs.reportValue.call(void 0, definition.key, decision); } return decision; }; } function getOrigin(definition) { if (definition.origin) return definition.origin; if (typeof _optionalChain([definition, 'access', _17 => _17.adapter, 'optionalAccess', _18 => _18.origin]) === "function") return definition.adapter.origin(definition.key); return _optionalChain([definition, 'access', _19 => _19.adapter, 'optionalAccess', _20 => _20.origin]); } function flag(definition) { const decide = getDecide(definition); const identify = getIdentify(definition); const run = getRun(definition, decide); const origin = getOrigin(definition); const flag2 = _chunkJYRFFBKMcjs.trace.call(void 0, async (...args) => { _chunkJYRFFBKMcjs.setSpanAttribute.call(void 0, "method", "decided"); if (typeof args[0] === "string" && Array.isArray(args[1])) { const [precomputedCode, precomputedGroup, secret] = args; if (precomputedCode && precomputedGroup) { _chunkJYRFFBKMcjs.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 = _chunkAVMIIWOFcjs.normalizeOptions.call(void 0, definition.options); flag2.description = definition.description; flag2.identify = identify ? _chunkJYRFFBKMcjs.trace.call(void 0, identify, { isVerboseTrace: false, name: "identify", attributes: { key: definition.key } }) : identify; flag2.decide = _chunkJYRFFBKMcjs.trace.call(void 0, decide, { isVerboseTrace: false, name: "decide", attributes: { key: definition.key } }); flag2.run = _chunkJYRFFBKMcjs.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.combine = combine; 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