1- try {
2- // crypto not available on globalThis in Node.js v18
3- // eslint-disable-next-line @typescript-eslint/no-require-imports
4- globalThis . crypto ??= require ( "node:crypto" ) . webcrypto ;
5- } catch {
6- // ignore
7- }
1+ import { sha256 } from "js-sha256" ;
82
93/**
104 * Represents a filter class with a specific type property.
@@ -265,19 +259,18 @@ export function unflattenJSON(data: Record<string, any>): Record<string, any> {
265259 * @param {string } hashInput - The input string used to generate the hash.
266260 * @return {number } A number between 0 and 100000 derived from the hash of the input string.
267261 */
268- export async function hashInt ( hashInput : string ) : Promise < number > {
262+ export function hashInt ( hashInput : string ) : number {
269263 // 1. hash the key and the partial rollout attribute
270264 // 2. take 20 bits from the hash and divide by 2^20 - 1 to get a number between 0 and 1
271265 // 3. multiply by 100000 to get a number between 0 and 100000 and compare it to the threshold
272266 //
273267 // we only need 20 bits to get to 100000 because 2^20 is 1048576
274- const msgUint8 = new TextEncoder ( ) . encode ( hashInput ) ;
275-
276- // Hash the message
277- const hashBuffer = await crypto . subtle . digest ( "SHA-256" , msgUint8 ) ;
268+ const value =
269+ new DataView ( sha256 . create ( ) . update ( hashInput ) . arrayBuffer ( ) ) . getUint32 (
270+ 0 ,
271+ true ,
272+ ) & 0xfffff ;
278273
279- const view = new DataView ( hashBuffer ) ;
280- const value = view . getUint32 ( 0 , true ) & 0xfffff ;
281274 return Math . floor ( ( value / 0xfffff ) * 100000 ) ;
282275}
283276
@@ -351,11 +344,11 @@ export function evaluate(
351344 }
352345}
353346
354- async function evaluateRecursively (
347+ function evaluateRecursively (
355348 filter : RuleFilter ,
356349 context : Record < string , string > ,
357350 missingContextFieldsSet : Set < string > ,
358- ) : Promise < boolean > {
351+ ) : boolean {
359352 switch ( filter . type ) {
360353 case "constant" :
361354 return filter . value ;
@@ -376,38 +369,30 @@ async function evaluateRecursively(
376369 return false ;
377370 }
378371
379- const hashVal = await hashInt (
372+ const hashVal = hashInt (
380373 `${ filter . key } .${ context [ filter . partialRolloutAttribute ] } ` ,
381374 ) ;
382375
383376 return hashVal < filter . partialRolloutThreshold ;
384377 }
385- case "group" : {
386- const isAnd = filter . operator === "and" ;
387- let result = isAnd ;
388- for ( const current of filter . filters ) {
389- // short-circuit if we know the result already
390- // could be simplified to isAnd !== result, but this is more readable
391- if ( ( isAnd && ! result ) || ( ! isAnd && result ) ) {
392- return result ;
378+ case "group" :
379+ return filter . filters . reduce ( ( acc , current ) => {
380+ if ( filter . operator === "and" ) {
381+ return (
382+ acc &&
383+ evaluateRecursively ( current , context , missingContextFieldsSet )
384+ ) ;
393385 }
394-
395- const newRes = await evaluateRecursively (
396- current ,
397- context ,
398- missingContextFieldsSet ,
386+ return (
387+ acc || evaluateRecursively ( current , context , missingContextFieldsSet )
399388 ) ;
400-
401- result = isAnd ? result && newRes : result || newRes ;
402- }
403- return result ;
404- }
389+ } , filter . operator === "and" ) ;
405390 case "negation" :
406- return ! ( await evaluateRecursively (
391+ return ! evaluateRecursively (
407392 filter . filter ,
408393 context ,
409394 missingContextFieldsSet ,
410- ) ) ;
395+ ) ;
411396 default :
412397 return false ;
413398 }
@@ -449,18 +434,16 @@ export interface EvaluationResult<T extends RuleValue> {
449434 missingContextFields ?: string [ ] ;
450435}
451436
452- export async function evaluateFeatureRules < T extends RuleValue > ( {
437+ export function evaluateFeatureRules < T extends RuleValue > ( {
453438 context,
454439 featureKey,
455440 rules,
456- } : EvaluationParams < T > ) : Promise < EvaluationResult < T > > {
441+ } : EvaluationParams < T > ) : EvaluationResult < T > {
457442 const flatContext = flattenJSON ( context ) ;
458443 const missingContextFieldsSet = new Set < string > ( ) ;
459444
460- const ruleEvaluationResults = await Promise . all (
461- rules . map ( ( rule ) =>
462- evaluateRecursively ( rule . filter , flatContext , missingContextFieldsSet ) ,
463- ) ,
445+ const ruleEvaluationResults = rules . map ( ( rule ) =>
446+ evaluateRecursively ( rule . filter , flatContext , missingContextFieldsSet ) ,
464447 ) ;
465448
466449 const missingContextFields = Array . from ( missingContextFieldsSet ) ;
0 commit comments