UNPKG

flags

Version:

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

164 lines (161 loc) 5.34 kB
// src/lib/normalize-options.ts function normalizeOptions(flagOptions) { if (!Array.isArray(flagOptions)) return flagOptions; return flagOptions.map((option) => { if (typeof option === "boolean") return { value: option }; if (typeof option === "number") return { value: option }; if (typeof option === "string") return { value: option }; if (option === null) return { value: option }; return option; }); } // src/lib/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/lib/serialization.ts import { CompactSign, base64url, compactVerify } from "jose"; var memoizedVerify = memoizeOne( (code, secret) => compactVerify(code, 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 CompactSign(uint8Array).setProtectedHeader({ alg: "HS256" }).sign(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 flag = flags[index]; if (!flag) { throw new Error(`flags: No flag at index ${index}`); } switch (valueIndex) { case 253 /* BOOLEAN_FALSE */: acc[flag.key] = false; break; case 254 /* BOOLEAN_TRUE */: acc[flag.key] = true; break; case 255 /* UNLISTED_VALUE */: acc[flag.key] = valuesArray[spilled++]; break; case 252 /* NULL */: acc[flag.key] = null; break; default: acc[flag.key] = flag.options?.[valueIndex]?.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((flag) => { const options = Array.isArray(flag.options) ? flag.options : []; const value = flagSet[flag.key]; if (!Object.prototype.hasOwnProperty.call(flagSet, flag.key) || value === void 0) { throw new Error(`flags: Missing value for flag "${flag.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); } export { memoizeOne, normalizeOptions, deserialize, serialize }; //# sourceMappingURL=chunk-O6VYPARG.js.map