UNPKG

@launchdarkly/js-server-sdk-common

Version:
67 lines 3.01 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); /** * Bucketing can be done by string or integer values. The need to be converted to a string * for the hashing process. * @param value The value to get a bucketable value for. * @returns The value as a string, or null if the value cannot be used for bucketing. */ function valueForBucketing(value) { if (typeof value === 'string') { return value; } if (Number.isInteger(value)) { return String(value); } return null; } class Bucketer { constructor(crypto) { this._crypto = crypto; } _sha1Hex(value) { const hash = this._crypto.createHash('sha1'); hash.update(value); if (!hash.digest) { // This represents an error in platform implementation. throw new Error('Platform must implement digest or asyncDigest'); } return hash.digest('hex'); } /** * Bucket the provided context using the provided parameters. * @param context The context to bucket. Can be a 'multi' kind context, but * the bucketing will be by a specific contained kind. * @param key A key to use in hashing. Typically the flag key or the segment key. * @param attr The attribute to use for bucketing. * @param salt A salt to use in hashing. * @param kindForRollout The kind to use for bucketing. * @param seed A seed to use in hashing. * * @returns A tuple where the first value is the bucket, and the second value indicates if there * was a context for the value specified by `kindForRollout`. If there was not a context for the * specified kind, then the `inExperiment` attribute should be `false`. */ bucket(context, key, attr, salt, kindForRollout = 'user', seed) { const value = context.valueForKind(attr, kindForRollout); const bucketableValue = valueForBucketing(value); // Bucketing cannot be done by the specified attribute value. if (bucketableValue === null) { // If we got a value, then we know there was a context, but if we didn't get a value, then // it could either be there wasn't an attribute, the attribute was undefined/null, or there // was not a context. So here check for the context. const hadContext = context.kinds.indexOf(kindForRollout) >= 0; return [0, hadContext]; } const prefix = seed ? Number(seed) : `${key}.${salt}`; const hashKey = `${prefix}.${bucketableValue}`; const hashVal = parseInt(this._sha1Hex(hashKey).substring(0, 15), 16); // This is how this has worked in previous implementations, but it is not // ideal. // The maximum safe integer representation in JS is 2^53 - 1. // eslint-disable-next-line @typescript-eslint/no-loss-of-precision return [hashVal / 0xfffffffffffffff, true]; } } exports.default = Bucketer; //# sourceMappingURL=Bucketer.js.map