flags
Version:
Flags SDK by Vercel - The feature flags toolkit for Next.js and SvelteKit
164 lines (156 loc) • 6.1 kB
JavaScript
Object.defineProperty(exports, "__esModule", {value: true}); 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; }// 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
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 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] = _optionalChain([flag, '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((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);
}
exports.memoizeOne = memoizeOne; exports.normalizeOptions = normalizeOptions; exports.deserialize = deserialize; exports.serialize = serialize;
//# sourceMappingURL=chunk-QLJ65HXQ.cjs.map
;